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.js ADDED
@@ -0,0 +1,904 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.ts
31
+ var index_exports = {};
32
+ __export(index_exports, {
33
+ default: () => index_default,
34
+ useScreenAnimation: () => useScreenAnimation
35
+ });
36
+ module.exports = __toCommonJS(index_exports);
37
+ var import_react_native3 = require("react-native");
38
+
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
+ });
48
+ var import_react_native_reanimated = require("react-native-reanimated");
49
+
50
+ // src/configs/specs.ts
51
+ var specs_exports = {};
52
+ __export(specs_exports, {
53
+ DefaultSpec: () => DefaultSpec
54
+ });
55
+ var DefaultSpec = {
56
+ stiffness: 1e3,
57
+ damping: 500,
58
+ mass: 3,
59
+ overshootClamping: true,
60
+ restSpeedThreshold: 0.01
61
+ };
62
+
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
+ // src/animation-engine.ts
302
+ var import_react_native_reanimated3 = require("react-native-reanimated");
303
+
304
+ // src/store/utils/create-vanilla-store.ts
305
+ var import_immer = require("immer");
306
+ var import_react2 = require("react");
307
+ function createVanillaStore(initialState) {
308
+ let state = initialState;
309
+ const listeners = /* @__PURE__ */ new Set();
310
+ const getState = () => state;
311
+ const setState = (updater) => {
312
+ const nextState = (0, import_immer.produce)(state, updater);
313
+ if (nextState !== state) {
314
+ state = nextState;
315
+ listeners.forEach((listener) => listener());
316
+ }
317
+ };
318
+ const subscribe = (listener) => {
319
+ listeners.add(listener);
320
+ return () => listeners.delete(listener);
321
+ };
322
+ const subscribeWithSelector = (selector, listener) => {
323
+ let previousSelectedState = selector(state);
324
+ const internalListener = () => {
325
+ const currentSelectedState = selector(getState());
326
+ if (!Object.is(previousSelectedState, currentSelectedState)) {
327
+ listener(currentSelectedState, previousSelectedState);
328
+ previousSelectedState = currentSelectedState;
329
+ }
330
+ };
331
+ const unsubscribe = subscribe(internalListener);
332
+ listener(previousSelectedState, previousSelectedState);
333
+ return unsubscribe;
334
+ };
335
+ function useStore(selector) {
336
+ return (0, import_react2.useSyncExternalStore)(subscribe, () => selector(getState()));
337
+ }
338
+ Object.assign(useStore, {
339
+ setState,
340
+ getState,
341
+ subscribe,
342
+ subscribeWithSelector
343
+ });
344
+ return useStore;
345
+ }
346
+
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
+ }
386
+ });
387
+ }
388
+ };
389
+
390
+ // src/utils/animate.ts
391
+ var import_react_native_reanimated2 = require("react-native-reanimated");
392
+ var animate = (toValue, config, callback) => {
393
+ "worklet";
394
+ const isSpring = typeof config === "object" && !("duration" in config) && !("easing" in config);
395
+ if (!isSpring) {
396
+ return (0, import_react_native_reanimated2.withTiming)(toValue, config, callback);
397
+ }
398
+ return (0, import_react_native_reanimated2.withSpring)(toValue, config, callback);
399
+ };
400
+
401
+ // src/animation-engine.ts
402
+ var animationValues = {
403
+ screenProgress: {},
404
+ gestureX: {},
405
+ gestureY: {},
406
+ normalizedGestureX: {},
407
+ normalizedGestureY: {},
408
+ gestureDragging: {}
409
+ };
410
+ var triggerAnimation = (route) => {
411
+ "worklet";
412
+ const { id, closing, status, transitionSpec, onAnimationFinish } = route;
413
+ const progressValue = animationValues.screenProgress[id];
414
+ if (!progressValue && __DEV__) {
415
+ console.warn(`Animation values not found for route: ${id}`);
416
+ return;
417
+ }
418
+ const animationConfig = closing ? transitionSpec?.close : transitionSpec?.open;
419
+ const targetValue = status || 0;
420
+ progressValue.value = animate(targetValue, animationConfig, (finished) => {
421
+ "worklet";
422
+ if (finished && onAnimationFinish) {
423
+ (0, import_react_native_reanimated3.runOnJS)(onAnimationFinish)(true);
424
+ }
425
+ });
426
+ };
427
+ RouteStore.use.subscribeWithSelector(
428
+ (state) => state.routes,
429
+ (currRoutes, prevRoutes) => {
430
+ const currKeys = Object.keys(currRoutes);
431
+ const prevKeys = Object.keys(prevRoutes);
432
+ const incomingKeys = currKeys.filter((k) => !prevKeys.includes(k));
433
+ const removedKeys = prevKeys.filter((k) => !currKeys.includes(k));
434
+ const changedKeys = currKeys.filter((k) => currRoutes[k] !== prevRoutes[k]);
435
+ const animatableValues = Object.values(animationValues);
436
+ for (const incomingKey of incomingKeys) {
437
+ for (const value of animatableValues) {
438
+ value[incomingKey] = (0, import_react_native_reanimated3.makeMutable)(0);
439
+ }
440
+ }
441
+ for (const removedKey of removedKeys) {
442
+ for (const value of animatableValues) {
443
+ (0, import_react_native_reanimated3.cancelAnimation)(value[removedKey]);
444
+ delete value[removedKey];
445
+ }
446
+ }
447
+ for (const changedKey of changedKeys) {
448
+ const currentRoute = currRoutes[changedKey];
449
+ if (currentRoute) {
450
+ triggerAnimation(currentRoute);
451
+ }
452
+ }
453
+ }
454
+ );
455
+
456
+ // src/utils/gesture/build-gesture-detector.ts
457
+ var import_react_native_gesture_handler = require("react-native-gesture-handler");
458
+ var import_react_native_reanimated4 = require("react-native-reanimated");
459
+
460
+ // src/utils/gesture/create-gesture-activation-criteria.ts
461
+ var createGestureActivationCriteria = ({
462
+ gestureDirection,
463
+ gestureResponseDistance
464
+ }) => {
465
+ const directions = Array.isArray(gestureDirection) ? gestureDirection : [gestureDirection];
466
+ if (directions.includes("bidirectional")) {
467
+ return {
468
+ activeOffsetX: [
469
+ -gestureResponseDistance,
470
+ gestureResponseDistance
471
+ ],
472
+ activeOffsetY: [
473
+ -gestureResponseDistance,
474
+ gestureResponseDistance
475
+ ]
476
+ };
477
+ }
478
+ const allowedDown = directions.includes("vertical");
479
+ const allowedUp = directions.includes("vertical-inverted");
480
+ const allowedRight = directions.includes("horizontal");
481
+ const allowedLeft = directions.includes("horizontal-inverted");
482
+ const toleranceX = 15;
483
+ const toleranceY = 20;
484
+ const dist = gestureResponseDistance;
485
+ const result = {};
486
+ const hasHorizontal = allowedLeft || allowedRight;
487
+ if (hasHorizontal) {
488
+ if (allowedLeft && allowedRight) {
489
+ result.activeOffsetX = [-dist, dist];
490
+ } else if (allowedLeft) {
491
+ result.activeOffsetX = -dist;
492
+ } else if (allowedRight) {
493
+ result.activeOffsetX = dist;
494
+ }
495
+ if (allowedRight && !allowedLeft) {
496
+ result.failOffsetX = -dist;
497
+ } else if (allowedLeft && !allowedRight) {
498
+ result.failOffsetX = dist;
499
+ }
500
+ } else {
501
+ result.failOffsetX = [-toleranceX, toleranceX];
502
+ }
503
+ const hasVertical = allowedUp || allowedDown;
504
+ if (hasVertical) {
505
+ if (allowedUp && allowedDown) {
506
+ result.activeOffsetY = [-dist, dist];
507
+ } else if (allowedUp) {
508
+ result.activeOffsetY = -dist;
509
+ } else if (allowedDown) {
510
+ result.activeOffsetY = dist;
511
+ }
512
+ if (allowedDown && !allowedUp) {
513
+ result.failOffsetY = -dist;
514
+ } else if (allowedUp && !allowedDown) {
515
+ result.failOffsetY = dist;
516
+ }
517
+ } else {
518
+ result.failOffsetY = [-toleranceY, toleranceY];
519
+ }
520
+ return result;
521
+ };
522
+
523
+ // src/utils/gesture/map-gesture-to-progress.ts
524
+ var mapGestureToProgress = (translation, dimension) => {
525
+ "worklet";
526
+ const rawProgress = translation / dimension;
527
+ return Math.max(0, Math.min(1, rawProgress));
528
+ };
529
+
530
+ // src/utils/gesture/build-gesture-detector.ts
531
+ var GESTURE_VELOCITY_IMPACT = 0.3;
532
+ var DEFAULT_GESTURE_RESPONSE_DISTANCE = 50;
533
+ var buildGestureDetector = ({
534
+ key,
535
+ progress,
536
+ config,
537
+ width,
538
+ height,
539
+ goBack
540
+ }) => {
541
+ const _translateX = animationValues.gestureX[key];
542
+ const _translateY = animationValues.gestureY[key];
543
+ const _normalizedGestureX = animationValues.normalizedGestureX[key];
544
+ const _normalizedGestureY = animationValues.normalizedGestureY[key];
545
+ const _isDragging = animationValues.gestureDragging[key];
546
+ const {
547
+ gestureDirection = "horizontal",
548
+ gestureEnabled = false,
549
+ transitionSpec,
550
+ gestureVelocityImpact = GESTURE_VELOCITY_IMPACT,
551
+ gestureResponseDistance = DEFAULT_GESTURE_RESPONSE_DISTANCE
552
+ } = config;
553
+ const directions = Array.isArray(gestureDirection) ? gestureDirection : [gestureDirection];
554
+ const panGesture = import_react_native_gesture_handler.Gesture.Pan().enabled(gestureEnabled).onStart(() => {
555
+ "worklet";
556
+ _isDragging.value = 1;
557
+ }).onUpdate((event) => {
558
+ "worklet";
559
+ let gestureProgress = 0;
560
+ _translateX.value = event.translationX;
561
+ _translateY.value = event.translationY;
562
+ _normalizedGestureX.value = (0, import_react_native_reanimated4.interpolate)(
563
+ event.translationX,
564
+ [-width, width],
565
+ [-1, 1],
566
+ "clamp"
567
+ );
568
+ _normalizedGestureY.value = (0, import_react_native_reanimated4.interpolate)(
569
+ event.translationY,
570
+ [-height, height],
571
+ [-1, 1],
572
+ "clamp"
573
+ );
574
+ if (directions.includes("bidirectional")) {
575
+ const distance = Math.sqrt(
576
+ event.translationX ** 2 + event.translationY ** 2
577
+ );
578
+ gestureProgress = mapGestureToProgress(distance, width);
579
+ } else {
580
+ let maxProgress = 0;
581
+ const allowedDown = directions.includes("vertical");
582
+ const allowedUp = directions.includes("vertical-inverted");
583
+ const allowedRight = directions.includes("horizontal");
584
+ const allowedLeft = directions.includes("horizontal-inverted");
585
+ if (allowedRight || allowedLeft) {
586
+ const absX = Math.abs(event.translationX);
587
+ const currentProgress = mapGestureToProgress(absX, width);
588
+ maxProgress = Math.max(maxProgress, currentProgress);
589
+ }
590
+ if (allowedUp || allowedDown) {
591
+ const absY = Math.abs(event.translationY);
592
+ const currentProgress = mapGestureToProgress(absY, height);
593
+ maxProgress = Math.max(maxProgress, currentProgress);
594
+ }
595
+ gestureProgress = maxProgress;
596
+ }
597
+ progress.value = 1 - gestureProgress;
598
+ }).onEnd((event) => {
599
+ "worklet";
600
+ const { translationX, translationY, velocityX, velocityY } = event;
601
+ let shouldDismiss = false;
602
+ const dismissThreshold = 0.5;
603
+ if (directions.includes("bidirectional")) {
604
+ const finalX = Math.abs(
605
+ translationX + velocityX * gestureVelocityImpact
606
+ );
607
+ const finalY = Math.abs(
608
+ translationY + velocityY * gestureVelocityImpact
609
+ );
610
+ const finalDistance = Math.sqrt(finalX ** 2 + finalY ** 2);
611
+ shouldDismiss = finalDistance > width * dismissThreshold;
612
+ } else {
613
+ const allowedDown = directions.includes("vertical");
614
+ const allowedUp = directions.includes("vertical-inverted");
615
+ const allowedRight = directions.includes("horizontal");
616
+ const allowedLeft = directions.includes("horizontal-inverted");
617
+ if (allowedRight && translationX + velocityX * gestureVelocityImpact > width * dismissThreshold) {
618
+ shouldDismiss = true;
619
+ } else if (allowedLeft && -translationX - velocityX * gestureVelocityImpact > width * dismissThreshold) {
620
+ shouldDismiss = true;
621
+ } else if (allowedDown && translationY + velocityY * gestureVelocityImpact > height * dismissThreshold) {
622
+ shouldDismiss = true;
623
+ } else if (allowedUp && -translationY - velocityY * gestureVelocityImpact > height * dismissThreshold) {
624
+ shouldDismiss = true;
625
+ }
626
+ }
627
+ const finalProgress = shouldDismiss ? 0 : 1;
628
+ const spec = shouldDismiss ? transitionSpec?.close : transitionSpec?.open;
629
+ const onFinish = shouldDismiss ? (isFinished) => {
630
+ "worklet";
631
+ if (isFinished) (0, import_react_native_reanimated4.runOnJS)(goBack)();
632
+ } : void 0;
633
+ progress.value = animate(finalProgress, spec, onFinish);
634
+ _translateX.value = animate(0, spec);
635
+ _translateY.value = animate(0, spec);
636
+ _normalizedGestureX.value = animate(0, spec);
637
+ _normalizedGestureY.value = animate(0, spec);
638
+ });
639
+ const criteria = createGestureActivationCriteria({
640
+ gestureDirection,
641
+ gestureResponseDistance
642
+ });
643
+ if (criteria?.activeOffsetX) {
644
+ panGesture.activeOffsetX(criteria.activeOffsetX);
645
+ }
646
+ if (criteria?.activeOffsetY) {
647
+ panGesture.activeOffsetY(criteria.activeOffsetY);
648
+ }
649
+ if (criteria?.failOffsetX) {
650
+ panGesture.failOffsetX(criteria.failOffsetX);
651
+ }
652
+ if (criteria?.failOffsetY) {
653
+ panGesture.failOffsetY(criteria.failOffsetY);
654
+ }
655
+ panGesture.enableTrackpadTwoFingerGesture(true);
656
+ const nativeGesture = import_react_native_gesture_handler.Gesture.Native().shouldCancelWhenOutside(false);
657
+ return import_react_native_gesture_handler.Gesture.Race(panGesture, nativeGesture);
658
+ };
659
+
660
+ // src/utils/noop-interpolator.ts
661
+ var noopinterpolator = () => {
662
+ "worklet";
663
+ return {
664
+ contentStyle: {},
665
+ overlayStyle: {}
666
+ };
667
+ };
668
+
669
+ // src/hooks/use-key.tsx
670
+ var import_native = require("@react-navigation/native");
671
+ var useKey = () => (0, import_native.useRoute)().key;
672
+
673
+ // src/hooks/use-screen-animation.tsx
674
+ var useAnimationBuilder = () => {
675
+ const key = useKey();
676
+ const dimensions = (0, import_react_native.useWindowDimensions)();
677
+ const insets = (0, import_react_native_safe_area_context.useSafeAreaInsets)();
678
+ const navigation = (0, import_native2.useNavigation)();
679
+ const { currentRoute, nextRoute } = RouteStore.use(
680
+ useShallow(({ routes, routeKeys }) => {
681
+ const current = routes[key];
682
+ if (!current) {
683
+ return { currentRoute: void 0, nextRoute: void 0 };
684
+ }
685
+ const currentScreenIndex = current.index;
686
+ const nextKey = routeKeys[currentScreenIndex + 1];
687
+ return {
688
+ currentRoute: current,
689
+ nextRoute: nextKey ? routes[nextKey] : void 0
690
+ };
691
+ })
692
+ );
693
+ const panGesture = (0, import_react3.useMemo)(
694
+ () => buildGestureDetector({
695
+ key,
696
+ progress: animationValues.screenProgress[key],
697
+ config: currentRoute || {
698
+ id: key,
699
+ name: key,
700
+ index: 0,
701
+ status: 0,
702
+ closing: false
703
+ },
704
+ width: dimensions.width,
705
+ height: dimensions.height,
706
+ goBack: navigation.goBack
707
+ }),
708
+ [key, currentRoute, dimensions.width, dimensions.height, navigation.goBack]
709
+ );
710
+ const progressFallback = (0, import_react_native_reanimated5.useSharedValue)(0);
711
+ const gestureDraggingFallback = (0, import_react_native_reanimated5.useSharedValue)(0);
712
+ const gestureXFallback = (0, import_react_native_reanimated5.useSharedValue)(0);
713
+ const gestureYFallback = (0, import_react_native_reanimated5.useSharedValue)(0);
714
+ const normalizedGestureXFallback = (0, import_react_native_reanimated5.useSharedValue)(0);
715
+ const normalizedGestureYFallback = (0, import_react_native_reanimated5.useSharedValue)(0);
716
+ return (0, import_react3.useMemo)(() => {
717
+ return {
718
+ current: {
719
+ progress: animationValues.screenProgress[key] || progressFallback,
720
+ gesture: {
721
+ isDragging: animationValues.gestureDragging[key] || gestureDraggingFallback,
722
+ x: animationValues.gestureX[key] || gestureXFallback,
723
+ y: animationValues.gestureY[key] || gestureYFallback,
724
+ normalizedX: animationValues.normalizedGestureX[key] || normalizedGestureXFallback,
725
+ normalizedY: animationValues.normalizedGestureY[key] || normalizedGestureYFallback
726
+ }
727
+ },
728
+ next: nextRoute && animationValues.screenProgress[nextRoute.id] ? {
729
+ progress: animationValues.screenProgress[nextRoute.id],
730
+ gesture: {
731
+ isDragging: animationValues.gestureDragging[nextRoute.id] || gestureDraggingFallback,
732
+ x: animationValues.gestureX[nextRoute.id] || gestureXFallback,
733
+ y: animationValues.gestureY[nextRoute.id] || gestureYFallback,
734
+ normalizedX: animationValues.normalizedGestureX[nextRoute.id] || normalizedGestureXFallback,
735
+ normalizedY: animationValues.normalizedGestureY[nextRoute.id] || normalizedGestureYFallback
736
+ }
737
+ } : void 0,
738
+ layouts: { screen: dimensions },
739
+ insets,
740
+ closing: currentRoute?.closing || false,
741
+ screenStyleInterpolator: nextRoute?.screenStyleInterpolator || currentRoute?.screenStyleInterpolator || noopinterpolator,
742
+ gestureDetector: panGesture
743
+ };
744
+ }, [
745
+ key,
746
+ currentRoute,
747
+ nextRoute,
748
+ dimensions,
749
+ insets,
750
+ panGesture,
751
+ progressFallback,
752
+ gestureDraggingFallback,
753
+ gestureXFallback,
754
+ gestureYFallback,
755
+ normalizedGestureXFallback,
756
+ normalizedGestureYFallback
757
+ ]);
758
+ };
759
+ var _useScreenAnimation = () => {
760
+ return useAnimationBuilder();
761
+ };
762
+ var useScreenAnimation = () => {
763
+ const {
764
+ screenStyleInterpolator: _,
765
+ gestureDetector: __,
766
+ ...animationProps
767
+ } = useAnimationBuilder();
768
+ return animationProps;
769
+ };
770
+
771
+ // src/utils/create-config.ts
772
+ var createConfig = ({
773
+ navigation: reactNavigation,
774
+ route,
775
+ ...config
776
+ }) => {
777
+ return {
778
+ focus: (e) => {
779
+ RouteStore.updateRoute(e.target, {
780
+ id: e.target,
781
+ name: route.name,
782
+ status: 1,
783
+ closing: false,
784
+ ...config
785
+ });
786
+ },
787
+ beforeRemove: (e) => {
788
+ e.preventDefault();
789
+ const handleFinish = (finished) => {
790
+ if (!finished) return;
791
+ if (reactNavigation.canGoBack()) {
792
+ reactNavigation.dispatch(e.data?.action);
793
+ RouteStore.removeRoute(e.target);
794
+ }
795
+ };
796
+ RouteStore.updateRoute(e.target, {
797
+ status: 0,
798
+ closing: true,
799
+ onAnimationFinish: handleFinish
800
+ });
801
+ }
802
+ };
803
+ };
804
+
805
+ // src/utils/create-transition-component.tsx
806
+ var import_react5 = require("react");
807
+ var import_react_native2 = require("react-native");
808
+ var import_react_native_gesture_handler2 = require("react-native-gesture-handler");
809
+ var import_react_native_reanimated7 = __toESM(require("react-native-reanimated"));
810
+
811
+ // src/hooks/use-skip-first-frame.tsx
812
+ var import_react4 = require("react");
813
+ var import_react_native_reanimated6 = require("react-native-reanimated");
814
+ var useSkipFirstFrame = () => {
815
+ const opacity = (0, import_react_native_reanimated6.useSharedValue)(0);
816
+ const style = (0, import_react_native_reanimated6.useAnimatedStyle)(() => {
817
+ "worklet";
818
+ return {
819
+ opacity: opacity.value
820
+ };
821
+ });
822
+ (0, import_react4.useEffect)(() => {
823
+ requestAnimationFrame(() => {
824
+ opacity.value = 1;
825
+ });
826
+ }, [opacity]);
827
+ return { style };
828
+ };
829
+
830
+ // src/utils/create-transition-component.tsx
831
+ var import_jsx_runtime = require("react/jsx-runtime");
832
+ function createTransitionComponent(Wrapped) {
833
+ const AnimatedComponent = import_react_native_reanimated7.default.createAnimatedComponent(Wrapped);
834
+ const Inner = (0, import_react5.forwardRef)(
835
+ (props, ref) => {
836
+ const { children, style, ...rest } = props;
837
+ const {
838
+ screenStyleInterpolator,
839
+ gestureDetector,
840
+ ...screenInterpolationProps
841
+ } = _useScreenAnimation();
842
+ const screenContainerStyle = (0, import_react_native_reanimated7.useAnimatedStyle)(() => {
843
+ "worklet";
844
+ return screenStyleInterpolator(screenInterpolationProps).contentStyle || {};
845
+ });
846
+ const overlayStyle = (0, import_react_native_reanimated7.useAnimatedStyle)(() => {
847
+ "worklet";
848
+ return screenStyleInterpolator(screenInterpolationProps).overlayStyle || {};
849
+ });
850
+ const { style: flickerFixStyle } = useSkipFirstFrame();
851
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react_native_reanimated7.default.View, { style: [{ flex: 1 }, flickerFixStyle], children: [
852
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native_gesture_handler2.GestureDetector, { gesture: gestureDetector, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
853
+ AnimatedComponent,
854
+ {
855
+ ...rest,
856
+ ref,
857
+ style: [
858
+ { flex: 1, position: "relative" },
859
+ screenContainerStyle,
860
+ style
861
+ ],
862
+ children
863
+ }
864
+ ) }),
865
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
866
+ import_react_native_reanimated7.default.View,
867
+ {
868
+ style: [
869
+ import_react_native2.StyleSheet.absoluteFillObject,
870
+ overlayStyle,
871
+ { zIndex: 1e4 }
872
+ ],
873
+ pointerEvents: "none"
874
+ }
875
+ )
876
+ ] });
877
+ }
878
+ );
879
+ Inner.displayName = `Transition(${Wrapped.displayName || Wrapped.name || "Component"})`;
880
+ return (0, import_react5.memo)(Inner);
881
+ }
882
+
883
+ // src/utils/default-screen-options.ts
884
+ var defaultScreenOptions = () => ({
885
+ presentation: "containedTransparentModal",
886
+ headerShown: false,
887
+ animation: "none"
888
+ });
889
+
890
+ // src/index.ts
891
+ var index_default = {
892
+ createTransitionComponent,
893
+ View: createTransitionComponent(import_react_native3.View),
894
+ Pressable: createTransitionComponent(import_react_native3.Pressable),
895
+ createConfig,
896
+ defaultScreenOptions,
897
+ presets: presets_exports,
898
+ specs: specs_exports
899
+ };
900
+ // Annotate the CommonJS export names for ESM import in node:
901
+ 0 && (module.exports = {
902
+ useScreenAnimation
903
+ });
904
+ //# sourceMappingURL=index.js.map