Thanks to visit codestin.com
Credit goes to github.com

Skip to content

cdkDrag: with cdkDropList -> wrong Cursor when dragging #20246

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
Flo0806 opened this issue Jul 24, 2020 · 14 comments
Open

cdkDrag: with cdkDropList -> wrong Cursor when dragging #20246

Flo0806 opened this issue Jul 24, 2020 · 14 comments
Labels
area: cdk/drag-drop P3 An issue that is relevant to core functions, but does not impede progress. Important, but not urgent

Comments

@Flo0806
Copy link

Flo0806 commented Jul 24, 2020

Hi all,
issue_1
issue_2

Here is a little sample.
https://stackblitz.com/angular/yvmnrxexppp?file=app%2Fcdk-drag-drop-connected-sorting-example.html

Since version 7.2.x (I noticed) there is the problem that the cursor (set in CSS) adapts to the underlying object during the dragging process.

If the cursor is changed to "move" and I use e.g. move an "input" (drag) then the cursor changes from move to input.

This phenomenon only occurs with cdkDropList, not when the cdkDrag property is only set for a single object.

The CSS:

.example-box {
  padding: 20px 10px;
  border-bottom: solid 1px #ccc;
  color: rgba(0, 0, 0, 0.87);

  display: flex;
  flex-direction: row;
  align-items: center;
  justify-content: space-between;
  box-sizing: border-box;
  cursor: move;
  background: white;
  font-size: 14px;
}

The HTML:

   <div class="col-md-4">

      <div class="drag-container">
        <div class="section-heading">Still Doing</div>
        <div cdkDropList #pendingList="cdkDropList" [cdkDropListData]="todo"
          [cdkDropListConnectedTo]="[doneList,reviewList]" class="item-list" (cdkDropListDropped)="drop($event)">
          <div class="item-box" *ngFor="let item of todo" cdkDrag>{{item}}</div>
        </div>
      </div>
    </div>
@Splaktar Splaktar changed the title cdkDrag with cdkDropList -> wrong Cursor when drag cdkDrag: with cdkDropList -> wrong Cursor when dragging Aug 7, 2020
@Splaktar
Copy link
Contributor

Splaktar commented Aug 7, 2020

Is this reporting an issue with the example or it's CSS? or is this reporting an issue with the cdkDrag directive?

@Flo0806
Copy link
Author

Flo0806 commented Aug 8, 2020

I think its a issue with the cdkDrag directive. Before version 7.2.x it works fine

@Splaktar
Copy link
Contributor

Splaktar commented Aug 8, 2020

Please submit Angular Material and CDK questions here and issues here. This repo is for docs infrastructure only.

I have transferred this issue to the correct repository for you.

@Splaktar Splaktar transferred this issue from angular/material.angular.io Aug 8, 2020
@Splaktar Splaktar added area: cdk/drag-drop needs triage This issue needs to be triaged by the team labels Aug 8, 2020
@crisbeto
Copy link
Member

crisbeto commented Aug 9, 2020

The way we detect whether the user's pointer is over a drop list is through document.elementFromPoint, but the problem is that the dragged element will always be under the user's pointer. We've worked around it by setting pointer-events: none on the element while it's dragging which is why your cursor goes away.

It could be worked around by using document.elementsFromPoint and skipping the dragged item's preview element while going through the array of results, but we'd have to account for some browser differences and do some testing to ensure that performance doesn't degrade since the latter method is doing a bit more work.

@crisbeto crisbeto added P3 An issue that is relevant to core functions, but does not impede progress. Important, but not urgent and removed needs triage This issue needs to be triaged by the team labels Aug 9, 2020
@marijanlekic
Copy link

marijanlekic commented Dec 14, 2020

While implementing custom drag and drop directive (before Angular CDK) i was able to resolve this by hiding/showing dragImage i in a single atomic cycle while mouse moves

  dragging(ev: MouseEvent) {
    // Get element behind drag image at following mouse coordinates
    dragImage.style.visibility = "hidden";
    const curHoveredElement = document.elementFromPoint(ev.clientX, ev.clientY);
    dragImage.style.visibility = "visible";
  }

Did not experience flickering/drawing problems so far (although it was tested on not big drag images/preview objects)

@newmanw
Copy link

newmanw commented Jan 16, 2021

@marijanlekic can you provide a complete example with html? EG what drag event are you listening to? What is the point of accessing the curHoveredElement?

Also @crisbeto you mention in a couple places, #15090 (comment), that this can be worked around with current APIs. An example (stackblitz would be awesome) of working around this with current APIs would go a long way.

@crisbeto
Copy link
Member

crisbeto commented Jan 17, 2021

@newmanw what I was referring to in that comment is that you can add a class to the body when dragging starts using the cdkDragStarted class and then remove it when it stops on cdkDragEnded. Then you can apply the cursor style to that global class. E.g.

body.is-dragging {
  cursor: move;
}

@marijanlekic
Copy link

@newmanw

I wrote my own drag and drop functionality so i dont use CDK drag events.
In the case that i wrote in my prev comment dragging is being called on event that is similar to dragMove (between drag start and drag end).
The point of curHoveredElement in my case is to trigger dragEnter, dragLeave, dragOver (or such custom events) on the element that is below dragging preview element.

By using "pointer-events:none" we are not not able to control cursor in the way that we want to (wrong cursor/getting cursor from the element that is below dragging preview element), and also, when we use pointerEvents: none, the element that is below dragging preview element, will get events such as "mouseenter", "mouseleave", "mousemove", etc.

So basically i resolved this by not setting "pointer-events:none". Instead, i set cursor to anything that i want to and i get element that is below dragging image by hiding preview element and showing it.

    previewImage.style.visibility = "hidden";
    // Get element below preview element
    const elementBelow = document.elementFromPoint(ev.clientX, ev.clientY);
    previewImage.style.visibility = "visible";
    // Trigger events on elementBelow (dragEnter, dragOver, dragLeave...)

For my needs, i did not face any rendering/other problems...

@ohryk-intellias
Copy link

I have fixed it with this custom directive. Just use it for the same element where the cdkDrag directive is hosted:

@Directive({
  selector: '[appDragCursor]',
})
export class DragCursorDirective implements OnInit, OnDestroy {
  private unsubscribe$: Subject<void> = new Subject();

  constructor(@Inject(WINDOW) private window: Window, private cdkDrag: CdkDrag) {}

  public ngOnInit(): void {
    this.cdkDrag.started.pipe(takeUntil(this.unsubscribe$)).subscribe(() => {
      this.window.document.body.style.cursor = 'move';
    });

    this.cdkDrag.ended.pipe(takeUntil(this.unsubscribe$)).subscribe(() => {
      this.window.document.body.style.cursor = 'auto';
    });
  }

  public ngOnDestroy(): void {
    this.unsubscribe$.next();
    this.unsubscribe$.complete();
  }
}

@joelkesler
Copy link
Contributor

joelkesler commented Sep 28, 2021

For myself, I added the following style override to re-enable the custom cursor while dragging.

.draggable-element .drag-handle{
  cursor: grab;
}

.draggable-element.cdk-drag-preview .drag-handle{
  pointer-events: auto;
  cursor: grabbing;
}

Live Example: https://stackblitz.com/edit/angular-g2yu5w?file=src/app/cdk-drag-drop-custom-placeholder-example.css

@Sebastian-G
Copy link

@joelkesler how are you able to drop the element now?

pointer-events: auto;

But im not able to drop my element if the style is not pointer-events: none;.
Propably the CDK "Overlay" blocks the event propagation.

@jgomesmv
Copy link

jgomesmv commented Mar 24, 2022

I'm facing the same issue. The solution presented by @joelkesler works but there's some latency to enable to drop in the target drop list. After trying @ohryk-intellias sugestion it works like a charm! Thanks for that!
But IMHO I think this should be supported by default :)

@Davidihl
Copy link

Davidihl commented Oct 2, 2024

I have fixed it with this custom directive. Just use it for the same element where the cdkDrag directive is hosted:

@Directive({
  selector: '[appDragCursor]',
})
export class DragCursorDirective implements OnInit, OnDestroy {
  private unsubscribe$: Subject<void> = new Subject();

  constructor(@Inject(WINDOW) private window: Window, private cdkDrag: CdkDrag) {}

  public ngOnInit(): void {
    this.cdkDrag.started.pipe(takeUntil(this.unsubscribe$)).subscribe(() => {
      this.window.document.body.style.cursor = 'move';
    });

    this.cdkDrag.ended.pipe(takeUntil(this.unsubscribe$)).subscribe(() => {
      this.window.document.body.style.cursor = 'auto';
    });
  }

  public ngOnDestroy(): void {
    this.unsubscribe$.next();
    this.unsubscribe$.complete();
  }
}

For anyone that cannot apply this solution directly because of error TS2552: Cannot find name 'WINDOW'. Did you mean 'Window'? I fixed it in my case by applying https://stackoverflow.com/a/58936690/19957183

@ohryk-intellias
Copy link

I have fixed it with this custom directive. Just use it for the same element where the cdkDrag directive is hosted:

@Directive({
  selector: '[appDragCursor]',
})
export class DragCursorDirective implements OnInit, OnDestroy {
  private unsubscribe$: Subject<void> = new Subject();

  constructor(@Inject(WINDOW) private window: Window, private cdkDrag: CdkDrag) {}

  public ngOnInit(): void {
    this.cdkDrag.started.pipe(takeUntil(this.unsubscribe$)).subscribe(() => {
      this.window.document.body.style.cursor = 'move';
    });

    this.cdkDrag.ended.pipe(takeUntil(this.unsubscribe$)).subscribe(() => {
      this.window.document.body.style.cursor = 'auto';
    });
  }

  public ngOnDestroy(): void {
    this.unsubscribe$.next();
    this.unsubscribe$.complete();
  }
}

For anyone that cannot apply this solution directly because of error TS2552: Cannot find name 'WINDOW'. Did you mean 'Window'? I fixed it in my case by applying https://stackoverflow.com/a/58936690/19957183

Hi, the WINDOW is the custom injection token. Something like this:

import { DOCUMENT } from '@angular/common';
import { inject, InjectionToken } from '@angular/core';

const WINDOW = new InjectionToken<Window>('Window', {
  factory: () => {
    const document = inject(DOCUMENT);
    return document.defaultView;
  }
});

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area: cdk/drag-drop P3 An issue that is relevant to core functions, but does not impede progress. Important, but not urgent
Projects
None yet
Development

No branches or pull requests

10 participants