use-gesture icon indicating copy to clipboard operation
use-gesture copied to clipboard

Exclude children from the gesture

Open dbismut opened this issue 3 years ago • 8 comments

cc @odusseys

Discussed in https://github.com/pmndrs/use-gesture/discussions/428

Originally posted by odusseys January 19, 2022 Hi, Is there a way to exclude specific children of an element from triggering the gesture / getting events as they should ? For example, is there a way to correctly select text within a draggable elements ?

dbismut avatar Jan 25 '22 10:01 dbismut

Responding to your last comment: it doesn't feel very idiomatic (in a react sense, but I understand the lib is meant to work for all JS frameworks including vanilla) to

  • be passing around / working with dom objects unless it's a last resort / building a library, so as a consumer of a react lib it is always much more appreciable to work with components, hooks and refs
  • send objects bottom-up in the props hierarchy. This to me is the biggest issue ; if the component you wish to exclude is nested deep, you will need to forward a ref multiple times down to that component in order for it to be used (or put it in a context ; but putting refs in a context is clunky). In particular refactoring becomes much more complex, and the props are muddled by an unneccessary ref which has little to do with the intrinsic purpose of the components.

For react, this use case seems to be most compatible with the Context API ; ie a provider at the useGesture level (useGesture and the child component would both subscribe to this context to communicate)

For the general vanilla JS purpose, I would rely on an element property (ex. use-gesture-ignore="true"), or something along those lines. This is less ideal for React as custom properties are not idiomatic, and don't work in a straightforward way if you want to wrap a fragment / list.

I find the react-dnd lib extremely well designed in this aspect, it allows to very easily and modularly communicate between drag / drop components without any prop sharing whatsoever.

odusseys avatar Jan 25 '22 13:01 odusseys

Can you point at where react-dnd is using the context api? Honestly it feels a bit convoluted to be using a <Provider /> for a lib such as use-gesture, but I'm happy to reconsider.

dbismut avatar Jan 25 '22 13:01 dbismut

the provider could be used optionally (only if you need a child to get access to the gesture / the gesture state) react-dnd has the provider here https://react-dnd.github.io/react-dnd/docs/api/dnd-provider but it's a bit different since all hooks / HOC subscribe to this provider, as a means to declare useDrag anywhere inside and useDrop anywhere else, and have both communicate without passing any props across.

odusseys avatar Jan 25 '22 14:01 odusseys

Would you be up for a PR? Note that the core package is platform agnostic (ie not React specific).

dbismut avatar Jan 29 '22 20:01 dbismut

I am under water with work but I'm putting a task in my todos to do this as soon as things clear out, would love to help

odusseys avatar Feb 04 '22 08:02 odusseys

I personnally use @vueuse/gesture based on this library...

I have the same problem : my users want to still select texts or have a "normal" behavior of the website.

As i didn't found anything in gesture options and as the "target" option juste do nothing (= event still triggered on children)

what i did is basically check if the dom element of the state.event is the one i want based on classList..contains(example) and element.id = the one i want..

Example :

const handler = (state: any): void => {

  // boolean : check if target element is a div.pannel or a div.page => other elements should not trigger swipe.
  let isCorrectDomTriggered =
      state.event.target.classList.contains('page') ||
      state.event.target.classList.contains('pannel') ||
      state.event.target.id == 'app_body';

  if(!isCorrectDomTriggered)
    return

  // other stuff = when correct dom triggered

}

// Composable usage // https://gesture.vueuse.org/use-drag.html#options useDrag(handler, { domTarget: draggy, filterTaps: true, axis: 'x' });

Maybe someone will have a more elegant way to achieve this... Have a good day :)

plalance avatar Aug 10 '22 14:08 plalance

cardRef.current.style.top = `${element.pageY + state.offset[1]}px`;
cardRef.current.style.left = `${element.pageX + state.offset[0]}px`;

I tried the same logic but the state offset still seems to be getting updated even though the draggable element didn't get dragged. So when I actually drag the element there is a sudden jerk.

devcer avatar Mar 23 '23 06:03 devcer

I personnally use @vueuse/gesture based on this library...

I have the same problem : my users want to still select texts or have a "normal" behavior of the website.

As i didn't found anything in gesture options and as the "target" option juste do nothing (= event still triggered on children)

what i did is basically check if the dom element of the state.event is the one i want based on classList..contains(example) and element.id = the one i want..

Example :

const handler = (state: any): void => {

  // boolean : check if target element is a div.pannel or a div.page => other elements should not trigger swipe.
  let isCorrectDomTriggered =
      state.event.target.classList.contains('page') ||
      state.event.target.classList.contains('pannel') ||
      state.event.target.id == 'app_body';

  if(!isCorrectDomTriggered)
    return

  // other stuff = when correct dom triggered

}

// Composable usage // https://gesture.vueuse.org/use-drag.html#options useDrag(handler, { domTarget: draggy, filterTaps: true, axis: 'x' });

Maybe someone will have a more elegant way to achieve this... Have a good day :)

this worked for me but idk if its ideal

clearly-outsane avatar Jul 17 '23 12:07 clearly-outsane