@rootnative/inertia 0.0.0-alpha.0 → 0.0.0-alpha.2

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 (79) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/README.md +2 -0
  3. package/dist/gestureLayer/index.d.mts +48 -7
  4. package/dist/gestureLayer/index.d.ts +48 -7
  5. package/dist/gestureLayer/index.js +107 -30
  6. package/dist/gestureLayer/index.mjs +107 -30
  7. package/dist/index.d.mts +127 -16
  8. package/dist/index.d.ts +127 -16
  9. package/dist/index.js +177 -62
  10. package/dist/index.mjs +175 -63
  11. package/dist/motion/Image.d.mts +1 -1
  12. package/dist/motion/Image.d.ts +1 -1
  13. package/dist/motion/Image.js +90 -50
  14. package/dist/motion/Image.mjs +90 -50
  15. package/dist/motion/Pressable.d.mts +1 -1
  16. package/dist/motion/Pressable.d.ts +1 -1
  17. package/dist/motion/Pressable.js +90 -50
  18. package/dist/motion/Pressable.mjs +90 -50
  19. package/dist/motion/ScrollView.d.mts +1 -1
  20. package/dist/motion/ScrollView.d.ts +1 -1
  21. package/dist/motion/ScrollView.js +90 -50
  22. package/dist/motion/ScrollView.mjs +90 -50
  23. package/dist/motion/Text.d.mts +1 -1
  24. package/dist/motion/Text.d.ts +1 -1
  25. package/dist/motion/Text.js +90 -50
  26. package/dist/motion/Text.mjs +90 -50
  27. package/dist/motion/View.d.mts +1 -1
  28. package/dist/motion/View.d.ts +1 -1
  29. package/dist/motion/View.js +90 -50
  30. package/dist/motion/View.mjs +90 -50
  31. package/dist/testing/index.js +0 -2
  32. package/dist/testing/index.mjs +0 -2
  33. package/dist/touch/index.d.mts +1 -1
  34. package/dist/touch/index.d.ts +1 -1
  35. package/dist/touch/index.js +0 -2
  36. package/dist/touch/index.mjs +0 -2
  37. package/dist/{types-cU43dEmH.d.mts → types-BzEgiUdJ.d.mts} +58 -9
  38. package/dist/{types-cU43dEmH.d.ts → types-BzEgiUdJ.d.ts} +58 -9
  39. package/dist/{useGesture-BnY65PlQ.d.ts → useGesture-BRLgiNOC.d.ts} +6 -4
  40. package/dist/{useGesture-DxtXdz-K.d.mts → useGesture-C0GBS7d2.d.mts} +6 -4
  41. package/llms.txt +9 -5
  42. package/package.json +2 -1
  43. package/src/config/MotionConfig.tsx +55 -9
  44. package/src/config/MotionConfigContext.ts +21 -1
  45. package/src/config/index.ts +7 -1
  46. package/src/config/namedTransitions.ts +80 -0
  47. package/src/gestureLayer/index.ts +1 -0
  48. package/src/gestureLayer/useGestureLayer.ts +92 -8
  49. package/src/index.ts +20 -2
  50. package/src/motion/createMotionComponent.tsx +36 -6
  51. package/src/transitions/cubicBezier.ts +126 -0
  52. package/src/transitions/index.ts +1 -0
  53. package/src/types.ts +63 -8
  54. package/src/values/useAnimation.ts +12 -6
  55. package/src/values/useBooleanSpring.ts +5 -3
  56. package/src/values/useGesture.ts +25 -7
  57. package/src/values/useMotionValue.ts +11 -0
  58. package/src/values/useScroll.ts +8 -1
  59. package/src/values/useSpring.ts +46 -9
  60. package/dist/gestureLayer/index.js.map +0 -1
  61. package/dist/gestureLayer/index.mjs.map +0 -1
  62. package/dist/index.js.map +0 -1
  63. package/dist/index.mjs.map +0 -1
  64. package/dist/motion/Image.js.map +0 -1
  65. package/dist/motion/Image.mjs.map +0 -1
  66. package/dist/motion/Pressable.js.map +0 -1
  67. package/dist/motion/Pressable.mjs.map +0 -1
  68. package/dist/motion/ScrollView.js.map +0 -1
  69. package/dist/motion/ScrollView.mjs.map +0 -1
  70. package/dist/motion/Text.js.map +0 -1
  71. package/dist/motion/Text.mjs.map +0 -1
  72. package/dist/motion/View.js.map +0 -1
  73. package/dist/motion/View.mjs.map +0 -1
  74. package/dist/testing/index.js.map +0 -1
  75. package/dist/testing/index.mjs.map +0 -1
  76. package/dist/touch/index.js.map +0 -1
  77. package/dist/touch/index.mjs.map +0 -1
  78. package/src/__type-tests__/animate.test-d.tsx +0 -88
  79. package/src/__type-tests__/variants.test-d.tsx +0 -67
@@ -60,6 +60,51 @@ interface NoAnimationTransition {
60
60
  type: 'no-animation';
61
61
  }
62
62
  type TransitionConfig = SpringTransition | TimingTransition | DecayTransition | NoAnimationTransition;
63
+ /**
64
+ * Augmentation point for typed named transitions. Empty by default — the
65
+ * registry is data fed to `<MotionConfig transitions={...}>` at runtime, so
66
+ * out of the box any string is accepted wherever a `TransitionName` is.
67
+ *
68
+ * Consumers who want their registered names to autocomplete (and typos to be
69
+ * compile errors) augment this interface with their design system's names:
70
+ *
71
+ * ```ts
72
+ * declare module '@rootnative/inertia' {
73
+ * interface RegisteredTransitions {
74
+ * 'state-hover': TransitionConfig
75
+ * 'selection': TransitionConfig
76
+ * }
77
+ * }
78
+ * ```
79
+ *
80
+ * The value type is ignored — only the keys matter. Inertia itself never
81
+ * declares a name here: presets are consumer data, not library surface.
82
+ */
83
+ interface RegisteredTransitions {
84
+ }
85
+ /**
86
+ * A named transition registered on the nearest `<MotionConfig transitions>`.
87
+ * Accepted anywhere a `TransitionConfig` is: the `transition` prop (top-level
88
+ * and per-property), the `layout` prop, and the value-layer hooks. Resolution
89
+ * happens on the JS thread against the nearest provider; unknown names warn
90
+ * in dev and fall back to the library default spring.
91
+ *
92
+ * Plain `string` until `RegisteredTransitions` is augmented; with an
93
+ * augmentation in place it narrows to the registered keys.
94
+ */
95
+ type TransitionName = keyof RegisteredTransitions extends never ? string : keyof RegisteredTransitions & string;
96
+ /**
97
+ * The registry shape accepted by `<MotionConfig transitions={...}>`: named
98
+ * `TransitionConfig`s. Names are consumer vocabulary (design tokens, semantic
99
+ * roles); Inertia ships no presets.
100
+ */
101
+ type NamedTransitions = Partial<Record<TransitionName, TransitionConfig>>;
102
+ /**
103
+ * A transition given either inline (`TransitionConfig`) or by registered name
104
+ * (`TransitionName`). This is the input type everywhere the public surface
105
+ * accepts a single transition.
106
+ */
107
+ type TransitionInput = TransitionConfig | TransitionName;
63
108
  /**
64
109
  * Repeat config — one shape, not three flags. Default `alternate: true`.
65
110
  */
@@ -73,7 +118,7 @@ type RepeatConfig = number | 'infinite' | {
73
118
  * here.
74
119
  */
75
120
  type PerPropertyTransition<S> = {
76
- [K in keyof S]?: TransitionConfig;
121
+ [K in keyof S]?: TransitionInput;
77
122
  };
78
123
  /**
79
124
  * Per-gesture-layer transition map. Each `gesture` sub-state animates a
@@ -85,12 +130,12 @@ type PerPropertyTransition<S> = {
85
130
  * collide with the primitive's inferred style keys.
86
131
  */
87
132
  interface GestureLayerTransitions {
88
- pressed?: TransitionConfig;
89
- focused?: TransitionConfig;
90
- focusVisible?: TransitionConfig;
91
- hovered?: TransitionConfig;
133
+ pressed?: TransitionInput;
134
+ focused?: TransitionInput;
135
+ focusVisible?: TransitionInput;
136
+ hovered?: TransitionInput;
92
137
  }
93
- type Transition<S> = TransitionConfig | (PerPropertyTransition<S> & GestureLayerTransitions);
138
+ type Transition<S> = TransitionInput | (PerPropertyTransition<S> & GestureLayerTransitions);
94
139
  /**
95
140
  * Transform shorthands that Inertia exposes on `animate` but that don't
96
141
  * appear on RN's typed ViewStyle as top-level keys. RN keeps `scale`,
@@ -259,7 +304,9 @@ interface MotionProps<C, V extends VariantsMap<C> = VariantsMap<C>> {
259
304
  gesture?: GestureSubStates<C>;
260
305
  /**
261
306
  * Per-property or top-level transition config. Per-property entries take
262
- * precedence over the top-level transition.
307
+ * precedence over the top-level transition. Anywhere a config object is
308
+ * accepted (top-level, per-property, per gesture layer) a `TransitionName`
309
+ * registered on the nearest `<MotionConfig transitions>` is accepted too.
263
310
  */
264
311
  transition?: Transition<AnimateStyle<C>>;
265
312
  /**
@@ -271,6 +318,8 @@ interface MotionProps<C, V extends VariantsMap<C> = VariantsMap<C>> {
271
318
  * - `true` — animate with the library's default spring.
272
319
  * - `TransitionConfig` — spring (react-spring vocab) or timing config; the
273
320
  * resolver bridges to Reanimated's `LinearTransition` builder.
321
+ * - `TransitionName` — a name registered on the nearest
322
+ * `<MotionConfig transitions>`.
274
323
  * - omitted / `false` — no layout animation (default).
275
324
  *
276
325
  * Only `'spring'` / `'timing'` / `'no-animation'` map to layout transitions
@@ -282,7 +331,7 @@ interface MotionProps<C, V extends VariantsMap<C> = VariantsMap<C>> {
282
331
  * own layout changes, `layoutId` animates from a different element's
283
332
  * last measured rect to this element's current rect.
284
333
  */
285
- layout?: boolean | TransitionConfig;
334
+ layout?: boolean | TransitionInput;
286
335
  /**
287
336
  * Shared-element transition id. When a Motion primitive with `layoutId`
288
337
  * unmounts, its last on-screen rect is recorded under that id; the next
@@ -336,4 +385,4 @@ interface MotionComponent<C extends ComponentType<any>> {
336
385
  displayName?: string;
337
386
  }
338
387
 
339
- export type { AnimatableValue as A, DecayTransition as D, EasingInput as E, GestureSubStates as G, MotionComponent as M, NoAnimationTransition as N, PerPropertyTransition as P, RepeatConfig as R, SpringTransition as S, TransitionConfig as T, VariantController as V, AnimateStyle as a, AnimationCallbackInfo as b, MotionProps as c, SequenceStep as d, TimingTransition as e, Transition as f, VariantsMap as g, GestureLayerTransitions as h };
388
+ export type { AnimatableValue as A, DecayTransition as D, EasingInput as E, GestureSubStates as G, MotionComponent as M, NamedTransitions as N, PerPropertyTransition as P, RegisteredTransitions as R, SpringTransition as S, TransitionInput as T, VariantController as V, TransitionConfig as a, TransitionName as b, AnimateStyle as c, AnimationCallbackInfo as d, EasingFunction as e, EasingFunctionFactory as f, MotionProps as g, NoAnimationTransition as h, RepeatConfig as i, SequenceStep as j, TimingTransition as k, Transition as l, VariantsMap as m, GestureLayerTransitions as n };
@@ -60,6 +60,51 @@ interface NoAnimationTransition {
60
60
  type: 'no-animation';
61
61
  }
62
62
  type TransitionConfig = SpringTransition | TimingTransition | DecayTransition | NoAnimationTransition;
63
+ /**
64
+ * Augmentation point for typed named transitions. Empty by default — the
65
+ * registry is data fed to `<MotionConfig transitions={...}>` at runtime, so
66
+ * out of the box any string is accepted wherever a `TransitionName` is.
67
+ *
68
+ * Consumers who want their registered names to autocomplete (and typos to be
69
+ * compile errors) augment this interface with their design system's names:
70
+ *
71
+ * ```ts
72
+ * declare module '@rootnative/inertia' {
73
+ * interface RegisteredTransitions {
74
+ * 'state-hover': TransitionConfig
75
+ * 'selection': TransitionConfig
76
+ * }
77
+ * }
78
+ * ```
79
+ *
80
+ * The value type is ignored — only the keys matter. Inertia itself never
81
+ * declares a name here: presets are consumer data, not library surface.
82
+ */
83
+ interface RegisteredTransitions {
84
+ }
85
+ /**
86
+ * A named transition registered on the nearest `<MotionConfig transitions>`.
87
+ * Accepted anywhere a `TransitionConfig` is: the `transition` prop (top-level
88
+ * and per-property), the `layout` prop, and the value-layer hooks. Resolution
89
+ * happens on the JS thread against the nearest provider; unknown names warn
90
+ * in dev and fall back to the library default spring.
91
+ *
92
+ * Plain `string` until `RegisteredTransitions` is augmented; with an
93
+ * augmentation in place it narrows to the registered keys.
94
+ */
95
+ type TransitionName = keyof RegisteredTransitions extends never ? string : keyof RegisteredTransitions & string;
96
+ /**
97
+ * The registry shape accepted by `<MotionConfig transitions={...}>`: named
98
+ * `TransitionConfig`s. Names are consumer vocabulary (design tokens, semantic
99
+ * roles); Inertia ships no presets.
100
+ */
101
+ type NamedTransitions = Partial<Record<TransitionName, TransitionConfig>>;
102
+ /**
103
+ * A transition given either inline (`TransitionConfig`) or by registered name
104
+ * (`TransitionName`). This is the input type everywhere the public surface
105
+ * accepts a single transition.
106
+ */
107
+ type TransitionInput = TransitionConfig | TransitionName;
63
108
  /**
64
109
  * Repeat config — one shape, not three flags. Default `alternate: true`.
65
110
  */
@@ -73,7 +118,7 @@ type RepeatConfig = number | 'infinite' | {
73
118
  * here.
74
119
  */
75
120
  type PerPropertyTransition<S> = {
76
- [K in keyof S]?: TransitionConfig;
121
+ [K in keyof S]?: TransitionInput;
77
122
  };
78
123
  /**
79
124
  * Per-gesture-layer transition map. Each `gesture` sub-state animates a
@@ -85,12 +130,12 @@ type PerPropertyTransition<S> = {
85
130
  * collide with the primitive's inferred style keys.
86
131
  */
87
132
  interface GestureLayerTransitions {
88
- pressed?: TransitionConfig;
89
- focused?: TransitionConfig;
90
- focusVisible?: TransitionConfig;
91
- hovered?: TransitionConfig;
133
+ pressed?: TransitionInput;
134
+ focused?: TransitionInput;
135
+ focusVisible?: TransitionInput;
136
+ hovered?: TransitionInput;
92
137
  }
93
- type Transition<S> = TransitionConfig | (PerPropertyTransition<S> & GestureLayerTransitions);
138
+ type Transition<S> = TransitionInput | (PerPropertyTransition<S> & GestureLayerTransitions);
94
139
  /**
95
140
  * Transform shorthands that Inertia exposes on `animate` but that don't
96
141
  * appear on RN's typed ViewStyle as top-level keys. RN keeps `scale`,
@@ -259,7 +304,9 @@ interface MotionProps<C, V extends VariantsMap<C> = VariantsMap<C>> {
259
304
  gesture?: GestureSubStates<C>;
260
305
  /**
261
306
  * Per-property or top-level transition config. Per-property entries take
262
- * precedence over the top-level transition.
307
+ * precedence over the top-level transition. Anywhere a config object is
308
+ * accepted (top-level, per-property, per gesture layer) a `TransitionName`
309
+ * registered on the nearest `<MotionConfig transitions>` is accepted too.
263
310
  */
264
311
  transition?: Transition<AnimateStyle<C>>;
265
312
  /**
@@ -271,6 +318,8 @@ interface MotionProps<C, V extends VariantsMap<C> = VariantsMap<C>> {
271
318
  * - `true` — animate with the library's default spring.
272
319
  * - `TransitionConfig` — spring (react-spring vocab) or timing config; the
273
320
  * resolver bridges to Reanimated's `LinearTransition` builder.
321
+ * - `TransitionName` — a name registered on the nearest
322
+ * `<MotionConfig transitions>`.
274
323
  * - omitted / `false` — no layout animation (default).
275
324
  *
276
325
  * Only `'spring'` / `'timing'` / `'no-animation'` map to layout transitions
@@ -282,7 +331,7 @@ interface MotionProps<C, V extends VariantsMap<C> = VariantsMap<C>> {
282
331
  * own layout changes, `layoutId` animates from a different element's
283
332
  * last measured rect to this element's current rect.
284
333
  */
285
- layout?: boolean | TransitionConfig;
334
+ layout?: boolean | TransitionInput;
286
335
  /**
287
336
  * Shared-element transition id. When a Motion primitive with `layoutId`
288
337
  * unmounts, its last on-screen rect is recorded under that id; the next
@@ -336,4 +385,4 @@ interface MotionComponent<C extends ComponentType<any>> {
336
385
  displayName?: string;
337
386
  }
338
387
 
339
- export type { AnimatableValue as A, DecayTransition as D, EasingInput as E, GestureSubStates as G, MotionComponent as M, NoAnimationTransition as N, PerPropertyTransition as P, RepeatConfig as R, SpringTransition as S, TransitionConfig as T, VariantController as V, AnimateStyle as a, AnimationCallbackInfo as b, MotionProps as c, SequenceStep as d, TimingTransition as e, Transition as f, VariantsMap as g, GestureLayerTransitions as h };
388
+ export type { AnimatableValue as A, DecayTransition as D, EasingInput as E, GestureSubStates as G, MotionComponent as M, NamedTransitions as N, PerPropertyTransition as P, RegisteredTransitions as R, SpringTransition as S, TransitionInput as T, VariantController as V, TransitionConfig as a, TransitionName as b, AnimateStyle as c, AnimationCallbackInfo as d, EasingFunction as e, EasingFunctionFactory as f, MotionProps as g, NoAnimationTransition as h, RepeatConfig as i, SequenceStep as j, TimingTransition as k, Transition as l, VariantsMap as m, GestureLayerTransitions as n };
@@ -1,5 +1,5 @@
1
1
  import { SharedValue } from 'react-native-reanimated';
2
- import { T as TransitionConfig, h as GestureLayerTransitions } from './types-cU43dEmH.js';
2
+ import { T as TransitionInput, n as GestureLayerTransitions } from './types-BzEgiUdJ.js';
3
3
 
4
4
  /**
5
5
  * Handler bag returned by `useGesture`. Spread on a `Pressable` to drive the
@@ -42,8 +42,10 @@ interface UseGestureResult {
42
42
  *
43
43
  * Transitions follow the same shape as the `gesture` prop's accompanying
44
44
  * `transition`: pass a single `TransitionConfig` to use for every layer, or a
45
- * `GestureLayerTransitions` map to give each layer its own. Layers without an
46
- * explicit transition fall back to the library default spring.
45
+ * `GestureLayerTransitions` map to give each layer its own. A `TransitionName`
46
+ * registered on the nearest `<MotionConfig transitions>` is accepted in both
47
+ * positions (top-level and per-layer). Layers without an explicit transition
48
+ * fall back to the library default spring.
47
49
  *
48
50
  * Reduced motion (via `<MotionConfig reducedMotion>`) collapses every
49
51
  * transition to `no-animation` so state changes snap instead of interpolating
@@ -79,6 +81,6 @@ interface UseGestureResult {
79
81
  * }
80
82
  * ```
81
83
  */
82
- declare function useGesture(transition?: TransitionConfig | GestureLayerTransitions): UseGestureResult;
84
+ declare function useGesture(transition?: TransitionInput | GestureLayerTransitions): UseGestureResult;
83
85
 
84
86
  export { type UseGestureHandlers as U, type UseGestureResult as a, useGesture as u };
@@ -1,5 +1,5 @@
1
1
  import { SharedValue } from 'react-native-reanimated';
2
- import { T as TransitionConfig, h as GestureLayerTransitions } from './types-cU43dEmH.mjs';
2
+ import { T as TransitionInput, n as GestureLayerTransitions } from './types-BzEgiUdJ.mjs';
3
3
 
4
4
  /**
5
5
  * Handler bag returned by `useGesture`. Spread on a `Pressable` to drive the
@@ -42,8 +42,10 @@ interface UseGestureResult {
42
42
  *
43
43
  * Transitions follow the same shape as the `gesture` prop's accompanying
44
44
  * `transition`: pass a single `TransitionConfig` to use for every layer, or a
45
- * `GestureLayerTransitions` map to give each layer its own. Layers without an
46
- * explicit transition fall back to the library default spring.
45
+ * `GestureLayerTransitions` map to give each layer its own. A `TransitionName`
46
+ * registered on the nearest `<MotionConfig transitions>` is accepted in both
47
+ * positions (top-level and per-layer). Layers without an explicit transition
48
+ * fall back to the library default spring.
47
49
  *
48
50
  * Reduced motion (via `<MotionConfig reducedMotion>`) collapses every
49
51
  * transition to `no-animation` so state changes snap instead of interpolating
@@ -79,6 +81,6 @@ interface UseGestureResult {
79
81
  * }
80
82
  * ```
81
83
  */
82
- declare function useGesture(transition?: TransitionConfig | GestureLayerTransitions): UseGestureResult;
84
+ declare function useGesture(transition?: TransitionInput | GestureLayerTransitions): UseGestureResult;
83
85
 
84
86
  export { type UseGestureHandlers as U, type UseGestureResult as a, useGesture as u };
package/llms.txt CHANGED
@@ -42,13 +42,13 @@ import { MotionScrollView } from '@rootnative/inertia/scroll-view'
42
42
 
43
43
  - `Motion.View` / `Motion.Text` / `Motion.Image` / `Motion.Pressable` / `Motion.ScrollView` — animatable primitives. Per-primitive style inference (no shared `ViewStyle & TextStyle & ImageStyle` fallback).
44
44
  - `<Presence>` — mount / unmount transitions; children need explicit `key`s. Exiting children get `pointerEvents: 'none'` automatically.
45
- - `<MotionConfig reducedMotion="user" | "never" | "always">` — gates motion against the OS reduce-motion setting (default `"user"`).
45
+ - `<MotionConfig reducedMotion="user" | "never" | "always" transitions={{ name: TransitionConfig }}>` — gates motion against the OS reduce-motion setting (default `"user"`) and registers named transitions for the subtree. A registered name is accepted anywhere a `TransitionConfig` is: the `transition` prop (top-level and per-property/per-layer), the `layout` prop, and the value-layer hooks (`transition="selection"`, `useBooleanSpring(checked, 'selection')`). Nested providers merge (child overrides per name); unknown names warn in dev and fall back to the default spring. Names are consumer vocabulary — no presets ship with the library. Optional compile-time narrowing via `declare module '@rootnative/inertia' { interface RegisteredTransitions { ... } }`. Custom components join the registry with `useNamedTransitions()` + `resolveNamedTransition(input, registry)`.
46
46
  - `useGesture(transition?)` — hook-form of the `gesture` prop. Returns 0↔1 progress shared values (`pressed`, `focused`, `focusVisible`, `hovered`) plus a `handlers` bag to spread on a `Pressable`. Use when one gesture needs to drive multiple animated siblings (focus rings, MD3 state-layer halos, multi-element compositions).
47
- - `useGestureLayer(states, options?)` — opt-in helper at `@rootnative/inertia/gesture-layer` for the **strongest-active-layer-wins** composition (MD3 state-layer haloes, iOS-translucent overlays). Supply per-state target maps (`rest` / `hovered` / `focused` / `focusVisible` / `pressed` / `disabled`); the hook owns the worklet. Numeric keys compose via clamped-max with `rest` as the floor; color keys compose via priority cascade with `interpolateColor`; `disabled` (JS-side flag) overrides every gesture layer. Returns `{ style, handlers }`. Reach for plain `useGesture` for additive blends or per-key custom rules.
47
+ - `useGestureLayer(states, options?)` — opt-in helper at `@rootnative/inertia/gesture-layer` for the **strongest-active-layer-wins** composition (MD3 state-layer haloes, iOS-translucent overlays). Supply per-state target maps (`rest` / `hovered` / `focused` / `focusVisible` / `pressed` / `disabled`); the hook owns the worklet. Numeric keys compose via clamped-max with `rest` as the floor; color keys compose via priority cascade with `interpolateColor`; `disabled` (JS-side flag) overrides every gesture layer. Returns `{ style, handlers, states }` — `states` is the per-state 0↔1 progress shared values (`hovered` / `focused` / `focusVisible` / `pressed` / `disabled`) behind the composed style, for deriving extra styles (e.g. `useShadow({ progress: states.hovered })`) from the same gesture wiring. Reach for plain `useGesture` for additive blends or per-key custom rules.
48
48
  - `useVariants(variants, initial?)` — returns `{ current, transitionTo }` controller for the `controller` prop.
49
49
  - `useMotionValue(initial)` — thin pass-through over `useSharedValue<T>`. Returns a `SharedValue<T>` directly so it interops with every Reanimated API without unwrapping.
50
- - `useAnimation(target, transition?)` — drive a `SharedValue<number>` toward `target` with any `TransitionConfig` (spring / timing / decay / no-animation, plus `repeat`). The general-purpose value-layer hook for boolean-state progress, indeterminate loops, and anywhere the same value needs to feed multiple `useAnimatedStyle` blocks.
51
- - `useSpring(target, config?)` — spring-only shorthand. Animates a `SharedValue<number>` toward `target` with react-spring vocab. `target` may be a plain number (effect-driven) or a `SharedValue<number>` (UI-thread reaction); the latter is the gesture-smoothing path.
50
+ - `useAnimation(target, transition?)` — drive a `SharedValue<number>` toward `target` with any `TransitionConfig` (spring / timing / decay / no-animation, plus `repeat`) or a registered `TransitionName`. The general-purpose value-layer hook for boolean-state progress, indeterminate loops, and anywhere the same value needs to feed multiple `useAnimatedStyle` blocks.
51
+ - `useSpring(target, config?)` — spring-only shorthand. Animates a `SharedValue<number>` toward `target` with react-spring vocab (or a spring-typed registered `TransitionName`; non-spring names warn and fall back to the default spring). `target` may be a plain number (effect-driven) or a `SharedValue<number>` (UI-thread reaction); the latter is the gesture-smoothing path.
52
52
  - `useBooleanSpring(active, config?)` — sugar over `useSpring` for the recurring "spring 0↔1 progress from a boolean" shape (checkbox checks, accordion expansions, drawer open/closed). Identical mechanics to `useSpring(active ? 1 : 0, config)` — the named form so the call site reads as the boolean it represents.
53
53
  - `useTransform(value, inputRange, outputRange, options?)` / `useTransform(transformer)` — interpolate a numeric shared value onto a number or color range, or derive any value from any number of shared values via a worklet. Non-worklet transformers are auto-wrapped.
54
54
  - `useShadow({ from, to, progress })` — pure value-layer interpolator between two `ShadowConfig`s (`shadowOpacity` / `shadowRadius` / `shadowOffset` / `elevation` / `shadowColor`) driven by a `SharedValue<number>` (0→1). Returns an animated style fragment to spread onto `style`; only emits keys present on either side, absent sides default to natural zero. The hook does not animate on its own — drive `progress` with a spring, a scroll-derived `useTransform`, or any other shared value source.
@@ -56,6 +56,10 @@ import { MotionScrollView } from '@rootnative/inertia/scroll-view'
56
56
  - `useScroll()` — returns `{ scrollX, scrollY, onScroll }` for use with `Motion.ScrollView`. Scroll events fire on the UI thread.
57
57
  - `createMotionComponent<C>(C)` — wrap any component with the same Motion prop surface, inferring style from `C`.
58
58
  - `buildReleaseAnimation(transition, toValue)` — worklet-safe single-step animation builder (spring / timing / decay / no-animation). For assigning Inertia-resolved animations to shared values from inside a gesture worklet. Used internally by `@rootnative/inertia-gestures`'s `useDrag({ onRelease })`.
59
+ - `resolveTransition(config, toValue, callback?)` — JS-thread resolver: bakes a `TransitionConfig` + target into a Reanimated animation (`withSpring` / `withTiming` / `withDecay`, plus `delay` / `repeat` wrapping) ready to assign to a shared value. The building block for custom animated components that accept Inertia's `transition` shape; this is what the gradient and SVG adapter packages use.
60
+ - `resolveAnimatableValue(value, base, factory?)` — one level up: resolves a full per-property `animate` value (plain target, `{ to, ...override }` step, or keyframe array → `withSequence` with sequence-level `repeat`) into one animation. Gives custom components the complete `animate` grammar.
61
+ - `cubicBezier(x1, y1, x2, y2)` / `cubicBezier(css)` — builds a `timing.easing` value from cubic-bezier control points: four numbers, a W3C CSS `cubic-bezier(0.2, 0, 0, 1)` string (design-token form), or a CSS keyword (`'linear'` | `'ease'` | `'ease-in'` | `'ease-out'` | `'ease-in-out'`). Makes CSS-format easing tokens directly consumable — pair with named transitions to feed a theme's motion tokens into the registry. Invalid input throws (`x1`/`x2` must be within [0, 1]; `step-*` and `linear(...)` unsupported).
62
+ - `ensureWorkletEasing(easing)` — normalizes any accepted easing input (plain function, worklet, or Reanimated 4 `EasingFunctionFactory`) into a worklet `withTiming` can consume. Only needed for manual `withTiming` calls — the resolvers and primitives apply it automatically.
59
63
 
60
64
  ## Motion props
61
65
 
@@ -132,7 +136,7 @@ Auto-layout transitions ship via the `layout` prop (`true` / `TransitionConfig`)
132
136
  ## Optional adapter packages
133
137
 
134
138
  - `@rootnative/inertia-gradients` — `MotionLinearGradient` over `expo-linear-gradient`. Animatable: `colors`, `start`, `end`, `locations`.
135
- - `@rootnative/inertia-svg` — `MotionPath` over `react-native-svg`. Animatable: `d` (path morphing on structurally-compatible paths), `fill`, `stroke`, `strokeWidth`, opacities, `strokeDashoffset`. Source and target paths must share the same command sequence after implicit-repeat expansion; remount with `key` to switch shape.
139
+ - `@rootnative/inertia-svg` — animatable SVG primitives over `react-native-svg`, all under the `MotionSvg` namespace (`MotionSvg.Path` / `.Circle` / `.Rect` / `.Line`) or as direct imports. `MotionPath` animates `d` (path morphing on structurally-compatible paths — same command sequence after implicit-repeat expansion; remount with `key` to switch shape), `fill`, `stroke`, `strokeWidth`, opacities, `strokeDashoffset`. `MotionCircle` / `MotionRect` / `MotionLine` animate their geometry props, paints, and `strokeDasharray` (element-wise; array length locked at mount). `createMotionSvgComponent(Component, { animatableProps, colorProps, arrayProps })` — the factory behind the prebuilt shapes — wraps any other `react-native-svg` element with the same `initial` / `animate` / `transition` surface; `transition` accepts named transitions from the nearest `<MotionConfig transitions>` (top-level and per-property).
136
140
  - `@rootnative/inertia-gestures` — `useDrag`, `useSwipe`, `usePan` over `react-native-gesture-handler`. `useDrag` accepts an `onRelease` worklet that returns per-axis Inertia release transitions (snap-to-tick spring, decay with bounds, etc.) — the release velocity stays on the UI thread, no JS round-trip.
137
141
 
138
142
  ## Docs
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rootnative/inertia",
3
- "version": "0.0.0-alpha.0",
3
+ "version": "0.0.0-alpha.2",
4
4
  "description": "Declarative animation primitives for React Native, built on react-native-reanimated.",
5
5
  "license": "MIT",
6
6
  "author": "RootNative",
@@ -107,6 +107,7 @@
107
107
  "LICENSE",
108
108
  "CHANGELOG.md",
109
109
  "!**/__tests__",
110
+ "!**/__type-tests__",
110
111
  "!**/*.test.*"
111
112
  ],
112
113
  "peerDependencies": {
@@ -1,26 +1,72 @@
1
1
  import { type ReactNode, useMemo } from 'react'
2
+ import { stableSig } from '../transitions/sig'
3
+ import { type NamedTransitions } from '../types'
2
4
  import {
3
5
  MotionConfigContext,
4
6
  type MotionConfigValue,
5
7
  type ReducedMotion,
8
+ useMotionConfig,
6
9
  } from './MotionConfigContext'
7
10
 
11
+ export interface MotionConfigProps {
12
+ /**
13
+ * How descendants respond to reduced-motion preferences. Inherits from the
14
+ * nearest ancestor `<MotionConfig>` when omitted (the root default is
15
+ * `'user'` — respect the OS accessibility setting).
16
+ */
17
+ reducedMotion?: ReducedMotion
18
+ /**
19
+ * Named transitions available to every descendant wherever a
20
+ * `TransitionConfig` is accepted — the `transition` prop (top-level and
21
+ * per-property), the `layout` prop, and the value-layer hooks:
22
+ *
23
+ * ```tsx
24
+ * <MotionConfig
25
+ * transitions={{
26
+ * 'state-press': { type: 'timing', duration: 100 },
27
+ * selection: { type: 'spring', tension: 380, friction: 33 },
28
+ * }}
29
+ * >
30
+ * <Motion.View animate={{ scale: 1 }} transition="selection" />
31
+ * </MotionConfig>
32
+ * ```
33
+ *
34
+ * Names resolve at the nearest provider; nested providers merge with the
35
+ * ancestor registry, child entries overriding same-named ancestor entries.
36
+ * Unknown names warn in dev and fall back to the library default spring.
37
+ * Names are consumer vocabulary — Inertia ships no presets.
38
+ */
39
+ transitions?: NamedTransitions
40
+ children: ReactNode
41
+ }
42
+
8
43
  /**
9
- * Provider that controls how descendant Motion primitives respond to
10
- * reduced-motion preferences. Wrap the root of your app once with the
44
+ * Provider for subtree-wide animation config: reduced-motion behaviour and
45
+ * the named-transition registry. Wrap the root of your app once with the
11
46
  * default (`reducedMotion="user"`) to respect the OS accessibility setting,
12
47
  * or scope a subtree with `'always'` / `'never'` for specific use cases.
48
+ * Nested providers inherit from their ancestor: an omitted prop keeps the
49
+ * ancestor's value, and `transitions` maps merge per name (child wins).
13
50
  */
14
51
  export function MotionConfig({
15
- reducedMotion = 'user',
52
+ reducedMotion,
53
+ transitions,
16
54
  children,
17
- }: {
18
- reducedMotion?: ReducedMotion
19
- children: ReactNode
20
- }) {
55
+ }: MotionConfigProps) {
56
+ const parent = useMotionConfig()
57
+ // Keyed on the structural signature, not object identity — consumers
58
+ // routinely pass a fresh `transitions={{ ... }}` literal each render and
59
+ // must not re-render every Motion descendant for it.
60
+ const transitionsSig = stableSig(transitions)
21
61
  const value = useMemo<MotionConfigValue>(
22
- () => ({ reducedMotion }),
23
- [reducedMotion],
62
+ () => ({
63
+ reducedMotion: reducedMotion ?? parent.reducedMotion,
64
+ transitions: transitions
65
+ ? { ...parent.transitions, ...transitions }
66
+ : parent.transitions,
67
+ }),
68
+ // eslint-disable-next-line react-hooks/exhaustive-deps
69
+ [reducedMotion, transitionsSig, parent],
24
70
  )
25
71
  return (
26
72
  <MotionConfigContext.Provider value={value}>
@@ -1,5 +1,6 @@
1
1
  import { createContext, useContext } from 'react'
2
2
  import { useReducedMotion } from 'react-native-reanimated'
3
+ import { type NamedTransitions } from '../types'
3
4
 
4
5
  /**
5
6
  * How descendant Motion primitives should treat reduced-motion preferences.
@@ -16,14 +17,22 @@ export type ReducedMotion = 'user' | 'never' | 'always'
16
17
 
17
18
  export interface MotionConfigValue {
18
19
  reducedMotion: ReducedMotion
20
+ /**
21
+ * Named transitions registered by ancestor `<MotionConfig transitions>`
22
+ * providers, already merged (nearest provider wins per name). Empty with no
23
+ * provider — every name then resolves to the unknown-name fallback.
24
+ */
25
+ transitions: NamedTransitions
19
26
  }
20
27
 
21
28
  /**
22
29
  * Default config used when a Motion primitive is rendered without a
23
- * `<MotionConfig>` ancestor. `'user'` means respect the OS setting.
30
+ * `<MotionConfig>` ancestor. `'user'` means respect the OS setting; no
31
+ * transitions are registered.
24
32
  */
25
33
  export const DEFAULT_MOTION_CONFIG: MotionConfigValue = {
26
34
  reducedMotion: 'user',
35
+ transitions: {},
27
36
  }
28
37
 
29
38
  export const MotionConfigContext = createContext<MotionConfigValue>(
@@ -44,6 +53,17 @@ export function useMotionConfig(): MotionConfigValue {
44
53
  * primitives call this to decide whether to swap transitions for
45
54
  * `no-animation`.
46
55
  */
56
+ /**
57
+ * Read the merged named-transition registry from the nearest `<MotionConfig>`
58
+ * ancestors. This is what the Motion primitives and value-layer hooks consult
59
+ * when a `TransitionName` string is passed where a `TransitionConfig` is
60
+ * accepted; custom animated components (and adapter packages) use it together
61
+ * with `resolveNamedTransition` to support names on their own surface.
62
+ */
63
+ export function useNamedTransitions(): NamedTransitions {
64
+ return useMotionConfig().transitions
65
+ }
66
+
47
67
  export function useShouldReduceMotion(): boolean {
48
68
  const { reducedMotion } = useMotionConfig()
49
69
  const osReduced = useReducedMotion()
@@ -1,9 +1,15 @@
1
- export { MotionConfig } from './MotionConfig'
1
+ export { MotionConfig, type MotionConfigProps } from './MotionConfig'
2
2
  export {
3
3
  MotionConfigContext,
4
4
  DEFAULT_MOTION_CONFIG,
5
5
  useMotionConfig,
6
+ useNamedTransitions,
6
7
  useShouldReduceMotion,
7
8
  type MotionConfigValue,
8
9
  type ReducedMotion,
9
10
  } from './MotionConfigContext'
11
+ export {
12
+ lookupNamedTransition,
13
+ resolveNamedTransition,
14
+ resolveNamedTransitionProp,
15
+ } from './namedTransitions'
@@ -0,0 +1,80 @@
1
+ import { isTopLevelTransition } from '../transitions/keys'
2
+ import {
3
+ type NamedTransitions,
4
+ type Transition,
5
+ type TransitionConfig,
6
+ type TransitionInput,
7
+ } from '../types'
8
+
9
+ declare const __DEV__: boolean
10
+
11
+ /**
12
+ * Fallback used when a name isn't found in the registry: the library default
13
+ * spring, matching what an omitted transition resolves to everywhere else.
14
+ */
15
+ const UNKNOWN_NAME_FALLBACK: TransitionConfig = { type: 'spring' }
16
+
17
+ /**
18
+ * Look a registered transition name up in a registry. Unknown names warn in
19
+ * dev and fall back to the library default spring — the animation still runs,
20
+ * it just isn't the one the token intended, which is the same failure
21
+ * softness the web platform gives an unknown CSS keyword.
22
+ */
23
+ export function lookupNamedTransition(
24
+ name: string,
25
+ registry: NamedTransitions,
26
+ ): TransitionConfig {
27
+ const cfg = registry[name]
28
+ if (cfg) return cfg
29
+ if (__DEV__) {
30
+ console.warn(
31
+ `[inertia] Unknown transition name "${name}" — falling back to the ` +
32
+ `default spring. Register it on a provider: ` +
33
+ `<MotionConfig transitions={{ '${name}': { ... } }}>.`,
34
+ )
35
+ }
36
+ return UNKNOWN_NAME_FALLBACK
37
+ }
38
+
39
+ /**
40
+ * Resolve a single transition input — a `TransitionConfig` passes through
41
+ * untouched, a `TransitionName` is looked up in `registry`. This is the
42
+ * building block adapter packages use to accept names wherever they accept a
43
+ * config (pair it with `useNamedTransitions()` for the registry).
44
+ */
45
+ export function resolveNamedTransition(
46
+ input: TransitionInput | undefined,
47
+ registry: NamedTransitions,
48
+ ): TransitionConfig | undefined {
49
+ if (input === undefined) return undefined
50
+ if (typeof input === 'string') return lookupNamedTransition(input, registry)
51
+ return input
52
+ }
53
+
54
+ /**
55
+ * Resolve every name inside a `transition` prop value — the top-level string
56
+ * form, and string values on the per-property / gesture-layer map form.
57
+ * Identity-preserving: when the input contains no names it is returned
58
+ * unchanged, so signature- and identity-keyed memoization downstream is
59
+ * unaffected for the config-object path.
60
+ */
61
+ export function resolveNamedTransitionProp<S>(
62
+ transition: Transition<S> | undefined,
63
+ registry: NamedTransitions,
64
+ ): Exclude<Transition<S>, string> | undefined {
65
+ if (transition === undefined) return undefined
66
+ if (typeof transition === 'string') {
67
+ return lookupNamedTransition(transition, registry)
68
+ }
69
+ if (isTopLevelTransition(transition)) return transition
70
+ const map = transition as Record<string, unknown>
71
+ let out: Record<string, unknown> | null = null
72
+ for (const key in map) {
73
+ const value = map[key]
74
+ if (typeof value === 'string') {
75
+ if (out === null) out = { ...map }
76
+ out[key] = lookupNamedTransition(value, registry)
77
+ }
78
+ }
79
+ return (out ?? transition) as Exclude<Transition<S>, string>
80
+ }
@@ -14,6 +14,7 @@
14
14
  */
15
15
  export { useGestureLayer } from './useGestureLayer'
16
16
  export type {
17
+ GestureLayerProgress,
17
18
  GestureLayerStates,
18
19
  GestureLayerStyle,
19
20
  UseGestureLayerOptions,