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

Custom keyboard bindings #40

Open
alexreardon opened this issue Aug 17, 2017 · 9 comments
Open

Custom keyboard bindings #40

alexreardon opened this issue Aug 17, 2017 · 9 comments

Comments

@alexreardon
Copy link
Collaborator

Add the ability to provide your own key bindings.

Still unsure if this is the right thing to do given that it is really easy to do the wrong thing by the browser - but we will investigate it!

@cliffmeyers
Copy link

Would you be receptive to a patch that allows the developer to customize whether the tabIndex: 0 is added to the Draggable items? The current impl has the unfortunate side effect of forcing a user to press TAB N times to bypass the list where N = number of items. In cases where the list is long and interacting with the list is optional, this is a bit of a step backward in usability. I would rather disable keyboard drag/drop so that the user can more easily navigate forward and backward through the form.

For List-based controls I've built in the past, I've modeled it after a radio button group where pressing tab will enter the list but the user uses the arrow keys to navigate between items.

Perhaps we could discuss a somewhat future-proofed API that allows for keyboard bindings to be either disabled or customized?

@alexreardon
Copy link
Collaborator Author

The tab index allows keyboard users to get to the draggable item. You are welcome to build your custom logic on top of this library. You can patch tabIndex in DragHandleProvided do disable tabbing at certain points. There are probably other mechanisms also. When we get to this issue we can take a deeper look at your suggestion :)

type DragHandleProvided = {|
  onMouseDown: (event: MouseEvent) => void,
  onKeyDown: (event: KeyboardEvent) => void,
  onClick: (event: MouseEvent) => void,
  tabIndex: number,
  'aria-grabbed': boolean,
  draggable: boolean,
  onDragStart: () => void,
  onDrop: () => void
|}

@cliffmeyers
Copy link

@alexreardon thanks for your reply and the nudge. After studying the code a bit more, I realized that provided.dragHandleProps contains the tabIndex: 0 value which I can omit before passing down to the child component. I'm very happy not to have fork/patch the internals of the library itself.

With respect to the ancillary discussion about tabs vs. arrow keys, I would just offer this example from the W3C Aria specs:

https://www.w3.org/TR/wai-aria-practices/examples/listbox/listbox.html

They recommend reserving the tab key for moving between controls, and using arrow keys to move between elements within the control. I acknowledge they aren't trying to address the more complex dnd interaction in this spec, although considering their guidelines might still be useful. If you're willing to revisit that, please ping me as I'd be interesting in assisting with the PR if possible.

@jaredcrowe
Copy link
Contributor

Thanks for this @cliffmeyers, we'll definitely take this into consideration when we revisit this feature :)

@albertpeiro
Copy link

albertpeiro commented Dec 11, 2018

I'd love to see this implemented. Some reasoning after fiddling with the current implementation.

Would be nice to have keys configurable for:

  • Start a drag
  • Move the drag
  • Stop the drag
  • Cancel the drag

One of the main reasons for this is that I have a use case where a Draggable contains text-editable content. While the user is writing, without having to focus out of their writing interaction, I'd like to give the ability to start a drag and finish it, while they're text editing (text never loses focus and cursor). Right now, while the focus is on the inner text element (an input or a contenteditable that lives inside a Draggable) I need to either disable dragging of the Draggable with isDisabled, or capture the event and stop its propagation, given that the spacebar is an obviously useful character to type while writing text :) If I don't do any of these options the spacebar key is captured as a drag start and the user can't type it in. It would be really nice to be able to reconfigure the start drag with CTRL key.

Bonus 1 HOLD/RELEASE DRAG KEY TO KEEP DRAG
It'd be even cooler if you could configure as well if the start key needs to be held for the drag to be kept. And when you release it, it would stop. Make this configurable, or provide access to the low level so it can be programmed.

Bonus 2 MULTIPLE KEYS FOR EACH EVENT
Let's make it a list of keys. Multiple keys to start, finish and cancel.

Bonus 3 FIX USABILITY OF CURRENT DRAGGING STATE
I've noticed when you are in the middle of a drag and the user doesn't finish it but instead clicks elsewhere with the mouse, the drag is cancelled. In my app I don't need this behaviour, a reorder of many items would be painful to repeat simply because I clicked the wrong key, user normally will do a lot of work while dragging - especially with the more cumbersome keyboard.
Making this configurable is also a good idea, it's just event key preferences at the end of the day.

More about previous point:
If dragging is not a big feature of your app and you get caught dragging an item by mistake (the spacebar is a big key, it's easy to press by mistake), you can't get out of it simply with the same device that you started it with - the keyboard - neither ESC, TAB or pressing any other key stops the drag, this leads to a bad user experience. It leaves my app in a state where the user is puzzled having the focus on an item which they can't tab out of (because the library preventsDefault TAB while dragging...). So my only option is to promote and educate user about a feature that not everyone is interested in, or that I simply want only my most advanced users to understand / discover.

Actual behavior

Spacebar is by default the only way to start a keyboard drag, finish it and cancel it. Not configurable.

Thank you!

@tobiasandersen
Copy link
Contributor

I'm also really interested in reconfiguring the shortcut for lifting a draggable. We're building a video editing system, where space is already used for playing/pausing videos.

If you're open to having it configurable I'd be happy to make a PR.

@pimterry
Copy link

I'd appreciate this. I've just migrated from react-sortable-hoc, which does provide this. I would want to keep all the existing key shortcuts, but also add:

  • Enter (in addition to space) to start & stop dragging
  • j/k for up/down to move elements in the list (vim keys)

@danieldelcore
Copy link
Collaborator

Hey folks 👋

Since v13.1.0, applying custom key bindings is possible by creating a custom keyboard sensor.

Alex has created a wonderful guide here, please have a read 🙏.

Note that when you provide custom sensors to the DragDropContext you'll need to set enableDefaultSensors to false and manually pass in the default and custom sensors that you require 😄.

@paulchan14
Copy link

paulchan14 commented Jul 21, 2021

Thanks for the wonderful work y'all are doing. I have been reading through Alex's guide to see if I need to create a custom keyboard sensor, but I'm also trying to figure if there is another way to accomplish what I'm trying to do.

For the app I'm working on, I want to have some additional styles around the Draggable IF the drag is initiated by keypress for visual keyboard users (a small hint as to how the keyboard controls work, and arrow icons to suggest the user type the arrow up/down key to reorder). For screen-reader / keyboard users, I the needed directions are provided, but for visual keyboard users, there isn't currently a hint being displayed.

If the user is starting the drag via mouse click, I don't want to show this information. I'm looking for a way to determine the type of event (click vs keypress) so I can set the keyboard user styles if needed. Does this sound like a case where I would want to build my own sensor, or is there a way to capture the event type already?

If I do need to build my own sensor, is there a way to just ...spread in the existing sensor to tap into the event and capture the type, or am I out of luck?

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

No branches or pull requests

8 participants