-
-
Notifications
You must be signed in to change notification settings - Fork 1k
Create gesturized pressable component #2942
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
Merged
latekvo
merged 149 commits into
main
from
@latekvo/add_gesture_handler_pressable_implementation
Jul 3, 2024
Merged
Changes from 12 commits
Commits
Show all changes
149 commits
Select commit
Hold shift + click to select a range
50d69a9
initial pressable component
latekvo 9bb949c
create package config for Pressable
latekvo 4215df4
add styling to pressable
latekvo 81fe6ce
add pressable example
latekvo 1980e3c
improve example
latekvo 0e5bb4e
add gesture tracking to pressable
latekvo dbaaff0
add support for all callbacks covered in docs
latekvo c4e1d7e
use all callbacks in the example
latekvo a2235ca
add delays, remove redundancy, remove old comments
latekvo 8c84f3e
fix critical typos
latekvo ecc9bcc
add pressRetentionOffset support
latekvo ea53645
fix invalid gesture composition
latekvo 91dff67
apply review suggestions
latekvo e0b1fcb
Merge branch 'main' into @latekvo/add_gesture_handler_pressable_imple…
latekvo 5f274cb
make gestures run on JS
latekvo 052351a
Merge branch '@latekvo/add_gesture_handler_pressable_implementation' …
latekvo 626b346
remove Pressable inner package and fix config
latekvo 2b96ebf
untangle circular deps
latekvo 1514f63
move pressable interfaces to different file
latekvo efae47f
fix exports config
latekvo 3b623f9
align onPressIn behaviour with original function
latekvo 73ec123
pressIn checking now allows for multi touch
latekvo b7075fd
improve onPressOut handling
latekvo d8864fd
cleanup touchWithinBounds and comments
latekvo 6fcdbe8
fix math for touchWithinBounds
latekvo 8918ab1
add onPress() callback
latekvo 4a053c0
Merge branch 'main' into @latekvo/add_gesture_handler_pressable_imple…
latekvo 77e61b7
Merge branch '@latekvo/add_gesture_handler_pressable_implementation' …
latekvo f628f08
add onPress callback to example
latekvo 51f3eba
fix longpress reactivity
latekvo ce96381
add option to add ripple
latekvo 6098a41
remove accidental line
latekvo 502b4d0
fix disabling pressable
latekvo e461a5a
change pressable types back to the original ones and remove custom pr…
latekvo d76f8f5
add touch cancellation detection
latekvo 98f569a
treat touchCancel like touchUp, fix crashing
latekvo 2b234be
add longPressEvent adapter
latekvo 403ad8c
cleanup conversion functions
latekvo 0581b59
fix type issues
latekvo e114dab
complete part of nativeTouch adapter
latekvo 9d5887e
test nativeEvent and remove unnecessary fields
latekvo 45d827d
add release_test showing both legacy and new pressable
latekvo 17172c1
add missing timestamp field
latekvo 7fc3722
Merge branch 'main' into @latekvo/add_gesture_handler_pressable_imple…
latekvo bdf37b3
fix typo
latekvo d0e4610
add types and rewrite code to fit react native docs
latekvo 9d56d47
Merge branch '@latekvo/add_gesture_handler_pressable_implementation' …
latekvo fb60554
fix typos
latekvo 30ccf0a
fix example and import issues
latekvo 040d669
Merge branch 'main' into @latekvo/add_gesture_handler_pressable_imple…
latekvo c836879
show hitSlop and pressRetentionOffset in example
latekvo f731f5b
fix available ref object and naming scheme
latekvo ac52e93
adjust colors to match RN docs
latekvo 691254e
update to useCallback instead of useRef
latekvo 7b26790
add hitslop and slop indicators to example
latekvo 7a7bec3
add press retention to tested cases, add notes
latekvo 0e258eb
fix crashing on android
latekvo e59e11d
fix hitSlop for ios and android
latekvo 1c13234
corrective changes to align with the original
latekvo 253d996
remove unnecessary wrappers
latekvo a453345
removed spamming event outputs
latekvo 57a7162
add inset recalculations to implement custom hitSlop
latekvo 41f9bb6
fix invalid pressRetentionOffset value in example
latekvo f29c82f
complete slop+retention functionality on android
latekvo 2834d02
complete ios implementation
latekvo 9d228c4
revert some code to fix android
latekvo 4720d60
enable functional style and children usage
latekvo cf00fd5
fix style setting states not triggering
latekvo 6d87412
remove holding unnecessary old states
latekvo cac52c4
fix state setting, further simplification
latekvo 909f295
fix touch bounds calculation
latekvo af60b00
cleanup
latekvo f6b49ed
change button type to work on web
latekvo 5d790c4
fix longpress behaviour
latekvo 704b2eb
adjust RawButton API to use styles
latekvo b9185c5
change pressable to use raw RNButton
latekvo a9aebfb
update new_api example
latekvo 69db9e6
atomize comparison example into multiple smaller examples
latekvo 6b4445f
adjust hitslop example to fit new format better
latekvo babe339
add missing test props to pressable
latekvo d721cf5
add ripple example and extend testing capabilities
latekvo dc20ff7
add functional styling example
latekvo 727c069
improve ripple example
latekvo 70ef07b
add correct config to ripple
latekvo a8e770f
add delayed press and longpress example
latekvo 2941a2c
fix testingBase typing
latekvo db1f36b
cleanup
latekvo 1b98577
add clear visual click signalling with Reanimated
latekvo 8c9cd32
add comments, visual improvements
latekvo e3439fb
fix delayed pressable example
latekvo 7a3e3f5
adjust hitslop example alignment
latekvo e017f88
fux setting ripple color on android
latekvo 3223351
add comments, improve visuals, add platform dependant example differe…
latekvo 3f46092
code cleanup
latekvo 25c43d1
move pressable to separate directory
latekvo f6c60fa
move all utility functions to separate file
latekvo 8b40fc6
cleanup and add missing parameters
latekvo 72cc53a
remove unnecessary useEffect
latekvo b1cb50a
add index exporting neccessary files from Pressable
latekvo a9efaab
add index.ts for pressable
latekvo 595d66e
Merge branch '@latekvo/add_gesture_handler_pressable_implementation' …
latekvo 9e68280
remove unnecessary null assertions and cases
latekvo 4127414
change directory name - tmp
latekvo da1a165
rename pressable directory
latekvo 9867e95
fix index exporting invalid directory
latekvo ab318c6
fix invalid import statements
latekvo b3bfb2f
add missing types
latekvo 5c678f6
memoize gestures
latekvo 2e1a728
remove unnecessary states
latekvo 8551aa3
add debug overlay
latekvo e33031a
add pressDelay functionality
latekvo 7bfe52e
add border example, missing props and styles
latekvo 0ca50e3
rename sub example snippets
latekvo 1ea72ca
fix IOS crash
latekvo bb1dc49
after testing, reduce cursor poiner to work just on the web
latekvo 4bc432f
improve verbosity
latekvo cad4eab
apply review suggestions and other cleanup
latekvo 14012b3
remove unnecessary not-null check
latekvo aeb265e
fix styling to work on all platforms
latekvo a298a11
fix delay press, fix styles and cleanup
latekvo 389e31e
remove debugging artifacts
latekvo 33e3819
review suggestions and cleanup
latekvo 758c6d2
change realease_test titles and comments
latekvo 9012cdf
Merge branch 'main' into @latekvo/add_gesture_handler_pressable_imple…
latekvo be54954
fix unusual unsupported border style behaviour
latekvo 5c8d189
fix pointer style on web
latekvo 23ead08
apply review suggestions
latekvo c38d33b
apply review suggestions
latekvo af73988
clear up a comment
latekvo 8bf5921
add hover delay example
latekvo 70a5aed
add handling stuck press on light touch
latekvo d9906ad
remove deafult hover delay, remove windows compatibility docstring
latekvo f5ba145
change config blocks into a config loop
latekvo c351938
fix double hover out bug
latekvo 2b548ef
rename variable as per suggestion
latekvo 86eac66
fix hit slop on IOS
latekvo df5d2fd
fix double calling of press up
latekvo 2917b06
apply suggestions to new_api example
latekvo 9683172
fix down state getting stuck when pressing down on scroll
latekvo 20f246b
cancel hoverIn/Out with delay if the other one happens before delay p…
latekvo d4f3659
apply review suggestion
latekvo 30e6274
update comment
latekvo 37afb78
fix scroll plus tap glitches
latekvo b9ba1d5
remove unnecessary comment
latekvo 362f894
remove borders example
latekvo 3d804ac
fix incorrect cancel handling (functional styling bug)
latekvo d1d23ba
Merge 'main', resolve conflicts.
latekvo a026c9c
remove unnecessary comment
latekvo 9d004a5
clarify comment
latekvo File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| { | ||
| "main": "../lib/commonjs/components/Pressable", | ||
| "module": "../lib/module/components/Pressable", | ||
| "react-native": "../src/components/Pressable", | ||
| "types": "../lib/typescript/components/Pressable.d.ts" | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,73 @@ | ||
| import React from 'react'; | ||
| import { StyleSheet, Text, View } from 'react-native'; | ||
| import { Pressable } from 'react-native-gesture-handler'; | ||
|
|
||
| export default function EmptyExample() { | ||
latekvo marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| const pressIn = () => { | ||
| console.log('Pressable pressed in'); | ||
| }; | ||
|
|
||
| const pressOut = () => { | ||
| console.log('Pressable pressed out'); | ||
| }; | ||
|
|
||
| const hoverIn = () => { | ||
| console.log('Hovered in'); | ||
| }; | ||
|
|
||
| const hoverOut = () => { | ||
| console.log('Hovered out'); | ||
| }; | ||
|
|
||
| const focus = () => { | ||
| console.log('Focused pressable'); | ||
| }; | ||
|
|
||
| const blur = () => { | ||
| console.log('Blurred pressable'); | ||
| }; | ||
|
|
||
| const longPress = () => { | ||
| console.log('Long pressed'); | ||
| }; | ||
| return ( | ||
| <View style={styles.container}> | ||
| <Pressable | ||
| style={styles.pressable} | ||
| onPressIn={pressIn} | ||
| onPressOut={pressOut} | ||
| onHoverIn={hoverIn} | ||
| onHoverOut={hoverOut} | ||
| onFocus={focus} | ||
| onBlur={blur} | ||
| onLongPress={longPress}> | ||
| <View style={styles.textWrapper}> | ||
| <Text style={styles.text}>Pressable!</Text> | ||
| </View> | ||
| </Pressable> | ||
| </View> | ||
| ); | ||
| } | ||
|
|
||
| const styles = StyleSheet.create({ | ||
| container: { | ||
| flex: 1, | ||
| justifyContent: 'center', | ||
| alignItems: 'center', | ||
| backgroundColor: '#F5FCFF', | ||
| }, | ||
| pressable: { | ||
| backgroundColor: 'mediumpurple', | ||
| width: 120, | ||
| height: 120, | ||
| margin: 'auto', | ||
| }, | ||
| textWrapper: { | ||
| flex: 1, | ||
| justifyContent: 'center', | ||
| alignItems: 'center', | ||
| }, | ||
| text: { | ||
| color: '#F5FCFF', | ||
| }, | ||
| }); | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,281 @@ | ||
| import React, { useRef } from 'react'; | ||
| import { | ||
| View, | ||
| AccessibilityProps, | ||
| ColorValue, | ||
| Insets, | ||
| NativeSyntheticEvent, | ||
| StyleProp, | ||
| TargetedEvent, | ||
| ViewProps, | ||
| ViewStyle, | ||
| StyleSheet, | ||
| } from 'react-native'; | ||
|
|
||
| import { | ||
| Gesture, | ||
| GestureDetector, | ||
| GestureHandlerRootView, | ||
| GestureStateChangeEvent, | ||
| GestureTouchEvent, | ||
| LongPressGestureHandlerEventPayload, | ||
| TouchData, | ||
| } from '../.'; | ||
latekvo marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| import { HoverGestureHandlerEventPayload } from '../handlers/gestures/hoverGesture'; | ||
|
|
||
| const DEFAULT_LONG_PRESS_DURATION = 500; | ||
| const DEFAULT_HOVER_DELAY = 0; | ||
|
|
||
| export interface PressableStateCallbackType { | ||
| readonly pressed: boolean; | ||
| } | ||
|
|
||
| export interface PressableAndroidRippleConfig { | ||
| color?: null | ColorValue | undefined; | ||
| borderless?: null | boolean | undefined; | ||
| radius?: null | number | undefined; | ||
| foreground?: null | boolean | undefined; | ||
| } | ||
|
|
||
| export interface PressableProps | ||
latekvo marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| extends AccessibilityProps, | ||
| Omit<ViewProps, 'children' | 'style' | 'hitSlop'> { | ||
| /** | ||
| * Called when the hover is activated to provide visual feedback. | ||
| */ | ||
| onHoverIn?: | ||
| | null | ||
| | (( | ||
| event: GestureStateChangeEvent<HoverGestureHandlerEventPayload> | ||
| ) => void); | ||
|
|
||
| /** | ||
| * Called when the hover is deactivated to undo visual feedback. | ||
| */ | ||
| onHoverOut?: | ||
| | null | ||
| | (( | ||
| event: GestureStateChangeEvent<HoverGestureHandlerEventPayload> | ||
| ) => void); | ||
|
|
||
| /** | ||
| * Called when a single tap gesture is detected. | ||
| */ | ||
| onPress?: null | ((event: GestureTouchEvent) => void); | ||
|
|
||
| /** | ||
| * Called when a touch is engaged before `onPress`. | ||
| */ | ||
| onPressIn?: null | ((event: GestureTouchEvent) => void); | ||
|
|
||
| /** | ||
| * Called when a touch is released before `onPress`. | ||
| */ | ||
| onPressOut?: null | ((event: GestureTouchEvent) => void); | ||
|
|
||
| /** | ||
| * Called when a long-tap gesture is detected. | ||
| */ | ||
| onLongPress?: | ||
| | null | ||
| | (( | ||
| event: GestureStateChangeEvent<LongPressGestureHandlerEventPayload> | ||
| ) => void); | ||
|
|
||
| /** | ||
| * Called after the element loses focus. | ||
| * @platform macos windows | ||
| */ | ||
| onBlur?: null | ((event: NativeSyntheticEvent<TargetedEvent>) => void); | ||
|
|
||
| /** | ||
| * Called after the element is focused. | ||
| * @platform macos windows | ||
| */ | ||
| onFocus?: null | ((event: NativeSyntheticEvent<TargetedEvent>) => void); | ||
|
|
||
| /** | ||
| * Either children or a render prop that receives a boolean reflecting whether | ||
| * the component is currently pressed. | ||
| */ | ||
| children?: | ||
| | React.ReactNode | ||
| | ((state: PressableStateCallbackType) => React.ReactNode); | ||
|
|
||
| /** | ||
| * Whether a press gesture can be interrupted by a parent gesture such as a | ||
| * scroll event. Defaults to true. | ||
| */ | ||
| cancelable?: null | boolean; | ||
|
|
||
| /** | ||
| * Duration to wait after hover in before calling `onHoverIn`. | ||
| * @platform macos windows | ||
| */ | ||
| delayHoverIn?: number | null; | ||
|
|
||
| /** | ||
| * Duration to wait after hover out before calling `onHoverOut`. | ||
| * @platform macos windows | ||
| */ | ||
| delayHoverOut?: number | null; | ||
|
|
||
| /** | ||
| * Duration (in milliseconds) from `onPressIn` before `onLongPress` is called. | ||
| */ | ||
| delayLongPress?: null | number; | ||
|
|
||
| /** | ||
| * Whether the press behavior is disabled. | ||
| */ | ||
| disabled?: null | boolean; | ||
|
|
||
| /** | ||
| * Additional distance outside of this view in which a press is detected. | ||
| */ | ||
| hitSlop?: null | Insets | number; | ||
|
|
||
| /** | ||
| * Additional distance outside of this view in which a touch is considered a | ||
| * press before `onPressOut` is triggered. | ||
| */ | ||
| pressRetentionOffset?: null | Insets | number; | ||
|
|
||
| /** | ||
| * If true, doesn't play system sound on touch. | ||
| */ | ||
| android_disableSound?: null | boolean; | ||
|
|
||
| /** | ||
| * Enables the Android ripple effect and configures its color. | ||
| */ | ||
| android_ripple?: null | PressableAndroidRippleConfig; | ||
|
|
||
| /** | ||
| * Used only for documentation or testing (e.g. snapshot testing). | ||
| */ | ||
| testOnly_pressed?: null | boolean; | ||
|
|
||
| /** | ||
| * Either view styles or a function that receives a boolean reflecting whether | ||
| * the component is currently pressed and returns view styles. | ||
| */ | ||
| style?: | ||
| | StyleProp<ViewStyle> | ||
| | ((state: PressableStateCallbackType) => StyleProp<ViewStyle>); | ||
|
|
||
| /** | ||
| * Duration (in milliseconds) to wait after press down before calling onPressIn. | ||
| */ | ||
| unstable_pressDelay?: number; | ||
| } | ||
|
|
||
| function touchWithinBounds(touch: TouchData, bounds: Insets): boolean { | ||
| const leftbound = bounds.left ? bounds.left < touch.absoluteX : true; | ||
| const rightbound = bounds.right ? bounds.right > touch.absoluteX : true; | ||
| const bottombound = bounds.bottom ? bounds.bottom < touch.absoluteY : true; | ||
| const topbound = bounds.top ? bounds.top > touch.absoluteY : true; | ||
|
|
||
| return leftbound && rightbound && topbound && bottombound; | ||
| } | ||
|
|
||
| export default function Pressable(props: PressableProps) { | ||
| const previousTouchData = useRef<TouchData[] | null>(null); | ||
|
|
||
| const pressRetentionOffset: Insets | null | undefined = | ||
| typeof props.pressRetentionOffset === 'number' | ||
| ? { | ||
| top: props.pressRetentionOffset, | ||
| left: props.pressRetentionOffset, | ||
| bottom: props.pressRetentionOffset, | ||
| right: props.pressRetentionOffset, | ||
| } | ||
| : props.pressRetentionOffset; | ||
latekvo marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| const touch = Gesture.Manual() | ||
latekvo marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
latekvo marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
latekvo marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| .onTouchesDown((event) => { | ||
| // note: hitslop checking support is built in | ||
| props.onPressIn?.(event); | ||
| previousTouchData.current = event.allTouches; | ||
latekvo marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| }) | ||
| .onTouchesUp((event) => { | ||
| // doesn't call onPressOut untill the last pointer leaves, while within bounds | ||
| if (event.allTouches.length > 1) { | ||
| return; | ||
| } | ||
|
|
||
| if (!pressRetentionOffset) { | ||
| props.onPressOut?.(event); | ||
| return; | ||
| } | ||
|
|
||
| if ( | ||
| previousTouchData.current?.find((touch) => | ||
| touchWithinBounds(touch, pressRetentionOffset) | ||
| ) | ||
| ) { | ||
| props.onPressOut?.(event); | ||
| } | ||
| }); | ||
|
|
||
| const press = Gesture.LongPress().onEnd((event, success) => { | ||
latekvo marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| if (success) { | ||
| props.onLongPress?.(event); | ||
| } | ||
| }); | ||
|
|
||
| const hover = Gesture.Hover() | ||
latekvo marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| .onBegin((event) => { | ||
| setTimeout( | ||
| () => props.onHoverIn?.(event), | ||
| props.delayHoverIn ?? DEFAULT_HOVER_DELAY | ||
| ); | ||
| }) | ||
| .onEnd((event) => { | ||
| setTimeout( | ||
| () => props.onHoverOut?.(event), | ||
| props.delayHoverOut ?? DEFAULT_HOVER_DELAY | ||
| ); | ||
| }); | ||
|
|
||
| press.minDuration(props.delayLongPress ?? DEFAULT_LONG_PRESS_DURATION); | ||
|
|
||
| // onBlur and onFocus don't exist in the docs | ||
|
|
||
| touch.hitSlop(props.hitSlop); | ||
| press.hitSlop(props.hitSlop); | ||
| hover.hitSlop(props.hitSlop); | ||
|
|
||
| // add props.pressRetentionOffset, according to docs, they're relative to pressable, not hitSlop | ||
|
|
||
| touch.enabled(!(props.disabled ?? false)); | ||
latekvo marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| press.enabled(!(props.disabled ?? false)); | ||
| hover.enabled(!(props.disabled ?? false)); | ||
|
|
||
| const gesture = Gesture.Simultaneous(hover, press, touch); | ||
|
|
||
| return ( | ||
| <GestureHandlerRootView> | ||
| <GestureDetector gesture={gesture}> | ||
| <View | ||
latekvo marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| style={[ | ||
| styles.container, | ||
| typeof props.style === 'function' | ||
| ? props.style({ pressed: false }) | ||
| : props.style, | ||
| ]}> | ||
| {typeof props.children === 'function' | ||
| ? props.children({ pressed: false }) | ||
| : props.children} | ||
| </View> | ||
| </GestureDetector> | ||
| </GestureHandlerRootView> | ||
| ); | ||
| } | ||
|
|
||
| const styles = StyleSheet.create({ | ||
| container: { | ||
| width: 'auto', | ||
| height: 'auto', | ||
| }, | ||
| }); | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.