react-native-screen-transitions 1.0.2 → 1.1.0

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.
package/dist/index.js CHANGED
@@ -34,18 +34,52 @@ __export(index_exports, {
34
34
  useScreenAnimation: () => useScreenAnimation
35
35
  });
36
36
  module.exports = __toCommonJS(index_exports);
37
+ var import_react_native5 = require("react-native");
38
+
39
+ // src/components/create-transition-aware-component.tsx
40
+ var import_react7 = require("react");
37
41
  var import_react_native3 = require("react-native");
42
+ var import_react_native_reanimated8 = __toESM(require("react-native-reanimated"));
38
43
 
39
- // src/configs/presets.ts
40
- var presets_exports = {};
41
- __export(presets_exports, {
42
- DraggableCard: () => DraggableCard,
43
- ElasticCard: () => ElasticCard,
44
- SlideFromBottom: () => SlideFromBottom,
45
- SlideFromTop: () => SlideFromTop,
46
- ZoomIn: () => ZoomIn
47
- });
44
+ // src/components/transition-gesture-handler-provider.tsx
45
+ var import_react4 = require("react");
46
+ var import_react_native_gesture_handler3 = require("react-native-gesture-handler");
47
+ var import_react_native_reanimated5 = require("react-native-reanimated");
48
+
49
+ // src/contexts/gesture.ts
50
+ var import_react = require("react");
51
+ var import_react_native_gesture_handler = require("react-native-gesture-handler");
48
52
  var import_react_native_reanimated = require("react-native-reanimated");
53
+ var GestureContext = (0, import_react.createContext)(
54
+ void 0
55
+ );
56
+ var useGestureContext = () => {
57
+ const context = (0, import_react.useContext)(GestureContext);
58
+ const scrollProgressFallback = (0, import_react_native_reanimated.useSharedValue)({
59
+ x: 0,
60
+ y: 0,
61
+ contentHeight: 0,
62
+ contentWidth: 0,
63
+ layoutHeight: 0,
64
+ layoutWidth: 0
65
+ });
66
+ if (!context) {
67
+ return {
68
+ panGesture: import_react_native_gesture_handler.Gesture.Pan(),
69
+ nativeGesture: import_react_native_gesture_handler.Gesture.Native(),
70
+ scrollProgress: scrollProgressFallback,
71
+ isPlaceholder: true
72
+ };
73
+ }
74
+ return context;
75
+ };
76
+
77
+ // src/hooks/use-build-gestures.tsx
78
+ var import_native3 = require("@react-navigation/native");
79
+ var import_react3 = require("react");
80
+ var import_react_native = require("react-native");
81
+ var import_react_native_gesture_handler2 = require("react-native-gesture-handler");
82
+ var import_react_native_reanimated4 = require("react-native-reanimated");
49
83
 
50
84
  // src/configs/specs.ts
51
85
  var specs_exports = {};
@@ -60,244 +94,6 @@ var DefaultSpec = {
60
94
  restSpeedThreshold: 0.01
61
95
  };
62
96
 
63
- // src/configs/presets.ts
64
- var SlideFromTop = (config = {}) => {
65
- return {
66
- gestureEnabled: true,
67
- gestureDirection: "vertical-inverted",
68
- screenStyleInterpolator: ({
69
- current,
70
- next,
71
- layouts: {
72
- screen: { height }
73
- }
74
- }) => {
75
- "worklet";
76
- const progress = current.progress.value + (next?.progress.value ?? 0);
77
- const y = (0, import_react_native_reanimated.interpolate)(progress, [0, 1, 2], [-height, 0, height]);
78
- return {
79
- contentStyle: {
80
- transform: [{ translateY: y }]
81
- }
82
- };
83
- },
84
- transitionSpec: {
85
- open: DefaultSpec,
86
- close: DefaultSpec
87
- },
88
- ...config
89
- };
90
- };
91
- var ZoomIn = (config = {}) => {
92
- return {
93
- gestureEnabled: false,
94
- screenStyleInterpolator: ({ current, next }) => {
95
- "worklet";
96
- const progress = current.progress.value + (next?.progress.value ?? 0);
97
- const scale = (0, import_react_native_reanimated.interpolate)(
98
- progress,
99
- [0, 1, 2],
100
- [0.5, 1, 0.5],
101
- import_react_native_reanimated.Extrapolation.CLAMP
102
- );
103
- const opacity = (0, import_react_native_reanimated.interpolate)(
104
- progress,
105
- [0, 1, 2],
106
- [0, 1, 0],
107
- import_react_native_reanimated.Extrapolation.CLAMP
108
- );
109
- return {
110
- contentStyle: {
111
- transform: [{ scale }],
112
- opacity
113
- }
114
- };
115
- },
116
- transitionSpec: {
117
- open: DefaultSpec,
118
- close: DefaultSpec
119
- },
120
- ...config
121
- };
122
- };
123
- var SlideFromBottom = (config = {}) => {
124
- return {
125
- gestureEnabled: true,
126
- gestureDirection: "vertical",
127
- screenStyleInterpolator: ({
128
- current,
129
- next,
130
- layouts: {
131
- screen: { height }
132
- }
133
- }) => {
134
- "worklet";
135
- const progress = current.progress.value + (next?.progress.value ?? 0);
136
- const y = (0, import_react_native_reanimated.interpolate)(progress, [0, 1, 2], [height, 0, -height]);
137
- return {
138
- contentStyle: {
139
- transform: [{ translateY: y }]
140
- }
141
- };
142
- },
143
- transitionSpec: {
144
- open: DefaultSpec,
145
- close: DefaultSpec
146
- },
147
- ...config
148
- };
149
- };
150
- var DraggableCard = (config = {}) => {
151
- return {
152
- gestureEnabled: true,
153
- gestureDirection: ["horizontal", "vertical"],
154
- screenStyleInterpolator: ({ current, next, layouts: { screen } }) => {
155
- "worklet";
156
- const progress = current.progress.value + (next?.progress.value ?? 0);
157
- const scale = (0, import_react_native_reanimated.interpolate)(progress, [0, 1, 2], [0, 1, 0.75]);
158
- const translateY = (0, import_react_native_reanimated.interpolate)(
159
- current.gesture.normalizedY.value,
160
- [-1, 1],
161
- [-screen.height * 0.5, screen.height * 0.5],
162
- "clamp"
163
- );
164
- const translateX = (0, import_react_native_reanimated.interpolate)(
165
- current.gesture.normalizedX.value,
166
- [-1, 1],
167
- [-screen.width * 0.5, screen.width * 0.5],
168
- "clamp"
169
- );
170
- return {
171
- contentStyle: {
172
- transform: [{ scale }, { translateY }, { translateX }]
173
- }
174
- };
175
- },
176
- transitionSpec: {
177
- open: DefaultSpec,
178
- close: DefaultSpec
179
- },
180
- ...config
181
- };
182
- };
183
- var ElasticCard = (config = { elasticFactor: 0.5 }) => {
184
- return {
185
- gestureEnabled: true,
186
- gestureDirection: "bidirectional",
187
- screenStyleInterpolator: ({ current, next, layouts: { screen } }) => {
188
- "worklet";
189
- const progress = current.progress.value + (next?.progress.value ?? 0);
190
- const scale = (0, import_react_native_reanimated.interpolate)(progress, [0, 1, 2], [0, 1, 0.8]);
191
- const maxElasticityX = screen.width * (config.elasticFactor ?? 0.5);
192
- const maxElasticityY = screen.height * (config.elasticFactor ?? 0.5);
193
- const translateX = (0, import_react_native_reanimated.interpolate)(
194
- current.gesture.normalizedX.value,
195
- [-1, 0, 1],
196
- [-maxElasticityX, 0, maxElasticityX],
197
- "clamp"
198
- );
199
- const translateY = (0, import_react_native_reanimated.interpolate)(
200
- current.gesture.normalizedY.value,
201
- [-1, 0, 1],
202
- [-maxElasticityY, 0, maxElasticityY],
203
- "clamp"
204
- );
205
- const overlayColor = (0, import_react_native_reanimated.interpolateColor)(
206
- next?.progress.value || 0,
207
- [0, 1],
208
- ["rgba(0,0,0,0)", "rgba(0,0,0,0.5)"]
209
- );
210
- return {
211
- contentStyle: {
212
- transform: [{ scale }, { translateX }, { translateY }]
213
- },
214
- overlayStyle: {
215
- backgroundColor: overlayColor
216
- }
217
- };
218
- },
219
- ...config
220
- };
221
- };
222
-
223
- // src/hooks/use-screen-animation.tsx
224
- var import_native2 = require("@react-navigation/native");
225
- var import_react3 = require("react");
226
- var import_react_native = require("react-native");
227
- var import_react_native_reanimated5 = require("react-native-reanimated");
228
- var import_react_native_safe_area_context = require("react-native-safe-area-context");
229
-
230
- // src/store/utils/use-shallow.tsx
231
- var import_react = __toESM(require("react"));
232
-
233
- // src/store/utils/shallow.ts
234
- var isIterable = (obj) => Symbol.iterator in obj;
235
- var hasIterableEntries = (value) => (
236
- // HACK: avoid checking entries type
237
- "entries" in value
238
- );
239
- var compareEntries = (valueA, valueB) => {
240
- const mapA = valueA instanceof Map ? valueA : new Map(valueA.entries());
241
- const mapB = valueB instanceof Map ? valueB : new Map(valueB.entries());
242
- if (mapA.size !== mapB.size) {
243
- return false;
244
- }
245
- for (const [key, value] of mapA) {
246
- if (!Object.is(value, mapB.get(key))) {
247
- return false;
248
- }
249
- }
250
- return true;
251
- };
252
- var compareIterables = (valueA, valueB) => {
253
- const iteratorA = valueA[Symbol.iterator]();
254
- const iteratorB = valueB[Symbol.iterator]();
255
- let nextA = iteratorA.next();
256
- let nextB = iteratorB.next();
257
- while (!nextA.done && !nextB.done) {
258
- if (!Object.is(nextA.value, nextB.value)) {
259
- return false;
260
- }
261
- nextA = iteratorA.next();
262
- nextB = iteratorB.next();
263
- }
264
- return !!nextA.done && !!nextB.done;
265
- };
266
- function shallow(valueA, valueB) {
267
- if (Object.is(valueA, valueB)) {
268
- return true;
269
- }
270
- if (typeof valueA !== "object" || valueA === null || typeof valueB !== "object" || valueB === null) {
271
- return false;
272
- }
273
- if (Object.getPrototypeOf(valueA) !== Object.getPrototypeOf(valueB)) {
274
- return false;
275
- }
276
- if (isIterable(valueA) && isIterable(valueB)) {
277
- if (hasIterableEntries(valueA) && hasIterableEntries(valueB)) {
278
- return compareEntries(valueA, valueB);
279
- }
280
- return compareIterables(valueA, valueB);
281
- }
282
- return compareEntries(
283
- { entries: () => Object.entries(valueA) },
284
- { entries: () => Object.entries(valueB) }
285
- );
286
- }
287
-
288
- // src/store/utils/use-shallow.tsx
289
- function useShallow(selector) {
290
- const prev = import_react.default.useRef(void 0);
291
- return (state) => {
292
- const next = selector(state);
293
- if (shallow(prev.current, next)) {
294
- return prev.current;
295
- }
296
- prev.current = next;
297
- return next;
298
- };
299
- }
300
-
301
97
  // src/animation-engine.ts
302
98
  var import_react_native_reanimated3 = require("react-native-reanimated");
303
99
 
@@ -344,54 +140,93 @@ function createVanillaStore(initialState) {
344
140
  return useStore;
345
141
  }
346
142
 
347
- // src/store/index.ts
348
- var useRouteStore = createVanillaStore({
349
- routes: {},
350
- routeKeys: []
351
- });
352
- var RouteStore = {
353
- use: useRouteStore,
354
- updateRoute: (key, value) => {
355
- if (!key) return;
356
- useRouteStore.setState(({ routeKeys, routes }) => {
357
- const currentRoute = routes[key];
358
- if (currentRoute) {
359
- routes[key] = {
360
- ...currentRoute,
361
- ...value
362
- };
363
- } else {
364
- const { name = "", status = 0, closing = false, ...rest } = value;
365
- const newIndex = routeKeys.length;
366
- routes[key] = {
367
- id: key,
368
- index: newIndex,
369
- name,
370
- status,
371
- closing,
372
- ...rest
373
- };
374
- routeKeys.push(key);
375
- }
376
- });
377
- },
378
- removeRoute: (key) => {
379
- if (!key) return;
380
- useRouteStore.setState(({ routes, routeKeys }) => {
381
- delete routes[key];
382
- const indexToRemove = routeKeys.indexOf(key);
383
- if (indexToRemove > -1) {
384
- routeKeys.splice(indexToRemove, 1);
385
- }
143
+ // src/store/utils/handle-screen-dismiss.tsx
144
+ var import_native = require("@react-navigation/native");
145
+ var handleScreenDismiss = (screenBeingDismissed, navigation) => {
146
+ const { screens } = ScreenStore.use.getState();
147
+ const dismissedScreen = screens[screenBeingDismissed];
148
+ if (!dismissedScreen) {
149
+ navigation.goBack();
150
+ return;
151
+ }
152
+ const childScreens = Object.values(screens).filter(
153
+ (screen) => screen.parentNavigatorKey === dismissedScreen.navigatorKey
154
+ );
155
+ if (childScreens.length > 0) {
156
+ ScreenStore.updateScreen(dismissedScreen.id, {
157
+ closing: true
386
158
  });
387
- },
388
- getPreviousRoute: (key) => {
389
- if (!key) return null;
390
- const index = useRouteStore.getState().routeKeys.indexOf(key);
391
- return index > -1 ? useRouteStore.getState().routes[useRouteStore.getState().routeKeys[index - 1]] : null;
159
+ navigation.dispatch(import_native.StackActions.pop(childScreens.length));
160
+ } else {
161
+ navigation.goBack();
392
162
  }
393
163
  };
394
164
 
165
+ // src/store/utils/remove-screen.tsx
166
+ var removeScreen = (key) => {
167
+ if (!key) return;
168
+ ScreenStore.use.setState(({ screens, screenKeys }) => {
169
+ delete screens[key];
170
+ const indexToRemove = screenKeys.indexOf(key);
171
+ if (indexToRemove > -1) {
172
+ screenKeys.splice(indexToRemove, 1);
173
+ }
174
+ });
175
+ };
176
+
177
+ // src/store/utils/should-skip-prevent-default.tsx
178
+ var shouldSkipPreventDefault = (key, navigatorState) => {
179
+ if (!key) return false;
180
+ const { screens } = ScreenStore.use.getState();
181
+ const currentScreen = screens[key];
182
+ const isLastScreenInStack = navigatorState.routes.length === 1 && navigatorState.routes[0].key === key;
183
+ const isParentNavigatorExiting = Boolean(
184
+ currentScreen?.parentNavigatorKey && Object.values(screens).some(
185
+ (screen) => screen.navigatorKey === currentScreen.parentNavigatorKey && screen.closing
186
+ )
187
+ );
188
+ return isLastScreenInStack || isParentNavigatorExiting;
189
+ };
190
+
191
+ // src/store/utils/update-screen.tsx
192
+ var updateScreen = (key, value) => {
193
+ if (!key) return;
194
+ ScreenStore.use.setState(({ screenKeys, screens }) => {
195
+ const currentScreen = screens[key];
196
+ if (currentScreen) {
197
+ screens[key] = {
198
+ ...currentScreen,
199
+ ...value
200
+ };
201
+ } else {
202
+ const { name = "", status = 0, closing = false, ...rest } = value;
203
+ const newIndex = screenKeys.length;
204
+ screens[key] = {
205
+ id: key,
206
+ index: newIndex,
207
+ name,
208
+ status,
209
+ closing,
210
+ ...rest
211
+ };
212
+ screenKeys.push(key);
213
+ }
214
+ });
215
+ };
216
+
217
+ // src/store/index.ts
218
+ var useScreenStore = createVanillaStore({
219
+ screens: {},
220
+ screenKeys: []
221
+ });
222
+ var ScreenStore = {
223
+ use: useScreenStore,
224
+ updateScreen,
225
+ removeScreen,
226
+ handleScreenDismiss,
227
+ shouldSkipPreventDefault
228
+ };
229
+
395
230
  // src/utils/animate.ts
396
231
  var import_react_native_reanimated2 = require("react-native-reanimated");
397
232
  var animate = (toValue, config, callback) => {
@@ -410,14 +245,15 @@ var animationValues = {
410
245
  gestureY: {},
411
246
  normalizedGestureX: {},
412
247
  normalizedGestureY: {},
413
- gestureDragging: {}
248
+ gestureDragging: {},
249
+ isDismissing: {}
414
250
  };
415
- var triggerAnimation = (route) => {
251
+ var triggerAnimation = (screen) => {
416
252
  "worklet";
417
- const { id, closing, status, transitionSpec, onAnimationFinish } = route;
253
+ const { id, closing, status, transitionSpec, onAnimationFinish } = screen;
418
254
  const progressValue = animationValues.screenProgress[id];
419
255
  if (!progressValue && __DEV__) {
420
- console.warn(`Animation values not found for route: ${id}`);
256
+ console.warn(`Animation values not found for screen: ${id}`);
421
257
  return;
422
258
  }
423
259
  const animationConfig = closing ? transitionSpec?.close : transitionSpec?.open;
@@ -429,14 +265,16 @@ var triggerAnimation = (route) => {
429
265
  }
430
266
  });
431
267
  };
432
- RouteStore.use.subscribeWithSelector(
433
- (state) => state.routes,
434
- (currRoutes, prevRoutes) => {
435
- const currKeys = Object.keys(currRoutes);
436
- const prevKeys = Object.keys(prevRoutes);
268
+ ScreenStore.use.subscribeWithSelector(
269
+ (state) => state.screens,
270
+ (currScreens, prevScreens) => {
271
+ const currKeys = Object.keys(currScreens);
272
+ const prevKeys = Object.keys(prevScreens);
437
273
  const incomingKeys = currKeys.filter((k) => !prevKeys.includes(k));
438
274
  const removedKeys = prevKeys.filter((k) => !currKeys.includes(k));
439
- const changedKeys = currKeys.filter((k) => currRoutes[k] !== prevRoutes[k]);
275
+ const changedKeys = currKeys.filter(
276
+ (k) => currScreens[k] !== prevScreens[k]
277
+ );
440
278
  const animatableValues = Object.values(animationValues);
441
279
  for (const incomingKey of incomingKeys) {
442
280
  for (const value of animatableValues) {
@@ -450,37 +288,101 @@ RouteStore.use.subscribeWithSelector(
450
288
  }
451
289
  }
452
290
  for (const changedKey of changedKeys) {
453
- const currentRoute = currRoutes[changedKey];
454
- if (currentRoute) {
455
- triggerAnimation(currentRoute);
291
+ const currentScreen = currScreens[changedKey];
292
+ if (currentScreen) {
293
+ triggerAnimation(currentScreen);
456
294
  }
457
295
  }
458
296
  }
459
297
  );
460
298
 
461
- // src/utils/gesture/build-gesture-detector.ts
462
- var import_react_native_gesture_handler = require("react-native-gesture-handler");
463
- var import_react_native_reanimated4 = require("react-native-reanimated");
464
-
465
- // src/utils/gesture/create-gesture-activation-criteria.ts
466
- var createGestureActivationCriteria = ({
467
- gestureDirection,
468
- gestureResponseDistance
299
+ // src/utils/create-config.ts
300
+ var createConfig = ({
301
+ navigation: reactNavigation,
302
+ route,
303
+ ...config
469
304
  }) => {
470
- const directions = Array.isArray(gestureDirection) ? gestureDirection : [gestureDirection];
471
- if (directions.includes("bidirectional")) {
472
- return {
473
- activeOffsetX: [
474
- -gestureResponseDistance,
475
- gestureResponseDistance
476
- ],
477
- activeOffsetY: [
478
- -gestureResponseDistance,
479
- gestureResponseDistance
480
- ]
481
- };
482
- }
483
- const allowedDown = directions.includes("vertical");
305
+ return {
306
+ focus: (e) => {
307
+ const parentNavigatorKey = reactNavigation.getParent()?.getState?.()?.key;
308
+ const navigatorKey = reactNavigation.getState().key;
309
+ ScreenStore.updateScreen(e.target, {
310
+ id: e.target,
311
+ name: route.name,
312
+ status: 1,
313
+ closing: false,
314
+ navigatorKey,
315
+ parentNavigatorKey,
316
+ ...config
317
+ });
318
+ },
319
+ beforeRemove: (e) => {
320
+ const shouldSkipPreventDefault2 = ScreenStore.shouldSkipPreventDefault(
321
+ e.target,
322
+ reactNavigation.getState()
323
+ );
324
+ if (shouldSkipPreventDefault2) {
325
+ ScreenStore.removeScreen(e.target);
326
+ return;
327
+ }
328
+ e.preventDefault();
329
+ const handleFinish = (finished) => {
330
+ if (!finished) return;
331
+ if (reactNavigation.canGoBack()) {
332
+ reactNavigation.dispatch(e.data?.action);
333
+ ScreenStore.removeScreen(e.target);
334
+ }
335
+ };
336
+ ScreenStore.updateScreen(e.target, {
337
+ status: 0,
338
+ closing: true,
339
+ onAnimationFinish: handleFinish
340
+ });
341
+ }
342
+ };
343
+ };
344
+ var createScreenConfig = (config) => {
345
+ return {
346
+ listeners: (l) => createConfig({ ...l, ...config || {} })
347
+ };
348
+ };
349
+
350
+ // src/utils/default-screen-options.ts
351
+ var defaultScreenOptions = () => ({
352
+ presentation: "containedTransparentModal",
353
+ headerShown: false,
354
+ animation: "none"
355
+ });
356
+
357
+ // src/utils/noop-interpolator.ts
358
+ var noopinterpolator = () => {
359
+ "worklet";
360
+ return {
361
+ contentStyle: {},
362
+ overlayStyle: {}
363
+ };
364
+ };
365
+
366
+ // src/utils/gesture/apply-gesture-activation-criteria.ts
367
+ var applyGestureActivationCriteria = ({
368
+ gestureDirection,
369
+ gestureResponseDistance,
370
+ panGesture
371
+ }) => {
372
+ const directions = Array.isArray(gestureDirection) ? gestureDirection : [gestureDirection];
373
+ if (directions.includes("bidirectional")) {
374
+ return {
375
+ activeOffsetX: [
376
+ -gestureResponseDistance,
377
+ gestureResponseDistance
378
+ ],
379
+ activeOffsetY: [
380
+ -gestureResponseDistance,
381
+ gestureResponseDistance
382
+ ]
383
+ };
384
+ }
385
+ const allowedDown = directions.includes("vertical");
484
386
  const allowedUp = directions.includes("vertical-inverted");
485
387
  const allowedRight = directions.includes("horizontal");
486
388
  const allowedLeft = directions.includes("horizontal-inverted");
@@ -522,7 +424,20 @@ var createGestureActivationCriteria = ({
522
424
  } else {
523
425
  result.failOffsetY = [-toleranceY, toleranceY];
524
426
  }
525
- return result;
427
+ if (result?.activeOffsetX) {
428
+ panGesture.activeOffsetX(result.activeOffsetX);
429
+ }
430
+ if (result?.activeOffsetY) {
431
+ panGesture.activeOffsetY(result.activeOffsetY);
432
+ }
433
+ if (result?.failOffsetX) {
434
+ panGesture.failOffsetX(result.failOffsetX);
435
+ }
436
+ if (result?.failOffsetY) {
437
+ panGesture.failOffsetY(result.failOffsetY);
438
+ }
439
+ panGesture.enableTrackpadTwoFingerGesture(true);
440
+ return panGesture;
526
441
  };
527
442
 
528
443
  // src/utils/gesture/map-gesture-to-progress.ts
@@ -532,315 +447,400 @@ var mapGestureToProgress = (translation, dimension) => {
532
447
  return Math.max(0, Math.min(1, rawProgress));
533
448
  };
534
449
 
535
- // src/utils/gesture/build-gesture-detector.ts
450
+ // src/hooks/use-key.tsx
451
+ var import_native2 = require("@react-navigation/native");
452
+ var useKey = () => (0, import_native2.useRoute)().key;
453
+
454
+ // src/hooks/use-build-gestures.tsx
536
455
  var GESTURE_VELOCITY_IMPACT = 0.3;
537
456
  var DEFAULT_GESTURE_RESPONSE_DISTANCE = 50;
538
- var buildGestureDetector = ({
539
- key,
540
- progress,
541
- config,
542
- width,
543
- height,
544
- goBack
457
+ var DEFAULT_GESTURE_DIRECTION = "horizontal";
458
+ var DEFAULT_GESTURE_ENABLED = false;
459
+ var useBuildGestures = ({
460
+ scrollProgress
545
461
  }) => {
546
- const _translateX = animationValues.gestureX[key];
547
- const _translateY = animationValues.gestureY[key];
548
- const _normalizedGestureX = animationValues.normalizedGestureX[key];
549
- const _normalizedGestureY = animationValues.normalizedGestureY[key];
550
- const _isDragging = animationValues.gestureDragging[key];
462
+ const key = useKey();
463
+ const dimensions = (0, import_react_native.useWindowDimensions)();
464
+ const navigation = (0, import_native3.useNavigation)();
465
+ const currentScreen = ScreenStore.use(
466
+ (0, import_react3.useCallback)((state) => state.screens[key], [key])
467
+ );
468
+ const handleDismiss = (0, import_react3.useCallback)(
469
+ (screenBeingDismissed) => {
470
+ ScreenStore.handleScreenDismiss(screenBeingDismissed, navigation);
471
+ },
472
+ [navigation]
473
+ );
474
+ const initialTouch = (0, import_react_native_reanimated4.useSharedValue)({
475
+ x: 0,
476
+ y: 0
477
+ });
478
+ const translateX = animationValues.gestureX[key];
479
+ const translateY = animationValues.gestureY[key];
480
+ const normalizedGestureX = animationValues.normalizedGestureX[key];
481
+ const normalizedGestureY = animationValues.normalizedGestureY[key];
482
+ const isDragging = animationValues.gestureDragging[key];
483
+ const isDismissing = animationValues.isDismissing[key];
484
+ const progress = animationValues.screenProgress[key] || 0;
551
485
  const {
552
- gestureDirection = "horizontal",
553
- gestureEnabled = false,
554
- transitionSpec,
486
+ gestureDirection = DEFAULT_GESTURE_DIRECTION,
487
+ gestureEnabled = DEFAULT_GESTURE_ENABLED,
488
+ transitionSpec = {
489
+ open: DefaultSpec,
490
+ close: DefaultSpec
491
+ },
555
492
  gestureVelocityImpact = GESTURE_VELOCITY_IMPACT,
556
493
  gestureResponseDistance = DEFAULT_GESTURE_RESPONSE_DISTANCE
557
- } = config;
494
+ } = currentScreen ?? {};
558
495
  const directions = Array.isArray(gestureDirection) ? gestureDirection : [gestureDirection];
559
- const panGesture = import_react_native_gesture_handler.Gesture.Pan().enabled(gestureEnabled).onStart(() => {
560
- "worklet";
561
- _isDragging.value = 1;
562
- }).onUpdate((event) => {
563
- "worklet";
564
- let gestureProgress = 0;
565
- _translateX.value = event.translationX;
566
- _translateY.value = event.translationY;
567
- _normalizedGestureX.value = (0, import_react_native_reanimated4.interpolate)(
568
- event.translationX,
569
- [-width, width],
570
- [-1, 1],
571
- "clamp"
572
- );
573
- _normalizedGestureY.value = (0, import_react_native_reanimated4.interpolate)(
574
- event.translationY,
575
- [-height, height],
576
- [-1, 1],
577
- "clamp"
578
- );
579
- if (directions.includes("bidirectional")) {
580
- const distance = Math.sqrt(
581
- event.translationX ** 2 + event.translationY ** 2
582
- );
583
- gestureProgress = mapGestureToProgress(distance, width);
584
- } else {
585
- let maxProgress = 0;
586
- const allowedDown = directions.includes("vertical");
587
- const allowedUp = directions.includes("vertical-inverted");
588
- const allowedRight = directions.includes("horizontal");
589
- const allowedLeft = directions.includes("horizontal-inverted");
590
- if (allowedRight || allowedLeft) {
591
- const absX = Math.abs(event.translationX);
592
- const currentProgress = mapGestureToProgress(absX, width);
593
- maxProgress = Math.max(maxProgress, currentProgress);
496
+ const nativeGesture = import_react_native_gesture_handler2.Gesture.Native();
497
+ const onTouchesDown = (0, import_react3.useCallback)(
498
+ (e) => {
499
+ "worklet";
500
+ const firstTouch = e.changedTouches[0];
501
+ initialTouch.value = { x: firstTouch.x, y: firstTouch.y };
502
+ },
503
+ [initialTouch]
504
+ );
505
+ const onTouchesMove = (0, import_react3.useCallback)(
506
+ (e, manager) => {
507
+ "worklet";
508
+ const touch = e.changedTouches[0];
509
+ const deltaX = touch.x - initialTouch.value.x;
510
+ const deltaY = touch.y - initialTouch.value.y;
511
+ const isVerticalSwipe = Math.abs(deltaY) > Math.abs(deltaX);
512
+ const isHorizontalSwipe = Math.abs(deltaX) > Math.abs(deltaY);
513
+ const isSwipingDown = isVerticalSwipe && deltaY > 0;
514
+ const isSwipingUp = isVerticalSwipe && deltaY < 0;
515
+ const isSwipingRight = isHorizontalSwipe && deltaX > 0;
516
+ const isSwipingLeft = isHorizontalSwipe && deltaX < 0;
517
+ const minMovement = 5;
518
+ const hasEnoughMovement = Math.abs(deltaX) > minMovement || Math.abs(deltaY) > minMovement;
519
+ if (!hasEnoughMovement) return;
520
+ if (isDragging.value) {
521
+ manager.activate();
522
+ return;
594
523
  }
595
- if (allowedUp || allowedDown) {
596
- const absY = Math.abs(event.translationY);
597
- const currentProgress = mapGestureToProgress(absY, height);
598
- maxProgress = Math.max(maxProgress, currentProgress);
524
+ let shouldActivate = false;
525
+ for (const direction of directions) {
526
+ switch (direction) {
527
+ case "vertical":
528
+ if (isSwipingDown) {
529
+ shouldActivate = scrollProgress.value.y <= 0;
530
+ }
531
+ break;
532
+ case "vertical-inverted":
533
+ if (isSwipingUp) {
534
+ const maxScrollableY = scrollProgress.value.contentHeight - scrollProgress.value.layoutHeight;
535
+ shouldActivate = scrollProgress.value.y >= maxScrollableY;
536
+ }
537
+ break;
538
+ case "horizontal":
539
+ if (isSwipingRight) {
540
+ shouldActivate = scrollProgress.value.x <= 0;
541
+ }
542
+ break;
543
+ case "horizontal-inverted":
544
+ if (isSwipingLeft) {
545
+ const maxProgress = scrollProgress.value.contentWidth - scrollProgress.value.layoutWidth;
546
+ shouldActivate = scrollProgress.value.x >= maxProgress;
547
+ }
548
+ break;
549
+ case "bidirectional":
550
+ if (isSwipingDown) {
551
+ shouldActivate = scrollProgress.value.y <= 0;
552
+ } else if (isSwipingUp) {
553
+ shouldActivate = scrollProgress.value.y <= 0;
554
+ } else if (isSwipingRight || isSwipingLeft) {
555
+ shouldActivate = true;
556
+ }
557
+ break;
558
+ }
559
+ if (shouldActivate) break;
599
560
  }
600
- gestureProgress = maxProgress;
601
- }
602
- progress.value = 1 - gestureProgress;
603
- }).onEnd((event) => {
561
+ if ((shouldActivate || isDragging.value) && !isDismissing.value) {
562
+ manager.activate();
563
+ } else {
564
+ manager.fail();
565
+ }
566
+ },
567
+ [initialTouch, directions, scrollProgress, isDragging, isDismissing]
568
+ );
569
+ const onStart = (0, import_react3.useCallback)(() => {
604
570
  "worklet";
605
- const { translationX, translationY, velocityX, velocityY } = event;
606
- let shouldDismiss = false;
607
- const dismissThreshold = 0.5;
608
- if (directions.includes("bidirectional")) {
609
- const finalX = Math.abs(
610
- translationX + velocityX * gestureVelocityImpact
571
+ isDragging.value = 1;
572
+ isDismissing.value = 0;
573
+ }, [isDragging, isDismissing]);
574
+ const onUpdate = (0, import_react3.useCallback)(
575
+ (event) => {
576
+ "worklet";
577
+ let gestureProgress = 0;
578
+ translateX.value = event.translationX;
579
+ translateY.value = event.translationY;
580
+ normalizedGestureX.value = (0, import_react_native_reanimated4.interpolate)(
581
+ event.translationX,
582
+ [-dimensions.width, dimensions.width],
583
+ [-1, 1],
584
+ "clamp"
611
585
  );
612
- const finalY = Math.abs(
613
- translationY + velocityY * gestureVelocityImpact
586
+ normalizedGestureY.value = (0, import_react_native_reanimated4.interpolate)(
587
+ event.translationY,
588
+ [-dimensions.height, dimensions.height],
589
+ [-1, 1],
590
+ "clamp"
614
591
  );
615
- const finalDistance = Math.sqrt(finalX ** 2 + finalY ** 2);
616
- shouldDismiss = finalDistance > width * dismissThreshold;
617
- } else {
618
- const allowedDown = directions.includes("vertical");
619
- const allowedUp = directions.includes("vertical-inverted");
620
- const allowedRight = directions.includes("horizontal");
621
- const allowedLeft = directions.includes("horizontal-inverted");
622
- if (allowedRight && translationX + velocityX * gestureVelocityImpact > width * dismissThreshold) {
623
- shouldDismiss = true;
624
- } else if (allowedLeft && -translationX - velocityX * gestureVelocityImpact > width * dismissThreshold) {
625
- shouldDismiss = true;
626
- } else if (allowedDown && translationY + velocityY * gestureVelocityImpact > height * dismissThreshold) {
627
- shouldDismiss = true;
628
- } else if (allowedUp && -translationY - velocityY * gestureVelocityImpact > height * dismissThreshold) {
629
- shouldDismiss = true;
592
+ if (directions.includes("bidirectional")) {
593
+ const distance = Math.sqrt(
594
+ event.translationX ** 2 + event.translationY ** 2
595
+ );
596
+ gestureProgress = mapGestureToProgress(distance, dimensions.width);
597
+ } else {
598
+ let maxProgress = 0;
599
+ const allowedDown = directions.includes("vertical");
600
+ const allowedUp = directions.includes("vertical-inverted");
601
+ const allowedRight = directions.includes("horizontal");
602
+ const allowedLeft = directions.includes("horizontal-inverted");
603
+ if (allowedRight && event.translationX > 0) {
604
+ const currentProgress = mapGestureToProgress(
605
+ event.translationX,
606
+ dimensions.width
607
+ );
608
+ maxProgress = Math.max(maxProgress, currentProgress);
609
+ }
610
+ if (allowedLeft && event.translationX < 0) {
611
+ const currentProgress = mapGestureToProgress(
612
+ -event.translationX,
613
+ dimensions.width
614
+ );
615
+ maxProgress = Math.max(maxProgress, currentProgress);
616
+ }
617
+ if (allowedDown && event.translationY > 0) {
618
+ const currentProgress = mapGestureToProgress(
619
+ event.translationY,
620
+ dimensions.height
621
+ );
622
+ maxProgress = Math.max(maxProgress, currentProgress);
623
+ }
624
+ if (allowedUp && event.translationY < 0) {
625
+ const currentProgress = mapGestureToProgress(
626
+ -event.translationY,
627
+ dimensions.height
628
+ );
629
+ maxProgress = Math.max(maxProgress, currentProgress);
630
+ }
631
+ gestureProgress = maxProgress;
630
632
  }
631
- }
632
- const finalProgress = shouldDismiss ? 0 : 1;
633
- const spec = shouldDismiss ? transitionSpec?.close : transitionSpec?.open;
634
- const onFinish = shouldDismiss ? (isFinished) => {
633
+ progress.value = 1 - gestureProgress;
634
+ },
635
+ [
636
+ dimensions,
637
+ directions,
638
+ translateX,
639
+ translateY,
640
+ normalizedGestureX,
641
+ normalizedGestureY,
642
+ progress
643
+ ]
644
+ );
645
+ const onEnd = (0, import_react3.useCallback)(
646
+ (event) => {
635
647
  "worklet";
636
- if (isFinished) (0, import_react_native_reanimated4.runOnJS)(goBack)();
637
- } : void 0;
638
- progress.value = animate(finalProgress, spec, onFinish);
639
- _translateX.value = animate(0, spec);
640
- _translateY.value = animate(0, spec);
641
- _normalizedGestureX.value = animate(0, spec);
642
- _normalizedGestureY.value = animate(0, spec);
643
- });
644
- const criteria = createGestureActivationCriteria({
648
+ const { translationX, translationY, velocityX, velocityY } = event;
649
+ const dismissThreshold = 0.5;
650
+ if (directions.includes("bidirectional")) {
651
+ const finalX = Math.abs(
652
+ translationX + velocityX * gestureVelocityImpact
653
+ );
654
+ const finalY = Math.abs(
655
+ translationY + velocityY * gestureVelocityImpact
656
+ );
657
+ const finalDistance = Math.sqrt(finalX ** 2 + finalY ** 2);
658
+ isDismissing.value = Number(
659
+ finalDistance > dimensions.width * dismissThreshold
660
+ );
661
+ } else {
662
+ const allowedDown = directions.includes("vertical");
663
+ const allowedUp = directions.includes("vertical-inverted");
664
+ const allowedRight = directions.includes("horizontal");
665
+ const allowedLeft = directions.includes("horizontal-inverted");
666
+ if (allowedRight && translationX + velocityX * gestureVelocityImpact > dimensions.width * dismissThreshold) {
667
+ isDismissing.value = 1;
668
+ } else if (allowedLeft && -translationX - velocityX * gestureVelocityImpact > dimensions.width * dismissThreshold) {
669
+ isDismissing.value = 1;
670
+ } else if (allowedDown && translationY + velocityY * gestureVelocityImpact > dimensions.height * dismissThreshold) {
671
+ isDismissing.value = 1;
672
+ } else if (allowedUp && -translationY - velocityY * gestureVelocityImpact > dimensions.height * dismissThreshold) {
673
+ isDismissing.value = 1;
674
+ }
675
+ }
676
+ const finalProgress = isDismissing.value ? 0 : 1;
677
+ const spec = isDismissing.value ? transitionSpec?.close : transitionSpec?.open;
678
+ const onFinish = isDismissing.value ? (isFinished) => {
679
+ "worklet";
680
+ if (isFinished) (0, import_react_native_reanimated4.runOnJS)(handleDismiss)(currentScreen?.id);
681
+ } : void 0;
682
+ progress.value = animate(finalProgress, spec, onFinish);
683
+ translateX.value = animate(0, spec);
684
+ translateY.value = animate(0, spec);
685
+ normalizedGestureX.value = animate(0, spec);
686
+ normalizedGestureY.value = animate(0, spec);
687
+ isDragging.value = 0;
688
+ },
689
+ [
690
+ dimensions,
691
+ directions,
692
+ translateX,
693
+ translateY,
694
+ normalizedGestureX,
695
+ normalizedGestureY,
696
+ progress,
697
+ handleDismiss,
698
+ currentScreen?.id,
699
+ transitionSpec?.close,
700
+ transitionSpec?.open,
701
+ gestureVelocityImpact,
702
+ isDragging,
703
+ isDismissing
704
+ ]
705
+ );
706
+ const panGesture = (0, import_react3.useMemo)(
707
+ () => import_react_native_gesture_handler2.Gesture.Pan().enabled(gestureEnabled).manualActivation(true).onTouchesDown(onTouchesDown).onTouchesMove(onTouchesMove).onStart(onStart).onUpdate(onUpdate).onEnd(onEnd).blocksExternalGesture(nativeGesture),
708
+ [
709
+ gestureEnabled,
710
+ nativeGesture,
711
+ onTouchesDown,
712
+ onTouchesMove,
713
+ onStart,
714
+ onUpdate,
715
+ onEnd
716
+ ]
717
+ );
718
+ applyGestureActivationCriteria({
645
719
  gestureDirection,
646
- gestureResponseDistance
720
+ gestureResponseDistance,
721
+ panGesture
647
722
  });
648
- if (criteria?.activeOffsetX) {
649
- panGesture.activeOffsetX(criteria.activeOffsetX);
650
- }
651
- if (criteria?.activeOffsetY) {
652
- panGesture.activeOffsetY(criteria.activeOffsetY);
653
- }
654
- if (criteria?.failOffsetX) {
655
- panGesture.failOffsetX(criteria.failOffsetX);
656
- }
657
- if (criteria?.failOffsetY) {
658
- panGesture.failOffsetY(criteria.failOffsetY);
659
- }
660
- panGesture.enableTrackpadTwoFingerGesture(true);
661
- const nativeGesture = import_react_native_gesture_handler.Gesture.Native().shouldCancelWhenOutside(false);
662
- return import_react_native_gesture_handler.Gesture.Race(panGesture, nativeGesture);
723
+ return { panGesture, nativeGesture };
663
724
  };
664
725
 
665
- // src/utils/noop-interpolator.ts
666
- var noopinterpolator = () => {
667
- "worklet";
668
- return {
669
- contentStyle: {},
670
- overlayStyle: {}
671
- };
726
+ // src/components/transition-gesture-handler-provider.tsx
727
+ var import_jsx_runtime = require("react/jsx-runtime");
728
+ var TransitionGestureHandlerProvider = ({
729
+ children
730
+ }) => {
731
+ const scrollProgress = (0, import_react_native_reanimated5.useSharedValue)({
732
+ x: 0,
733
+ y: 0,
734
+ contentHeight: 0,
735
+ contentWidth: 0,
736
+ layoutHeight: 0,
737
+ layoutWidth: 0
738
+ });
739
+ const { panGesture, nativeGesture } = useBuildGestures({
740
+ scrollProgress
741
+ });
742
+ const value = (0, import_react4.useMemo)(() => {
743
+ return {
744
+ panGesture,
745
+ scrollProgress,
746
+ nativeGesture
747
+ };
748
+ }, [panGesture, scrollProgress, nativeGesture]);
749
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(GestureContext.Provider, { value, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native_gesture_handler3.GestureDetector, { gesture: panGesture, children }) });
672
750
  };
673
751
 
674
- // src/hooks/use-key.tsx
675
- var import_native = require("@react-navigation/native");
676
- var useKey = () => (0, import_native.useRoute)().key;
677
-
678
752
  // src/hooks/use-screen-animation.tsx
753
+ var import_react5 = require("react");
754
+ var import_react_native2 = require("react-native");
755
+ var import_react_native_reanimated6 = require("react-native-reanimated");
756
+ var import_react_native_safe_area_context = require("react-native-safe-area-context");
679
757
  var useAnimationBuilder = () => {
680
758
  const key = useKey();
681
- const dimensions = (0, import_react_native.useWindowDimensions)();
759
+ const dimensions = (0, import_react_native2.useWindowDimensions)();
682
760
  const insets = (0, import_react_native_safe_area_context.useSafeAreaInsets)();
683
- const navigation = (0, import_native2.useNavigation)();
684
- const { currentRoute, nextRoute } = RouteStore.use(
685
- useShallow(({ routes, routeKeys }) => {
686
- const current = routes[key];
687
- if (!current) {
688
- return { currentRoute: void 0, nextRoute: void 0 };
689
- }
690
- const currentScreenIndex = current.index;
691
- const nextKey = routeKeys[currentScreenIndex + 1];
692
- const next = nextKey ? routes[nextKey] : void 0;
693
- const isSameNavigator = next?.navigatorKey === current.navigatorKey;
694
- if (!isSameNavigator) {
695
- return {
696
- currentRoute: current,
697
- nextRoute: void 0
698
- };
699
- }
700
- return {
701
- currentRoute: current,
702
- nextRoute: next
703
- };
704
- })
761
+ const progressFallback = (0, import_react_native_reanimated6.useSharedValue)(0);
762
+ const gestureDraggingFallback = (0, import_react_native_reanimated6.useSharedValue)(0);
763
+ const gestureXFallback = (0, import_react_native_reanimated6.useSharedValue)(0);
764
+ const gestureYFallback = (0, import_react_native_reanimated6.useSharedValue)(0);
765
+ const normalizedGestureXFallback = (0, import_react_native_reanimated6.useSharedValue)(0);
766
+ const normalizedGestureYFallback = (0, import_react_native_reanimated6.useSharedValue)(0);
767
+ const isDismissingFallback = (0, import_react_native_reanimated6.useSharedValue)(0);
768
+ const currentScreen = ScreenStore.use(
769
+ (0, import_react5.useCallback)((state) => state.screens[key], [key])
705
770
  );
706
- const panGesture = (0, import_react3.useMemo)(
707
- () => buildGestureDetector({
708
- key,
709
- progress: animationValues.screenProgress[key],
710
- config: currentRoute || {
711
- id: key,
712
- name: key,
713
- index: 0,
714
- status: 0,
715
- closing: false
771
+ const actualNextScreen = ScreenStore.use(
772
+ (0, import_react5.useCallback)(
773
+ (state) => {
774
+ const current = state.screens[key];
775
+ if (!current) return void 0;
776
+ const nextKey = state.screenKeys[current.index + 1];
777
+ const nextScreen = nextKey ? state.screens[nextKey] : void 0;
778
+ const shouldUseNext = nextScreen?.navigatorKey === current?.navigatorKey;
779
+ return shouldUseNext ? nextScreen : void 0;
716
780
  },
717
- width: dimensions.width,
718
- height: dimensions.height,
719
- goBack: navigation.goBack
781
+ [key]
782
+ )
783
+ );
784
+ const getAnimationValuesForScreen = (0, import_react5.useCallback)(
785
+ (screenId) => ({
786
+ progress: animationValues.screenProgress[screenId] || progressFallback,
787
+ gesture: {
788
+ isDragging: animationValues.gestureDragging[screenId] || gestureDraggingFallback,
789
+ x: animationValues.gestureX[screenId] || gestureXFallback,
790
+ y: animationValues.gestureY[screenId] || gestureYFallback,
791
+ normalizedX: animationValues.normalizedGestureX[screenId] || normalizedGestureXFallback,
792
+ normalizedY: animationValues.normalizedGestureY[screenId] || normalizedGestureYFallback,
793
+ isDismissing: animationValues.isDismissing[screenId] || isDismissingFallback
794
+ }
720
795
  }),
721
- [key, currentRoute, dimensions.width, dimensions.height, navigation.goBack]
796
+ [
797
+ progressFallback,
798
+ gestureDraggingFallback,
799
+ gestureXFallback,
800
+ gestureYFallback,
801
+ normalizedGestureXFallback,
802
+ normalizedGestureYFallback,
803
+ isDismissingFallback
804
+ ]
722
805
  );
723
- const progressFallback = (0, import_react_native_reanimated5.useSharedValue)(0);
724
- const gestureDraggingFallback = (0, import_react_native_reanimated5.useSharedValue)(0);
725
- const gestureXFallback = (0, import_react_native_reanimated5.useSharedValue)(0);
726
- const gestureYFallback = (0, import_react_native_reanimated5.useSharedValue)(0);
727
- const normalizedGestureXFallback = (0, import_react_native_reanimated5.useSharedValue)(0);
728
- const normalizedGestureYFallback = (0, import_react_native_reanimated5.useSharedValue)(0);
729
- return (0, import_react3.useMemo)(() => {
806
+ return (0, import_react5.useMemo)(() => {
730
807
  return {
731
- current: {
732
- progress: animationValues.screenProgress[key] || progressFallback,
733
- gesture: {
734
- isDragging: animationValues.gestureDragging[key] || gestureDraggingFallback,
735
- x: animationValues.gestureX[key] || gestureXFallback,
736
- y: animationValues.gestureY[key] || gestureYFallback,
737
- normalizedX: animationValues.normalizedGestureX[key] || normalizedGestureXFallback,
738
- normalizedY: animationValues.normalizedGestureY[key] || normalizedGestureYFallback
739
- }
740
- },
741
- next: nextRoute && animationValues.screenProgress[nextRoute.id] ? {
742
- progress: animationValues.screenProgress[nextRoute.id],
743
- gesture: {
744
- isDragging: animationValues.gestureDragging[nextRoute.id] || gestureDraggingFallback,
745
- x: animationValues.gestureX[nextRoute.id] || gestureXFallback,
746
- y: animationValues.gestureY[nextRoute.id] || gestureYFallback,
747
- normalizedX: animationValues.normalizedGestureX[nextRoute.id] || normalizedGestureXFallback,
748
- normalizedY: animationValues.normalizedGestureY[nextRoute.id] || normalizedGestureYFallback
749
- }
750
- } : void 0,
808
+ current: getAnimationValuesForScreen(key),
809
+ next: actualNextScreen ? getAnimationValuesForScreen(actualNextScreen.id) : void 0,
751
810
  layouts: { screen: dimensions },
752
811
  insets,
753
- closing: currentRoute?.closing || false,
754
- screenStyleInterpolator: nextRoute?.screenStyleInterpolator || currentRoute?.screenStyleInterpolator || noopinterpolator,
755
- gestureDetector: panGesture
812
+ closing: currentScreen?.closing || false,
813
+ screenStyleInterpolator: actualNextScreen?.screenStyleInterpolator || currentScreen?.screenStyleInterpolator || noopinterpolator
756
814
  };
757
815
  }, [
758
816
  key,
759
- currentRoute,
760
- nextRoute,
817
+ currentScreen,
818
+ actualNextScreen,
761
819
  dimensions,
762
820
  insets,
763
- panGesture,
764
- progressFallback,
765
- gestureDraggingFallback,
766
- gestureXFallback,
767
- gestureYFallback,
768
- normalizedGestureXFallback,
769
- normalizedGestureYFallback
821
+ getAnimationValuesForScreen
770
822
  ]);
771
823
  };
772
824
  var _useScreenAnimation = () => {
773
825
  return useAnimationBuilder();
774
826
  };
775
827
  var useScreenAnimation = () => {
776
- const {
777
- screenStyleInterpolator: _,
778
- gestureDetector: __,
779
- ...animationProps
780
- } = useAnimationBuilder();
828
+ const { screenStyleInterpolator: _, ...animationProps } = useAnimationBuilder();
781
829
  return animationProps;
782
830
  };
783
831
 
784
- // src/utils/create-config.ts
785
- var createConfig = ({
786
- navigation: reactNavigation,
787
- route,
788
- ...config
789
- }) => {
790
- return {
791
- focus: (e) => {
792
- const navigatorKey = reactNavigation.getState().key;
793
- RouteStore.updateRoute(e.target, {
794
- id: e.target,
795
- name: route.name,
796
- status: 1,
797
- closing: false,
798
- navigatorKey,
799
- ...config
800
- });
801
- },
802
- beforeRemove: (e) => {
803
- const navigatorState = reactNavigation.getState();
804
- const isLastScreenInStack = navigatorState.routes.length === 1 && navigatorState.routes[0].key === e.target;
805
- if (isLastScreenInStack) {
806
- RouteStore.removeRoute(e.target);
807
- return;
808
- }
809
- e.preventDefault();
810
- const handleFinish = (finished) => {
811
- if (!finished) return;
812
- if (reactNavigation.canGoBack()) {
813
- reactNavigation.dispatch(e.data?.action);
814
- RouteStore.removeRoute(e.target);
815
- }
816
- };
817
- RouteStore.updateRoute(e.target, {
818
- status: 0,
819
- closing: true,
820
- onAnimationFinish: handleFinish
821
- });
822
- }
823
- };
824
- };
825
-
826
- // src/utils/create-transition-component.tsx
827
- var import_react5 = require("react");
828
- var import_react_native2 = require("react-native");
829
- var import_react_native_gesture_handler2 = require("react-native-gesture-handler");
830
- var import_react_native_reanimated7 = __toESM(require("react-native-reanimated"));
831
-
832
832
  // src/hooks/use-skip-first-frame.tsx
833
- var import_react4 = require("react");
834
- var import_react_native_reanimated6 = require("react-native-reanimated");
833
+ var import_react6 = require("react");
834
+ var import_react_native_reanimated7 = require("react-native-reanimated");
835
835
  var useSkipFirstFrame = () => {
836
- const opacity = (0, import_react_native_reanimated6.useSharedValue)(0);
837
- const style = (0, import_react_native_reanimated6.useAnimatedStyle)(() => {
836
+ const opacity = (0, import_react_native_reanimated7.useSharedValue)(0);
837
+ const style = (0, import_react_native_reanimated7.useAnimatedStyle)(() => {
838
838
  "worklet";
839
839
  return {
840
840
  opacity: opacity.value
841
841
  };
842
842
  });
843
- (0, import_react4.useEffect)(() => {
843
+ (0, import_react6.useEffect)(() => {
844
844
  requestAnimationFrame(() => {
845
845
  opacity.value = 1;
846
846
  });
@@ -848,29 +848,32 @@ var useSkipFirstFrame = () => {
848
848
  return { style };
849
849
  };
850
850
 
851
- // src/utils/create-transition-component.tsx
852
- var import_jsx_runtime = require("react/jsx-runtime");
853
- function createTransitionComponent(Wrapped) {
854
- const AnimatedComponent = import_react_native_reanimated7.default.createAnimatedComponent(Wrapped);
855
- const Inner = (0, import_react5.forwardRef)(
851
+ // src/components/create-transition-aware-component.tsx
852
+ var import_jsx_runtime2 = require("react/jsx-runtime");
853
+ function createTransitionAwareComponent(Wrapped) {
854
+ const AnimatedComponent = import_react_native_reanimated8.default.createAnimatedComponent(Wrapped);
855
+ const Inner = (0, import_react7.forwardRef)(
856
856
  (props, ref) => {
857
857
  const { children, style, ...rest } = props;
858
- const {
859
- screenStyleInterpolator,
860
- gestureDetector,
861
- ...screenInterpolationProps
862
- } = _useScreenAnimation();
863
- const screenContainerStyle = (0, import_react_native_reanimated7.useAnimatedStyle)(() => {
858
+ const { screenStyleInterpolator, ...screenInterpolationProps } = _useScreenAnimation();
859
+ const screenContainerStyle = (0, import_react_native_reanimated8.useAnimatedStyle)(() => {
864
860
  "worklet";
865
861
  return screenStyleInterpolator(screenInterpolationProps).contentStyle || {};
866
862
  });
867
- const overlayStyle = (0, import_react_native_reanimated7.useAnimatedStyle)(() => {
863
+ const overlayStyle = (0, import_react_native_reanimated8.useAnimatedStyle)(() => {
868
864
  "worklet";
869
865
  return screenStyleInterpolator(screenInterpolationProps).overlayStyle || {};
870
866
  });
871
867
  const { style: flickerFixStyle } = useSkipFirstFrame();
872
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react_native_reanimated7.default.View, { style: [{ flex: 1 }, flickerFixStyle], children: [
873
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native_gesture_handler2.GestureDetector, { gesture: gestureDetector, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
868
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(TransitionGestureHandlerProvider, { children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react_native_reanimated8.default.View, { style: [{ flex: 1 }, flickerFixStyle], children: [
869
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
870
+ import_react_native_reanimated8.default.View,
871
+ {
872
+ style: [import_react_native3.StyleSheet.absoluteFillObject, overlayStyle],
873
+ pointerEvents: "none"
874
+ }
875
+ ),
876
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
874
877
  AnimatedComponent,
875
878
  {
876
879
  ...rest,
@@ -882,41 +885,282 @@ function createTransitionComponent(Wrapped) {
882
885
  ],
883
886
  children
884
887
  }
885
- ) }),
886
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
887
- import_react_native_reanimated7.default.View,
888
- {
889
- style: [
890
- import_react_native2.StyleSheet.absoluteFillObject,
891
- overlayStyle,
892
- { zIndex: 1e4 }
893
- ],
894
- pointerEvents: "none"
895
- }
896
888
  )
897
- ] });
889
+ ] }) });
898
890
  }
899
891
  );
900
892
  Inner.displayName = `Transition(${Wrapped.displayName || Wrapped.name || "Component"})`;
901
- return (0, import_react5.memo)(Inner);
893
+ return (0, import_react7.memo)(Inner);
902
894
  }
903
895
 
904
- // src/utils/default-screen-options.ts
905
- var defaultScreenOptions = () => ({
906
- presentation: "containedTransparentModal",
907
- headerShown: false,
908
- animation: "none"
896
+ // src/components/create-transition-aware-scrollable.tsx
897
+ var import_react9 = require("react");
898
+ var import_react_native4 = require("react-native");
899
+ var import_react_native_gesture_handler4 = require("react-native-gesture-handler");
900
+ var import_react_native_reanimated10 = __toESM(require("react-native-reanimated"));
901
+
902
+ // src/hooks/use-scroll-progress.tsx
903
+ var import_react8 = require("react");
904
+ var import_react_native_reanimated9 = require("react-native-reanimated");
905
+ var useScrollProgress = (props) => {
906
+ const { scrollProgress } = useGestureContext();
907
+ const scrollHandler = (0, import_react_native_reanimated9.useAnimatedScrollHandler)({
908
+ onScroll: (event) => {
909
+ scrollProgress.modify((value) => {
910
+ "worklet";
911
+ return {
912
+ ...value,
913
+ x: event.contentOffset.x,
914
+ y: event.contentOffset.y,
915
+ layoutHeight: event.layoutMeasurement.height,
916
+ layoutWidth: event.layoutMeasurement.width,
917
+ contentHeight: event.contentSize.height,
918
+ contentWidth: event.contentSize.width
919
+ };
920
+ });
921
+ }
922
+ });
923
+ const onContentSizeChange = (0, import_react8.useCallback)(
924
+ (width, height) => {
925
+ props.onContentSizeChange?.(width, height);
926
+ scrollProgress.modify((value) => {
927
+ "worklet";
928
+ return {
929
+ ...value,
930
+ contentWidth: width,
931
+ contentHeight: height
932
+ };
933
+ });
934
+ },
935
+ [scrollProgress, props.onContentSizeChange]
936
+ );
937
+ return {
938
+ scrollHandler,
939
+ onContentSizeChange
940
+ };
941
+ };
942
+
943
+ // src/components/create-transition-aware-scrollable.tsx
944
+ var import_jsx_runtime3 = require("react/jsx-runtime");
945
+ function createTransitionAwareScrollable(ScrollableComponent) {
946
+ const WithTransitionAwareness = createTransitionAwareComponent(import_react_native4.View);
947
+ const AnimatedScrollableComponent = import_react_native_reanimated10.default.createAnimatedComponent(ScrollableComponent);
948
+ const WithScrollAwareness = (0, import_react9.forwardRef)((props, ref) => {
949
+ const { nativeGesture } = useGestureContext();
950
+ const { scrollHandler, onContentSizeChange } = useScrollProgress({
951
+ onScroll: props.onScroll,
952
+ onContentSizeChange: props.onContentSizeChange
953
+ });
954
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native_gesture_handler4.GestureDetector, { gesture: nativeGesture, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
955
+ AnimatedScrollableComponent,
956
+ {
957
+ ...props,
958
+ ref,
959
+ onScroll: scrollHandler,
960
+ onContentSizeChange,
961
+ scrollEventThrottle: props.scrollEventThrottle || 16
962
+ }
963
+ ) });
964
+ });
965
+ const Wrapped = (0, import_react9.forwardRef)((props, ref) => {
966
+ const { isPlaceholder } = useGestureContext();
967
+ if (isPlaceholder) {
968
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(WithTransitionAwareness, { children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(WithScrollAwareness, { ...props, ref }) });
969
+ }
970
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(WithScrollAwareness, { ...props, ref });
971
+ });
972
+ WithScrollAwareness.displayName = `Transition(${ScrollableComponent.displayName || ScrollableComponent.name || "Component"})`;
973
+ return (0, import_react9.memo)(Wrapped);
974
+ }
975
+
976
+ // src/configs/presets.ts
977
+ var presets_exports = {};
978
+ __export(presets_exports, {
979
+ DraggableCard: () => DraggableCard,
980
+ ElasticCard: () => ElasticCard,
981
+ SlideFromBottom: () => SlideFromBottom,
982
+ SlideFromTop: () => SlideFromTop,
983
+ ZoomIn: () => ZoomIn
909
984
  });
985
+ var import_react_native_reanimated11 = require("react-native-reanimated");
986
+ var SlideFromTop = (config = {}) => {
987
+ return {
988
+ gestureEnabled: true,
989
+ gestureDirection: "vertical-inverted",
990
+ screenStyleInterpolator: ({
991
+ current,
992
+ next,
993
+ layouts: {
994
+ screen: { height }
995
+ }
996
+ }) => {
997
+ "worklet";
998
+ const progress = current.progress.value + (next?.progress.value ?? 0);
999
+ const y = (0, import_react_native_reanimated11.interpolate)(progress, [0, 1, 2], [-height, 0, height]);
1000
+ return {
1001
+ contentStyle: {
1002
+ transform: [{ translateY: y }]
1003
+ }
1004
+ };
1005
+ },
1006
+ transitionSpec: {
1007
+ open: DefaultSpec,
1008
+ close: DefaultSpec
1009
+ },
1010
+ ...config
1011
+ };
1012
+ };
1013
+ var ZoomIn = (config = {}) => {
1014
+ return {
1015
+ gestureEnabled: false,
1016
+ screenStyleInterpolator: ({ current, next }) => {
1017
+ "worklet";
1018
+ const progress = current.progress.value + (next?.progress.value ?? 0);
1019
+ const scale = (0, import_react_native_reanimated11.interpolate)(
1020
+ progress,
1021
+ [0, 1, 2],
1022
+ [0.5, 1, 0.5],
1023
+ import_react_native_reanimated11.Extrapolation.CLAMP
1024
+ );
1025
+ const opacity = (0, import_react_native_reanimated11.interpolate)(
1026
+ progress,
1027
+ [0, 1, 2],
1028
+ [0, 1, 0],
1029
+ import_react_native_reanimated11.Extrapolation.CLAMP
1030
+ );
1031
+ return {
1032
+ contentStyle: {
1033
+ transform: [{ scale }],
1034
+ opacity
1035
+ }
1036
+ };
1037
+ },
1038
+ transitionSpec: {
1039
+ open: DefaultSpec,
1040
+ close: DefaultSpec
1041
+ },
1042
+ ...config
1043
+ };
1044
+ };
1045
+ var SlideFromBottom = (config = {}) => {
1046
+ return {
1047
+ gestureEnabled: true,
1048
+ gestureDirection: "vertical",
1049
+ screenStyleInterpolator: ({
1050
+ current,
1051
+ next,
1052
+ layouts: {
1053
+ screen: { height }
1054
+ }
1055
+ }) => {
1056
+ "worklet";
1057
+ const progress = current.progress.value + (next?.progress.value ?? 0);
1058
+ const y = (0, import_react_native_reanimated11.interpolate)(progress, [0, 1, 2], [height, 0, -height]);
1059
+ return {
1060
+ contentStyle: {
1061
+ transform: [{ translateY: y }]
1062
+ }
1063
+ };
1064
+ },
1065
+ transitionSpec: {
1066
+ open: DefaultSpec,
1067
+ close: DefaultSpec
1068
+ },
1069
+ ...config
1070
+ };
1071
+ };
1072
+ var DraggableCard = (config = {}) => {
1073
+ return {
1074
+ gestureEnabled: true,
1075
+ gestureDirection: ["horizontal", "vertical"],
1076
+ screenStyleInterpolator: ({ current, next, layouts: { screen } }) => {
1077
+ "worklet";
1078
+ const progress = current.progress.value + (next?.progress.value ?? 0);
1079
+ const scale = (0, import_react_native_reanimated11.interpolate)(progress, [0, 1, 2], [0, 1, 0.75]);
1080
+ const translateY = (0, import_react_native_reanimated11.interpolate)(
1081
+ current.gesture.normalizedY.value,
1082
+ [-1, 1],
1083
+ [-screen.height * 0.5, screen.height * 0.5],
1084
+ "clamp"
1085
+ );
1086
+ const translateX = (0, import_react_native_reanimated11.interpolate)(
1087
+ current.gesture.normalizedX.value,
1088
+ [-1, 1],
1089
+ [-screen.width * 0.5, screen.width * 0.5],
1090
+ "clamp"
1091
+ );
1092
+ return {
1093
+ contentStyle: {
1094
+ transform: [{ scale }, { translateY }, { translateX }]
1095
+ }
1096
+ };
1097
+ },
1098
+ transitionSpec: {
1099
+ open: DefaultSpec,
1100
+ close: DefaultSpec
1101
+ },
1102
+ ...config
1103
+ };
1104
+ };
1105
+ var ElasticCard = (config = { elasticFactor: 0.5 }) => {
1106
+ return {
1107
+ gestureEnabled: true,
1108
+ gestureDirection: "bidirectional",
1109
+ screenStyleInterpolator: ({ current, next, layouts: { screen } }) => {
1110
+ "worklet";
1111
+ const progress = current.progress.value + (next?.progress.value ?? 0);
1112
+ const scale = (0, import_react_native_reanimated11.interpolate)(progress, [0, 1, 2], [0, 1, 0.8]);
1113
+ const maxElasticityX = screen.width * (config.elasticFactor ?? 0.5);
1114
+ const maxElasticityY = screen.height * (config.elasticFactor ?? 0.5);
1115
+ const translateX = (0, import_react_native_reanimated11.interpolate)(
1116
+ current.gesture.normalizedX.value,
1117
+ [-1, 0, 1],
1118
+ [-maxElasticityX, 0, maxElasticityX],
1119
+ "clamp"
1120
+ );
1121
+ const translateY = (0, import_react_native_reanimated11.interpolate)(
1122
+ current.gesture.normalizedY.value,
1123
+ [-1, 0, 1],
1124
+ [-maxElasticityY, 0, maxElasticityY],
1125
+ "clamp"
1126
+ );
1127
+ const overlayColor = (0, import_react_native_reanimated11.interpolateColor)(
1128
+ next?.progress.value || 0,
1129
+ [0, 1],
1130
+ ["rgba(0,0,0,0)", "rgba(0,0,0,0.5)"]
1131
+ );
1132
+ return {
1133
+ contentStyle: {
1134
+ transform: [{ scale }, { translateX }, { translateY }]
1135
+ },
1136
+ overlayStyle: {
1137
+ backgroundColor: overlayColor
1138
+ }
1139
+ };
1140
+ },
1141
+ ...config
1142
+ };
1143
+ };
910
1144
 
911
1145
  // src/index.ts
912
1146
  var index_default = {
913
- createTransitionComponent,
914
- View: createTransitionComponent(import_react_native3.View),
915
- Pressable: createTransitionComponent(import_react_native3.Pressable),
1147
+ View: createTransitionAwareComponent(import_react_native5.View),
1148
+ Pressable: createTransitionAwareComponent(import_react_native5.Pressable),
1149
+ ScrollView: createTransitionAwareScrollable(import_react_native5.ScrollView),
1150
+ FlatList: createTransitionAwareScrollable(import_react_native5.FlatList),
916
1151
  createConfig,
1152
+ createScreenConfig,
917
1153
  defaultScreenOptions,
918
1154
  presets: presets_exports,
919
- specs: specs_exports
1155
+ specs: specs_exports,
1156
+ /**
1157
+ * Create a transition aware component
1158
+ */
1159
+ createTransitionAwareComponent,
1160
+ /**
1161
+ * Create a transition aware scrollable component
1162
+ */
1163
+ createTransitionAwareScrollable
920
1164
  };
921
1165
  // Annotate the CommonJS export names for ESM import in node:
922
1166
  0 && (module.exports = {