-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Preventing long press on touch devices from selecting text of pressable elements #2457
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
Changes from all commits
ba14e65
9a19db9
202ade1
acfe3ad
f8bc103
243d814
e73fb52
3cfd01d
15dbd3b
35eb3c5
008c22f
64058f0
8198200
1304695
ce28bd7
9270d28
1e8b22b
c62a7a1
a57144b
fd04343
b7e4564
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -35,7 +35,9 @@ export interface PressProps extends PressEvents { | |||||||||||||||
| * still pressed, onPressStart will be fired again. If set to `true`, the press is canceled | ||||||||||||||||
| * when the pointer leaves the target and onPressStart will not be fired if the pointer returns. | ||||||||||||||||
| */ | ||||||||||||||||
| shouldCancelOnPointerExit?: boolean | ||||||||||||||||
| shouldCancelOnPointerExit?: boolean, | ||||||||||||||||
| /** Whether text selection should be enabled on the pressable element. */ | ||||||||||||||||
| allowTextSelectionOnPress?: boolean | ||||||||||||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there a case where the user has this set to false, interacts with the page, the user or the app sets this to true and things don't perform (restore/disable) correctly? Also, this seems look a cool feature, but what components are using it?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So by default we want this behavior for our RSP components (aka prevent text selection on press so Link/Accordion text doesn't get highlighted on long press). This option is introduced so people can opt out of this behavior. As for the case you described, it should get cleaned up here react-spectrum/packages/@react-aria/interactions/src/usePress.ts Lines 639 to 645 in 64058f0
The
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Did we have a specific use case for this option so far?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @devongovett No specific use case yet, added it here cuz of what we discussed in our original slack convo. I can go ahead and remove it for now and we can introduce it if people request it?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. brought |
||||||||||||||||
| } | ||||||||||||||||
|
|
||||||||||||||||
| export interface PressHookProps extends PressProps { | ||||||||||||||||
|
|
@@ -99,6 +101,7 @@ export function usePress(props: PressHookProps): PressResult { | |||||||||||||||
| isPressed: isPressedProp, | ||||||||||||||||
| preventFocusOnPress, | ||||||||||||||||
| shouldCancelOnPointerExit, | ||||||||||||||||
| allowTextSelectionOnPress, | ||||||||||||||||
| // eslint-disable-next-line @typescript-eslint/no-unused-vars | ||||||||||||||||
| ref: _, // Removing `ref` from `domProps` because TypeScript is dumb, | ||||||||||||||||
| ...domProps | ||||||||||||||||
|
|
@@ -217,7 +220,9 @@ export function usePress(props: PressHookProps): PressResult { | |||||||||||||||
| state.activePointerId = null; | ||||||||||||||||
| state.pointerType = null; | ||||||||||||||||
| removeAllGlobalListeners(); | ||||||||||||||||
| restoreTextSelection(); | ||||||||||||||||
| if (!allowTextSelectionOnPress) { | ||||||||||||||||
| restoreTextSelection(state.target); | ||||||||||||||||
| } | ||||||||||||||||
| } | ||||||||||||||||
| }; | ||||||||||||||||
|
|
||||||||||||||||
|
|
@@ -329,7 +334,10 @@ export function usePress(props: PressHookProps): PressResult { | |||||||||||||||
| focusWithoutScrolling(e.currentTarget); | ||||||||||||||||
| } | ||||||||||||||||
|
|
||||||||||||||||
| disableTextSelection(); | ||||||||||||||||
| if (!allowTextSelectionOnPress) { | ||||||||||||||||
| disableTextSelection(state.target); | ||||||||||||||||
| } | ||||||||||||||||
|
|
||||||||||||||||
| triggerPressStart(e, state.pointerType); | ||||||||||||||||
|
|
||||||||||||||||
| addGlobalListener(document, 'pointermove', onPointerMove, false); | ||||||||||||||||
|
|
@@ -404,7 +412,9 @@ export function usePress(props: PressHookProps): PressResult { | |||||||||||||||
| state.activePointerId = null; | ||||||||||||||||
| state.pointerType = null; | ||||||||||||||||
| removeAllGlobalListeners(); | ||||||||||||||||
| restoreTextSelection(); | ||||||||||||||||
| if (!allowTextSelectionOnPress) { | ||||||||||||||||
| restoreTextSelection(state.target); | ||||||||||||||||
| } | ||||||||||||||||
| } | ||||||||||||||||
| }; | ||||||||||||||||
|
|
||||||||||||||||
|
|
@@ -535,7 +545,10 @@ export function usePress(props: PressHookProps): PressResult { | |||||||||||||||
| focusWithoutScrolling(e.currentTarget); | ||||||||||||||||
| } | ||||||||||||||||
|
|
||||||||||||||||
| disableTextSelection(); | ||||||||||||||||
| if (!allowTextSelectionOnPress) { | ||||||||||||||||
| disableTextSelection(state.target); | ||||||||||||||||
| } | ||||||||||||||||
|
|
||||||||||||||||
| triggerPressStart(e, state.pointerType); | ||||||||||||||||
|
|
||||||||||||||||
| addGlobalListener(window, 'scroll', onScroll, true); | ||||||||||||||||
|
|
@@ -588,7 +601,9 @@ export function usePress(props: PressHookProps): PressResult { | |||||||||||||||
| state.activePointerId = null; | ||||||||||||||||
| state.isOverTarget = false; | ||||||||||||||||
| state.ignoreEmulatedMouseEvents = true; | ||||||||||||||||
| restoreTextSelection(); | ||||||||||||||||
| if (!allowTextSelectionOnPress) { | ||||||||||||||||
| restoreTextSelection(state.target); | ||||||||||||||||
| } | ||||||||||||||||
| removeAllGlobalListeners(); | ||||||||||||||||
| }; | ||||||||||||||||
|
|
||||||||||||||||
|
|
@@ -625,13 +640,17 @@ export function usePress(props: PressHookProps): PressResult { | |||||||||||||||
| } | ||||||||||||||||
|
|
||||||||||||||||
| return pressProps; | ||||||||||||||||
| }, [addGlobalListener, isDisabled, preventFocusOnPress, removeAllGlobalListeners]); | ||||||||||||||||
| }, [addGlobalListener, isDisabled, preventFocusOnPress, removeAllGlobalListeners, allowTextSelectionOnPress]); | ||||||||||||||||
|
|
||||||||||||||||
| // Remove user-select: none in case component unmounts immediately after pressStart | ||||||||||||||||
| // eslint-disable-next-line arrow-body-style | ||||||||||||||||
| useEffect(() => { | ||||||||||||||||
| return () => restoreTextSelection(); | ||||||||||||||||
| }, []); | ||||||||||||||||
| return () => { | ||||||||||||||||
| if (!allowTextSelectionOnPress) { | ||||||||||||||||
| restoreTextSelection(ref.current.target); | ||||||||||||||||
|
LFDanLu marked this conversation as resolved.
|
||||||||||||||||
| } | ||||||||||||||||
| }; | ||||||||||||||||
| }, [allowTextSelectionOnPress]); | ||||||||||||||||
|
|
||||||||||||||||
| return { | ||||||||||||||||
| isPressed: isPressedProp || isPressed, | ||||||||||||||||
|
|
||||||||||||||||
Uh oh!
There was an error while loading. Please reload this page.