-
Hi, I'm trying to implement a draggable container that also has a button, which can't be dragged when clicked or keyboard inputed. I found this thread here: #377 where HelKyle suggests implementing a custom Sensor if you want to prevent a button from being dragged when clicked/keyboard inputed. I implemented a custom Mouse Sensor successfully, but I'm having difficulty implementing a custom Keyboard Sensor. With my current code implementation, pressing Enter while a button is focused still causes it to drag. I would like it to not cause a drag. Here is my code: import type { KeyboardEvent } from 'react';
import {
KeyboardSensor as LibKeyboardSensor,
Activators,
KeyboardSensorOptions,
KeyboardCodes,
KeyboardCode,
} from '@dnd-kit/core';
const defaultKeyboardCodes: KeyboardCodes = {
start: [KeyboardCode.Space, KeyboardCode.Enter],
cancel: [KeyboardCode.Esc],
end: [KeyboardCode.Space, KeyboardCode.Enter],
};
export class KeyboardSensor extends LibKeyboardSensor {
static activators: Activators<KeyboardSensorOptions> = [
{
eventName: 'onKeyDown' as const,
handler: (
event: React.KeyboardEvent,
{ keyboardCodes = defaultKeyboardCodes, onActivation },
{ active }
) => {
const { code } = event.nativeEvent;
if (keyboardCodes.start.includes(code)) {
const activator = active.activatorNode.current;
if (activator && event.target !== activator) {
return false;
}
event.preventDefault();
onActivation?.({ event: event.nativeEvent });
return shouldHandleEvent(active.node.current);
}
return false;
},
},
];
}
// Prevent dragging if the user attempts to drag an element with the data attribute data-no-dnd='true'
function shouldHandleEvent(element: HTMLElement | null) {
let cur = element;
while (cur) {
if (cur.dataset && cur.dataset.noDnd) {
return false;
}
cur = cur.parentElement;
}
return true;
} // Container.tsx
return (
<div // this node is sent to active.node
ref={ref}
tabIndex={0}
{...handleProps} // handleProps={{...attributes, ...listeners }}
>
<div>
<p>{label}</p>
<button
onClick={() => console.log('test')}
data-no-dnd='true'
>
Delete
</button>
</div>
<ul>{children}</ul>
</div>
) My code is based on the HelKyle's suggestion and the Keyboard Sensor code from the dnd-kit repo EDIT: I've debugged the code using VSCode and it appears that when a button is focused and Enter is pressed, the node being sent to the sensor is the Does anyone have any ideas why the What's a possible work-around solution? |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment
-
So far the only solution I've come up with is to do this: // Container.tsx
return (
<div // this node is sent to active.node
ref={ref}
tabIndex={0}
{...handleProps} // handleProps={{...attributes, ...listeners }}
>
<div>
<p>{label}</p>
<button
onClick={() => console.log('test')}
onKeyDown={(e) => {
if (e.key === 'Enter' || e.key === 'Space') {
console.log('test)
e.stopPropagation(); // prevent the event from bubbling (and therefore triggering listeners on parent div)
}
}}
data-no-dnd='true'
>
Delete
</button>
</div>
<ul>{children}</ul>
</div>
) Is there a better solution? |
Beta Was this translation helpful? Give feedback.
So far the only solution I've come up with is to do this:
Is the…