@rootnative/inertia 0.0.10 → 0.0.11

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 (83) hide show
  1. package/CHANGELOG.md +28 -1
  2. package/README.md +2 -2
  3. package/dist/{chunk-6FCATUMY.mjs → chunk-2Q6JMDYZ.mjs} +1 -1
  4. package/dist/chunk-2UREDR4Z.js +8 -0
  5. package/dist/{chunk-3ZNHPT5Z.js → chunk-3PB27UIE.js} +2 -2
  6. package/dist/{chunk-JENYDTXH.mjs → chunk-5EYIZG72.mjs} +10 -3
  7. package/dist/{chunk-Z23AMUSW.mjs → chunk-7RMSLKQQ.mjs} +1 -1
  8. package/dist/{chunk-7BH3CAO5.mjs → chunk-DT2KVAAO.mjs} +1 -1
  9. package/dist/{chunk-F4HMCXAX.mjs → chunk-FKJLPYGY.mjs} +2 -2
  10. package/dist/{chunk-2UGWAB6N.js → chunk-HBURWGCZ.js} +43 -36
  11. package/dist/chunk-JAJHKJC5.js +8 -0
  12. package/dist/chunk-JBMTTICC.js +8 -0
  13. package/dist/{chunk-QXKPIENP.mjs → chunk-JGQ6YRDN.mjs} +1 -1
  14. package/dist/{chunk-DRA2K53O.mjs → chunk-LHW4VNWD.mjs} +1 -1
  15. package/dist/{chunk-4EZ5DBL3.js → chunk-NTG6PSGZ.js} +8 -8
  16. package/dist/{chunk-KXU6VH2Y.js → chunk-QDMSFRQR.js} +4 -4
  17. package/dist/{chunk-HEAWMZEM.mjs → chunk-QIRKPDXA.mjs} +13 -2
  18. package/dist/{chunk-N33GSDNQ.js → chunk-QX5JHRZ2.js} +12 -1
  19. package/dist/{chunk-VKXSL3ZL.js → chunk-R7YD6L3D.js} +2 -2
  20. package/dist/{chunk-CUHOCONB.mjs → chunk-RO4XFTQ6.mjs} +1 -1
  21. package/dist/{chunk-OLOSU2BD.mjs → chunk-UGYBFAVY.mjs} +1 -1
  22. package/dist/{chunk-46766TBM.js → chunk-XLKI2MSD.js} +2 -2
  23. package/dist/gestureLayer/index.d.mts +2 -2
  24. package/dist/gestureLayer/index.d.ts +2 -2
  25. package/dist/gestureLayer/index.js +11 -11
  26. package/dist/gestureLayer/index.mjs +3 -3
  27. package/dist/index.d.mts +49 -32
  28. package/dist/index.d.ts +49 -32
  29. package/dist/index.js +62 -62
  30. package/dist/index.mjs +19 -19
  31. package/dist/motion/FlatList.d.mts +2 -1
  32. package/dist/motion/FlatList.d.ts +2 -1
  33. package/dist/motion/FlatList.js +5 -5
  34. package/dist/motion/FlatList.mjs +4 -4
  35. package/dist/motion/Image.d.mts +2 -1
  36. package/dist/motion/Image.d.ts +2 -1
  37. package/dist/motion/Image.js +5 -5
  38. package/dist/motion/Image.mjs +4 -4
  39. package/dist/motion/Pressable.d.mts +2 -1
  40. package/dist/motion/Pressable.d.ts +2 -1
  41. package/dist/motion/Pressable.js +5 -5
  42. package/dist/motion/Pressable.mjs +4 -4
  43. package/dist/motion/ScrollView.d.mts +2 -1
  44. package/dist/motion/ScrollView.d.ts +2 -1
  45. package/dist/motion/ScrollView.js +5 -5
  46. package/dist/motion/ScrollView.mjs +4 -4
  47. package/dist/motion/Text.d.mts +2 -1
  48. package/dist/motion/Text.d.ts +2 -1
  49. package/dist/motion/Text.js +5 -5
  50. package/dist/motion/Text.mjs +4 -4
  51. package/dist/motion/View.d.mts +2 -1
  52. package/dist/motion/View.d.ts +2 -1
  53. package/dist/motion/View.js +5 -5
  54. package/dist/motion/View.mjs +4 -4
  55. package/dist/touch/index.d.mts +5 -4
  56. package/dist/touch/index.d.ts +5 -4
  57. package/dist/touch/index.js +3 -3
  58. package/dist/touch/index.mjs +1 -1
  59. package/dist/{types-08mG8fNk.d.mts → types-C5Zbs9MC.d.mts} +12 -1
  60. package/dist/{types-08mG8fNk.d.ts → types-C5Zbs9MC.d.ts} +12 -1
  61. package/dist/{useGesture-D1lHeQQ6.d.ts → useGesture-Be0ZhCWj.d.ts} +1 -1
  62. package/dist/{useGesture-DjszaHWz.d.mts → useGesture-Ve8EGjTr.d.mts} +1 -1
  63. package/dist/useTranslateStyle-BDrOD97O.d.mts +58 -0
  64. package/dist/useTranslateStyle-BDrOD97O.d.ts +58 -0
  65. package/jest-preset.js +31 -2
  66. package/llms.txt +2 -1
  67. package/package.json +13 -12
  68. package/src/index.ts +3 -0
  69. package/src/internal/boxShadow.ts +24 -2
  70. package/src/touch/useTouchDrag.ts +5 -3
  71. package/src/transitions/resolveSequence.ts +42 -2
  72. package/src/types.ts +14 -1
  73. package/src/values/index.ts +3 -1
  74. package/src/values/useColorCascade.ts +3 -3
  75. package/src/values/useColorTransition.ts +17 -2
  76. package/src/values/useInterpolatedStyle.ts +8 -1
  77. package/src/values/useShadow.ts +18 -2
  78. package/src/values/useTranslateStyle.ts +18 -2
  79. package/dist/applyBounds-BYGiHrYO.d.mts +0 -18
  80. package/dist/applyBounds-BYGiHrYO.d.ts +0 -18
  81. package/dist/chunk-PLKXCJIB.js +0 -8
  82. package/dist/chunk-UBHQJFUW.js +0 -8
  83. package/dist/chunk-XKNEQDAT.js +0 -8
package/jest-preset.js CHANGED
@@ -1,7 +1,7 @@
1
1
  // Jest preset for projects consuming `@rootnative/inertia` and its sibling
2
2
  // adapter packages (`@rootnative/inertia-gestures`, `-gradients`, `-svg`).
3
3
  //
4
- // Layered on top of `react-native`'s own preset. Adds:
4
+ // Layered on top of `@react-native/jest-preset`. Adds:
5
5
  // - the `react-native-worklets` + Reanimated mock surface Inertia exercises
6
6
  // (worklet stubs, animation primitives, color/layout utilities)
7
7
  // - `transformIgnorePatterns` widened so Jest transforms the published
@@ -19,7 +19,36 @@
19
19
  // If you need to allowlist additional packages for transformation, extend
20
20
  // `transformIgnorePatterns` in your own config — Jest merges over the preset.
21
21
 
22
- const rnPreset = require('react-native/jest-preset')
22
+ // This package is deliberately **not** declared as a peer dependency of
23
+ // `@rootnative/inertia`. React Native pins it to an exact version (RN 0.86.3
24
+ // requires exactly `@react-native/jest-preset@0.86.3`), so any range we
25
+ // declared would advertise versions that cannot install — the sdk-compat range
26
+ // audit fails on it — and an exact pin would break on every RN patch. The
27
+ // version relationship belongs to `react-native`, which already declares it.
28
+ //
29
+ // Resolve `@react-native/jest-preset` directly rather than through the
30
+ // `react-native/jest-preset` shim. RN 0.86 moved the preset into its own
31
+ // package and left the old path as a shim that re-exports it — but it declares
32
+ // the new package as an **optional** peer, and no package manager installs an
33
+ // optional peer. So on RN 0.86 the shim throws a migration error for any
34
+ // consumer who has not installed it by hand, which is what a consumer of this
35
+ // preset would have hit. Requiring it here means the failure names this
36
+ // package's requirement instead.
37
+ let rnPreset
38
+ try {
39
+ rnPreset = require('@react-native/jest-preset')
40
+ } catch (error) {
41
+ if (error.code === 'MODULE_NOT_FOUND') {
42
+ throw new Error(
43
+ '[inertia] `@rootnative/inertia/jest-preset` needs `@react-native/jest-preset`.\n' +
44
+ 'React Native 0.86 moved its Jest preset into that package and declares\n' +
45
+ 'it as an optional peer, so it is not installed for you. Add it as a\n' +
46
+ 'devDependency at the version matching your react-native:\n\n' +
47
+ ' npm install --save-dev @react-native/jest-preset\n',
48
+ )
49
+ }
50
+ throw error
51
+ }
23
52
 
24
53
  module.exports = {
25
54
  ...rnPreset,
package/llms.txt CHANGED
@@ -88,7 +88,8 @@ import { MotionFlatList } from '@rootnative/inertia/flat-list'
88
88
  - `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.
89
89
  - `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).
90
90
  - `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.
91
- - `TRANSPARENT` — `'rgba(0, 0, 0, 0)'`, the value to seed a colour shared value with when a custom component has no other source. Not `'transparent'`: Reanimated's colour table maps that keyword to `undefined`, so it is the one CSS colour name `isColor()` rejects, and a slot resting at it cannot be animated away from under `withTiming` the target still snaps in at the end, under `withSpring` (the default) the animation never settles. Only affects values passed to `withSpring` / `withTiming`; `interpolateColor` parses the keyword fine, so hooks that interpolate colours themselves are unaffected.
91
+ - `TRANSPARENT` — `'rgba(0, 0, 0, 0)'`, the value to seed a colour shared value with when a custom component has no other source. It exists because Reanimated up to 4.4 mapped the CSS keyword `'transparent'` to `undefined` in its colour table, making it the one name `isColor()` rejected: a slot resting at it could not be animated away from, and under `withSpring` (the default) the animation never settled. **Reanimated 4.5 fixed that** the keyword now maps to a real packed colour — so within the supported peer band (`>=4.5.0 <4.6.0`) either spelling animates. The constant stays exported as an explicit, unambiguous seed and as the safe choice for code that may run against an older Reanimated.
92
+ - `TranslateStyle` / `ColorStyle` / `ShadowStyle` (types) — what `useTranslateStyle`, `useColorTransition` / `useColorCascade`, and `useShadow` return. Plain style objects, **not** Reanimated's branded `AnimatedStyleHandle`: Reanimated 4.5 brands the value `useAnimatedStyle` returns, and a branded value is rejected inside a `StyleProp` array, so stating the plain shape is what keeps a style-array call site cast-free. Same reasoning as `InterpolatedStyle<K>` above. The brand is compile-time only — the value is still the animated style Reanimated produced.
92
93
  - `SharedValue<T>` (type) — re-exported from the root entry so props/refs that carry a value-layer hook result can be typed without importing from `react-native-reanimated`.
93
94
  - `@rootnative/inertia/reanimated` — render-layer interop subpath: pure re-exports of `Animated`, `createAnimatedComponent`, `useAnimatedStyle`, `useAnimatedProps`, `useDerivedValue`, `interpolate`, `interpolateColor`, `Extrapolation`, `cancelAnimation` (plus `SharedValue` / `DerivedValue` / `AnimatedStyle` / `AnimatedProps` types) under their original Reanimated names, for components that own their own `useAnimatedStyle` worklet over Inertia-driven shared values. Names must stay original — the Reanimated Babel plugin auto-workletizes callbacks by callee name. The `with*` animation factories are deliberately not re-exported; start animations with `useAnimation` / `resolveTransition` / `buildReleaseAnimation`.
94
95
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rootnative/inertia",
3
- "version": "0.0.10",
3
+ "version": "0.0.11",
4
4
  "description": "Declarative animation primitives for React Native, built on react-native-reanimated.",
5
5
  "license": "MIT",
6
6
  "author": "RootNative",
@@ -126,10 +126,10 @@
126
126
  ],
127
127
  "peerDependencies": {
128
128
  "@testing-library/react-native": ">=13.0.0",
129
- "react": ">=19.0.0",
130
- "react-native": ">=0.81.0",
131
- "react-native-reanimated": ">=4.0.0",
132
- "react-native-worklets": ">=0.5.0"
129
+ "react": ">=19.2.3 <20.0.0",
130
+ "react-native": ">=0.83.0 <0.87.0",
131
+ "react-native-reanimated": ">=4.5.0 <4.6.0",
132
+ "react-native-worklets": ">=0.10.0 <0.11.0"
133
133
  },
134
134
  "peerDependenciesMeta": {
135
135
  "@testing-library/react-native": {
@@ -137,17 +137,18 @@
137
137
  }
138
138
  },
139
139
  "devDependencies": {
140
- "@react-native/babel-preset": "^0.81.5",
140
+ "@react-native/babel-preset": "^0.86.3",
141
+ "@react-native/jest-preset": "^0.86.3",
141
142
  "@size-limit/preset-small-lib": "^11.1.0",
142
143
  "@testing-library/react-native": "^13.3.3",
143
144
  "@types/jest": "^29.5.14",
144
- "@types/react": "^19.1.0",
145
+ "@types/react": "^19.2.0",
145
146
  "jest": "^29.7.0",
146
- "react": "19.1.0",
147
- "react-native": "0.81.5",
148
- "react-native-reanimated": "~4.1.1",
149
- "react-native-worklets": "~0.5.1",
150
- "react-test-renderer": "19.1.0",
147
+ "react": "19.2.3",
148
+ "react-native": "0.86.3",
149
+ "react-native-reanimated": "4.5.1",
150
+ "react-native-worklets": "0.10.1",
151
+ "react-test-renderer": "19.2.3",
151
152
  "size-limit": "^11.1.0",
152
153
  "tsup": "^8.3.5",
153
154
  "typescript": "^5.7.3"
package/src/index.ts CHANGED
@@ -79,12 +79,15 @@ export type {
79
79
  Animator,
80
80
  BoxShadowLayer,
81
81
  ColorCascadeLayer,
82
+ ColorStyle,
82
83
  ColorStyleKey,
83
84
  ExtrapolationMode,
84
85
  InterpolatedStyle,
85
86
  InterpolatedStyleMap,
87
+ TranslateStyle,
86
88
  NumericStyleKey,
87
89
  ShadowConfig,
90
+ ShadowStyle,
88
91
  TransformKey,
89
92
  UseColorCascadeOptions,
90
93
  UseColorTransitionOptions,
@@ -164,7 +164,7 @@ export function resolveBoxShadowInput(
164
164
  offsetY: coerceLength(layer.offsetY, 'offsetY'),
165
165
  blurRadius: coerceLength(layer.blurRadius, 'blurRadius'),
166
166
  spreadDistance: coerceLength(layer.spreadDistance, 'spreadDistance'),
167
- color: layer.color ?? 'black',
167
+ color: coerceColor(layer.color),
168
168
  inset: layer.inset ?? false,
169
169
  }))
170
170
  }
@@ -273,7 +273,10 @@ export type BoxShadowInput =
273
273
  // rejected) by `coerceLength` like every other length.
274
274
  blurRadius?: unknown
275
275
  spreadDistance?: number | string | undefined
276
- color?: string | undefined
276
+ // `unknown` because RN 0.86 widened this from `string` to `ColorValue`,
277
+ // which includes the opaque handle `PlatformColor()` returns. Coerced
278
+ // (and rejected) by `coerceColor`.
279
+ color?: unknown
277
280
  inset?: boolean | undefined
278
281
  }>
279
282
 
@@ -284,6 +287,25 @@ export type BoxShadowInput =
284
287
  * `parseBoxShadow` — px and unitless only, and anything else throws rather
285
288
  * than silently animating from a `NaN`.
286
289
  */
290
+ /**
291
+ * Coerce one RN colour field to a string.
292
+ *
293
+ * RN 0.86 widened `BoxShadowValue.color` from `string` to `ColorValue`, which
294
+ * admits the opaque handle `PlatformColor()` and `DynamicColorIOS()` return.
295
+ * That handle is a platform reference, not a colour this code can read, so it
296
+ * cannot be interpolated. Reject it here with a message that names the cause,
297
+ * rather than letting it reach the worklet and animate to `NaN`.
298
+ */
299
+ function coerceColor(value: unknown): string {
300
+ if (value === undefined) return 'black'
301
+ if (typeof value === 'string') return value
302
+ throw new Error(
303
+ '[inertia] boxShadow: color must be a colour string, got ' +
304
+ `${typeof value}. PlatformColor and DynamicColorIOS values cannot be ` +
305
+ 'interpolated — pass a string colour on an animated shadow.',
306
+ )
307
+ }
308
+
287
309
  function coerceLength(value: unknown, field: string): number {
288
310
  if (value === undefined) return 0
289
311
  if (typeof value === 'number') return value
@@ -5,9 +5,11 @@ import {
5
5
  type PanResponderInstance,
6
6
  } from 'react-native'
7
7
  import { useSharedValue, type SharedValue } from 'react-native-reanimated'
8
- import type { useAnimatedStyle } from 'react-native-reanimated'
9
8
  import { buildReleaseAnimation } from '../transitions'
10
- import { useTranslateStyle } from '../values/useTranslateStyle'
9
+ import {
10
+ useTranslateStyle,
11
+ type TranslateStyle,
12
+ } from '../values/useTranslateStyle'
11
13
  import { applyBounds } from './applyBounds'
12
14
  import type { TransitionConfig } from '../types'
13
15
 
@@ -22,7 +24,7 @@ export interface UseTouchDragResult {
22
24
  /** Spread onto a `View` / `Pressable` to install the pan responder. */
23
25
  panHandlers: PanResponderInstance['panHandlers']
24
26
  /** Stable animated `transform` style. */
25
- animatedStyle: ReturnType<typeof useAnimatedStyle>
27
+ animatedStyle: TranslateStyle
26
28
  /** Live x translation, persistent across gestures. */
27
29
  dragX: SharedValue<number>
28
30
  /** Live y translation, persistent across gestures. */
@@ -1,4 +1,4 @@
1
- import { withSequence } from 'react-native-reanimated'
1
+ import { withSequence, withTiming } from 'react-native-reanimated'
2
2
  import {
3
3
  applyRepeat,
4
4
  repeatOf,
@@ -13,6 +13,18 @@ import {
13
13
  type TransitionConfig,
14
14
  } from '../types'
15
15
 
16
+ /** A zero-length timing, for a snapped step inside an animated sequence. */
17
+ const INSTANT = { duration: 0 } as const
18
+
19
+ /**
20
+ * True when a resolved step is a bare target rather than a Reanimated
21
+ * animation object. `buildOne` returns the target itself for `no-animation`,
22
+ * which is what the reduced-motion gate collapses every step to.
23
+ */
24
+ function isSnapped(step: unknown): step is number | string {
25
+ return typeof step === 'number' || typeof step === 'string'
26
+ }
27
+
16
28
  /**
17
29
  * True when the value is a `{ to, ...transitionOverride }` sequence step.
18
30
  * Plain numbers and plain transition objects fail this check.
@@ -37,6 +49,12 @@ function isStepObject<V>(
37
49
  * 3. array of either → `withSequence` of resolved steps, with the
38
50
  * top-level `repeat` applied at the **sequence level** (not per step).
39
51
  * Per-step `repeat` overrides remain step-local.
52
+ *
53
+ * `withSequence` writes a `finished` flag onto each argument it is handed, so
54
+ * a bare target throws `Cannot create property 'finished' on number` and takes
55
+ * the whole render down with it. Two paths produce bare targets, and both are
56
+ * handled below rather than at the call site: reduced motion (every step) and
57
+ * a per-step `{ type: 'no-animation' }` override (some steps).
40
58
  */
41
59
  export function resolveAnimatableValue<V extends number | string>(
42
60
  value: AnimatableValue<V>,
@@ -49,7 +67,29 @@ export function resolveAnimatableValue<V extends number | string>(
49
67
  const animations = steps.map((step, i) =>
50
68
  resolveStep(step, stepBase, factory?.('step', i)),
51
69
  )
52
- const seq = withSequence(...(animations as never[]))
70
+ // The reduced-motion gate. `mergeTransition` hands a `no-animation` base
71
+ // back untouched for every step, so the whole sequence is a snap and there
72
+ // is nothing to sequence. Settle on the last step — where the sequence
73
+ // would have ended. `repeat` and `delay` are already dropped for
74
+ // `no-animation` (`repeatOf` and `delayOf` both return `undefined`), so
75
+ // nothing needs to wrap this.
76
+ //
77
+ // Read off the config rather than sniffing the resolved values: under the
78
+ // Jest mock `withSpring` and `withTiming` also return bare targets, so a
79
+ // shape test reports every sequence as snapped and the gate stops being
80
+ // observable in either direction.
81
+ if (stepBase?.type === 'no-animation' && animations.length > 0) {
82
+ return animations[animations.length - 1]
83
+ }
84
+ // A step that declares `{ type: 'no-animation' }` inside a sequence that
85
+ // otherwise animates resolves to a bare target of its own. The step still
86
+ // owns its slot in the order, so it becomes an instant timing rather than
87
+ // being dropped.
88
+ const seq = withSequence(
89
+ ...(animations.map((a) =>
90
+ isSnapped(a) ? withTiming(a as number, INSTANT) : a,
91
+ ) as never[]),
92
+ )
53
93
  return applyRepeat(seq, base ? repeatOf(base) : undefined, {
54
94
  sequence: true,
55
95
  })
package/src/types.ts CHANGED
@@ -5,6 +5,7 @@ import {
5
5
  type Ref,
6
6
  } from 'react'
7
7
  import { type BoxShadowValue, type StyleProp } from 'react-native'
8
+ import { type AnimatedProps } from 'react-native-reanimated'
8
9
 
9
10
  /**
10
11
  * A single animation step's destination, optionally overriding the transition
@@ -555,6 +556,14 @@ export interface MotionProps<C, V extends VariantsMap<C> = VariantsMap<C>> {
555
556
  * Props of a Motion primitive for a given underlying component `C` and a
556
557
  * concrete variants map `V`: the component's own props (minus `style`, which
557
558
  * we replace with an animated style) intersected with the Motion props.
559
+ *
560
+ * `style` is taken from `AnimatedProps<…>`, not from `C` directly, because a
561
+ * Motion primitive renders `Animated.createAnimatedComponent(C)` — so the
562
+ * styles it accepts are the ones that animated component accepts. Reanimated
563
+ * 4.5 brands what `useAnimatedStyle` returns, and `ComponentProps<C>['style']`
564
+ * rejects a branded value, which would have made the sanctioned escape hatch
565
+ * (Principle 2: own your `useAnimatedStyle` over Inertia-driven shared values,
566
+ * then pass it to a `Motion.*` primitive) require a cast at every call site.
558
567
  */
559
568
  export type MotionComponentProps<
560
569
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -562,7 +571,11 @@ export type MotionComponentProps<
562
571
  V extends VariantsMap<ComponentProps<C>> = VariantsMap<ComponentProps<C>>,
563
572
  > = Omit<ComponentProps<C>, 'style'> &
564
573
  MotionProps<ComponentProps<C>, V> & {
565
- style?: ComponentProps<C>['style']
574
+ style?: AnimatedProps<ComponentProps<C>> extends {
575
+ style?: infer S
576
+ }
577
+ ? S
578
+ : ComponentProps<C>['style']
566
579
  ref?: Ref<unknown>
567
580
  }
568
581
 
@@ -8,6 +8,7 @@ export {
8
8
  } from './useColorCascade'
9
9
  export {
10
10
  useColorTransition,
11
+ type ColorStyle,
11
12
  type ColorStyleKey,
12
13
  type UseColorTransitionOptions,
13
14
  } from './useColorTransition'
@@ -36,7 +37,8 @@ export {
36
37
  useShadow,
37
38
  type BoxShadowLayer,
38
39
  type ShadowConfig,
40
+ type ShadowStyle,
39
41
  type UseShadowOptions,
40
42
  } from './useShadow'
41
- export { useTranslateStyle } from './useTranslateStyle'
43
+ export { useTranslateStyle, type TranslateStyle } from './useTranslateStyle'
42
44
  export { useVariants } from './useVariants'
@@ -4,7 +4,7 @@ import {
4
4
  useAnimatedStyle,
5
5
  type SharedValue,
6
6
  } from 'react-native-reanimated'
7
- import type { ColorStyleKey } from './useColorTransition'
7
+ import type { ColorStyle, ColorStyleKey } from './useColorTransition'
8
8
 
9
9
  /**
10
10
  * One layer in a color cascade: its own `progress` shared value (0→1) and the
@@ -70,7 +70,7 @@ export function useColorCascade(
70
70
  rest: string,
71
71
  layers: readonly ColorCascadeLayer[],
72
72
  options?: UseColorCascadeOptions,
73
- ): ReturnType<typeof useAnimatedStyle> {
73
+ ): ColorStyle {
74
74
  const key = options?.key ?? 'backgroundColor'
75
75
 
76
76
  // Resolve the layer chain into two flat arrays the worklet closes over — the
@@ -121,5 +121,5 @@ export function useColorCascade(
121
121
  )
122
122
  }
123
123
  return { [key]: acc }
124
- })
124
+ }) as unknown as ColorStyle
125
125
  }
@@ -20,6 +20,21 @@ export type ColorStyleKey =
20
20
  | 'tintColor'
21
21
  | 'shadowColor'
22
22
 
23
+ /**
24
+ * A one-key colour style fragment, as returned by {@link useColorTransition}
25
+ * and {@link useColorCascade}.
26
+ *
27
+ * Deliberately **not** `ReturnType<typeof useAnimatedStyle>`. Reanimated 4.5
28
+ * brands that value (`AnimatedStyleHandle`), and a branded type is rejected
29
+ * inside a `StyleProp` array — so every call site would need a cast, which is
30
+ * the defect `InterpolatedStyle` was introduced to remove in `0.0.9`. The brand
31
+ * is compile-time only, so the runtime value is unchanged.
32
+ *
33
+ * Optional per key because which slot is filled is chosen at run time from
34
+ * `options.key`; exactly one is ever present.
35
+ */
36
+ export type ColorStyle = { [K in ColorStyleKey]?: string }
37
+
23
38
  export interface UseColorTransitionOptions {
24
39
  /**
25
40
  * Which style slot the interpolated color is emitted under. Defaults to
@@ -56,7 +71,7 @@ export function useColorTransition(
56
71
  progress: SharedValue<number>,
57
72
  range: readonly [string, string],
58
73
  options?: UseColorTransitionOptions,
59
- ): ReturnType<typeof useAnimatedStyle> {
74
+ ): ColorStyle {
60
75
  // Resolve the slot key once on the JS thread so the worklet body
61
76
  // consumes a single string literal — consistent with the JS-thread
62
77
  // resolver principle that keeps `Object.keys`-style walks off the UI
@@ -68,5 +83,5 @@ export function useColorTransition(
68
83
  return useAnimatedStyle(() => {
69
84
  'worklet'
70
85
  return { [key]: interpolateColor(progress.value, [0, 1], [from, to]) }
71
- })
86
+ }) as unknown as ColorStyle
72
87
  }
@@ -308,6 +308,13 @@ export function useInterpolatedStyle<K extends keyof InterpolatedStyleMap>(
308
308
  // The worklet builds a `Record<string, unknown>` by design — the emitted keys
309
309
  // are only known from `entries` at run time. `InterpolatedStyle<K>` is the
310
310
  // static statement of that same shape, so the cast is where the two meet.
311
+ //
312
+ // Through `unknown` because Reanimated 4.5 brands what `useAnimatedStyle`
313
+ // returns (`AnimatedStyleHandle`), and a branded type no longer overlaps a
314
+ // plain style object. Erasing the brand is the point of this hook: the
315
+ // returned value has to drop into a `StyleProp<ViewStyle>` array without the
316
+ // consumer casting. The brand is compile-time only, so the runtime value is
317
+ // unchanged — it is still the animated style Reanimated produced.
311
318
  return useAnimatedStyle(() => {
312
319
  'worklet'
313
320
  const out: Record<string, unknown> = {}
@@ -338,5 +345,5 @@ export function useInterpolatedStyle<K extends keyof InterpolatedStyleMap>(
338
345
  }
339
346
  if (transform.length > 0) out.transform = transform
340
347
  return out
341
- }) as InterpolatedStyle<K>
348
+ }) as unknown as InterpolatedStyle<K>
342
349
  }
@@ -43,6 +43,22 @@ export interface ShadowConfig {
43
43
  boxShadow?: string | readonly BoxShadowLayer[]
44
44
  }
45
45
 
46
+ /**
47
+ * The style fragment {@link useShadow} returns: the native shadow keys plus the
48
+ * CSS `boxShadow`, each present only when the paired configs drive it.
49
+ *
50
+ * Deliberately **not** `ReturnType<typeof useAnimatedStyle>` — see
51
+ * {@link ColorStyle} for why the brand is erased here.
52
+ */
53
+ export type ShadowStyle = {
54
+ shadowColor?: string
55
+ shadowOffset?: { width: number; height: number }
56
+ shadowOpacity?: number
57
+ shadowRadius?: number
58
+ elevation?: number
59
+ boxShadow?: string
60
+ }
61
+
46
62
  export interface UseShadowOptions {
47
63
  /** Shadow state at `progress === 0`. */
48
64
  from: ShadowConfig
@@ -97,7 +113,7 @@ export function useShadow({
97
113
  from,
98
114
  to,
99
115
  progress,
100
- }: UseShadowOptions): ReturnType<typeof useAnimatedStyle> {
116
+ }: UseShadowOptions): ShadowStyle {
101
117
  // Resolve presence + endpoints once on the JS thread so the worklet body
102
118
  // consumes flat literals — consistent with the JS-thread resolver
103
119
  // principle that keeps `Object.keys`-style walks off the UI thread.
@@ -198,5 +214,5 @@ export function useShadow({
198
214
  out.boxShadow = css
199
215
  }
200
216
  return out
201
- })
217
+ }) as unknown as ShadowStyle
202
218
  }
@@ -1,5 +1,21 @@
1
+ import type { ViewStyle } from 'react-native'
1
2
  import { useAnimatedStyle, type SharedValue } from 'react-native-reanimated'
2
3
 
4
+ /**
5
+ * What {@link useTranslateStyle} returns: a plain style object that owns
6
+ * `transform`.
7
+ *
8
+ * Deliberately **not** `ReturnType<typeof useAnimatedStyle>`. Reanimated 4.5
9
+ * brands that value (`AnimatedStyleHandle`), and a branded type is rejected
10
+ * inside a `StyleProp<ViewStyle>` array — so every call site would need a cast,
11
+ * which is the exact defect `InterpolatedStyle` was introduced to remove in
12
+ * `0.0.9`. The brand is compile-time only, so the runtime value is unchanged:
13
+ * it is still the animated style Reanimated produced.
14
+ */
15
+ export type TranslateStyle = {
16
+ transform: NonNullable<ViewStyle['transform']>
17
+ }
18
+
3
19
  /**
4
20
  * Animated style that translates by two shared values:
5
21
  * `transform: [{ translateX: x }, { translateY: y }]`.
@@ -24,8 +40,8 @@ import { useAnimatedStyle, type SharedValue } from 'react-native-reanimated'
24
40
  export function useTranslateStyle(
25
41
  x: SharedValue<number>,
26
42
  y: SharedValue<number>,
27
- ): ReturnType<typeof useAnimatedStyle> {
43
+ ): TranslateStyle {
28
44
  return useAnimatedStyle(() => ({
29
45
  transform: [{ translateX: x.value }, { translateY: y.value }],
30
- }))
46
+ })) as unknown as TranslateStyle
31
47
  }
@@ -1,18 +0,0 @@
1
- /**
2
- * Clamp `value` to `[min, max]`. When `elastic > 0` the overshoot past a
3
- * bound is scaled by `elastic` instead of hard-clamped, giving a rubber-band
4
- * feel. `min` / `max` may be `undefined` to leave that side unbounded.
5
- *
6
- * Worklet, so it runs both inside a gesture-handler pan handler on the UI
7
- * thread (`useDrag`) and from a JS-thread `PanResponder` callback
8
- * (`useTouchDrag`).
9
- *
10
- * ```ts
11
- * applyBounds(120, -100, 100, 0) // 100
12
- * applyBounds(120, -100, 100, 0.5) // 110
13
- * applyBounds(50, undefined, 100, 0) // 50
14
- * ```
15
- */
16
- declare function applyBounds(value: number, min: number | undefined, max: number | undefined, elastic: number): number;
17
-
18
- export { applyBounds as a };
@@ -1,18 +0,0 @@
1
- /**
2
- * Clamp `value` to `[min, max]`. When `elastic > 0` the overshoot past a
3
- * bound is scaled by `elastic` instead of hard-clamped, giving a rubber-band
4
- * feel. `min` / `max` may be `undefined` to leave that side unbounded.
5
- *
6
- * Worklet, so it runs both inside a gesture-handler pan handler on the UI
7
- * thread (`useDrag`) and from a JS-thread `PanResponder` callback
8
- * (`useTouchDrag`).
9
- *
10
- * ```ts
11
- * applyBounds(120, -100, 100, 0) // 100
12
- * applyBounds(120, -100, 100, 0.5) // 110
13
- * applyBounds(50, undefined, 100, 0) // 50
14
- * ```
15
- */
16
- declare function applyBounds(value: number, min: number | undefined, max: number | undefined, elastic: number): number;
17
-
18
- export { applyBounds as a };
@@ -1,8 +0,0 @@
1
- 'use strict';
2
-
3
- var chunk2UGWAB6N_js = require('./chunk-2UGWAB6N.js');
4
- var reactNative = require('react-native');
5
-
6
- var MotionView = chunk2UGWAB6N_js.createMotionComponent(reactNative.View);
7
-
8
- exports.MotionView = MotionView;
@@ -1,8 +0,0 @@
1
- 'use strict';
2
-
3
- var chunk2UGWAB6N_js = require('./chunk-2UGWAB6N.js');
4
- var reactNative = require('react-native');
5
-
6
- var MotionText = chunk2UGWAB6N_js.createMotionComponent(reactNative.Text);
7
-
8
- exports.MotionText = MotionText;
@@ -1,8 +0,0 @@
1
- 'use strict';
2
-
3
- var chunk2UGWAB6N_js = require('./chunk-2UGWAB6N.js');
4
- var reactNative = require('react-native');
5
-
6
- var MotionImage = chunk2UGWAB6N_js.createMotionComponent(reactNative.Image);
7
-
8
- exports.MotionImage = MotionImage;