react-native-reanimated-carousel 4.0.0-alpha.5 → 4.0.0-alpha.7

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 (88) hide show
  1. package/lib/commonjs/{layouts → components}/BaseLayout.js +4 -2
  2. package/lib/commonjs/components/BaseLayout.js.map +1 -0
  3. package/lib/commonjs/components/Carousel.js +8 -5
  4. package/lib/commonjs/components/Carousel.js.map +1 -1
  5. package/lib/commonjs/components/ScrollViewGesture.js +34 -30
  6. package/lib/commonjs/components/ScrollViewGesture.js.map +1 -1
  7. package/lib/commonjs/hooks/useCarouselController.js +4 -3
  8. package/lib/commonjs/hooks/useCarouselController.js.map +1 -1
  9. package/lib/commonjs/hooks/useLayoutConfig.js.map +1 -1
  10. package/lib/commonjs/hooks/useOffsetX.js +9 -6
  11. package/lib/commonjs/hooks/useOffsetX.js.map +1 -1
  12. package/lib/commonjs/hooks/useOffsetX.test.js +53 -0
  13. package/lib/commonjs/hooks/useOffsetX.test.js.map +1 -0
  14. package/lib/commonjs/hooks/usePanGestureProxy.js +84 -0
  15. package/lib/commonjs/hooks/usePanGestureProxy.js.map +1 -0
  16. package/lib/commonjs/hooks/usePanGestureProxy.test.js +397 -0
  17. package/lib/commonjs/hooks/usePanGestureProxy.test.js.map +1 -0
  18. package/lib/commonjs/hooks/useUpdateGestureConfig.js.map +1 -1
  19. package/lib/commonjs/hooks/useVisibleRanges.js +20 -8
  20. package/lib/commonjs/hooks/useVisibleRanges.js.map +1 -1
  21. package/lib/commonjs/hooks/useVisibleRanges.test.js +162 -0
  22. package/lib/commonjs/hooks/useVisibleRanges.test.js.map +1 -0
  23. package/lib/commonjs/utils/handleroffset-direction.js +5 -5
  24. package/lib/commonjs/utils/handleroffset-direction.js.map +1 -1
  25. package/lib/commonjs/utils/handleroffset-direction.test.js +46 -0
  26. package/lib/commonjs/utils/handleroffset-direction.test.js.map +1 -0
  27. package/lib/module/{layouts → components}/BaseLayout.js +4 -2
  28. package/lib/module/components/BaseLayout.js.map +1 -0
  29. package/lib/module/components/Carousel.js +7 -4
  30. package/lib/module/components/Carousel.js.map +1 -1
  31. package/lib/module/components/ScrollViewGesture.js +36 -31
  32. package/lib/module/components/ScrollViewGesture.js.map +1 -1
  33. package/lib/module/hooks/useCarouselController.js +4 -3
  34. package/lib/module/hooks/useCarouselController.js.map +1 -1
  35. package/lib/module/hooks/useLayoutConfig.js.map +1 -1
  36. package/lib/module/hooks/useOffsetX.js +9 -6
  37. package/lib/module/hooks/useOffsetX.js.map +1 -1
  38. package/lib/module/hooks/useOffsetX.test.js +48 -0
  39. package/lib/module/hooks/useOffsetX.test.js.map +1 -0
  40. package/lib/module/hooks/usePanGestureProxy.js +71 -0
  41. package/lib/module/hooks/usePanGestureProxy.js.map +1 -0
  42. package/lib/module/hooks/usePanGestureProxy.test.js +383 -0
  43. package/lib/module/hooks/usePanGestureProxy.test.js.map +1 -0
  44. package/lib/module/hooks/useUpdateGestureConfig.js.map +1 -1
  45. package/lib/module/hooks/useVisibleRanges.js +20 -8
  46. package/lib/module/hooks/useVisibleRanges.js.map +1 -1
  47. package/lib/module/hooks/useVisibleRanges.test.js +157 -0
  48. package/lib/module/hooks/useVisibleRanges.test.js.map +1 -0
  49. package/lib/module/utils/handleroffset-direction.js +5 -5
  50. package/lib/module/utils/handleroffset-direction.js.map +1 -1
  51. package/lib/module/utils/handleroffset-direction.test.js +41 -0
  52. package/lib/module/utils/handleroffset-direction.test.js.map +1 -0
  53. package/lib/typescript/hooks/useCarouselController.d.ts +2 -1
  54. package/lib/typescript/hooks/useLayoutConfig.d.ts +1 -1
  55. package/lib/typescript/hooks/useOffsetX.test.d.ts +1 -0
  56. package/lib/typescript/hooks/usePanGestureProxy.d.ts +9 -0
  57. package/lib/typescript/hooks/usePanGestureProxy.test.d.ts +1 -0
  58. package/lib/typescript/hooks/useUpdateGestureConfig.d.ts +3 -2
  59. package/lib/typescript/hooks/useVisibleRanges.d.ts +1 -0
  60. package/lib/typescript/hooks/useVisibleRanges.test.d.ts +1 -0
  61. package/lib/typescript/types.d.ts +5 -0
  62. package/lib/typescript/utils/handleroffset-direction.d.ts +2 -1
  63. package/lib/typescript/utils/handleroffset-direction.test.d.ts +1 -0
  64. package/package.json +2 -1
  65. package/src/{layouts → components}/BaseLayout.tsx +10 -8
  66. package/src/components/Carousel.tsx +5 -2
  67. package/src/components/ScrollViewGesture.tsx +48 -43
  68. package/src/hooks/useCarouselController.tsx +16 -13
  69. package/src/hooks/useLayoutConfig.ts +1 -1
  70. package/src/hooks/useOffsetX.test.ts +54 -0
  71. package/src/hooks/useOffsetX.ts +33 -31
  72. package/src/hooks/usePanGestureProxy.test.tsx +377 -0
  73. package/src/hooks/usePanGestureProxy.ts +110 -0
  74. package/src/hooks/useUpdateGestureConfig.ts +4 -2
  75. package/src/hooks/useVisibleRanges.test.tsx +179 -0
  76. package/src/hooks/useVisibleRanges.tsx +25 -9
  77. package/src/types.ts +5 -0
  78. package/src/utils/handleroffset-direction.test.ts +52 -0
  79. package/src/utils/handleroffset-direction.ts +12 -9
  80. package/lib/commonjs/layouts/BaseLayout.js.map +0 -1
  81. package/lib/commonjs/layouts/ParallaxLayout.js +0 -84
  82. package/lib/commonjs/layouts/ParallaxLayout.js.map +0 -1
  83. package/lib/module/layouts/BaseLayout.js.map +0 -1
  84. package/lib/module/layouts/ParallaxLayout.js +0 -61
  85. package/lib/module/layouts/ParallaxLayout.js.map +0 -1
  86. package/lib/typescript/layouts/ParallaxLayout.d.ts +0 -13
  87. package/src/layouts/ParallaxLayout.tsx +0 -141
  88. /package/lib/typescript/{layouts → components}/BaseLayout.d.ts +0 -0
@@ -0,0 +1,377 @@
1
+ import React from "react";
2
+ import { Text } from "react-native";
3
+ import type { PanGesture, PanGestureHandler, TapGesture } from "react-native-gesture-handler";
4
+ import { Gesture, GestureDetector, GestureHandlerRootView, State } from "react-native-gesture-handler";
5
+
6
+ import { cleanup, render } from "@testing-library/react-native";
7
+ import { fireGestureHandler, getByGestureTestId } from "react-native-gesture-handler/jest-utils";
8
+
9
+ import { usePanGestureProxy } from "./usePanGestureProxy";
10
+
11
+ beforeEach(cleanup);
12
+
13
+ const mockedEventHandlers = () => {
14
+ return {
15
+ begin: jest.fn(),
16
+ start: jest.fn(),
17
+ active: jest.fn(),
18
+ end: jest.fn(),
19
+ fail: jest.fn(),
20
+ cancel: jest.fn(),
21
+ finish: jest.fn(),
22
+ };
23
+ };
24
+
25
+ const mockedEventHandlersFromUser = () => {
26
+ return {
27
+ begin: jest.fn(),
28
+ start: jest.fn(),
29
+ active: jest.fn(),
30
+ end: jest.fn(),
31
+ fail: jest.fn(),
32
+ cancel: jest.fn(),
33
+ finish: jest.fn(),
34
+ };
35
+ };
36
+
37
+ describe("Using RNGH v2 gesture API", () => {
38
+ interface SingleHandlerProps {
39
+ handlers: ReturnType<typeof mockedEventHandlers>
40
+ handlersFromUser: ReturnType<typeof mockedEventHandlers>
41
+ treatStartAsUpdate?: boolean
42
+ }
43
+
44
+ function SingleHandler({ handlers, handlersFromUser, treatStartAsUpdate }: SingleHandlerProps) {
45
+ const pan = usePanGestureProxy({
46
+ onConfigurePanGesture: (gesture: PanGesture) => {
47
+ // This is user's customizations
48
+ gesture
49
+ .onStart(treatStartAsUpdate ? handlers.active : handlers.start)
50
+ .onBegin(handlersFromUser.begin)
51
+ .onUpdate(handlersFromUser.active)
52
+ .onEnd(handlersFromUser.end)
53
+ .onFinalize(handlers.finish)
54
+ .withTestId("pan");
55
+ },
56
+ onGestureBegin: handlers.begin,
57
+ onGestureUpdate: handlers.active,
58
+ onGestureEnd: handlers.end,
59
+ options: { enabled: true },
60
+ });
61
+
62
+ return (
63
+ <GestureHandlerRootView>
64
+ <GestureDetector gesture={pan}>
65
+ <Text>v2 API test</Text>
66
+ </GestureDetector>
67
+ </GestureHandlerRootView>
68
+ );
69
+ }
70
+
71
+ interface RacingHandlersProps {
72
+ tapHandlers: ReturnType<typeof mockedEventHandlers>
73
+ panHandlers: ReturnType<typeof mockedEventHandlers>
74
+ }
75
+
76
+ function RacingHandlers({ tapHandlers, panHandlers }: RacingHandlersProps) {
77
+ const tap = Gesture.Tap()
78
+ .onBegin(tapHandlers.begin)
79
+ .onEnd(tapHandlers.end)
80
+ .withTestId("tap");
81
+
82
+ const pan = usePanGestureProxy({
83
+ onConfigurePanGesture: (_: PanGesture) => {
84
+ _
85
+ .onFinalize(panHandlers.finish)
86
+ .withTestId("pan");
87
+ },
88
+ onGestureBegin: panHandlers.begin,
89
+ onGestureUpdate: panHandlers.active,
90
+ onGestureEnd: panHandlers.end,
91
+ options: { enabled: true },
92
+ });
93
+
94
+ return (
95
+ <GestureHandlerRootView>
96
+ <GestureDetector gesture={Gesture.Race(tap, pan)}>
97
+ <Text>v2 API test</Text>
98
+ </GestureDetector>
99
+ </GestureHandlerRootView>
100
+ );
101
+ }
102
+
103
+ it("sends events to handlers", () => {
104
+ const tapHandlers = mockedEventHandlers();
105
+ const panHandlers = mockedEventHandlers();
106
+ render(
107
+ <RacingHandlers tapHandlers={tapHandlers} panHandlers={panHandlers} />,
108
+ );
109
+
110
+ fireGestureHandler<PanGesture>(getByGestureTestId("pan"), [
111
+ { state: State.BEGAN },
112
+ { state: State.ACTIVE },
113
+ { state: State.END },
114
+ ]);
115
+ expect(panHandlers.begin).toBeCalledWith(
116
+ expect.objectContaining({ state: State.BEGAN }),
117
+ );
118
+ expect(panHandlers.finish).toBeCalled();
119
+ expect(tapHandlers.begin).not.toBeCalled();
120
+ });
121
+
122
+ it("sends events with additional data to handlers", () => {
123
+ const panHandlers = mockedEventHandlers();
124
+ const panHandlersFromUser = mockedEventHandlersFromUser();
125
+ render(<SingleHandler handlers={panHandlers} handlersFromUser={panHandlersFromUser} treatStartAsUpdate />);
126
+ fireGestureHandler<PanGesture>(getByGestureTestId("pan"), [
127
+ { state: State.BEGAN, translationX: 0 },
128
+ { state: State.ACTIVE, translationX: 10 },
129
+ { translationX: 20 },
130
+ { translationX: 20 },
131
+ { state: State.END, translationX: 30 },
132
+ ]);
133
+
134
+ expect(panHandlersFromUser.begin).toBeCalledTimes(1);
135
+ expect(panHandlersFromUser.active).toBeCalledTimes(2);
136
+ expect(panHandlersFromUser.end).toBeCalledTimes(1);
137
+
138
+ expect(panHandlers.active).toBeCalledTimes(3);
139
+ expect(panHandlers.active).toHaveBeenLastCalledWith(
140
+ expect.objectContaining({ translationX: 20 }),
141
+ );
142
+ });
143
+ });
144
+
145
+ describe("Event list validation", () => {
146
+ interface SingleHandlerProps {
147
+ handlers: ReturnType<typeof mockedEventHandlers>
148
+ handlersFromUser: ReturnType<typeof mockedEventHandlers>
149
+ treatStartAsUpdate?: boolean
150
+ }
151
+
152
+ function SingleHandler({ handlers, handlersFromUser, treatStartAsUpdate }: SingleHandlerProps) {
153
+ const pan = usePanGestureProxy({
154
+ onConfigurePanGesture: (_: PanGesture) => {
155
+ _
156
+ .onStart(treatStartAsUpdate ? handlers.active : handlers.start)
157
+ .onBegin(handlersFromUser.begin)
158
+ .onUpdate(handlersFromUser.active)
159
+ .onEnd(handlersFromUser.end)
160
+ .onFinalize(handlers.finish)
161
+ .withTestId("pan");
162
+ },
163
+ onGestureBegin: handlers.begin,
164
+ onGestureUpdate: handlers.active,
165
+ onGestureEnd: handlers.end,
166
+ options: { enabled: true },
167
+ });
168
+
169
+ return (
170
+ <GestureHandlerRootView>
171
+ <GestureDetector gesture={pan}>
172
+ <Text>v2 API test</Text>
173
+ </GestureDetector>
174
+ </GestureHandlerRootView>
175
+ );
176
+ }
177
+
178
+ it("throws error when oldState doesn't correspond to previous event's state", () => {
179
+ const panHandlers = mockedEventHandlers();
180
+ const panHandlersFromUser = mockedEventHandlersFromUser();
181
+ render(<SingleHandler handlers={panHandlers} handlersFromUser={panHandlersFromUser} />);
182
+
183
+ expect(() => {
184
+ fireGestureHandler<PanGesture>(getByGestureTestId("pan"), [
185
+ { oldState: State.UNDETERMINED, state: State.BEGAN, x: 0, y: 10 },
186
+ { oldState: State.UNDETERMINED, state: State.ACTIVE, x: 1, y: 11 },
187
+ ]);
188
+ }).toThrow(
189
+ "when state changes, oldState should be the same as previous event' state",
190
+ );
191
+ });
192
+
193
+ it.each([[State.END], [State.FAILED], [State.CANCELLED]])(
194
+ "correctly handles events ending with state %s",
195
+ (lastState) => {
196
+ const panHandlers = mockedEventHandlers();
197
+ const panHandlersFromUser = mockedEventHandlersFromUser();
198
+ render(<SingleHandler handlers={panHandlers} handlersFromUser={panHandlersFromUser} />);
199
+ fireGestureHandler<PanGesture>(getByGestureTestId("pan"), [
200
+ { state: State.BEGAN },
201
+ { state: State.ACTIVE },
202
+ { state: lastState },
203
+ ]);
204
+
205
+ expect(panHandlersFromUser.begin).toBeCalledTimes(1);
206
+ expect(panHandlersFromUser.active).toBeCalledTimes(0);
207
+ expect(panHandlersFromUser.end).toBeCalledTimes(1);
208
+
209
+ if (lastState === State.END)
210
+ expect(panHandlers.end).toBeCalled();
211
+
212
+ else
213
+ expect(panHandlers.finish).toBeCalledWith(expect.any(Object), false);
214
+ },
215
+ );
216
+ });
217
+
218
+ describe("Filling event list with defaults", () => {
219
+ interface RacingTapAndPanProps {
220
+ handlers: ReturnType<typeof mockedEventHandlers>
221
+ treatStartAsUpdate?: boolean
222
+ }
223
+
224
+ function RacingTapAndPan({
225
+ handlers,
226
+ treatStartAsUpdate,
227
+ }: RacingTapAndPanProps) {
228
+ const tap = Gesture.Tap()
229
+ .onBegin(handlers.begin)
230
+ .onEnd(handlers.end)
231
+ .withTestId("tap");
232
+
233
+ const pan = usePanGestureProxy({
234
+ onConfigurePanGesture: (_: PanGesture) => {
235
+ _
236
+ .onStart(treatStartAsUpdate ? handlers.active : handlers.start)
237
+ .onFinalize(handlers.finish)
238
+ .withTestId("pan");
239
+ },
240
+ onGestureBegin: handlers.begin,
241
+ onGestureUpdate: handlers.active,
242
+ onGestureEnd: handlers.end,
243
+ options: { enabled: true },
244
+ });
245
+
246
+ return (
247
+ <GestureHandlerRootView>
248
+ <GestureDetector gesture={Gesture.Exclusive(pan, tap)}>
249
+ <Text>v2 API test</Text>
250
+ </GestureDetector>
251
+ </GestureHandlerRootView>
252
+ );
253
+ }
254
+
255
+ it("fills oldState if not passed", () => {
256
+ const handlers = mockedEventHandlers();
257
+ render(<RacingTapAndPan handlers={handlers} treatStartAsUpdate />);
258
+ fireGestureHandler<PanGestureHandler>(getByGestureTestId("pan"), [
259
+ { state: State.BEGAN },
260
+ { state: State.ACTIVE },
261
+ { state: State.ACTIVE },
262
+ { state: State.ACTIVE },
263
+ { state: State.END },
264
+ ]);
265
+
266
+ expect(handlers.begin).toBeCalledWith(
267
+ expect.objectContaining({ oldState: State.UNDETERMINED }),
268
+ );
269
+ expect(handlers.active).nthCalledWith(
270
+ 1,
271
+ expect.objectContaining({ oldState: State.BEGAN }),
272
+ );
273
+ expect(handlers.active).lastCalledWith(
274
+ expect.not.objectContaining({ oldState: expect.anything() }),
275
+ );
276
+ expect(handlers.end).toBeCalledWith(
277
+ expect.objectContaining({ oldState: State.ACTIVE }),
278
+ true,
279
+ );
280
+ });
281
+
282
+ it("fills missing ACTIVE states", () => {
283
+ const panHandlers = mockedEventHandlers();
284
+ render(<RacingTapAndPan handlers={panHandlers} treatStartAsUpdate />);
285
+ fireGestureHandler<PanGesture>(getByGestureTestId("pan"), [
286
+ { state: State.BEGAN, x: 0, y: 10 },
287
+ { state: State.ACTIVE, x: 1, y: 11 },
288
+ { x: 2, y: 12 },
289
+ { x: 3, y: 13 },
290
+ { state: State.END, x: 4, y: 14 },
291
+ ]);
292
+
293
+ expect(panHandlers.active).toBeCalledTimes(3);
294
+ expect(panHandlers.active).toHaveBeenLastCalledWith(
295
+ expect.objectContaining({ x: 3, y: 13 }),
296
+ );
297
+ });
298
+
299
+ it("fills BEGIN and END events for discrete handlers", () => {
300
+ const handlers = mockedEventHandlers();
301
+ render(<RacingTapAndPan handlers={handlers} treatStartAsUpdate />);
302
+ fireGestureHandler<TapGesture>(getByGestureTestId("tap"), [{ x: 5 }]);
303
+ expect(handlers.begin).toBeCalledTimes(1);
304
+ expect(handlers.end).toBeCalledTimes(1);
305
+ });
306
+
307
+ it("with FAILED event, fills BEGIN event for discrete handlers", () => {
308
+ const handlers = mockedEventHandlers();
309
+ render(<RacingTapAndPan handlers={handlers} treatStartAsUpdate />);
310
+ fireGestureHandler<TapGesture>(getByGestureTestId("tap"), [
311
+ { state: State.FAILED },
312
+ ]);
313
+ expect(handlers.begin).toBeCalledTimes(1);
314
+ expect(handlers.end).toBeCalledTimes(1);
315
+ expect(handlers.end).toBeCalledWith(expect.anything(), false);
316
+ });
317
+
318
+ it("uses event data from first event in filled BEGIN, ACTIVE events", () => {
319
+ const handlers = mockedEventHandlers();
320
+ render(<RacingTapAndPan handlers={handlers} treatStartAsUpdate />);
321
+ fireGestureHandler<PanGesture>(getByGestureTestId("pan"), [{ x: 120 }]);
322
+ expect(handlers.begin).toBeCalledWith(expect.objectContaining({ x: 120 }));
323
+ expect(handlers.active).toHaveBeenNthCalledWith(
324
+ 1,
325
+ expect.objectContaining({ x: 120 }),
326
+ );
327
+ });
328
+
329
+ it("uses event data from last event in filled END events", () => {
330
+ const handlers = mockedEventHandlers();
331
+ render(<RacingTapAndPan handlers={handlers} treatStartAsUpdate />);
332
+ fireGestureHandler<PanGesture>(getByGestureTestId("pan"), [
333
+ { x: 120, state: State.FAILED },
334
+ ]);
335
+ expect(handlers.begin).toBeCalledTimes(1);
336
+ expect(handlers.active).toBeCalledTimes(1);
337
+ expect(handlers.end).toBeCalledWith(
338
+ expect.objectContaining({ x: 120 }),
339
+ false,
340
+ );
341
+ });
342
+
343
+ it("uses event data filled events", () => {
344
+ const handlers = mockedEventHandlers();
345
+ render(<RacingTapAndPan handlers={handlers} treatStartAsUpdate />);
346
+ fireGestureHandler<PanGesture>(getByGestureTestId("pan"), [
347
+ { x: 5, y: 15 },
348
+ { x: 6, y: 16 },
349
+ { x: 7, y: 17 },
350
+ ]);
351
+ expect(handlers.begin).toBeCalledWith(
352
+ expect.objectContaining({ x: 5, y: 15 }),
353
+ );
354
+ expect(handlers.active).toBeCalledTimes(3);
355
+ expect(handlers.end).toBeCalledWith(
356
+ expect.objectContaining({ x: 7, y: 17 }),
357
+ true,
358
+ );
359
+ });
360
+
361
+ it("fills BEGIN and END events when they're not present, for discrete handlers", () => {
362
+ const handlers = mockedEventHandlers();
363
+ render(<RacingTapAndPan handlers={handlers} treatStartAsUpdate />);
364
+ fireGestureHandler<TapGesture>(getByGestureTestId("tap"));
365
+ expect(handlers.begin).toBeCalledTimes(1);
366
+ expect(handlers.end).toHaveBeenCalledTimes(1);
367
+ });
368
+
369
+ it("fills BEGIN, ACTIVE and END events when they're not present, for continuous handlers", () => {
370
+ const handlers = mockedEventHandlers();
371
+ render(<RacingTapAndPan handlers={handlers} treatStartAsUpdate />);
372
+ fireGestureHandler<PanGesture>(getByGestureTestId("pan"));
373
+ expect(handlers.begin).toBeCalledTimes(1);
374
+ expect(handlers.active).toBeCalledTimes(1);
375
+ expect(handlers.end).toHaveBeenCalledTimes(1);
376
+ });
377
+ });
@@ -0,0 +1,110 @@
1
+ import { useMemo } from "react";
2
+ import type { GestureStateChangeEvent, GestureUpdateEvent, PanGesture, PanGestureHandlerEventPayload } from "react-native-gesture-handler";
3
+ import { Gesture } from "react-native-gesture-handler";
4
+
5
+ import type { GestureConfig } from "./useUpdateGestureConfig";
6
+ import { useUpdateGestureConfig } from "./useUpdateGestureConfig";
7
+
8
+ export const usePanGestureProxy = (
9
+ customization: {
10
+ onConfigurePanGesture?: (gesture: PanGesture) => void
11
+ onGestureBegin: (event: GestureStateChangeEvent<PanGestureHandlerEventPayload>) => void
12
+ onGestureUpdate: (event: GestureUpdateEvent<PanGestureHandlerEventPayload>) => void
13
+ onGestureEnd: (event: GestureStateChangeEvent<PanGestureHandlerEventPayload>, success: boolean) => void
14
+ options?: GestureConfig
15
+ },
16
+ ) => {
17
+ const {
18
+ onConfigurePanGesture,
19
+ onGestureBegin,
20
+ onGestureUpdate,
21
+ onGestureEnd,
22
+ options = {},
23
+ } = customization;
24
+
25
+ const gesture = useMemo(() => {
26
+ const gesture = Gesture.Pan();
27
+
28
+ // Save the original gesture callbacks
29
+ const originalGestures = {
30
+ onBegin: gesture.onBegin,
31
+ onUpdate: gesture.onUpdate,
32
+ onEnd: gesture.onEnd,
33
+ };
34
+
35
+ // Save the user defined gesture callbacks
36
+ const userDefinedConflictGestures: {
37
+ onBegin?: Parameters<(typeof gesture)["onBegin"]>[0]
38
+ onUpdate?: Parameters<(typeof gesture)["onUpdate"]>[0]
39
+ onEnd?: Parameters<(typeof gesture)["onEnd"]>[0]
40
+ } = {
41
+ onBegin: undefined,
42
+ onUpdate: undefined,
43
+ onEnd: undefined,
44
+ };
45
+
46
+ const fakeOnBegin: typeof gesture.onBegin = (cb) => {
47
+ // Using fakeOnBegin to save the user defined callback
48
+ userDefinedConflictGestures.onBegin = cb;
49
+ return gesture;
50
+ };
51
+
52
+ const fakeOnUpdate: typeof gesture.onUpdate = (cb) => {
53
+ // Using fakeOnUpdate to save the user defined callback
54
+ userDefinedConflictGestures.onUpdate = cb;
55
+ return gesture;
56
+ };
57
+
58
+ const fakeOnEnd: typeof gesture.onEnd = (cb) => {
59
+ // Using fakeOnEnd to save the user defined callback
60
+ userDefinedConflictGestures.onEnd = cb;
61
+ return gesture;
62
+ };
63
+
64
+ // Setup the fake callbacks
65
+ gesture.onBegin = fakeOnBegin;
66
+ gesture.onUpdate = fakeOnUpdate;
67
+ gesture.onEnd = fakeOnEnd;
68
+
69
+ if (onConfigurePanGesture)
70
+ // Get the gesture with the user defined configuration
71
+ onConfigurePanGesture(gesture);
72
+
73
+ // Restore the original callbacks
74
+ gesture.onBegin = originalGestures.onBegin;
75
+ gesture.onUpdate = originalGestures.onUpdate;
76
+ gesture.onEnd = originalGestures.onEnd;
77
+
78
+ // Setup the original callbacks with the user defined callbacks
79
+ gesture
80
+ .onBegin((e) => {
81
+ onGestureBegin(e);
82
+
83
+ if (userDefinedConflictGestures.onBegin)
84
+ userDefinedConflictGestures.onBegin(e);
85
+ })
86
+ .onUpdate((e) => {
87
+ onGestureUpdate(e);
88
+
89
+ if (userDefinedConflictGestures.onUpdate)
90
+ userDefinedConflictGestures.onUpdate(e);
91
+ })
92
+ .onEnd((e, success) => {
93
+ onGestureEnd(e, success);
94
+
95
+ if (userDefinedConflictGestures.onEnd)
96
+ userDefinedConflictGestures.onEnd(e, success);
97
+ });
98
+
99
+ return gesture;
100
+ }, [
101
+ onGestureBegin,
102
+ onGestureUpdate,
103
+ onGestureEnd,
104
+ onConfigurePanGesture,
105
+ ]);
106
+
107
+ useUpdateGestureConfig(gesture, options);
108
+
109
+ return gesture;
110
+ };
@@ -1,9 +1,11 @@
1
1
  import { useEffect } from "react";
2
2
  import type { PanGesture } from "react-native-gesture-handler";
3
3
 
4
- export const useUpdateGestureConfig = (gesture: PanGesture, config: {
4
+ export interface GestureConfig {
5
5
  enabled?: boolean
6
- }) => {
6
+ }
7
+
8
+ export const useUpdateGestureConfig = (gesture: PanGesture, config: GestureConfig) => {
7
9
  const { enabled } = config;
8
10
 
9
11
  useEffect(() => {
@@ -0,0 +1,179 @@
1
+ import { useSharedValue } from "react-native-reanimated";
2
+
3
+ import { renderHook } from "@testing-library/react-hooks";
4
+
5
+ import { useVisibleRanges } from "./useVisibleRanges";
6
+
7
+ const viewSize = 393;
8
+
9
+ describe("useVisibleRanges", () => {
10
+ it("should only display the front of the list when loop is false", async () => {
11
+ const hook = renderHook(() => {
12
+ const translation = useSharedValue(-0);
13
+ const range = useVisibleRanges({
14
+ total: 10,
15
+ translation,
16
+ viewSize,
17
+ windowSize: 4,
18
+ loop: false,
19
+ });
20
+
21
+ return range;
22
+ });
23
+
24
+ const expected = hook.result.current.value;
25
+
26
+ expect(expected).toMatchInlineSnapshot(`
27
+ {
28
+ "negativeRange": [
29
+ -3,
30
+ 0,
31
+ ],
32
+ "positiveRange": [
33
+ 0,
34
+ 3,
35
+ ],
36
+ }
37
+ `);
38
+ });
39
+
40
+ it("should display the rear of the list and the front of the list when loop is true", async () => {
41
+ const hook = renderHook(() => {
42
+ const translation = useSharedValue(-0);
43
+ const range = useVisibleRanges({
44
+ total: 10,
45
+ translation,
46
+ viewSize,
47
+ windowSize: 4,
48
+ loop: true,
49
+ });
50
+
51
+ return range;
52
+ });
53
+
54
+ const expected = hook.result.current.value;
55
+
56
+ expect(expected).toMatchInlineSnapshot(`
57
+ {
58
+ "negativeRange": [
59
+ 8,
60
+ 9,
61
+ ],
62
+ "positiveRange": [
63
+ 0,
64
+ 2,
65
+ ],
66
+ }
67
+ `);
68
+ });
69
+
70
+ it("should shows the increased range of the list when the loop is false and swiped the carousel.", async () => {
71
+ const slide0hook = renderHook(() => {
72
+ const translation = useSharedValue(-0 * viewSize);
73
+ const range = useVisibleRanges({
74
+ total: 10,
75
+ translation,
76
+ viewSize,
77
+ windowSize: 4,
78
+ loop: false,
79
+ });
80
+
81
+ return range;
82
+ }).result.current.value;
83
+
84
+ const slide1hook = renderHook(() => {
85
+ const translation = useSharedValue(-1 * viewSize);
86
+ const range = useVisibleRanges({
87
+ total: 10,
88
+ translation,
89
+ viewSize,
90
+ windowSize: 4,
91
+ loop: false,
92
+ });
93
+
94
+ return range;
95
+ }).result.current.value;
96
+
97
+ const slide2hook = renderHook(() => {
98
+ const translation = useSharedValue(-2 * viewSize);
99
+ const range = useVisibleRanges({
100
+ total: 10,
101
+ translation,
102
+ viewSize,
103
+ windowSize: 4,
104
+ loop: false,
105
+ });
106
+
107
+ return range;
108
+ }).result.current.value;
109
+
110
+ const slide3hook = renderHook(() => {
111
+ const translation = useSharedValue(-3 * viewSize);
112
+ const range = useVisibleRanges({
113
+ total: 10,
114
+ translation,
115
+ viewSize,
116
+ windowSize: 4,
117
+ loop: false,
118
+ });
119
+
120
+ return range;
121
+ }).result.current.value;
122
+
123
+ // [0,3] Display the 0,1,2,3 items.
124
+ expect(slide0hook).toMatchInlineSnapshot(`
125
+ {
126
+ "negativeRange": [
127
+ -3,
128
+ 0,
129
+ ],
130
+ "positiveRange": [
131
+ 0,
132
+ 3,
133
+ ],
134
+ }
135
+ `);
136
+
137
+ // [1,4] Display the 1,2,3,4 items.
138
+ expect(slide1hook).toMatchInlineSnapshot(`
139
+ {
140
+ "negativeRange": [
141
+ -2,
142
+ 1,
143
+ ],
144
+ "positiveRange": [
145
+ 1,
146
+ 4,
147
+ ],
148
+ }
149
+ `);
150
+
151
+ // [2,5] Display the 2,3,4,5 items.
152
+ expect(slide2hook).toMatchInlineSnapshot(`
153
+ {
154
+ "negativeRange": [
155
+ -1,
156
+ 2,
157
+ ],
158
+ "positiveRange": [
159
+ 2,
160
+ 5,
161
+ ],
162
+ }
163
+ `);
164
+
165
+ // [3.6] Display the 3,4,5,6 items.
166
+ expect(slide3hook).toMatchInlineSnapshot(`
167
+ {
168
+ "negativeRange": [
169
+ 0,
170
+ 3,
171
+ ],
172
+ "positiveRange": [
173
+ 3,
174
+ 6,
175
+ ],
176
+ }
177
+ `);
178
+ });
179
+ });