react-native-screen-transitions 1.0.1

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.mjs ADDED
@@ -0,0 +1,891 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __export = (target, all) => {
3
+ for (var name in all)
4
+ __defProp(target, name, { get: all[name], enumerable: true });
5
+ };
6
+
7
+ // src/index.ts
8
+ import { Pressable, View } from "react-native";
9
+
10
+ // src/configs/presets.ts
11
+ var presets_exports = {};
12
+ __export(presets_exports, {
13
+ DraggableCard: () => DraggableCard,
14
+ ElasticCard: () => ElasticCard,
15
+ SlideFromBottom: () => SlideFromBottom,
16
+ SlideFromTop: () => SlideFromTop,
17
+ ZoomIn: () => ZoomIn
18
+ });
19
+ import {
20
+ Extrapolation,
21
+ interpolate,
22
+ interpolateColor
23
+ } from "react-native-reanimated";
24
+
25
+ // src/configs/specs.ts
26
+ var specs_exports = {};
27
+ __export(specs_exports, {
28
+ DefaultSpec: () => DefaultSpec
29
+ });
30
+ var DefaultSpec = {
31
+ stiffness: 1e3,
32
+ damping: 500,
33
+ mass: 3,
34
+ overshootClamping: true,
35
+ restSpeedThreshold: 0.01
36
+ };
37
+
38
+ // src/configs/presets.ts
39
+ var SlideFromTop = (config = {}) => {
40
+ return {
41
+ gestureEnabled: true,
42
+ gestureDirection: "vertical-inverted",
43
+ screenStyleInterpolator: ({
44
+ current,
45
+ next,
46
+ layouts: {
47
+ screen: { height }
48
+ }
49
+ }) => {
50
+ "worklet";
51
+ const progress = current.progress.value + (next?.progress.value ?? 0);
52
+ const y = interpolate(progress, [0, 1, 2], [-height, 0, height]);
53
+ return {
54
+ contentStyle: {
55
+ transform: [{ translateY: y }]
56
+ }
57
+ };
58
+ },
59
+ transitionSpec: {
60
+ open: DefaultSpec,
61
+ close: DefaultSpec
62
+ },
63
+ ...config
64
+ };
65
+ };
66
+ var ZoomIn = (config = {}) => {
67
+ return {
68
+ gestureEnabled: false,
69
+ screenStyleInterpolator: ({ current, next }) => {
70
+ "worklet";
71
+ const progress = current.progress.value + (next?.progress.value ?? 0);
72
+ const scale = interpolate(
73
+ progress,
74
+ [0, 1, 2],
75
+ [0.5, 1, 0.5],
76
+ Extrapolation.CLAMP
77
+ );
78
+ const opacity = interpolate(
79
+ progress,
80
+ [0, 1, 2],
81
+ [0, 1, 0],
82
+ Extrapolation.CLAMP
83
+ );
84
+ return {
85
+ contentStyle: {
86
+ transform: [{ scale }],
87
+ opacity
88
+ }
89
+ };
90
+ },
91
+ transitionSpec: {
92
+ open: DefaultSpec,
93
+ close: DefaultSpec
94
+ },
95
+ ...config
96
+ };
97
+ };
98
+ var SlideFromBottom = (config = {}) => {
99
+ return {
100
+ gestureEnabled: true,
101
+ gestureDirection: "vertical",
102
+ screenStyleInterpolator: ({
103
+ current,
104
+ next,
105
+ layouts: {
106
+ screen: { height }
107
+ }
108
+ }) => {
109
+ "worklet";
110
+ const progress = current.progress.value + (next?.progress.value ?? 0);
111
+ const y = interpolate(progress, [0, 1, 2], [height, 0, -height]);
112
+ return {
113
+ contentStyle: {
114
+ transform: [{ translateY: y }]
115
+ }
116
+ };
117
+ },
118
+ transitionSpec: {
119
+ open: DefaultSpec,
120
+ close: DefaultSpec
121
+ },
122
+ ...config
123
+ };
124
+ };
125
+ var DraggableCard = (config = {}) => {
126
+ return {
127
+ gestureEnabled: true,
128
+ gestureDirection: ["horizontal", "vertical"],
129
+ screenStyleInterpolator: ({ current, next, layouts: { screen } }) => {
130
+ "worklet";
131
+ const progress = current.progress.value + (next?.progress.value ?? 0);
132
+ const scale = interpolate(progress, [0, 1, 2], [0, 1, 0.75]);
133
+ const translateY = interpolate(
134
+ current.gesture.normalizedY.value,
135
+ [-1, 1],
136
+ [-screen.height * 0.5, screen.height * 0.5],
137
+ "clamp"
138
+ );
139
+ const translateX = interpolate(
140
+ current.gesture.normalizedX.value,
141
+ [-1, 1],
142
+ [-screen.width * 0.5, screen.width * 0.5],
143
+ "clamp"
144
+ );
145
+ return {
146
+ contentStyle: {
147
+ transform: [{ scale }, { translateY }, { translateX }]
148
+ }
149
+ };
150
+ },
151
+ transitionSpec: {
152
+ open: DefaultSpec,
153
+ close: DefaultSpec
154
+ },
155
+ ...config
156
+ };
157
+ };
158
+ var ElasticCard = (config = { elasticFactor: 0.5 }) => {
159
+ return {
160
+ gestureEnabled: true,
161
+ gestureDirection: "bidirectional",
162
+ screenStyleInterpolator: ({ current, next, layouts: { screen } }) => {
163
+ "worklet";
164
+ const progress = current.progress.value + (next?.progress.value ?? 0);
165
+ const scale = interpolate(progress, [0, 1, 2], [0, 1, 0.8]);
166
+ const maxElasticityX = screen.width * (config.elasticFactor ?? 0.5);
167
+ const maxElasticityY = screen.height * (config.elasticFactor ?? 0.5);
168
+ const translateX = interpolate(
169
+ current.gesture.normalizedX.value,
170
+ [-1, 0, 1],
171
+ [-maxElasticityX, 0, maxElasticityX],
172
+ "clamp"
173
+ );
174
+ const translateY = interpolate(
175
+ current.gesture.normalizedY.value,
176
+ [-1, 0, 1],
177
+ [-maxElasticityY, 0, maxElasticityY],
178
+ "clamp"
179
+ );
180
+ const overlayColor = interpolateColor(
181
+ next?.progress.value || 0,
182
+ [0, 1],
183
+ ["rgba(0,0,0,0)", "rgba(0,0,0,0.5)"]
184
+ );
185
+ return {
186
+ contentStyle: {
187
+ transform: [{ scale }, { translateX }, { translateY }]
188
+ },
189
+ overlayStyle: {
190
+ backgroundColor: overlayColor
191
+ }
192
+ };
193
+ },
194
+ ...config
195
+ };
196
+ };
197
+
198
+ // src/hooks/use-screen-animation.tsx
199
+ import { useNavigation } from "@react-navigation/native";
200
+ import { useMemo } from "react";
201
+ import { useWindowDimensions } from "react-native";
202
+ import { useSharedValue } from "react-native-reanimated";
203
+ import { useSafeAreaInsets } from "react-native-safe-area-context";
204
+
205
+ // src/store/utils/use-shallow.tsx
206
+ import React from "react";
207
+
208
+ // src/store/utils/shallow.ts
209
+ var isIterable = (obj) => Symbol.iterator in obj;
210
+ var hasIterableEntries = (value) => (
211
+ // HACK: avoid checking entries type
212
+ "entries" in value
213
+ );
214
+ var compareEntries = (valueA, valueB) => {
215
+ const mapA = valueA instanceof Map ? valueA : new Map(valueA.entries());
216
+ const mapB = valueB instanceof Map ? valueB : new Map(valueB.entries());
217
+ if (mapA.size !== mapB.size) {
218
+ return false;
219
+ }
220
+ for (const [key, value] of mapA) {
221
+ if (!Object.is(value, mapB.get(key))) {
222
+ return false;
223
+ }
224
+ }
225
+ return true;
226
+ };
227
+ var compareIterables = (valueA, valueB) => {
228
+ const iteratorA = valueA[Symbol.iterator]();
229
+ const iteratorB = valueB[Symbol.iterator]();
230
+ let nextA = iteratorA.next();
231
+ let nextB = iteratorB.next();
232
+ while (!nextA.done && !nextB.done) {
233
+ if (!Object.is(nextA.value, nextB.value)) {
234
+ return false;
235
+ }
236
+ nextA = iteratorA.next();
237
+ nextB = iteratorB.next();
238
+ }
239
+ return !!nextA.done && !!nextB.done;
240
+ };
241
+ function shallow(valueA, valueB) {
242
+ if (Object.is(valueA, valueB)) {
243
+ return true;
244
+ }
245
+ if (typeof valueA !== "object" || valueA === null || typeof valueB !== "object" || valueB === null) {
246
+ return false;
247
+ }
248
+ if (Object.getPrototypeOf(valueA) !== Object.getPrototypeOf(valueB)) {
249
+ return false;
250
+ }
251
+ if (isIterable(valueA) && isIterable(valueB)) {
252
+ if (hasIterableEntries(valueA) && hasIterableEntries(valueB)) {
253
+ return compareEntries(valueA, valueB);
254
+ }
255
+ return compareIterables(valueA, valueB);
256
+ }
257
+ return compareEntries(
258
+ { entries: () => Object.entries(valueA) },
259
+ { entries: () => Object.entries(valueB) }
260
+ );
261
+ }
262
+
263
+ // src/store/utils/use-shallow.tsx
264
+ function useShallow(selector) {
265
+ const prev = React.useRef(void 0);
266
+ return (state) => {
267
+ const next = selector(state);
268
+ if (shallow(prev.current, next)) {
269
+ return prev.current;
270
+ }
271
+ prev.current = next;
272
+ return next;
273
+ };
274
+ }
275
+
276
+ // src/animation-engine.ts
277
+ import {
278
+ cancelAnimation,
279
+ makeMutable,
280
+ runOnJS
281
+ } from "react-native-reanimated";
282
+
283
+ // src/store/utils/create-vanilla-store.ts
284
+ import { produce } from "immer";
285
+ import { useSyncExternalStore } from "react";
286
+ function createVanillaStore(initialState) {
287
+ let state = initialState;
288
+ const listeners = /* @__PURE__ */ new Set();
289
+ const getState = () => state;
290
+ const setState = (updater) => {
291
+ const nextState = produce(state, updater);
292
+ if (nextState !== state) {
293
+ state = nextState;
294
+ listeners.forEach((listener) => listener());
295
+ }
296
+ };
297
+ const subscribe = (listener) => {
298
+ listeners.add(listener);
299
+ return () => listeners.delete(listener);
300
+ };
301
+ const subscribeWithSelector = (selector, listener) => {
302
+ let previousSelectedState = selector(state);
303
+ const internalListener = () => {
304
+ const currentSelectedState = selector(getState());
305
+ if (!Object.is(previousSelectedState, currentSelectedState)) {
306
+ listener(currentSelectedState, previousSelectedState);
307
+ previousSelectedState = currentSelectedState;
308
+ }
309
+ };
310
+ const unsubscribe = subscribe(internalListener);
311
+ listener(previousSelectedState, previousSelectedState);
312
+ return unsubscribe;
313
+ };
314
+ function useStore(selector) {
315
+ return useSyncExternalStore(subscribe, () => selector(getState()));
316
+ }
317
+ Object.assign(useStore, {
318
+ setState,
319
+ getState,
320
+ subscribe,
321
+ subscribeWithSelector
322
+ });
323
+ return useStore;
324
+ }
325
+
326
+ // src/store/index.ts
327
+ var useRouteStore = createVanillaStore({
328
+ routes: {},
329
+ routeKeys: []
330
+ });
331
+ var RouteStore = {
332
+ use: useRouteStore,
333
+ updateRoute: (key, value) => {
334
+ if (!key) return;
335
+ useRouteStore.setState(({ routeKeys, routes }) => {
336
+ const currentRoute = routes[key];
337
+ if (currentRoute) {
338
+ routes[key] = {
339
+ ...currentRoute,
340
+ ...value
341
+ };
342
+ } else {
343
+ const { name = "", status = 0, closing = false, ...rest } = value;
344
+ const newIndex = routeKeys.length;
345
+ routes[key] = {
346
+ id: key,
347
+ index: newIndex,
348
+ name,
349
+ status,
350
+ closing,
351
+ ...rest
352
+ };
353
+ routeKeys.push(key);
354
+ }
355
+ });
356
+ },
357
+ removeRoute: (key) => {
358
+ if (!key) return;
359
+ useRouteStore.setState(({ routes, routeKeys }) => {
360
+ delete routes[key];
361
+ const indexToRemove = routeKeys.indexOf(key);
362
+ if (indexToRemove > -1) {
363
+ routeKeys.splice(indexToRemove, 1);
364
+ }
365
+ });
366
+ }
367
+ };
368
+
369
+ // src/utils/animate.ts
370
+ import {
371
+ withSpring,
372
+ withTiming
373
+ } from "react-native-reanimated";
374
+ var animate = (toValue, config, callback) => {
375
+ "worklet";
376
+ const isSpring = typeof config === "object" && !("duration" in config) && !("easing" in config);
377
+ if (!isSpring) {
378
+ return withTiming(toValue, config, callback);
379
+ }
380
+ return withSpring(toValue, config, callback);
381
+ };
382
+
383
+ // src/animation-engine.ts
384
+ var animationValues = {
385
+ screenProgress: {},
386
+ gestureX: {},
387
+ gestureY: {},
388
+ normalizedGestureX: {},
389
+ normalizedGestureY: {},
390
+ gestureDragging: {}
391
+ };
392
+ var triggerAnimation = (route) => {
393
+ "worklet";
394
+ const { id, closing, status, transitionSpec, onAnimationFinish } = route;
395
+ const progressValue = animationValues.screenProgress[id];
396
+ if (!progressValue && __DEV__) {
397
+ console.warn(`Animation values not found for route: ${id}`);
398
+ return;
399
+ }
400
+ const animationConfig = closing ? transitionSpec?.close : transitionSpec?.open;
401
+ const targetValue = status || 0;
402
+ progressValue.value = animate(targetValue, animationConfig, (finished) => {
403
+ "worklet";
404
+ if (finished && onAnimationFinish) {
405
+ runOnJS(onAnimationFinish)(true);
406
+ }
407
+ });
408
+ };
409
+ RouteStore.use.subscribeWithSelector(
410
+ (state) => state.routes,
411
+ (currRoutes, prevRoutes) => {
412
+ const currKeys = Object.keys(currRoutes);
413
+ const prevKeys = Object.keys(prevRoutes);
414
+ const incomingKeys = currKeys.filter((k) => !prevKeys.includes(k));
415
+ const removedKeys = prevKeys.filter((k) => !currKeys.includes(k));
416
+ const changedKeys = currKeys.filter((k) => currRoutes[k] !== prevRoutes[k]);
417
+ const animatableValues = Object.values(animationValues);
418
+ for (const incomingKey of incomingKeys) {
419
+ for (const value of animatableValues) {
420
+ value[incomingKey] = makeMutable(0);
421
+ }
422
+ }
423
+ for (const removedKey of removedKeys) {
424
+ for (const value of animatableValues) {
425
+ cancelAnimation(value[removedKey]);
426
+ delete value[removedKey];
427
+ }
428
+ }
429
+ for (const changedKey of changedKeys) {
430
+ const currentRoute = currRoutes[changedKey];
431
+ if (currentRoute) {
432
+ triggerAnimation(currentRoute);
433
+ }
434
+ }
435
+ }
436
+ );
437
+
438
+ // src/utils/gesture/build-gesture-detector.ts
439
+ import { Gesture } from "react-native-gesture-handler";
440
+ import {
441
+ interpolate as interpolate2,
442
+ runOnJS as runOnJS2
443
+ } from "react-native-reanimated";
444
+
445
+ // src/utils/gesture/create-gesture-activation-criteria.ts
446
+ var createGestureActivationCriteria = ({
447
+ gestureDirection,
448
+ gestureResponseDistance
449
+ }) => {
450
+ const directions = Array.isArray(gestureDirection) ? gestureDirection : [gestureDirection];
451
+ if (directions.includes("bidirectional")) {
452
+ return {
453
+ activeOffsetX: [
454
+ -gestureResponseDistance,
455
+ gestureResponseDistance
456
+ ],
457
+ activeOffsetY: [
458
+ -gestureResponseDistance,
459
+ gestureResponseDistance
460
+ ]
461
+ };
462
+ }
463
+ const allowedDown = directions.includes("vertical");
464
+ const allowedUp = directions.includes("vertical-inverted");
465
+ const allowedRight = directions.includes("horizontal");
466
+ const allowedLeft = directions.includes("horizontal-inverted");
467
+ const toleranceX = 15;
468
+ const toleranceY = 20;
469
+ const dist = gestureResponseDistance;
470
+ const result = {};
471
+ const hasHorizontal = allowedLeft || allowedRight;
472
+ if (hasHorizontal) {
473
+ if (allowedLeft && allowedRight) {
474
+ result.activeOffsetX = [-dist, dist];
475
+ } else if (allowedLeft) {
476
+ result.activeOffsetX = -dist;
477
+ } else if (allowedRight) {
478
+ result.activeOffsetX = dist;
479
+ }
480
+ if (allowedRight && !allowedLeft) {
481
+ result.failOffsetX = -dist;
482
+ } else if (allowedLeft && !allowedRight) {
483
+ result.failOffsetX = dist;
484
+ }
485
+ } else {
486
+ result.failOffsetX = [-toleranceX, toleranceX];
487
+ }
488
+ const hasVertical = allowedUp || allowedDown;
489
+ if (hasVertical) {
490
+ if (allowedUp && allowedDown) {
491
+ result.activeOffsetY = [-dist, dist];
492
+ } else if (allowedUp) {
493
+ result.activeOffsetY = -dist;
494
+ } else if (allowedDown) {
495
+ result.activeOffsetY = dist;
496
+ }
497
+ if (allowedDown && !allowedUp) {
498
+ result.failOffsetY = -dist;
499
+ } else if (allowedUp && !allowedDown) {
500
+ result.failOffsetY = dist;
501
+ }
502
+ } else {
503
+ result.failOffsetY = [-toleranceY, toleranceY];
504
+ }
505
+ return result;
506
+ };
507
+
508
+ // src/utils/gesture/map-gesture-to-progress.ts
509
+ var mapGestureToProgress = (translation, dimension) => {
510
+ "worklet";
511
+ const rawProgress = translation / dimension;
512
+ return Math.max(0, Math.min(1, rawProgress));
513
+ };
514
+
515
+ // src/utils/gesture/build-gesture-detector.ts
516
+ var GESTURE_VELOCITY_IMPACT = 0.3;
517
+ var DEFAULT_GESTURE_RESPONSE_DISTANCE = 50;
518
+ var buildGestureDetector = ({
519
+ key,
520
+ progress,
521
+ config,
522
+ width,
523
+ height,
524
+ goBack
525
+ }) => {
526
+ const _translateX = animationValues.gestureX[key];
527
+ const _translateY = animationValues.gestureY[key];
528
+ const _normalizedGestureX = animationValues.normalizedGestureX[key];
529
+ const _normalizedGestureY = animationValues.normalizedGestureY[key];
530
+ const _isDragging = animationValues.gestureDragging[key];
531
+ const {
532
+ gestureDirection = "horizontal",
533
+ gestureEnabled = false,
534
+ transitionSpec,
535
+ gestureVelocityImpact = GESTURE_VELOCITY_IMPACT,
536
+ gestureResponseDistance = DEFAULT_GESTURE_RESPONSE_DISTANCE
537
+ } = config;
538
+ const directions = Array.isArray(gestureDirection) ? gestureDirection : [gestureDirection];
539
+ const panGesture = Gesture.Pan().enabled(gestureEnabled).onStart(() => {
540
+ "worklet";
541
+ _isDragging.value = 1;
542
+ }).onUpdate((event) => {
543
+ "worklet";
544
+ let gestureProgress = 0;
545
+ _translateX.value = event.translationX;
546
+ _translateY.value = event.translationY;
547
+ _normalizedGestureX.value = interpolate2(
548
+ event.translationX,
549
+ [-width, width],
550
+ [-1, 1],
551
+ "clamp"
552
+ );
553
+ _normalizedGestureY.value = interpolate2(
554
+ event.translationY,
555
+ [-height, height],
556
+ [-1, 1],
557
+ "clamp"
558
+ );
559
+ if (directions.includes("bidirectional")) {
560
+ const distance = Math.sqrt(
561
+ event.translationX ** 2 + event.translationY ** 2
562
+ );
563
+ gestureProgress = mapGestureToProgress(distance, width);
564
+ } else {
565
+ let maxProgress = 0;
566
+ const allowedDown = directions.includes("vertical");
567
+ const allowedUp = directions.includes("vertical-inverted");
568
+ const allowedRight = directions.includes("horizontal");
569
+ const allowedLeft = directions.includes("horizontal-inverted");
570
+ if (allowedRight || allowedLeft) {
571
+ const absX = Math.abs(event.translationX);
572
+ const currentProgress = mapGestureToProgress(absX, width);
573
+ maxProgress = Math.max(maxProgress, currentProgress);
574
+ }
575
+ if (allowedUp || allowedDown) {
576
+ const absY = Math.abs(event.translationY);
577
+ const currentProgress = mapGestureToProgress(absY, height);
578
+ maxProgress = Math.max(maxProgress, currentProgress);
579
+ }
580
+ gestureProgress = maxProgress;
581
+ }
582
+ progress.value = 1 - gestureProgress;
583
+ }).onEnd((event) => {
584
+ "worklet";
585
+ const { translationX, translationY, velocityX, velocityY } = event;
586
+ let shouldDismiss = false;
587
+ const dismissThreshold = 0.5;
588
+ if (directions.includes("bidirectional")) {
589
+ const finalX = Math.abs(
590
+ translationX + velocityX * gestureVelocityImpact
591
+ );
592
+ const finalY = Math.abs(
593
+ translationY + velocityY * gestureVelocityImpact
594
+ );
595
+ const finalDistance = Math.sqrt(finalX ** 2 + finalY ** 2);
596
+ shouldDismiss = finalDistance > width * dismissThreshold;
597
+ } else {
598
+ const allowedDown = directions.includes("vertical");
599
+ const allowedUp = directions.includes("vertical-inverted");
600
+ const allowedRight = directions.includes("horizontal");
601
+ const allowedLeft = directions.includes("horizontal-inverted");
602
+ if (allowedRight && translationX + velocityX * gestureVelocityImpact > width * dismissThreshold) {
603
+ shouldDismiss = true;
604
+ } else if (allowedLeft && -translationX - velocityX * gestureVelocityImpact > width * dismissThreshold) {
605
+ shouldDismiss = true;
606
+ } else if (allowedDown && translationY + velocityY * gestureVelocityImpact > height * dismissThreshold) {
607
+ shouldDismiss = true;
608
+ } else if (allowedUp && -translationY - velocityY * gestureVelocityImpact > height * dismissThreshold) {
609
+ shouldDismiss = true;
610
+ }
611
+ }
612
+ const finalProgress = shouldDismiss ? 0 : 1;
613
+ const spec = shouldDismiss ? transitionSpec?.close : transitionSpec?.open;
614
+ const onFinish = shouldDismiss ? (isFinished) => {
615
+ "worklet";
616
+ if (isFinished) runOnJS2(goBack)();
617
+ } : void 0;
618
+ progress.value = animate(finalProgress, spec, onFinish);
619
+ _translateX.value = animate(0, spec);
620
+ _translateY.value = animate(0, spec);
621
+ _normalizedGestureX.value = animate(0, spec);
622
+ _normalizedGestureY.value = animate(0, spec);
623
+ });
624
+ const criteria = createGestureActivationCriteria({
625
+ gestureDirection,
626
+ gestureResponseDistance
627
+ });
628
+ if (criteria?.activeOffsetX) {
629
+ panGesture.activeOffsetX(criteria.activeOffsetX);
630
+ }
631
+ if (criteria?.activeOffsetY) {
632
+ panGesture.activeOffsetY(criteria.activeOffsetY);
633
+ }
634
+ if (criteria?.failOffsetX) {
635
+ panGesture.failOffsetX(criteria.failOffsetX);
636
+ }
637
+ if (criteria?.failOffsetY) {
638
+ panGesture.failOffsetY(criteria.failOffsetY);
639
+ }
640
+ panGesture.enableTrackpadTwoFingerGesture(true);
641
+ const nativeGesture = Gesture.Native().shouldCancelWhenOutside(false);
642
+ return Gesture.Race(panGesture, nativeGesture);
643
+ };
644
+
645
+ // src/utils/noop-interpolator.ts
646
+ var noopinterpolator = () => {
647
+ "worklet";
648
+ return {
649
+ contentStyle: {},
650
+ overlayStyle: {}
651
+ };
652
+ };
653
+
654
+ // src/hooks/use-key.tsx
655
+ import { useRoute } from "@react-navigation/native";
656
+ var useKey = () => useRoute().key;
657
+
658
+ // src/hooks/use-screen-animation.tsx
659
+ var useAnimationBuilder = () => {
660
+ const key = useKey();
661
+ const dimensions = useWindowDimensions();
662
+ const insets = useSafeAreaInsets();
663
+ const navigation = useNavigation();
664
+ const { currentRoute, nextRoute } = RouteStore.use(
665
+ useShallow(({ routes, routeKeys }) => {
666
+ const current = routes[key];
667
+ if (!current) {
668
+ return { currentRoute: void 0, nextRoute: void 0 };
669
+ }
670
+ const currentScreenIndex = current.index;
671
+ const nextKey = routeKeys[currentScreenIndex + 1];
672
+ return {
673
+ currentRoute: current,
674
+ nextRoute: nextKey ? routes[nextKey] : void 0
675
+ };
676
+ })
677
+ );
678
+ const panGesture = useMemo(
679
+ () => buildGestureDetector({
680
+ key,
681
+ progress: animationValues.screenProgress[key],
682
+ config: currentRoute || {
683
+ id: key,
684
+ name: key,
685
+ index: 0,
686
+ status: 0,
687
+ closing: false
688
+ },
689
+ width: dimensions.width,
690
+ height: dimensions.height,
691
+ goBack: navigation.goBack
692
+ }),
693
+ [key, currentRoute, dimensions.width, dimensions.height, navigation.goBack]
694
+ );
695
+ const progressFallback = useSharedValue(0);
696
+ const gestureDraggingFallback = useSharedValue(0);
697
+ const gestureXFallback = useSharedValue(0);
698
+ const gestureYFallback = useSharedValue(0);
699
+ const normalizedGestureXFallback = useSharedValue(0);
700
+ const normalizedGestureYFallback = useSharedValue(0);
701
+ return useMemo(() => {
702
+ return {
703
+ current: {
704
+ progress: animationValues.screenProgress[key] || progressFallback,
705
+ gesture: {
706
+ isDragging: animationValues.gestureDragging[key] || gestureDraggingFallback,
707
+ x: animationValues.gestureX[key] || gestureXFallback,
708
+ y: animationValues.gestureY[key] || gestureYFallback,
709
+ normalizedX: animationValues.normalizedGestureX[key] || normalizedGestureXFallback,
710
+ normalizedY: animationValues.normalizedGestureY[key] || normalizedGestureYFallback
711
+ }
712
+ },
713
+ next: nextRoute && animationValues.screenProgress[nextRoute.id] ? {
714
+ progress: animationValues.screenProgress[nextRoute.id],
715
+ gesture: {
716
+ isDragging: animationValues.gestureDragging[nextRoute.id] || gestureDraggingFallback,
717
+ x: animationValues.gestureX[nextRoute.id] || gestureXFallback,
718
+ y: animationValues.gestureY[nextRoute.id] || gestureYFallback,
719
+ normalizedX: animationValues.normalizedGestureX[nextRoute.id] || normalizedGestureXFallback,
720
+ normalizedY: animationValues.normalizedGestureY[nextRoute.id] || normalizedGestureYFallback
721
+ }
722
+ } : void 0,
723
+ layouts: { screen: dimensions },
724
+ insets,
725
+ closing: currentRoute?.closing || false,
726
+ screenStyleInterpolator: nextRoute?.screenStyleInterpolator || currentRoute?.screenStyleInterpolator || noopinterpolator,
727
+ gestureDetector: panGesture
728
+ };
729
+ }, [
730
+ key,
731
+ currentRoute,
732
+ nextRoute,
733
+ dimensions,
734
+ insets,
735
+ panGesture,
736
+ progressFallback,
737
+ gestureDraggingFallback,
738
+ gestureXFallback,
739
+ gestureYFallback,
740
+ normalizedGestureXFallback,
741
+ normalizedGestureYFallback
742
+ ]);
743
+ };
744
+ var _useScreenAnimation = () => {
745
+ return useAnimationBuilder();
746
+ };
747
+ var useScreenAnimation = () => {
748
+ const {
749
+ screenStyleInterpolator: _,
750
+ gestureDetector: __,
751
+ ...animationProps
752
+ } = useAnimationBuilder();
753
+ return animationProps;
754
+ };
755
+
756
+ // src/utils/create-config.ts
757
+ var createConfig = ({
758
+ navigation: reactNavigation,
759
+ route,
760
+ ...config
761
+ }) => {
762
+ return {
763
+ focus: (e) => {
764
+ RouteStore.updateRoute(e.target, {
765
+ id: e.target,
766
+ name: route.name,
767
+ status: 1,
768
+ closing: false,
769
+ ...config
770
+ });
771
+ },
772
+ beforeRemove: (e) => {
773
+ e.preventDefault();
774
+ const handleFinish = (finished) => {
775
+ if (!finished) return;
776
+ if (reactNavigation.canGoBack()) {
777
+ reactNavigation.dispatch(e.data?.action);
778
+ RouteStore.removeRoute(e.target);
779
+ }
780
+ };
781
+ RouteStore.updateRoute(e.target, {
782
+ status: 0,
783
+ closing: true,
784
+ onAnimationFinish: handleFinish
785
+ });
786
+ }
787
+ };
788
+ };
789
+
790
+ // src/utils/create-transition-component.tsx
791
+ import { forwardRef, memo } from "react";
792
+ import { StyleSheet } from "react-native";
793
+ import { GestureDetector } from "react-native-gesture-handler";
794
+ import Animated, {
795
+ useAnimatedStyle as useAnimatedStyle2
796
+ } from "react-native-reanimated";
797
+
798
+ // src/hooks/use-skip-first-frame.tsx
799
+ import { useEffect } from "react";
800
+ import { useAnimatedStyle, useSharedValue as useSharedValue2 } from "react-native-reanimated";
801
+ var useSkipFirstFrame = () => {
802
+ const opacity = useSharedValue2(0);
803
+ const style = useAnimatedStyle(() => {
804
+ "worklet";
805
+ return {
806
+ opacity: opacity.value
807
+ };
808
+ });
809
+ useEffect(() => {
810
+ requestAnimationFrame(() => {
811
+ opacity.value = 1;
812
+ });
813
+ }, [opacity]);
814
+ return { style };
815
+ };
816
+
817
+ // src/utils/create-transition-component.tsx
818
+ import { jsx, jsxs } from "react/jsx-runtime";
819
+ function createTransitionComponent(Wrapped) {
820
+ const AnimatedComponent = Animated.createAnimatedComponent(Wrapped);
821
+ const Inner = forwardRef(
822
+ (props, ref) => {
823
+ const { children, style, ...rest } = props;
824
+ const {
825
+ screenStyleInterpolator,
826
+ gestureDetector,
827
+ ...screenInterpolationProps
828
+ } = _useScreenAnimation();
829
+ const screenContainerStyle = useAnimatedStyle2(() => {
830
+ "worklet";
831
+ return screenStyleInterpolator(screenInterpolationProps).contentStyle || {};
832
+ });
833
+ const overlayStyle = useAnimatedStyle2(() => {
834
+ "worklet";
835
+ return screenStyleInterpolator(screenInterpolationProps).overlayStyle || {};
836
+ });
837
+ const { style: flickerFixStyle } = useSkipFirstFrame();
838
+ return /* @__PURE__ */ jsxs(Animated.View, { style: [{ flex: 1 }, flickerFixStyle], children: [
839
+ /* @__PURE__ */ jsx(GestureDetector, { gesture: gestureDetector, children: /* @__PURE__ */ jsx(
840
+ AnimatedComponent,
841
+ {
842
+ ...rest,
843
+ ref,
844
+ style: [
845
+ { flex: 1, position: "relative" },
846
+ screenContainerStyle,
847
+ style
848
+ ],
849
+ children
850
+ }
851
+ ) }),
852
+ /* @__PURE__ */ jsx(
853
+ Animated.View,
854
+ {
855
+ style: [
856
+ StyleSheet.absoluteFillObject,
857
+ overlayStyle,
858
+ { zIndex: 1e4 }
859
+ ],
860
+ pointerEvents: "none"
861
+ }
862
+ )
863
+ ] });
864
+ }
865
+ );
866
+ Inner.displayName = `Transition(${Wrapped.displayName || Wrapped.name || "Component"})`;
867
+ return memo(Inner);
868
+ }
869
+
870
+ // src/utils/default-screen-options.ts
871
+ var defaultScreenOptions = () => ({
872
+ presentation: "containedTransparentModal",
873
+ headerShown: false,
874
+ animation: "none"
875
+ });
876
+
877
+ // src/index.ts
878
+ var index_default = {
879
+ createTransitionComponent,
880
+ View: createTransitionComponent(View),
881
+ Pressable: createTransitionComponent(Pressable),
882
+ createConfig,
883
+ defaultScreenOptions,
884
+ presets: presets_exports,
885
+ specs: specs_exports
886
+ };
887
+ export {
888
+ index_default as default,
889
+ useScreenAnimation
890
+ };
891
+ //# sourceMappingURL=index.mjs.map