Skip to content
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

Customize the cursor during drag #325

Closed
dshorowitz opened this issue Nov 5, 2015 · 37 comments
Closed

Customize the cursor during drag #325

dshorowitz opened this issue Nov 5, 2015 · 37 comments
Labels

Comments

@dshorowitz
Copy link

Fantastic library! Thanks for all your hard work in putting it together. I have a quick question: is it possible to customize the cursor such that during a drag operation, the cursor is always set to the same thing (specifically I want to set it to the dragging icon)?

In other words, regardless of whether you are over a drop zone or not, the cursor remains the same. This is similar to how Trello has the cursor while dragging a note, for example.

Thanks in advance!

@gaearon
Copy link
Member

gaearon commented Nov 6, 2015

You can probably set a cursor style on the whole body (or application root div) while isDragging is true. You can use DragLayer to listen to isDragging changes.

@dshorowitz
Copy link
Author

Thanks, I wish that worked! Unfortunately, the cursor style is overridden by something (I'm on Chrome), even when setting the cursor style to document.body and marking it as "!important". Any other ideas?

@gaearon
Copy link
Member

gaearon commented Nov 6, 2015

Maybe it's just how HTML5 drag and drop API works. In this case the only solution is to create a custom backend that uses mouse events instead of HTML5 drag and drop events.

@dshorowitz
Copy link
Author

I was hoping you wouldn't say that :) Thanks!

@gazpachu
Copy link

I've just found out about this issue. Unfortunately, it seems not posible to change the icon while dragging :(

@alexeymolchan
Copy link

Anyone managed to resolve this?

@augbog
Copy link

augbog commented Aug 7, 2017

Also went and looked into this. Yes it seems that this is sadly a browser limitation in that the API for DataTransfer.effectAllowed only supports a handful of options:

https://developer.mozilla.org/en-US/docs/Web/API/DataTransfer/effectAllowed

So the only way to get around this is, as @gaearon mentioned, which is to use a different custom backend :/ This might be something worth including in the HTML5 backend documentation as that might not be something intuitive to people.

@larspa
Copy link

larspa commented Feb 7, 2018

Well a very wrong but working solution would be adding a dragging class to the body so that body and every DOM Object inside the body will get cursor: grabbing !important. Nothing will than override it.
This is what worked for me:

:global {
    body.dragging,
    body.dragging * {
        cursor: url('./assets/cursors/grabbing.cur'), move !important;
    }
}

@simonewebdesign
Copy link

simonewebdesign commented May 10, 2019

Another approach using JS could be something like this:

window.addEventListener('drag', () => {
  document.body.style.cursor = 'grabbing';
}, true)

It doesn't seem to be working consistently though...

@dwilt
Copy link

dwilt commented Aug 19, 2019

I can't get any of the suggested solutions here to work. Anything update on this?

@nathanbrud
Copy link

Switching to TouchBackend & using css to set cursor worked for me.

<DndProvider backend={TouchBackend} options={{ enableTouchEvents: false, enableMouseEvents: true }}>
  <div className=`my-app ${isDragging ? 'dragging' : ''}`>
      .... draglayers here
  </div>
</DndProvider>
.dragging {
  cursor: grabbing
}

@niron1
Copy link

niron1 commented Nov 14, 2019

Switching to TouchBackend & using css to set cursor worked for me.

<DndProvider backend={TouchBackend} options={{ enableTouchEvents: false, enableMouseEvents: true }}>
  <div className=`my-app ${isDragging ? 'dragging' : ''}`>
      .... draglayers here
  </div>
</DndProvider>
.dragging {
  cursor: grabbing
}

not working for me

@jinxin0112
Copy link

+1

@shiranZe
Copy link

shiranZe commented Jun 4, 2020

someone managed to resolve this?

@kurochkinSergei
Copy link

Switching to TouchBackend & using css to set cursor worked for me.

<DndProvider backend={TouchBackend} options={{ enableTouchEvents: false, enableMouseEvents: true }}>
  <div className=`my-app ${isDragging ? 'dragging' : ''}`>
      .... draglayers here
  </div>
</DndProvider>
.dragging {
  cursor: grabbing
}

switching to TouchBackend worked for me

DnD provider:

import { DndProvider } from 'react-dnd';
import { TouchBackend } from 'react-dnd-touch-backend';

...
<DndProvider backend={TouchBackend} options={{enableMouseEvents: true}}>
...
</DndProvider>

Drag handlers implementation:

    import { useDrag, useDrop } from 'react-dnd';
    ....

    const [...] = useDrag({
      ...
      begin: () => {
        document.body.classList.add('dragging');
        ...
     },
      end: () => {
        document.body.classList.remove('dragging');
        ...
      },
    });

css file

   body.dragging: {
      cursor: crosshair !important;
   },

@FullstackJack
Copy link

FullstackJack commented Jan 28, 2021

Amazing that the TouchBackend actually worked using the solution outlined by @kurochkinSergei. Hopefully that doesn't introduce any regressions.

@MoSheikh
Copy link

Anyone have any success with this at all? Setting the document.body class does not work using HTML5 backend.

@FermiDirak
Copy link

FermiDirak commented Oct 6, 2021

Found a working solution. The trick is to update the draggable element's cursor style when the element isDragging.

function MyDraggableComponent () {
  const [{ isDragging }, drag] = useDrag(() => ({
    ...
    collect: (monitor) => ({
      isDragging: monitor.isDragging(),
    }),
  }));
  
  return (
    <div ref={drag} style={{cursor: isDragging ? 'grabbing' : 'grab' }}>
      Draggable Element
    </div>
  );
}

See this minimum reproduction of the problem and the solution below:
https://codesandbox.io/s/tender-ives-bie31?file=/src/Knight.tsx:649-700

@hmeyerx
Copy link

hmeyerx commented Oct 18, 2021

Found a working solution. The trick is to update the draggable element's cursor style when the element isDragging.

function MyDraggableComponent () {
  const [{ isDragging }, drag] = useDrag(() => ({
    ...
    collect: (monitor) => ({
      isDragging: monitor.isDragging(),
    }),
  }));
  
  return (
    <div ref={drag} style={{cursor: isDragging ? 'grabbing' : 'grab' }}>
      Draggable Element
    </div>
  );
}

See this minimum reproduction of the problem and the solution below: https://codesandbox.io/s/tender-ives-bie31?file=/src/Knight.tsx:649-700

That codesandbox doesn't seem to work in Chrome 93.0.4577.82 (cursor not set to grabbing).

@nahumzs
Copy link

nahumzs commented Oct 21, 2021

@FermiDirak I just the tested the codesandbox on chorme 94.0.4606.81` and seems ok tome @hmeyerx

@hmeyerx
Copy link

hmeyerx commented Oct 22, 2021

2021-10-21 18 10 35

@FermiDirak I just the tested the codesandbox on chorme 94.0.4606.81` and seems ok tome @hmeyerx

is this different from what you're seeing?

@cyberscorpion
Copy link

Any working solution for this in Chrome and Safari together?

@HSunboy
Copy link

HSunboy commented Feb 9, 2022

set dropEffect in useDrag

useDrag({
    ...
    options: {
      dropEffect: 'copy'
    }
  });

@oscarestigz
Copy link

Still nothing to fix this?

@HTMLhead
Copy link

What about using state on wrapper component?

const style = (dragging: boolean) => ({
  cursor: dragging ? "grabbing" : "unset",
  width: 400
});

export const Container: FC = () => {
  {
    const [dragging, setDragging] = useState(false);
    ....

    return (
      <>
        <div style={style(dragging)}>
          {cards.map((card, i) => renderCard(card, i, dragging, setDragging))}
        </div>
      </>
    );
  }
};

Similar to FermiDirak's solution. The difference is that the useDrag method is used in the child component.
Thanks for your solution @FermiDirak. It saved my day.
minimum reproduction

@shahbazyanartur
Copy link

shahbazyanartur commented Apr 21, 2022

On time drag cursor sometimes changes to pointer, sometimes changes to grabbing, but I want to each time drag it changed to grabbing, have you any solution ?

return (
        <div ref={preview}
             className='field-name field-name-border'
             style={{width: `${columnWidths[index + 1]}px`}}
             onMouseOver={onMouseMove}
             onMouseOut={onMouseMove}
        >
            <div
                className={`card field-card rounded-0 m-0 h-100 border-1 d-flex justify-content-center align-items-center 
                              noselect cursor-grab ${isOver && "border-dashed"}`}
                onDoubleClick={() => handlePreferredValuePopUpOpen(index)}
                ref={drop}
            >
                <div ref={drag}
                     className={`${isDragging ? 'cursor-grabbing drag-icon-hide' : 'cursor-move'} ${isMouseOver ? 'drag-icon-show' : 'drag-icon-hide'}`}
                     data-handler-id={handlerId}
                >
                    {dragIcon}
                </div>
                <span style={{opacity}}>{headerName.name}</span>
            </div>
        </div>
    )

>

@daliusd
Copy link

daliusd commented May 24, 2022

It looks like many people still try to use HTML5 backend and set cursor. That's not possible and here is Chrome bug for that (BTW Firefox works better with that): https://bugs.chromium.org/p/chromium/issues/detail?id=1232555 - feel free to star it at least - maybe that will help with visibility of this issue from Chrome side. Regular users do not care about this problem, it affects only grabbing cursor obsessed developers (or UX designers).

In the end using Touch backend is the way to go - you still need to understand what you are doing but at least setting cursor works and is not blocked by Chrome somehow. IMHO touch backend is not worse than HTML5 backend and actually I found it being more responsive with custom drag layer.

@hein-j
Copy link

hein-j commented Jun 4, 2022

For those using a custom drag layer, I got mine working with TouchBackend. It didn't compromise any other functionality, as @daliusd suggests.

Provider:

 <DndProvider backend={TouchBackend} options={{ enableMouseEvents: true }}>...

CustomDragLayer:

...
  return (
    <div className='layer'>
      <div
        className='preview' >
        {preview}
      </div>
    </div>
  );

CustomDragLayer scss:

.layer {
    position: fixed;
    pointer-events: none;
    z-index: 100;
    left: 0;
    top: 0;
    width: 100%;
    height: 100%;
}

body {
// you can conditionally set this, only when dragging
* {
   cursor: grabbing !important; 
   user-select: none // without this I get the text cursor in Safari 
  }
}

@blactrojan
Copy link

blactrojan commented Jun 17, 2022

the above solution by @hein-j worked for me. the css wasn't required, just added this

 useEffect(() => {
    if (isDragging) document.body.style.cursor = 'grab !important';
    else document.body.style.cursor = 'normal';
  }, [isDragging]);

@daliusd
Copy link

daliusd commented Jun 17, 2022

the above solution by @hein-j worked for me. the css wasn't required, just added this

 useEffect(() => {
    if (isDragging) document.body.style.cursor = 'grab !important';
    else document.body.style.cursor = 'normal';
  }, [isDragging]);

Test on Safari and Firefox. In the end you might need css anyway.

@TheMikeyRoss
Copy link

any update on this one?

@daliusd
Copy link

daliusd commented Nov 15, 2023

any update on this one?

Read comments. It is Chrome bug.

@TheMikeyRoss
Copy link

TheMikeyRoss commented Nov 15, 2023

Read comments. It is Chrome bug.

Oh ok, I'm so sorry to bother you

@daliusd
Copy link

daliusd commented Nov 15, 2023

Read comments. It is Chrome bug.

Oh ok, I'm so sorry to bother you

Sorry for the tone 😄 I just tried to answer quickly 😉

@TheMikeyRoss
Copy link

Sorry for the tone 😄 I just tried to answer quickly 😉

all good brother 😁👍

@longnt80
Copy link

longnt80 commented Jan 4, 2024

any update on this one?

Read comments. It is Chrome bug.

Looks like same problem with Safari too.

@Cristian-T250
Copy link

Custom cursor designs are the digital signature of user experience, offering a personal touch that resonates with visitors. For any forward-thinking marketing agency in Las Vegas, leveraging these bespoke elements is essential for crafting immersive and memorable interactions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests