react-native-screen-transitions 3.6.0-alpha.4 → 3.6.0-beta.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.
Files changed (50) hide show
  1. package/lib/commonjs/shared/providers/screen/styles/helpers/resolve-slot-styles/index.js +94 -13
  2. package/lib/commonjs/shared/providers/screen/styles/helpers/resolve-slot-styles/index.js.map +1 -1
  3. package/lib/commonjs/shared/providers/screen/styles/helpers/resolve-slot-styles/materialize-slot.js +4 -1
  4. package/lib/commonjs/shared/providers/screen/styles/helpers/resolve-slot-styles/materialize-slot.js.map +1 -1
  5. package/lib/commonjs/shared/providers/screen/styles/helpers/resolve-slot-styles/types.js +4 -0
  6. package/lib/commonjs/shared/providers/screen/styles/hooks/use-interpolated-style-maps.js +85 -28
  7. package/lib/commonjs/shared/providers/screen/styles/hooks/use-interpolated-style-maps.js.map +1 -1
  8. package/lib/commonjs/shared/providers/screen/styles/hooks/use-resolved-slot-style-map.js +5 -4
  9. package/lib/commonjs/shared/providers/screen/styles/hooks/use-resolved-slot-style-map.js.map +1 -1
  10. package/lib/commonjs/shared/providers/screen/styles/styles.provider.js +2 -2
  11. package/lib/commonjs/shared/providers/screen/styles/styles.provider.js.map +1 -1
  12. package/lib/commonjs/shared/stores/bounds/internals/links.js +18 -3
  13. package/lib/commonjs/shared/stores/bounds/internals/links.js.map +1 -1
  14. package/lib/commonjs/shared/utils/bounds/navigation/zoom/build.js +5 -2
  15. package/lib/commonjs/shared/utils/bounds/navigation/zoom/build.js.map +1 -1
  16. package/lib/module/shared/providers/screen/styles/helpers/resolve-slot-styles/index.js +95 -14
  17. package/lib/module/shared/providers/screen/styles/helpers/resolve-slot-styles/index.js.map +1 -1
  18. package/lib/module/shared/providers/screen/styles/helpers/resolve-slot-styles/materialize-slot.js +4 -1
  19. package/lib/module/shared/providers/screen/styles/helpers/resolve-slot-styles/materialize-slot.js.map +1 -1
  20. package/lib/module/shared/providers/screen/styles/helpers/resolve-slot-styles/types.js +2 -0
  21. package/lib/module/shared/providers/screen/styles/hooks/use-interpolated-style-maps.js +84 -28
  22. package/lib/module/shared/providers/screen/styles/hooks/use-interpolated-style-maps.js.map +1 -1
  23. package/lib/module/shared/providers/screen/styles/hooks/use-resolved-slot-style-map.js +5 -4
  24. package/lib/module/shared/providers/screen/styles/hooks/use-resolved-slot-style-map.js.map +1 -1
  25. package/lib/module/shared/providers/screen/styles/styles.provider.js +2 -2
  26. package/lib/module/shared/providers/screen/styles/styles.provider.js.map +1 -1
  27. package/lib/module/shared/stores/bounds/internals/links.js +18 -3
  28. package/lib/module/shared/stores/bounds/internals/links.js.map +1 -1
  29. package/lib/module/shared/utils/bounds/navigation/zoom/build.js +5 -2
  30. package/lib/module/shared/utils/bounds/navigation/zoom/build.js.map +1 -1
  31. package/lib/typescript/shared/providers/screen/styles/helpers/resolve-slot-styles/index.d.ts +4 -4
  32. package/lib/typescript/shared/providers/screen/styles/helpers/resolve-slot-styles/index.d.ts.map +1 -1
  33. package/lib/typescript/shared/providers/screen/styles/helpers/resolve-slot-styles/materialize-slot.d.ts.map +1 -1
  34. package/lib/typescript/shared/providers/screen/styles/helpers/resolve-slot-styles/types.d.ts +2 -0
  35. package/lib/typescript/shared/providers/screen/styles/helpers/resolve-slot-styles/types.d.ts.map +1 -1
  36. package/lib/typescript/shared/providers/screen/styles/hooks/use-interpolated-style-maps.d.ts +6 -6
  37. package/lib/typescript/shared/providers/screen/styles/hooks/use-interpolated-style-maps.d.ts.map +1 -1
  38. package/lib/typescript/shared/providers/screen/styles/hooks/use-resolved-slot-style-map.d.ts +3 -2
  39. package/lib/typescript/shared/providers/screen/styles/hooks/use-resolved-slot-style-map.d.ts.map +1 -1
  40. package/lib/typescript/shared/stores/bounds/internals/links.d.ts.map +1 -1
  41. package/lib/typescript/shared/utils/bounds/navigation/zoom/build.d.ts.map +1 -1
  42. package/package.json +1 -1
  43. package/src/shared/providers/screen/styles/helpers/resolve-slot-styles/index.ts +140 -19
  44. package/src/shared/providers/screen/styles/helpers/resolve-slot-styles/materialize-slot.ts +5 -1
  45. package/src/shared/providers/screen/styles/helpers/resolve-slot-styles/types.ts +4 -0
  46. package/src/shared/providers/screen/styles/hooks/use-interpolated-style-maps.tsx +132 -44
  47. package/src/shared/providers/screen/styles/hooks/use-resolved-slot-style-map.tsx +7 -5
  48. package/src/shared/providers/screen/styles/styles.provider.tsx +2 -2
  49. package/src/shared/stores/bounds/internals/links.ts +27 -3
  50. package/src/shared/utils/bounds/navigation/zoom/build.ts +6 -2
@@ -3,11 +3,13 @@ import { NO_STYLES } from "../../../../constants";
3
3
  import { SystemStore } from "../../../../stores/system.store";
4
4
  import type {
5
5
  NormalizedTransitionInterpolatedStyle,
6
+ ScreenStyleInterpolator,
6
7
  TransitionInterpolatedStyle,
7
8
  } from "../../../../types/animation.types";
8
9
  import { logger } from "../../../../utils/logger";
9
10
  import { useScreenAnimationContext } from "../../animation";
10
11
  import { useBuildBoundsAccessor } from "../../animation/helpers/accessors/use-build-bounds-accessor";
12
+ import type { ScreenInterpolatorFrame } from "../../animation/helpers/pipeline";
11
13
  import { syncSelectedInterpolatorOptions } from "../../animation/helpers/selected-interpolator-options";
12
14
  import { useDescriptorDerivations } from "../../descriptors";
13
15
  import {
@@ -16,10 +18,93 @@ import {
16
18
  } from "../../options";
17
19
  import { normalizeSlots } from "../helpers/normalize-slots";
18
20
  import { preserveAnimatedPropsOnly } from "../helpers/preserve-animated-props-only";
21
+ import type { LocalStyleLayers } from "../helpers/resolve-slot-styles";
19
22
  import { stripInterpolatorOptions } from "../helpers/strip-interpolator-options";
20
23
 
24
+ const NO_STYLE_LAYERS: LocalStyleLayers = [];
25
+
26
+ type InterpolatorResult = {
27
+ stylesMap: NormalizedTransitionInterpolatedStyle;
28
+ rawStyleMap: TransitionInterpolatedStyle | undefined;
29
+ };
30
+
31
+ type RunInterpolatorParams = {
32
+ interpolator: ScreenStyleInterpolator | undefined;
33
+ props: ScreenInterpolatorFrame;
34
+ progress: ScreenInterpolatorFrame["progress"];
35
+ next: ScreenInterpolatorFrame["next"];
36
+ bounds: Parameters<ScreenStyleInterpolator>[0]["bounds"];
37
+ shouldDeferStyleBuckets: boolean;
38
+ };
39
+
40
+ const normalizeRawStyleMap = (
41
+ rawStyleMap: TransitionInterpolatedStyle | undefined,
42
+ shouldDeferStyleBuckets: boolean,
43
+ ) => {
44
+ "worklet";
45
+
46
+ if (!rawStyleMap) {
47
+ return NO_STYLES;
48
+ }
49
+
50
+ const stylesMap = normalizeSlots(stripInterpolatorOptions(rawStyleMap));
51
+
52
+ return shouldDeferStyleBuckets
53
+ ? preserveAnimatedPropsOnly(stylesMap)
54
+ : stylesMap;
55
+ };
56
+
57
+ const runInterpolator = ({
58
+ interpolator,
59
+ props,
60
+ progress,
61
+ next,
62
+ bounds,
63
+ shouldDeferStyleBuckets,
64
+ }: RunInterpolatorParams): InterpolatorResult | undefined => {
65
+ "worklet";
66
+
67
+ if (!interpolator) {
68
+ return undefined;
69
+ }
70
+
71
+ try {
72
+ const raw = interpolator({
73
+ ...props,
74
+ progress,
75
+ next,
76
+ bounds,
77
+ });
78
+
79
+ const rawStyleMap: TransitionInterpolatedStyle | undefined =
80
+ typeof raw === "object" && raw != null ? raw : undefined;
81
+
82
+ return {
83
+ rawStyleMap,
84
+ stylesMap: normalizeRawStyleMap(rawStyleMap, shouldDeferStyleBuckets),
85
+ };
86
+ } catch (_) {
87
+ if (__DEV__) {
88
+ logger.warn("screenStyleInterpolator must be a worklet");
89
+ }
90
+
91
+ return undefined;
92
+ }
93
+ };
94
+
95
+ const appendLayer = (
96
+ layers: LocalStyleLayers,
97
+ result: InterpolatorResult | undefined,
98
+ ) => {
99
+ "worklet";
100
+
101
+ if (result) {
102
+ layers.push(result.stylesMap);
103
+ }
104
+ };
105
+
21
106
  /**
22
- * Builds the raw interpolated styles map for the current screen pass.
107
+ * Builds the raw interpolated style layers for the current screen pass.
23
108
  *
24
109
  * This hook exists to stabilize style ownership during rapid navigation,
25
110
  * especially when an interactive close gesture overlaps with a new navigation
@@ -39,9 +124,9 @@ import { stripInterpolatorOptions } from "../helpers/strip-interpolator-options"
39
124
  * hidden window so animated props and runtime options stay warm, but defer style
40
125
  * buckets so measurement sees the final untransformed layout.
41
126
  *
42
- * The result stays as a single slot map. Resolution happens downstream, where
43
- * slot ids determine whether a slot may inherit from ancestors or must remain
44
- * local to the owning screen container.
127
+ * The result is ordered from lowest to highest priority. Resolution happens
128
+ * downstream, where slot ids determine whether slots inherit from ancestors and
129
+ * where higher owner layers override lower owner layers per key.
45
130
  */
46
131
  export const useInterpolatedStylesMap = () => {
47
132
  const { currentScreenKey } = useDescriptorDerivations();
@@ -61,7 +146,7 @@ export const useInterpolatedStylesMap = () => {
61
146
 
62
147
  const isGesturingDuringCloseAnimation = useSharedValue(false);
63
148
 
64
- return useDerivedValue<NormalizedTransitionInterpolatedStyle>(() => {
149
+ return useDerivedValue<LocalStyleLayers>(() => {
65
150
  "worklet";
66
151
  screenInterpolatorPropsRevision.get();
67
152
  const props = screenInterpolatorProps.get();
@@ -99,16 +184,6 @@ export const useInterpolatedStylesMap = () => {
99
184
 
100
185
  const interpolatorOptionsOwner =
101
186
  isInGestureMode || !nextInterpolator ? "current" : "next";
102
- const interpolator =
103
- interpolatorOptionsOwner === "current"
104
- ? currentInterpolator
105
- : nextInterpolator;
106
-
107
- if (!interpolator) {
108
- syncSelectedInterpolatorOptions(selectedInterpolatorOptions, "current");
109
- syncScreenOptionsOverrides(undefined, screenOptions);
110
- return NO_STYLES;
111
- }
112
187
 
113
188
  let effectiveProgress = progress;
114
189
  let effectiveNext = next;
@@ -118,42 +193,55 @@ export const useInterpolatedStylesMap = () => {
118
193
  effectiveNext = undefined;
119
194
  }
120
195
 
121
- try {
122
- const raw = interpolator({
123
- ...props,
124
- progress: effectiveProgress,
125
- next: effectiveNext,
126
- bounds: boundsAccessor,
127
- });
128
-
129
- const rawStyleMap: TransitionInterpolatedStyle | undefined =
130
- typeof raw === "object" && raw != null ? raw : undefined;
196
+ const currentResult = runInterpolator({
197
+ interpolator: currentInterpolator,
198
+ props,
199
+ progress: effectiveProgress,
200
+ next: effectiveNext,
201
+ bounds: boundsAccessor,
202
+ shouldDeferStyleBuckets,
203
+ });
131
204
 
205
+ if (interpolatorOptionsOwner === "current") {
132
206
  syncSelectedInterpolatorOptions(
133
207
  selectedInterpolatorOptions,
134
- interpolatorOptionsOwner,
135
- rawStyleMap?.options,
136
- );
137
- syncScreenOptionsOverrides(
138
- interpolatorOptionsOwner === "current" ? rawStyleMap : undefined,
139
- screenOptions,
208
+ "current",
209
+ currentResult?.rawStyleMap?.options,
140
210
  );
211
+ syncScreenOptionsOverrides(currentResult?.rawStyleMap, screenOptions);
141
212
 
142
- const stylesMap = !rawStyleMap
143
- ? NO_STYLES
144
- : normalizeSlots(stripInterpolatorOptions(rawStyleMap));
145
-
146
- return shouldDeferStyleBuckets
147
- ? preserveAnimatedPropsOnly(stylesMap)
148
- : stylesMap;
149
- } catch (_) {
150
- if (__DEV__) {
151
- logger.warn("screenStyleInterpolator must be a worklet");
213
+ if (!currentResult) {
214
+ return NO_STYLE_LAYERS;
152
215
  }
153
216
 
154
- syncSelectedInterpolatorOptions(selectedInterpolatorOptions, "current");
155
- syncScreenOptionsOverrides(undefined, screenOptions);
156
- return NO_STYLES;
217
+ return [currentResult.stylesMap];
157
218
  }
219
+
220
+ const nextResult = runInterpolator({
221
+ interpolator: nextInterpolator,
222
+ props,
223
+ progress: effectiveProgress,
224
+ next: effectiveNext,
225
+ bounds: boundsAccessor,
226
+ shouldDeferStyleBuckets,
227
+ });
228
+
229
+ syncSelectedInterpolatorOptions(
230
+ selectedInterpolatorOptions,
231
+ "next",
232
+ nextResult?.rawStyleMap?.options,
233
+ );
234
+ syncScreenOptionsOverrides(undefined, screenOptions);
235
+
236
+ const layers: LocalStyleLayers = [];
237
+
238
+ appendLayer(layers, currentResult);
239
+ appendLayer(layers, nextResult);
240
+
241
+ if (layers.length === 0) {
242
+ return NO_STYLE_LAYERS;
243
+ }
244
+
245
+ return layers;
158
246
  });
159
247
  };
@@ -7,18 +7,19 @@ import { NO_STYLES } from "../../../../constants";
7
7
  import type { NormalizedTransitionInterpolatedStyle } from "../../../../types/animation.types";
8
8
  import { useScreenAnimationContext } from "../../animation";
9
9
  import {
10
+ type LocalStyleLayers,
10
11
  type ResettableStyleStatesBySlot,
11
12
  resolveSlotStyles,
12
13
  reuseEqualResolvedSlots,
13
14
  } from "../helpers/resolve-slot-styles";
14
15
 
15
16
  interface UseResolvedStylesMapParams {
16
- currentStylesMap: SharedValue<NormalizedTransitionInterpolatedStyle>;
17
+ localStylesMaps: SharedValue<LocalStyleLayers>;
17
18
  ancestorStylesMap?: SharedValue<NormalizedTransitionInterpolatedStyle>;
18
19
  }
19
20
 
20
21
  export const useResolvedStylesMap = ({
21
- currentStylesMap,
22
+ localStylesMaps,
22
23
  ancestorStylesMap,
23
24
  }: UseResolvedStylesMapParams) => {
24
25
  const { screenInterpolatorProps, screenInterpolatorPropsRevision } =
@@ -34,13 +35,14 @@ export const useResolvedStylesMap = ({
34
35
  screenInterpolatorPropsRevision.get();
35
36
 
36
37
  const props = screenInterpolatorProps.get();
37
- // Keep missing local slots alive while another route drives this screen.
38
- // Custom ids and keys from slots that still exist are still reset.
38
+ // Keep missing local slots alive only when another route drives this screen
39
+ // and no active local style layer is available. Once a current/next layer
40
+ // runs, omitted local slots and dropped keys are intentional reset signals.
39
41
  const deferLocalSlotResets = !props.focused && !props.current.closing;
40
42
 
41
43
  const { resolvedStylesMap, nextPreviousStyleStatesBySlot } =
42
44
  resolveSlotStyles({
43
- currentStylesMap: currentStylesMap.get(),
45
+ localStylesMaps: localStylesMaps.get(),
44
46
  ancestorStylesMap: ancestorStylesMap?.get() ?? NO_STYLES,
45
47
  previousStyleStatesBySlot: previousStyleStatesBySlot.get(),
46
48
  deferLocalSlotResets,
@@ -26,10 +26,10 @@ export const {
26
26
  })<Props, ScreenStylesContextValue>(({ children, isFloatingOverlay }) => {
27
27
  const parentContext = useContext(ScreenStylesContext);
28
28
 
29
- const rawStylesMap = useInterpolatedStylesMap();
29
+ const rawStylesMaps = useInterpolatedStylesMap();
30
30
 
31
31
  const stylesMap = useResolvedStylesMap({
32
- currentStylesMap: rawStylesMap,
32
+ localStylesMaps: rawStylesMaps,
33
33
  ancestorStylesMap: parentContext?.stylesMap,
34
34
  });
35
35
 
@@ -216,6 +216,20 @@ const isCompletedLink = (link: TagLink | null): link is TagLink => {
216
216
  return !!link?.destination;
217
217
  };
218
218
 
219
+ const getPendingSourceLink = (
220
+ state: LinkPairsState,
221
+ pairKey: ScreenPairKey,
222
+ linkKey: LinkKey,
223
+ ): TagLink | null => {
224
+ "worklet";
225
+ const sourceScreenKey = getSourceScreenKeyFromPairKey(pairKey);
226
+ const pendingPairKey = createPendingPairKey(sourceScreenKey);
227
+
228
+ if (pendingPairKey === pairKey) return null;
229
+
230
+ return getPairLink(state, pendingPairKey, linkKey);
231
+ };
232
+
219
233
  function getResolvedLink(
220
234
  pairKey: ScreenPairKey,
221
235
  tag: TagID,
@@ -226,12 +240,22 @@ function getResolvedLink(
226
240
  const group = getGroupKeyFromTag(tag);
227
241
  const requestedLink = getPairLink(state, pairKey, linkKey);
228
242
 
243
+ // Press-triggered zoom captures the source before navigation under a pending
244
+ // source<> pair. If the destination screen has no Boundary.View, nothing
245
+ // promotes that source into source<>destination, but zoom can still animate to
246
+ // its default top target from the pending source bounds.
247
+ const fallbackPendingLink = requestedLink
248
+ ? null
249
+ : getPendingSourceLink(state, pairKey, linkKey);
250
+
251
+ const link = requestedLink ?? fallbackPendingLink;
252
+
229
253
  // Group active ids can update before the new member has a full source/destination
230
254
  // link, so unresolved grouped links fall back to the initial id's measurements.
231
- if (!group || isCompletedLink(requestedLink)) {
255
+ if (!group || isCompletedLink(link)) {
232
256
  return {
233
257
  tag,
234
- link: requestedLink,
258
+ link,
235
259
  };
236
260
  }
237
261
 
@@ -248,7 +272,7 @@ function getResolvedLink(
248
272
 
249
273
  return {
250
274
  tag,
251
- link: requestedLink,
275
+ link,
252
276
  };
253
277
  }
254
278
 
@@ -416,6 +416,10 @@ export function buildZoomStyles({
416
416
  Math.abs(unfocusedContentScale),
417
417
  EPSILON,
418
418
  );
419
+ // A naturally settled close can leave this as the last emitted style frame,
420
+ // so drop temporary stacking here instead of waiting for a later reset pass.
421
+ const shouldElevateUnfocusedElement =
422
+ !props.active.closing || !props.active.logicallySettled;
419
423
 
420
424
  const scaleShiftX = computeCenterScaleShift({
421
425
  center: elementCenterX,
@@ -480,8 +484,8 @@ export function buildZoomStyles({
480
484
  },
481
485
  ],
482
486
  opacity: zoomOptions?.debug ? 1 : unfocusedFade,
483
- zIndex: 9999,
484
- elevation: 9999,
487
+ zIndex: shouldElevateUnfocusedElement ? 9999 : 0,
488
+ elevation: shouldElevateUnfocusedElement ? 9999 : 0,
485
489
  };
486
490
 
487
491
  return {