@rootnative/inertia 0.0.7 → 0.0.9

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 (57) hide show
  1. package/CHANGELOG.md +31 -1
  2. package/README.md +1 -1
  3. package/dist/chunk-24NUL23E.js +8 -0
  4. package/dist/chunk-4XBX7KOD.js +8 -0
  5. package/dist/{chunk-OBRGJAST.js → chunk-6AN5X3YE.js} +3 -3
  6. package/dist/{chunk-WGCF3WQG.js → chunk-6NOCY764.js} +2 -2
  7. package/dist/{chunk-KBFN4TRO.mjs → chunk-6YIUETBF.mjs} +1 -1
  8. package/dist/{chunk-6V7MOBXO.js → chunk-E3ALFSH2.js} +64 -33
  9. package/dist/{chunk-ZOBOKLAS.mjs → chunk-EN4PN7X3.mjs} +1 -1
  10. package/dist/{chunk-RUIOM2YZ.mjs → chunk-GSUC4HWP.mjs} +1 -1
  11. package/dist/{chunk-QJYFTTM2.js → chunk-IHVMNOS4.js} +2 -2
  12. package/dist/{chunk-HPJMIBBK.mjs → chunk-IX6SEOSK.mjs} +41 -12
  13. package/dist/{chunk-52QSN2VQ.mjs → chunk-JBK5ZLMH.mjs} +1 -1
  14. package/dist/{chunk-OW5XTGVN.mjs → chunk-L4JFECXU.mjs} +2 -2
  15. package/dist/{chunk-VSK2E35J.mjs → chunk-M4766VUV.mjs} +1 -1
  16. package/dist/{chunk-Z3HCJ43H.mjs → chunk-MFAFB4K7.mjs} +1 -1
  17. package/dist/{chunk-4NAMD62I.js → chunk-MUT6BTZS.js} +7 -7
  18. package/dist/{chunk-WMLS4TMX.mjs → chunk-QNLSCOR5.mjs} +1 -1
  19. package/dist/{chunk-REYL77RE.mjs → chunk-W7NTRSPD.mjs} +17 -10
  20. package/dist/{chunk-PM6CVGXJ.js → chunk-WNVHPMBI.js} +16 -8
  21. package/dist/chunk-Z5USXWTE.js +8 -0
  22. package/dist/{chunk-QYR66E2G.js → chunk-ZBHQPVWE.js} +2 -2
  23. package/dist/gestureLayer/index.js +10 -10
  24. package/dist/gestureLayer/index.mjs +3 -3
  25. package/dist/index.d.mts +91 -4
  26. package/dist/index.d.ts +91 -4
  27. package/dist/index.js +69 -53
  28. package/dist/index.mjs +24 -20
  29. package/dist/motion/FlatList.js +5 -5
  30. package/dist/motion/FlatList.mjs +4 -4
  31. package/dist/motion/Image.js +5 -5
  32. package/dist/motion/Image.mjs +4 -4
  33. package/dist/motion/Pressable.js +5 -5
  34. package/dist/motion/Pressable.mjs +4 -4
  35. package/dist/motion/ScrollView.js +5 -5
  36. package/dist/motion/ScrollView.mjs +4 -4
  37. package/dist/motion/Text.js +5 -5
  38. package/dist/motion/Text.mjs +4 -4
  39. package/dist/motion/View.js +5 -5
  40. package/dist/motion/View.mjs +4 -4
  41. package/dist/touch/index.js +3 -3
  42. package/dist/touch/index.mjs +1 -1
  43. package/llms.txt +19 -2
  44. package/package.json +1 -1
  45. package/src/index.ts +5 -0
  46. package/src/motion/createMotionComponent.tsx +45 -10
  47. package/src/stagger/Stagger.tsx +75 -0
  48. package/src/stagger/StaggerContext.ts +25 -0
  49. package/src/stagger/index.ts +2 -0
  50. package/src/transitions/index.ts +6 -1
  51. package/src/transitions/resolve.ts +7 -1
  52. package/src/transitions/runtime.ts +22 -7
  53. package/src/values/index.ts +1 -0
  54. package/src/values/useInterpolatedStyle.ts +37 -7
  55. package/dist/chunk-NPJ46457.js +0 -8
  56. package/dist/chunk-YUTS6JRU.js +0 -8
  57. package/dist/chunk-ZSOCRHU3.js +0 -8
@@ -43,7 +43,9 @@ import {
43
43
  useSharedLayout,
44
44
  } from '../layout'
45
45
  import { usePresence } from '../presence'
46
+ import { useStaggerDelay } from '../stagger'
46
47
  import {
48
+ applyDelay,
47
49
  isTopLevelTransition,
48
50
  resolveAnimatableValue,
49
51
  resolveTransition,
@@ -547,6 +549,13 @@ export function createMotionComponent<C extends ComponentType<any>>(
547
549
  // at app start — a runtime toggle takes effect on the next launch.
548
550
  const shouldReduceMotion = useShouldReduceMotion()
549
551
 
552
+ // Per-child delay assigned by the nearest <Stagger> ancestor; 0 outside
553
+ // one. Applied in the value-driving effect, not here: the effect's
554
+ // closure reads the value current at the render that starts an
555
+ // animation, and a delay change alone must not re-trigger anything —
556
+ // so it is deliberately absent from the effect's deps.
557
+ const staggerDelay = useStaggerDelay()
558
+
550
559
  // Pin the latest `onAnimationEnd` in a ref so the worklet callback always
551
560
  // dispatches against the current closure without re-resolving the
552
561
  // animation graph. Worklets can read refs via `runOnJS`.
@@ -962,6 +971,22 @@ export function createMotionComponent<C extends ComponentType<any>>(
962
971
  transition,
963
972
  )
964
973
 
974
+ // <Stagger> delay for this run, wrapped around each key's fully
975
+ // resolved animation via `applyDelay` — once per animation, never
976
+ // merged into the base config (a sequence applies a base `delay` per
977
+ // step, which would stretch the whole sequence by N × delay). Composes
978
+ // with a consumer's own `transition.delay`, which `resolveTransition`
979
+ // has already applied inside.
980
+ //
981
+ // Deliberately 0 while exiting (an exit delayed by list position holds
982
+ // <Presence>'s unmount hostage to the cascade) and under reduced
983
+ // motion (a snap must not be deferred — same contract as `delayOf`
984
+ // returning `undefined` for `no-animation`).
985
+ const runStagger =
986
+ shouldReduceMotion || isExiting || staggerDelay <= 0
987
+ ? 0
988
+ : staggerDelay
989
+
965
990
  // Count transform axes participating in this effect run so the factory
966
991
  // can coalesce their terminal callbacks into a single transform-group
967
992
  // event. `undefined` when no transform axis is animating, which lets
@@ -996,6 +1021,7 @@ export function createMotionComponent<C extends ComponentType<any>>(
996
1021
  boxShadowInsets,
997
1022
  shadowTarget,
998
1023
  cfg,
1024
+ runStagger,
999
1025
  makeKeyCallbackFactory(
1000
1026
  'boxShadow',
1001
1027
  sharedValues.boxShadow,
@@ -1051,12 +1077,15 @@ export function createMotionComponent<C extends ComponentType<any>>(
1051
1077
  // `'no-animation'` (and so under reduced motion) it is assigned
1052
1078
  // straight into the slot, and the next animation would start from a
1053
1079
  // value that can't be parsed.
1054
- sharedValues[key].value = resolveAnimatableValue(
1055
- COLOR_KEY_SET.has(key)
1056
- ? normalizeAnimatableColorTarget(target)
1057
- : target,
1058
- cfg,
1059
- factory,
1080
+ sharedValues[key].value = applyDelay(
1081
+ resolveAnimatableValue(
1082
+ COLOR_KEY_SET.has(key)
1083
+ ? normalizeAnimatableColorTarget(target)
1084
+ : target,
1085
+ cfg,
1086
+ factory,
1087
+ ),
1088
+ runStagger,
1060
1089
  ) as never
1061
1090
  }
1062
1091
 
@@ -1655,6 +1684,7 @@ function driveBoxShadow(
1655
1684
  insetSlot: SharedValue<boolean[] | null>,
1656
1685
  target: BoxShadowInput,
1657
1686
  cfg: TransitionConfig | undefined,
1687
+ staggerDelay: number,
1658
1688
  factory: CallbackFactory | undefined,
1659
1689
  ): void {
1660
1690
  const currentLayers = payloadToLayers(slot.value as BoxShadowPayload)
@@ -1666,10 +1696,15 @@ function driveBoxShadow(
1666
1696
  if (from.length !== currentLayers.length) slot.value = layersToPayload(from)
1667
1697
  // `resolveTransition` is typed for the scalar surface it was written for;
1668
1698
  // Reanimated itself accepts the structured target and recurses into it.
1669
- slot.value = resolveTransition(
1670
- cfg,
1671
- layersToPayload(to) as unknown as number,
1672
- factory?.('animation', undefined),
1699
+ // The stagger wrap sits outside, same as the scalar path — this key can't
1700
+ // be a sequence, but keeping the delay out of `cfg` keeps one rule.
1701
+ slot.value = applyDelay(
1702
+ resolveTransition(
1703
+ cfg,
1704
+ layersToPayload(to) as unknown as number,
1705
+ factory?.('animation', undefined),
1706
+ ),
1707
+ staggerDelay,
1673
1708
  ) as AnimatableSlotValue
1674
1709
  }
1675
1710
 
@@ -0,0 +1,75 @@
1
+ import { Children, isValidElement, type ReactNode } from 'react'
2
+ import { StaggerContext } from './StaggerContext'
3
+
4
+ export interface StaggerProps {
5
+ children?: ReactNode
6
+ /**
7
+ * Milliseconds between consecutive children. Child `i` (in render order)
8
+ * receives a delay of `delay + i * interval`.
9
+ */
10
+ interval: number
11
+ /**
12
+ * Base delay in milliseconds applied to every child before the interval,
13
+ * so the whole cascade can start late without pushing the spacing into
14
+ * each child. Defaults to `0`.
15
+ */
16
+ delay?: number
17
+ /**
18
+ * Which end of the child list starts the cascade. `'first'` (default)
19
+ * staggers top-down in render order; `'last'` reverses it, so the final
20
+ * child animates first.
21
+ */
22
+ from?: 'first' | 'last'
23
+ /**
24
+ * Turn the stagger on or off in one place. When `false`, every child gets
25
+ * a delay of `0` — the single-switch escape hatch for "cascade in, but
26
+ * not back out": pass `enabled={revealed}` and the hide direction snaps
27
+ * together. Defaults to `true`.
28
+ */
29
+ enabled?: boolean
30
+ }
31
+
32
+ /**
33
+ * Assign each child a stagger delay from its position, so a list entrance
34
+ * cascades without every child computing `index * ms` itself.
35
+ *
36
+ * The parent owns the timing: reordering, filtering, or reversing the list
37
+ * re-derives every delay from the new render order, and `enabled` turns the
38
+ * whole cascade off in one place. Each child slot gets its own provider, so
39
+ * a `Motion.*` primitive anywhere inside child `i`'s subtree inherits child
40
+ * `i`'s delay.
41
+ *
42
+ * The delay applies to the declarative animations of the `Motion.*`
43
+ * primitives underneath — the mount animation (`initial` → `animate`) and
44
+ * any later `animate` change. It deliberately does not delay `gesture`
45
+ * feedback, `<Presence>` exits, or reduced-motion snaps.
46
+ *
47
+ * `<Stagger>` renders no host view — only per-child context providers.
48
+ */
49
+ export function Stagger({
50
+ children,
51
+ interval,
52
+ delay = 0,
53
+ from = 'first',
54
+ enabled = true,
55
+ }: StaggerProps) {
56
+ // `Children.toArray` drops `null` / `undefined` / booleans, so conditional
57
+ // children don't leave holes in the cascade, and it assigns stable
58
+ // element keys that the providers below reuse.
59
+ const items = Children.toArray(children)
60
+ const count = items.length
61
+ return (
62
+ <>
63
+ {items.map((child, index) => {
64
+ const position = from === 'last' ? count - 1 - index : index
65
+ const childDelay = enabled ? delay + position * interval : 0
66
+ const key = isValidElement(child) ? (child.key ?? index) : index
67
+ return (
68
+ <StaggerContext.Provider key={key} value={childDelay}>
69
+ {child}
70
+ </StaggerContext.Provider>
71
+ )
72
+ })}
73
+ </>
74
+ )
75
+ }
@@ -0,0 +1,25 @@
1
+ import { createContext, useContext } from 'react'
2
+
3
+ /**
4
+ * Per-child stagger delay in milliseconds, provided by `<Stagger>`. `0`
5
+ * outside a `<Stagger>` (or under a disabled one), which every consumer
6
+ * treats as "no stagger".
7
+ *
8
+ * Internal wiring — consumers use `<Stagger>`; custom animated components
9
+ * read the value through `useStaggerDelay()`.
10
+ */
11
+ export const StaggerContext = createContext(0)
12
+
13
+ /**
14
+ * The stagger delay (in milliseconds) assigned to this element's child slot
15
+ * by the nearest `<Stagger>` ancestor. `0` when there is none, when the
16
+ * stagger is disabled, or for the first child of a zero-`delay` stagger.
17
+ *
18
+ * The `Motion.*` primitives consume this automatically. Custom animated
19
+ * components built on `resolveTransition` / `resolveAnimatableValue` call
20
+ * this hook and add the returned value to their animation delay so they
21
+ * participate in a `<Stagger>` the same way the built-in primitives do.
22
+ */
23
+ export function useStaggerDelay(): number {
24
+ return useContext(StaggerContext)
25
+ }
@@ -0,0 +1,2 @@
1
+ export { Stagger, type StaggerProps } from './Stagger'
2
+ export { useStaggerDelay } from './StaggerContext'
@@ -1,4 +1,9 @@
1
- export { resolveTransition, type CallbackFactory } from './resolve'
1
+ export {
2
+ applyDelay,
3
+ resolveTransition,
4
+ type AnimationCallback,
5
+ type CallbackFactory,
6
+ } from './resolve'
2
7
  export { resolveAnimatableValue } from './resolveSequence'
3
8
  export { cubicBezier } from './cubicBezier'
4
9
  export { ensureWorkletEasing } from './easing'
@@ -116,7 +116,13 @@ export function applyRepeat(
116
116
  return withRepeat(animation as never, count, alternate)
117
117
  }
118
118
 
119
- function applyDelay(animation: unknown, delay: number | undefined) {
119
+ /**
120
+ * Wrap an animation in `withDelay`. A missing / zero / negative delay is a
121
+ * pass-through. Exported for the factory's stagger wrap, which must delay a
122
+ * fully-resolved animation exactly once — merging the delay into the base
123
+ * config instead would re-apply it per sequence step.
124
+ */
125
+ export function applyDelay(animation: unknown, delay: number | undefined) {
120
126
  if (!delay || delay <= 0) return animation
121
127
  return withDelay(delay, animation as never)
122
128
  }
@@ -5,6 +5,7 @@ import {
5
5
  withTiming,
6
6
  } from 'react-native-reanimated'
7
7
  import { springToReanimated } from './spring'
8
+ import { type AnimationCallback } from './resolve'
8
9
  import { type TransitionConfig } from '../types'
9
10
 
10
11
  const DEFAULT_TIMING_DURATION = 250
@@ -26,13 +27,23 @@ const DEFAULT_TIMING_DURATION = 250
26
27
  *
27
28
  * For decay transitions, `toValue` is ignored — decay decelerates from the
28
29
  * SV's current position via its own physics. Pass `0` if you don't have one.
30
+ *
31
+ * `callback`, when provided, fires once when the animation settles — the same
32
+ * `(finished) => void` shape Reanimated's `with*` factories accept. It runs on
33
+ * the UI thread, so bridge to JS with `runOnJS(...)` inside it. For
34
+ * `no-animation` the callback fires synchronously with `finished: true`, since
35
+ * a direct assignment has no settle point of its own.
29
36
  */
30
37
  export function buildReleaseAnimation(
31
38
  transition: TransitionConfig,
32
39
  toValue: number,
40
+ callback?: AnimationCallback,
33
41
  ): unknown {
34
42
  'worklet'
35
- if (transition.type === 'no-animation') return toValue
43
+ if (transition.type === 'no-animation') {
44
+ if (callback) callback(true, toValue)
45
+ return toValue
46
+ }
36
47
  if (transition.type === 'decay') {
37
48
  const cfg: {
38
49
  velocity: number
@@ -43,7 +54,7 @@ export function buildReleaseAnimation(
43
54
  cfg.deceleration = transition.deceleration
44
55
  }
45
56
  if (transition.clamp !== undefined) cfg.clamp = transition.clamp
46
- return withDecay(cfg)
57
+ return withDecay(cfg, callback as never)
47
58
  }
48
59
  if (transition.type === 'timing') {
49
60
  // Reanimated 4's `Easing.bezier(...)` returns an `EasingFunctionFactory`
@@ -54,10 +65,14 @@ export function buildReleaseAnimation(
54
65
  e && typeof e === 'object' && 'factory' in e
55
66
  ? e.factory()
56
67
  : (e ?? Easing.inOut(Easing.ease))
57
- return withTiming(toValue, {
58
- duration: transition.duration ?? DEFAULT_TIMING_DURATION,
59
- easing: easingFn,
60
- })
68
+ return withTiming(
69
+ toValue,
70
+ {
71
+ duration: transition.duration ?? DEFAULT_TIMING_DURATION,
72
+ easing: easingFn,
73
+ },
74
+ callback as never,
75
+ )
61
76
  }
62
- return withSpring(toValue, springToReanimated(transition))
77
+ return withSpring(toValue, springToReanimated(transition), callback as never)
63
78
  }
@@ -18,6 +18,7 @@ export {
18
18
  } from './useGesture'
19
19
  export {
20
20
  useInterpolatedStyle,
21
+ type InterpolatedStyle,
21
22
  type InterpolatedStyleMap,
22
23
  type NumericStyleKey,
23
24
  type TransformKey,
@@ -1,4 +1,5 @@
1
1
  import { useMemo } from 'react'
2
+ import type { ImageStyle, TextStyle, ViewStyle } from 'react-native'
2
3
  import {
3
4
  Extrapolation,
4
5
  interpolate,
@@ -83,6 +84,29 @@ export type InterpolatedStyleMap = {
83
84
  [K in ColorStyleKey]?: readonly string[]
84
85
  }
85
86
 
87
+ /**
88
+ * The style shape a map of `K` produces. Transform keys collapse into the
89
+ * single `transform` array the worklet emits; every other key survives under
90
+ * its own name, typed from the RN style family it belongs to.
91
+ *
92
+ * This is what lets the return narrow to `ViewStyle` at a call site whose map
93
+ * holds only view keys. Reanimated's own `useAnimatedStyle` resolves to
94
+ * `DefaultStyle` (`ViewStyle | ImageStyle | TextStyle`), and that union is
95
+ * rejected inside a `StyleProp<ViewStyle>` array — `TextStyle` fails on
96
+ * `cursor` — so a consumer had to cast.
97
+ */
98
+ export type InterpolatedStyle<K extends keyof InterpolatedStyleMap> = {
99
+ [P in Exclude<K, TransformKey>]: P extends keyof TextStyle
100
+ ? TextStyle[P]
101
+ : P extends keyof ImageStyle
102
+ ? ImageStyle[P]
103
+ : P extends keyof ViewStyle
104
+ ? ViewStyle[P]
105
+ : never
106
+ } & (Extract<K, TransformKey> extends never
107
+ ? unknown
108
+ : { transform: NonNullable<ViewStyle['transform']> })
109
+
86
110
  export interface UseInterpolatedStyleOptions {
87
111
  /**
88
112
  * Input range mapped onto every key's output range. Defaults to `[0, 1]`
@@ -262,17 +286,17 @@ function buildEntries(
262
286
  * hook stays fully declarative and hashable so unchanged maps produce zero
263
287
  * new UI-thread closures.
264
288
  */
265
- export function useInterpolatedStyle(
289
+ export function useInterpolatedStyle<K extends keyof InterpolatedStyleMap>(
266
290
  progress: SharedValue<number>,
267
- map: InterpolatedStyleMap,
291
+ map: Pick<InterpolatedStyleMap, K>,
268
292
  options?: UseInterpolatedStyleOptions,
269
- ): ReturnType<typeof useAnimatedStyle> {
293
+ ): InterpolatedStyle<K> {
270
294
  const extrapolate = mapExtrapolation(options?.extrapolate)
271
295
 
272
296
  // Order-preserving signature: the map's key order is load-bearing (transform
273
297
  // lifting emits axes in author order), so `stableSig` (which sorts keys) is
274
298
  // wrong here — sign the ordered key/output pairs plus the options directly.
275
- const sig = buildSignature(map, options)
299
+ const sig = buildSignature(map as InterpolatedStyleMap, options)
276
300
 
277
301
  // Resolve every key's plan once on the JS thread so the worklet body only
278
302
  // consumes flat arrays — consistent with the JS-thread resolver principle
@@ -280,9 +304,15 @@ export function useInterpolatedStyle(
280
304
  // principle 8). Memoized on `sig` so a fresh-but-equal map literal each
281
305
  // render yields the same `entries` reference — Reanimated then sees an
282
306
  // unchanged closure dependency and does not rebuild the UI-thread worklet.
283
- // eslint-disable-next-line react-hooks/exhaustive-deps
284
- const entries = useMemo<Entry[]>(() => buildEntries(map, options), [sig])
307
+ const entries = useMemo<Entry[]>(
308
+ () => buildEntries(map as InterpolatedStyleMap, options),
309
+ // eslint-disable-next-line react-hooks/exhaustive-deps
310
+ [sig],
311
+ )
285
312
 
313
+ // The worklet builds a `Record<string, unknown>` by design — the emitted keys
314
+ // are only known from `entries` at run time. `InterpolatedStyle<K>` is the
315
+ // static statement of that same shape, so the cast is where the two meet.
286
316
  return useAnimatedStyle(() => {
287
317
  'worklet'
288
318
  const out: Record<string, unknown> = {}
@@ -313,7 +343,7 @@ export function useInterpolatedStyle(
313
343
  }
314
344
  if (transform.length > 0) out.transform = transform
315
345
  return out
316
- })
346
+ }) as InterpolatedStyle<K>
317
347
  }
318
348
 
319
349
  declare const __DEV__: boolean
@@ -1,8 +0,0 @@
1
- 'use strict';
2
-
3
- var chunk6V7MOBXO_js = require('./chunk-6V7MOBXO.js');
4
- var reactNative = require('react-native');
5
-
6
- var MotionView = chunk6V7MOBXO_js.createMotionComponent(reactNative.View);
7
-
8
- exports.MotionView = MotionView;
@@ -1,8 +0,0 @@
1
- 'use strict';
2
-
3
- var chunk6V7MOBXO_js = require('./chunk-6V7MOBXO.js');
4
- var reactNative = require('react-native');
5
-
6
- var MotionImage = chunk6V7MOBXO_js.createMotionComponent(reactNative.Image);
7
-
8
- exports.MotionImage = MotionImage;
@@ -1,8 +0,0 @@
1
- 'use strict';
2
-
3
- var chunk6V7MOBXO_js = require('./chunk-6V7MOBXO.js');
4
- var reactNative = require('react-native');
5
-
6
- var MotionText = chunk6V7MOBXO_js.createMotionComponent(reactNative.Text);
7
-
8
- exports.MotionText = MotionText;