@react-native-oh-tpl/react-native-gesture-handler 2.12.6-1 → 2.12.6-2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/harmony/gesture_handler/LICENSE +21 -0
  2. package/harmony/gesture_handler/OAT.xml +44 -0
  3. package/harmony/gesture_handler/README.OpenSource +11 -0
  4. package/harmony/gesture_handler/README.md +1 -0
  5. package/harmony/gesture_handler/build-profile.json5 +7 -7
  6. package/harmony/gesture_handler/hvigorfile.ts +2 -2
  7. package/harmony/gesture_handler/index.ets +2 -2
  8. package/harmony/gesture_handler/oh-package.json5 +13 -11
  9. package/harmony/gesture_handler/src/main/cpp/CMakeLists.txt +8 -8
  10. package/harmony/gesture_handler/src/main/cpp/GestureHandlerPackage.cpp +33 -33
  11. package/harmony/gesture_handler/src/main/cpp/GestureHandlerPackage.h +14 -14
  12. package/harmony/gesture_handler/src/main/cpp/RNGestureHandlerButtonComponentDescriptor.h +60 -60
  13. package/harmony/gesture_handler/src/main/cpp/RNGestureHandlerModule.cpp +17 -17
  14. package/harmony/gesture_handler/src/main/cpp/RNGestureHandlerModule.h +11 -11
  15. package/harmony/gesture_handler/src/main/cpp/RNGestureHandlerRootViewComponentDescriptor.h +60 -60
  16. package/harmony/gesture_handler/src/main/ets/CircularBuffer.ts +42 -42
  17. package/harmony/gesture_handler/src/main/ets/Event.ts +67 -67
  18. package/harmony/gesture_handler/src/main/ets/EventDispatcher.ts +37 -37
  19. package/harmony/gesture_handler/src/main/ets/GestureHandler.ts +663 -663
  20. package/harmony/gesture_handler/src/main/ets/GestureHandlerArkUIAdapter.ets +201 -201
  21. package/harmony/gesture_handler/src/main/ets/GestureHandlerFactory.ts +44 -44
  22. package/harmony/gesture_handler/src/main/ets/GestureHandlerOrchestrator.ts +280 -280
  23. package/harmony/gesture_handler/src/main/ets/GestureHandlerPackage.ts +22 -22
  24. package/harmony/gesture_handler/src/main/ets/GestureHandlerRegistry.ts +27 -27
  25. package/harmony/gesture_handler/src/main/ets/InteractionManager.ts +108 -108
  26. package/harmony/gesture_handler/src/main/ets/LeastSquareSolver.ts +182 -182
  27. package/harmony/gesture_handler/src/main/ets/NativeViewGestureHandler.ts +114 -114
  28. package/harmony/gesture_handler/src/main/ets/OutgoingEvent.ts +33 -33
  29. package/harmony/gesture_handler/src/main/ets/PanGestureHandler.ts +327 -327
  30. package/harmony/gesture_handler/src/main/ets/PointerTracker.ts +239 -239
  31. package/harmony/gesture_handler/src/main/ets/RNGHError.ts +4 -4
  32. package/harmony/gesture_handler/src/main/ets/RNGHLogger.ts +28 -28
  33. package/harmony/gesture_handler/src/main/ets/RNGHRootTouchHandler.ets +57 -57
  34. package/harmony/gesture_handler/src/main/ets/RNGestureHandlerButton.ets +36 -36
  35. package/harmony/gesture_handler/src/main/ets/RNGestureHandlerModule.ts +125 -125
  36. package/harmony/gesture_handler/src/main/ets/RNGestureHandlerRootView.ets +56 -55
  37. package/harmony/gesture_handler/src/main/ets/RNOHScrollLocker.ts +10 -10
  38. package/harmony/gesture_handler/src/main/ets/State.ts +46 -46
  39. package/harmony/gesture_handler/src/main/ets/TapGestureHandler.ts +205 -205
  40. package/harmony/gesture_handler/src/main/ets/Vector2D.ts +36 -36
  41. package/harmony/gesture_handler/src/main/ets/VelocityTracker.ts +98 -98
  42. package/harmony/gesture_handler/src/main/ets/View.ts +70 -70
  43. package/harmony/gesture_handler/src/main/ets/ViewRegistry.ts +42 -42
  44. package/harmony/gesture_handler/src/main/ets/pages/Index.ets +16 -16
  45. package/harmony/gesture_handler/src/main/ets/webviewability/WebviewAbility.ts +41 -41
  46. package/harmony/gesture_handler/src/main/module.json5 +6 -6
  47. package/harmony/gesture_handler/src/main/resources/base/element/color.json +7 -7
  48. package/harmony/gesture_handler/src/main/resources/base/element/string.json +15 -15
  49. package/harmony/gesture_handler/src/main/resources/base/profile/main_pages.json +5 -5
  50. package/harmony/gesture_handler/src/main/resources/en_US/element/string.json +15 -15
  51. package/harmony/gesture_handler/src/main/resources/zh_CN/element/string.json +15 -15
  52. package/harmony/gesture_handler.har +0 -0
  53. package/lib/commonjs/components/touchables/GenericTouchable.js +9 -9
  54. package/lib/commonjs/components/touchables/TouchableOpacity.js +2 -2
  55. package/lib/commonjs/handlers/createNativeWrapper.js +6 -6
  56. package/lib/commonjs/handlers/gestures/GestureDetector.js +3 -3
  57. package/lib/module/components/touchables/GenericTouchable.js +9 -9
  58. package/lib/module/components/touchables/TouchableOpacity.js +2 -2
  59. package/lib/module/handlers/createNativeWrapper.js +6 -6
  60. package/lib/module/handlers/gestures/GestureDetector.js +3 -3
  61. package/package.json +70 -70
  62. package/src/RNGestureHandlerModule.ts +6 -6
  63. package/src/components/GestureButtons.tsx +334 -334
  64. package/src/components/GestureHandlerButton.tsx +5 -5
  65. package/src/components/GestureHandlerRootView.tsx +34 -34
  66. package/src/components/RNGestureHandlerButton.tsx +23 -23
  67. package/src/components/touchables/GenericTouchable.tsx +301 -301
  68. package/src/components/touchables/TouchableOpacity.tsx +76 -76
  69. package/src/components/touchables/TouchableWithoutFeedback.tsx +14 -14
  70. package/src/components/touchables/index.ts +7 -7
  71. package/src/handlers/NativeViewGestureHandler.ts +55 -55
  72. package/src/handlers/PanGestureHandler.ts +327 -327
  73. package/src/handlers/TapGestureHandler.ts +95 -95
  74. package/src/handlers/createHandler.tsx +535 -535
  75. package/src/handlers/createNativeWrapper.tsx +81 -81
  76. package/src/handlers/gestureHandlerCommon.ts +15 -15
  77. package/src/handlers/gestures/GestureDetector.tsx +823 -823
  78. package/src/index.ts +172 -172
  79. package/src/init.ts +18 -18
@@ -1,301 +1,301 @@
1
- import * as React from 'react';
2
- import { Component } from 'react';
3
- import {
4
- Animated,
5
- Platform,
6
- StyleProp,
7
- ViewStyle,
8
- TouchableWithoutFeedbackProps,
9
- Insets,
10
- } from 'react-native';
11
-
12
- import { State } from 'react-native-gesture-handler/src/State';
13
- import { BaseButton } from '../GestureButtons';
14
-
15
- import {
16
- GestureEvent,
17
- HandlerStateChangeEvent,
18
- } from 'react-native-gesture-handler/src/handlers/gestureHandlerCommon';
19
- import { NativeViewGestureHandlerPayload } from 'react-native-gesture-handler/src/handlers/NativeViewGestureHandler';
20
-
21
- /**
22
- * Each touchable is a states' machine which preforms transitions.
23
- * On very beginning (and on the very end or recognition) touchable is
24
- * UNDETERMINED. Then it moves to BEGAN. If touchable recognizes that finger
25
- * travel outside it transits to special MOVED_OUTSIDE state. Gesture recognition
26
- * finishes in UNDETERMINED state.
27
- */
28
- export const TOUCHABLE_STATE = {
29
- UNDETERMINED: 0,
30
- BEGAN: 1,
31
- MOVED_OUTSIDE: 2,
32
- } as const;
33
-
34
- type TouchableState = (typeof TOUCHABLE_STATE)[keyof typeof TOUCHABLE_STATE];
35
-
36
- export interface GenericTouchableProps
37
- extends Omit<TouchableWithoutFeedbackProps, 'hitSlop'> {
38
- // Decided to drop not used fields from RN's implementation.
39
- // e.g. onBlur and onFocus as well as deprecated props. - TODO: this comment may be unuseful in this moment
40
-
41
- // TODO: in RN these events get native event parameter, which prolly could be used in our implementation too
42
- onPress?: () => void;
43
- onPressIn?: () => void;
44
- onPressOut?: () => void;
45
- onLongPress?: () => void;
46
-
47
- nativeID?: string;
48
- shouldActivateOnStart?: boolean;
49
- disallowInterruption?: boolean;
50
-
51
- containerStyle?: StyleProp<ViewStyle>;
52
- hitSlop?: Insets | number;
53
- }
54
-
55
- interface InternalProps {
56
- extraButtonProps: any;
57
- onStateChange?: (oldState: TouchableState, newState: TouchableState) => void;
58
- }
59
-
60
- // TODO: maybe can be better
61
- // TODO: all clearTimeout have ! added, maybe they shouldn't ?
62
- type Timeout = ReturnType<typeof setTimeout> | null | undefined;
63
-
64
- /**
65
- * GenericTouchable is not intented to be used as it is.
66
- * Should be treated as a source for the rest of touchables
67
- */
68
-
69
- export default class GenericTouchable extends Component<
70
- GenericTouchableProps & InternalProps
71
- > {
72
- static defaultProps = {
73
- delayLongPress: 600,
74
- extraButtonProps: {
75
- rippleColor: 'transparent',
76
- exclusive: true,
77
- },
78
- };
79
-
80
- // timeout handlers
81
- pressInTimeout: Timeout;
82
- pressOutTimeout: Timeout;
83
- longPressTimeout: Timeout;
84
-
85
- // This flag is required since recognition of longPress implies not-invoking onPress
86
- longPressDetected = false;
87
-
88
- pointerInside = true;
89
-
90
- // State of touchable
91
- STATE: TouchableState = TOUCHABLE_STATE.UNDETERMINED;
92
-
93
- // handlePressIn in called on first touch on traveling inside component.
94
- // Handles state transition with delay.
95
- handlePressIn() {
96
- if (this.props.delayPressIn) {
97
- this.pressInTimeout = setTimeout(() => {
98
- this.moveToState(TOUCHABLE_STATE.BEGAN);
99
- this.pressInTimeout = null;
100
- }, this.props.delayPressIn);
101
- } else {
102
- this.moveToState(TOUCHABLE_STATE.BEGAN);
103
- }
104
- if (this.props.onLongPress) {
105
- const time =
106
- (this.props.delayPressIn || 0) + (this.props.delayLongPress || 0);
107
- this.longPressTimeout = setTimeout(this.onLongPressDetected, time);
108
- }
109
- }
110
- // handleMoveOutside in called on traveling outside component.
111
- // Handles state transition with delay.
112
- handleMoveOutside() {
113
- if (this.props.delayPressOut) {
114
- this.pressOutTimeout =
115
- this.pressOutTimeout ||
116
- setTimeout(() => {
117
- this.moveToState(TOUCHABLE_STATE.MOVED_OUTSIDE);
118
- this.pressOutTimeout = null;
119
- }, this.props.delayPressOut);
120
- } else {
121
- this.moveToState(TOUCHABLE_STATE.MOVED_OUTSIDE);
122
- }
123
- }
124
-
125
- // handleGoToUndetermined transits to UNDETERMINED state with proper delay
126
- handleGoToUndetermined() {
127
- clearTimeout(this.pressOutTimeout!); // TODO: maybe it can be undefined
128
- if (this.props.delayPressOut) {
129
- this.pressOutTimeout = setTimeout(() => {
130
- if (this.STATE === TOUCHABLE_STATE.UNDETERMINED) {
131
- this.moveToState(TOUCHABLE_STATE.BEGAN);
132
- }
133
- this.moveToState(TOUCHABLE_STATE.UNDETERMINED);
134
- this.pressOutTimeout = null;
135
- }, this.props.delayPressOut);
136
- } else {
137
- if (this.STATE === TOUCHABLE_STATE.UNDETERMINED) {
138
- this.moveToState(TOUCHABLE_STATE.BEGAN);
139
- }
140
- this.moveToState(TOUCHABLE_STATE.UNDETERMINED);
141
- }
142
- }
143
-
144
- componentDidMount() {
145
- this.reset();
146
- }
147
- // reset timeout to prevent memory leaks.
148
- reset() {
149
- this.longPressDetected = false;
150
- this.pointerInside = true;
151
- clearTimeout(this.pressInTimeout!);
152
- clearTimeout(this.pressOutTimeout!);
153
- clearTimeout(this.longPressTimeout!);
154
- this.pressOutTimeout = null;
155
- this.longPressTimeout = null;
156
- this.pressInTimeout = null;
157
- }
158
-
159
- // All states' transitions are defined here.
160
- moveToState(newState: TouchableState) {
161
- if (newState === this.STATE) {
162
- // Ignore dummy transitions
163
- return;
164
- }
165
- if (newState === TOUCHABLE_STATE.BEGAN) {
166
- // First touch and moving inside
167
- this.props.onPressIn?.();
168
- } else if (newState === TOUCHABLE_STATE.MOVED_OUTSIDE) {
169
- // Moving outside
170
- this.props.onPressOut?.();
171
- } else if (newState === TOUCHABLE_STATE.UNDETERMINED) {
172
- // Need to reset each time on transition to UNDETERMINED
173
- this.reset();
174
- if (this.STATE === TOUCHABLE_STATE.BEGAN) {
175
- // ... and if it happens inside button.
176
- this.props.onPressOut?.();
177
- }
178
- }
179
- // Finally call lister (used by subclasses)
180
- this.props.onStateChange?.(this.STATE, newState);
181
- // ... and make transition.
182
- this.STATE = newState;
183
- }
184
-
185
- onGestureEvent = ({
186
- nativeEvent: { pointerInside },
187
- }: GestureEvent<NativeViewGestureHandlerPayload>) => {
188
- if (this.pointerInside !== pointerInside) {
189
- if (pointerInside) {
190
- this.onMoveIn();
191
- } else {
192
- this.onMoveOut();
193
- }
194
- }
195
- this.pointerInside = pointerInside;
196
- };
197
-
198
- onHandlerStateChange = ({
199
- nativeEvent,
200
- }: HandlerStateChangeEvent<NativeViewGestureHandlerPayload>) => {
201
- const { state } = nativeEvent;
202
- if (state === State.CANCELLED || state === State.FAILED) {
203
- // Need to handle case with external cancellation (e.g. by ScrollView)
204
- this.moveToState(TOUCHABLE_STATE.UNDETERMINED);
205
- } else if (
206
- // This platform check is an implication of slightly different behavior of handlers on different platform.
207
- // And Android "Active" state is achieving on first move of a finger, not on press in.
208
- // On iOS event on "Began" is not delivered.
209
- state === (Platform.OS !== 'android' ? State.ACTIVE : State.BEGAN) &&
210
- this.STATE === TOUCHABLE_STATE.UNDETERMINED
211
- ) {
212
- // Moving inside requires
213
- this.handlePressIn();
214
- } else if (state === State.END) {
215
- const shouldCallOnPress =
216
- !this.longPressDetected &&
217
- this.STATE !== TOUCHABLE_STATE.MOVED_OUTSIDE &&
218
- this.pressOutTimeout === null;
219
- this.handleGoToUndetermined();
220
- if (shouldCallOnPress) {
221
- // Calls only inside component whether no long press was called previously
222
- this.props.onPress?.();
223
- }
224
- }
225
- };
226
-
227
- onLongPressDetected = () => {
228
- this.longPressDetected = true;
229
- // checked for in the caller of `onLongPressDetected`, but better to check twice
230
- this.props.onLongPress?.();
231
- };
232
-
233
- componentWillUnmount() {
234
- // to prevent memory leaks
235
- this.reset();
236
- }
237
-
238
- onMoveIn() {
239
- if (this.STATE === TOUCHABLE_STATE.MOVED_OUTSIDE) {
240
- // This call is not throttled with delays (like in RN's implementation).
241
- this.moveToState(TOUCHABLE_STATE.BEGAN);
242
- }
243
- }
244
-
245
- onMoveOut() {
246
- // long press should no longer be detected
247
- clearTimeout(this.longPressTimeout!);
248
- this.longPressTimeout = null;
249
- if (this.STATE === TOUCHABLE_STATE.BEGAN) {
250
- this.handleMoveOutside();
251
- }
252
- }
253
-
254
- render() {
255
- const hitSlop =
256
- (typeof this.props.hitSlop === 'number'
257
- ? {
258
- top: this.props.hitSlop,
259
- left: this.props.hitSlop,
260
- bottom: this.props.hitSlop,
261
- right: this.props.hitSlop,
262
- }
263
- : this.props.hitSlop) ?? undefined;
264
-
265
- const coreProps = {
266
- accessible: this.props.accessible !== false,
267
- accessibilityLabel: this.props.accessibilityLabel,
268
- accessibilityHint: this.props.accessibilityHint,
269
- accessibilityRole: this.props.accessibilityRole,
270
- // TODO: check if changed to no 's' correctly, also removed 2 props that are no longer available: `accessibilityComponentType` and `accessibilityTraits`,
271
- // would be good to check if it is ok for sure, see: https://github.com/facebook/react-native/issues/24016
272
- accessibilityState: this.props.accessibilityState,
273
- accessibilityActions: this.props.accessibilityActions,
274
- onAccessibilityAction: this.props.onAccessibilityAction,
275
- nativeID: this.props.nativeID,
276
- onLayout: this.props.onLayout,
277
- };
278
-
279
- return (
280
- <BaseButton
281
- style={this.props.containerStyle}
282
- onHandlerStateChange={
283
- // TODO: not sure if it can be undefined instead of null
284
- this.props.disabled ? undefined : this.onHandlerStateChange
285
- }
286
- onGestureEvent={this.onGestureEvent}
287
- hitSlop={hitSlop}
288
- shouldActivateOnStart={this.props.shouldActivateOnStart}
289
- disallowInterruption={this.props.disallowInterruption}
290
- testID={this.props.testID}
291
- touchSoundDisabled={this.props.touchSoundDisabled ?? false}
292
- enabled={!this.props.disabled}
293
- {...this.props.extraButtonProps}
294
- >
295
- <Animated.View {...coreProps} style={this.props.style}>
296
- {this.props.children}
297
- </Animated.View>
298
- </BaseButton>
299
- );
300
- }
301
- }
1
+ import * as React from 'react';
2
+ import { Component } from 'react';
3
+ import {
4
+ Animated,
5
+ Platform,
6
+ StyleProp,
7
+ ViewStyle,
8
+ TouchableWithoutFeedbackProps,
9
+ Insets,
10
+ } from 'react-native';
11
+
12
+ import { State } from 'react-native-gesture-handler/src/State';
13
+ import { BaseButton } from '../GestureButtons';
14
+
15
+ import {
16
+ GestureEvent,
17
+ HandlerStateChangeEvent,
18
+ } from 'react-native-gesture-handler/src/handlers/gestureHandlerCommon';
19
+ import { NativeViewGestureHandlerPayload } from 'react-native-gesture-handler/src/handlers/NativeViewGestureHandler';
20
+
21
+ /**
22
+ * Each touchable is a states' machine which preforms transitions.
23
+ * On very beginning (and on the very end or recognition) touchable is
24
+ * UNDETERMINED. Then it moves to BEGAN. If touchable recognizes that finger
25
+ * travel outside it transits to special MOVED_OUTSIDE state. Gesture recognition
26
+ * finishes in UNDETERMINED state.
27
+ */
28
+ export const TOUCHABLE_STATE = {
29
+ UNDETERMINED: 0,
30
+ BEGAN: 1,
31
+ MOVED_OUTSIDE: 2,
32
+ } as const;
33
+
34
+ type TouchableState = (typeof TOUCHABLE_STATE)[keyof typeof TOUCHABLE_STATE];
35
+
36
+ export interface GenericTouchableProps
37
+ extends Omit<TouchableWithoutFeedbackProps, 'hitSlop'> {
38
+ // Decided to drop not used fields from RN's implementation.
39
+ // e.g. onBlur and onFocus as well as deprecated props. - TODO: this comment may be unuseful in this moment
40
+
41
+ // TODO: in RN these events get native event parameter, which prolly could be used in our implementation too
42
+ onPress?: () => void;
43
+ onPressIn?: () => void;
44
+ onPressOut?: () => void;
45
+ onLongPress?: () => void;
46
+
47
+ nativeID?: string;
48
+ shouldActivateOnStart?: boolean;
49
+ disallowInterruption?: boolean;
50
+
51
+ containerStyle?: StyleProp<ViewStyle>;
52
+ hitSlop?: Insets | number;
53
+ }
54
+
55
+ interface InternalProps {
56
+ extraButtonProps: any;
57
+ onStateChange?: (oldState: TouchableState, newState: TouchableState) => void;
58
+ }
59
+
60
+ // TODO: maybe can be better
61
+ // TODO: all clearTimeout have ! added, maybe they shouldn't ?
62
+ type Timeout = ReturnType<typeof setTimeout> | null | undefined;
63
+
64
+ /**
65
+ * GenericTouchable is not intented to be used as it is.
66
+ * Should be treated as a source for the rest of touchables
67
+ */
68
+
69
+ export default class GenericTouchable extends Component<
70
+ GenericTouchableProps & InternalProps
71
+ > {
72
+ static defaultProps = {
73
+ delayLongPress: 600,
74
+ extraButtonProps: {
75
+ rippleColor: 'transparent',
76
+ exclusive: true,
77
+ },
78
+ };
79
+
80
+ // timeout handlers
81
+ pressInTimeout: Timeout;
82
+ pressOutTimeout: Timeout;
83
+ longPressTimeout: Timeout;
84
+
85
+ // This flag is required since recognition of longPress implies not-invoking onPress
86
+ longPressDetected = false;
87
+
88
+ pointerInside = true;
89
+
90
+ // State of touchable
91
+ STATE: TouchableState = TOUCHABLE_STATE.UNDETERMINED;
92
+
93
+ // handlePressIn in called on first touch on traveling inside component.
94
+ // Handles state transition with delay.
95
+ handlePressIn() {
96
+ if (this.props.delayPressIn) {
97
+ this.pressInTimeout = setTimeout(() => {
98
+ this.moveToState(TOUCHABLE_STATE.BEGAN);
99
+ this.pressInTimeout = null;
100
+ }, this.props.delayPressIn);
101
+ } else {
102
+ this.moveToState(TOUCHABLE_STATE.BEGAN);
103
+ }
104
+ if (this.props.onLongPress) {
105
+ const time =
106
+ (this.props.delayPressIn || 0) + (this.props.delayLongPress || 0);
107
+ this.longPressTimeout = setTimeout(this.onLongPressDetected, time);
108
+ }
109
+ }
110
+ // handleMoveOutside in called on traveling outside component.
111
+ // Handles state transition with delay.
112
+ handleMoveOutside() {
113
+ if (this.props.delayPressOut) {
114
+ this.pressOutTimeout =
115
+ this.pressOutTimeout ||
116
+ setTimeout(() => {
117
+ this.moveToState(TOUCHABLE_STATE.MOVED_OUTSIDE);
118
+ this.pressOutTimeout = null;
119
+ }, this.props.delayPressOut);
120
+ } else {
121
+ this.moveToState(TOUCHABLE_STATE.MOVED_OUTSIDE);
122
+ }
123
+ }
124
+
125
+ // handleGoToUndetermined transits to UNDETERMINED state with proper delay
126
+ handleGoToUndetermined() {
127
+ clearTimeout(this.pressOutTimeout!); // TODO: maybe it can be undefined
128
+ if (this.props.delayPressOut) {
129
+ this.pressOutTimeout = setTimeout(() => {
130
+ if (this.STATE === TOUCHABLE_STATE.UNDETERMINED) {
131
+ this.moveToState(TOUCHABLE_STATE.BEGAN);
132
+ }
133
+ this.moveToState(TOUCHABLE_STATE.UNDETERMINED);
134
+ this.pressOutTimeout = null;
135
+ }, this.props.delayPressOut);
136
+ } else {
137
+ if (this.STATE === TOUCHABLE_STATE.UNDETERMINED) {
138
+ this.moveToState(TOUCHABLE_STATE.BEGAN);
139
+ }
140
+ this.moveToState(TOUCHABLE_STATE.UNDETERMINED);
141
+ }
142
+ }
143
+
144
+ componentDidMount() {
145
+ this.reset();
146
+ }
147
+ // reset timeout to prevent memory leaks.
148
+ reset() {
149
+ this.longPressDetected = false;
150
+ this.pointerInside = true;
151
+ clearTimeout(this.pressInTimeout!);
152
+ clearTimeout(this.pressOutTimeout!);
153
+ clearTimeout(this.longPressTimeout!);
154
+ this.pressOutTimeout = null;
155
+ this.longPressTimeout = null;
156
+ this.pressInTimeout = null;
157
+ }
158
+
159
+ // All states' transitions are defined here.
160
+ moveToState(newState: TouchableState) {
161
+ if (newState === this.STATE) {
162
+ // Ignore dummy transitions
163
+ return;
164
+ }
165
+ if (newState === TOUCHABLE_STATE.BEGAN) {
166
+ // First touch and moving inside
167
+ this.props.onPressIn?.();
168
+ } else if (newState === TOUCHABLE_STATE.MOVED_OUTSIDE) {
169
+ // Moving outside
170
+ this.props.onPressOut?.();
171
+ } else if (newState === TOUCHABLE_STATE.UNDETERMINED) {
172
+ // Need to reset each time on transition to UNDETERMINED
173
+ this.reset();
174
+ if (this.STATE === TOUCHABLE_STATE.BEGAN) {
175
+ // ... and if it happens inside button.
176
+ this.props.onPressOut?.();
177
+ }
178
+ }
179
+ // Finally call lister (used by subclasses)
180
+ this.props.onStateChange?.(this.STATE, newState);
181
+ // ... and make transition.
182
+ this.STATE = newState;
183
+ }
184
+
185
+ onGestureEvent = ({
186
+ nativeEvent: { pointerInside },
187
+ }: GestureEvent<NativeViewGestureHandlerPayload>) => {
188
+ if (this.pointerInside !== pointerInside) {
189
+ if (pointerInside) {
190
+ this.onMoveIn();
191
+ } else {
192
+ this.onMoveOut();
193
+ }
194
+ }
195
+ this.pointerInside = pointerInside;
196
+ };
197
+
198
+ onHandlerStateChange = ({
199
+ nativeEvent,
200
+ }: HandlerStateChangeEvent<NativeViewGestureHandlerPayload>) => {
201
+ const { state } = nativeEvent;
202
+ if (state === State.CANCELLED || state === State.FAILED) {
203
+ // Need to handle case with external cancellation (e.g. by ScrollView)
204
+ this.moveToState(TOUCHABLE_STATE.UNDETERMINED);
205
+ } else if (
206
+ // This platform check is an implication of slightly different behavior of handlers on different platform.
207
+ // And Android "Active" state is achieving on first move of a finger, not on press in.
208
+ // On iOS event on "Began" is not delivered.
209
+ state === (Platform.OS !== 'android' ? State.ACTIVE : State.BEGAN) &&
210
+ this.STATE === TOUCHABLE_STATE.UNDETERMINED
211
+ ) {
212
+ // Moving inside requires
213
+ this.handlePressIn();
214
+ } else if (state === State.END) {
215
+ const shouldCallOnPress =
216
+ !this.longPressDetected &&
217
+ this.STATE !== TOUCHABLE_STATE.MOVED_OUTSIDE &&
218
+ this.pressOutTimeout === null;
219
+ this.handleGoToUndetermined();
220
+ if (shouldCallOnPress) {
221
+ // Calls only inside component whether no long press was called previously
222
+ this.props.onPress?.();
223
+ }
224
+ }
225
+ };
226
+
227
+ onLongPressDetected = () => {
228
+ this.longPressDetected = true;
229
+ // checked for in the caller of `onLongPressDetected`, but better to check twice
230
+ this.props.onLongPress?.();
231
+ };
232
+
233
+ componentWillUnmount() {
234
+ // to prevent memory leaks
235
+ this.reset();
236
+ }
237
+
238
+ onMoveIn() {
239
+ if (this.STATE === TOUCHABLE_STATE.MOVED_OUTSIDE) {
240
+ // This call is not throttled with delays (like in RN's implementation).
241
+ this.moveToState(TOUCHABLE_STATE.BEGAN);
242
+ }
243
+ }
244
+
245
+ onMoveOut() {
246
+ // long press should no longer be detected
247
+ clearTimeout(this.longPressTimeout!);
248
+ this.longPressTimeout = null;
249
+ if (this.STATE === TOUCHABLE_STATE.BEGAN) {
250
+ this.handleMoveOutside();
251
+ }
252
+ }
253
+
254
+ render() {
255
+ const hitSlop =
256
+ (typeof this.props.hitSlop === 'number'
257
+ ? {
258
+ top: this.props.hitSlop,
259
+ left: this.props.hitSlop,
260
+ bottom: this.props.hitSlop,
261
+ right: this.props.hitSlop,
262
+ }
263
+ : this.props.hitSlop) ?? undefined;
264
+
265
+ const coreProps = {
266
+ accessible: this.props.accessible !== false,
267
+ accessibilityLabel: this.props.accessibilityLabel,
268
+ accessibilityHint: this.props.accessibilityHint,
269
+ accessibilityRole: this.props.accessibilityRole,
270
+ // TODO: check if changed to no 's' correctly, also removed 2 props that are no longer available: `accessibilityComponentType` and `accessibilityTraits`,
271
+ // would be good to check if it is ok for sure, see: https://github.com/facebook/react-native/issues/24016
272
+ accessibilityState: this.props.accessibilityState,
273
+ accessibilityActions: this.props.accessibilityActions,
274
+ onAccessibilityAction: this.props.onAccessibilityAction,
275
+ nativeID: this.props.nativeID,
276
+ onLayout: this.props.onLayout,
277
+ };
278
+
279
+ return (
280
+ <BaseButton
281
+ style={this.props.containerStyle}
282
+ onHandlerStateChange={
283
+ // TODO: not sure if it can be undefined instead of null
284
+ this.props.disabled ? undefined : this.onHandlerStateChange
285
+ }
286
+ onGestureEvent={this.onGestureEvent}
287
+ hitSlop={hitSlop}
288
+ shouldActivateOnStart={this.props.shouldActivateOnStart}
289
+ disallowInterruption={this.props.disallowInterruption}
290
+ testID={this.props.testID}
291
+ touchSoundDisabled={this.props.touchSoundDisabled ?? false}
292
+ enabled={!this.props.disabled}
293
+ {...this.props.extraButtonProps}
294
+ >
295
+ <Animated.View {...coreProps} style={this.props.style}>
296
+ {this.props.children}
297
+ </Animated.View>
298
+ </BaseButton>
299
+ );
300
+ }
301
+ }