Angular Material CDK Drag and Drop: Style preview element conditionally

Viewed 585

I have two lists with the cdkDropList directive from Angular Material CDK. Let's call them list 1 and list 2. The list items contain images. The lists are not next to each other, so there are some other DOM elements between (I call it "empty space"). When dragging an item from one list to the other I see a preview of the dragged item (the image).

<!-- list 1 -->
<ul cdkDropList>
 <li *ngFor="..." cdkDrag>
  <img src="..." />
 </li>
</ul>

<!-- some other DOM elements -->

<!-- list 2 -->
<ul cdkDropList>
 <li *ngFor="..." cdkDrag>
  <img src="..." />
 </li>
</ul>

So far, so good.

What I want to achieve is to style the image conditionally (e.g. by placing a red border around it) but only if the dragged element is not either over list 1 or list 2. I got it to work halfway by using the *cdkDragPreview directive and setting a class conditionally by updating a field previewIsDraggedOutsideContainer in the *.ts file

<!-- component.html -->
<ul cdkDropList>
 <li *ngFor="..." cdkDrag
    (cdkDragMoved)="onMoved($event)"
 >
  <div *cdkDragPreview>
   <div [class.drag-element-preview-warning]="previewIsDraggedOutsideContainer">
    <img src="..." />
   </div>
  </div>
 </li>
</ul>

The field previewIsDraggedOutsideContainer is updated by comparing the cursor's position with every move with the bounding box of the DOM element the item is dragged from.

// component.ts
  public onMoved($event: CdkDragMove<any>): void {
    const boundingClientRect = $event.source.dropContainer.element.nativeElement.getBoundingClientRect();
    const pointerPosition = $event.pointerPosition;
    const isInEmptySpace =
      pointerPosition.x < boundingClientRect.x ||
      pointerPosition.x > boundingClientRect.x + boundingClientRect.width ||
      pointerPosition.y < boundingClientRect.y ||
      pointerPosition.y > boundingClientRect.y + boundingClientRect.height;
    this.previewIsDraggedOutsideContainer = isInEmptySpace;
  }

This works halfway, because the class is correctly added when moving the item outside the original list (e.g. list 1). However, it should be removed again when the item is dragged over another CDK-List (e.g. list 2). I already tried out working with cdkDragExited on the list item or cdkDropListExited on the list and setting previewIsDraggedOutsideContainer = false there. However, these event emitter only emit when the item is dragged over another DropList and they don't fire again when the dragged item is dragged away again to the "empty space". So I don't see a way I can calculate whether the dragged item is over any CDK-Drop-list at any given point of the drag-move. Also I'm not sure if listening to the cdkDragMoved event is a good idea because it will probably hurt performance...

Is there a way to achieve what I want to do? And if yes, what's the most elegant way?

0 Answers
Related