@rootnative/inertia 0.0.5 → 0.0.6

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 (37) hide show
  1. package/CHANGELOG.md +36 -1
  2. package/README.md +1 -1
  3. package/dist/{chunk-RIVVBABB.mjs → chunk-2ICQLWH2.mjs} +81 -34
  4. package/dist/{chunk-27U6766G.js → chunk-5MGWHOSV.js} +2 -2
  5. package/dist/{chunk-VQ5D35UA.js → chunk-767UKZXG.js} +81 -33
  6. package/dist/{chunk-27MKBTSG.mjs → chunk-BAAQI37F.mjs} +1 -1
  7. package/dist/{chunk-OAUWLPQH.mjs → chunk-C3EDC5ZW.mjs} +1 -1
  8. package/dist/{chunk-GTJ6VAH5.mjs → chunk-CMHVF6F4.mjs} +1 -1
  9. package/dist/{chunk-COEJVWRZ.mjs → chunk-F7LJX56B.mjs} +1 -1
  10. package/dist/chunk-FWWVHLXB.js +8 -0
  11. package/dist/chunk-K5SI6VXS.js +8 -0
  12. package/dist/{chunk-XSK5MNUH.mjs → chunk-PFPO7DX2.mjs} +1 -1
  13. package/dist/chunk-STARIT6W.js +8 -0
  14. package/dist/{chunk-QQVDZKSD.js → chunk-X7B5WR5A.js} +2 -2
  15. package/dist/index.d.mts +29 -1
  16. package/dist/index.d.ts +29 -1
  17. package/dist/index.js +26 -22
  18. package/dist/index.mjs +12 -12
  19. package/dist/motion/Image.js +3 -3
  20. package/dist/motion/Image.mjs +2 -2
  21. package/dist/motion/Pressable.js +3 -3
  22. package/dist/motion/Pressable.mjs +2 -2
  23. package/dist/motion/ScrollView.js +3 -3
  24. package/dist/motion/ScrollView.mjs +2 -2
  25. package/dist/motion/Text.js +3 -3
  26. package/dist/motion/Text.mjs +2 -2
  27. package/dist/motion/View.js +3 -3
  28. package/dist/motion/View.mjs +2 -2
  29. package/llms.txt +1 -0
  30. package/package.json +1 -1
  31. package/src/index.ts +4 -0
  32. package/src/internal/boxShadow.ts +71 -9
  33. package/src/internal/color.ts +70 -0
  34. package/src/motion/createMotionComponent.tsx +93 -47
  35. package/dist/chunk-67GHRCF6.js +0 -8
  36. package/dist/chunk-GPOMFIIU.js +0 -8
  37. package/dist/chunk-K63LXGKS.js +0 -8
@@ -23,11 +23,18 @@ import {
23
23
  } from '../config'
24
24
  import { isFocusVisible } from '../gestures'
25
25
  import {
26
+ layersToPayload,
26
27
  normalizeBoxShadow,
28
+ payloadToLayers,
27
29
  prepareBoxShadowAnimation,
28
- type AnimatedBoxShadowLayer,
30
+ type BoxShadowPayload,
29
31
  type SplitBoxShadow,
30
32
  } from '../internal/boxShadow'
33
+ import {
34
+ normalizeAnimatableColor,
35
+ normalizeAnimatableColorTarget,
36
+ TRANSPARENT,
37
+ } from '../internal/color'
31
38
  import { warnOnce } from '../internal/warnOnce'
32
39
  import {
33
40
  resolveLayoutTransition,
@@ -191,16 +198,20 @@ const COLOR_KEYS = [
191
198
  const SHADOW_OFFSET_KEYS = ['shadowOffsetWidth', 'shadowOffsetHeight'] as const
192
199
 
193
200
  // Keys whose shared value holds a structure rather than a scalar. `boxShadow`
194
- // is the only one: its slot carries an array of layer objects, which
201
+ // is the only one: its slot carries the layers keyed by index, which
195
202
  // Reanimated's animation drivers recurse into, animating each leaf number and
196
- // color independently (`arrayOnStart` → `objectOnStart` in Reanimated's
197
- // `animation/util.ts`).
203
+ // color independently (`objectOnStart` in Reanimated's `animation/util.ts`).
204
+ //
205
+ // Index-keyed and not an array, which is load-bearing — Reanimated recurses
206
+ // into objects but not into arrays. See `BoxShadowPayload` for the mechanism
207
+ // and for what it looked like when this was an array (`0.0.4`/`0.0.5`: dead
208
+ // under the default spring, silently correct-looking under timing).
198
209
  //
199
- // Passing the CSS string through instead would NOT work — a box-shadow string
200
- // isn't a color, so it lands in Reanimated's prefix-number-suffix branch,
201
- // which is built for values like '100%' and would pull a single number out of
202
- // a four-value shadow. All string parsing therefore happens on the JS thread,
203
- // once per change, in `internal/boxShadow.ts`.
210
+ // Passing the CSS string through instead would NOT work either — a box-shadow
211
+ // string isn't a color, so it lands in Reanimated's prefix-number-suffix
212
+ // branch, which is built for values like '100%' and would pull a single number
213
+ // out of a four-value shadow. All string parsing therefore happens on the JS
214
+ // thread, once per change, in `internal/boxShadow.ts`.
204
215
  const STRUCTURED_KEYS = ['boxShadow'] as const
205
216
 
206
217
  // Keys a shared-element transition (`layoutId`) carries from the source
@@ -253,15 +264,16 @@ const SHARED_STYLE_KEY_SET = new Set<AnimatableKey>(SHARED_STYLE_KEYS)
253
264
 
254
265
  /**
255
266
  * What a per-key shared value can hold. Scalars for every key except
256
- * `boxShadow`, whose slot carries the layer array (see `STRUCTURED_KEYS`).
267
+ * `boxShadow`, whose slot carries the index-keyed layer payload (see
268
+ * `STRUCTURED_KEYS`).
257
269
  */
258
- type AnimatableSlotValue = number | string | readonly AnimatedBoxShadowLayer[]
270
+ type AnimatableSlotValue = number | string | BoxShadowPayload
259
271
 
260
272
  /**
261
273
  * Resting `boxShadow` — no shadow at all. Frozen and hoisted so every
262
274
  * primitive shares one reference and an untouched slot never allocates.
263
275
  */
264
- const NO_BOX_SHADOW: readonly AnimatedBoxShadowLayer[] = Object.freeze([])
276
+ const NO_BOX_SHADOW: BoxShadowPayload = Object.freeze({})
265
277
 
266
278
  const GESTURE_LAYER_NAMES = [
267
279
  'hovered',
@@ -341,15 +353,21 @@ const DEFAULT_RESTING: Record<AnimatableKey, AnimatableSlotValue> = {
341
353
  gap: 0,
342
354
  rowGap: 0,
343
355
  columnGap: 0,
344
- // 'transparent' is the only safe universal default for colors: it works as
345
- // an initial seed for any color animation (no jarring opaque flash on mount
346
- // when `initial` is omitted) and rgba(0,0,0,0) interpolates cleanly into
347
- // any opaque target via Reanimated's color util.
348
- backgroundColor: 'transparent',
349
- borderColor: 'transparent',
350
- color: 'transparent',
351
- tintColor: 'transparent',
352
- shadowColor: 'transparent',
356
+ // Fully transparent black is the only safe universal default for colors: it
357
+ // works as an initial seed for any color animation (no jarring opaque flash
358
+ // on mount when `initial` is omitted) and interpolates cleanly into any
359
+ // opaque target.
360
+ //
361
+ // Spelled `rgba(0, 0, 0, 0)` and NOT `'transparent'`, which is not optional.
362
+ // Reanimated's color-name table maps `transparent` to `undefined`, so it is
363
+ // the one named color `isColor()` rejects — a slot resting at the keyword
364
+ // takes the prefix-number-suffix branch on its next animation and produces
365
+ // `NaN` instead of a color. See `TRANSPARENT` in `internal/boxShadow.ts`.
366
+ backgroundColor: TRANSPARENT,
367
+ borderColor: TRANSPARENT,
368
+ color: TRANSPARENT,
369
+ tintColor: TRANSPARENT,
370
+ shadowColor: TRANSPARENT,
353
371
  shadowOffsetWidth: 0,
354
372
  shadowOffsetHeight: 0,
355
373
  boxShadow: NO_BOX_SHADOW,
@@ -730,12 +748,20 @@ export function createMotionComponent<C extends ComponentType<any>>(
730
748
  const raw = flat.boxShadow as BoxShadowInput | undefined
731
749
  if (raw !== undefined) {
732
750
  styleRestingShadow = normalizeBoxShadow(raw)
733
- styleResting.boxShadow = styleRestingShadow.layers
751
+ styleResting.boxShadow = layersToPayload(
752
+ styleRestingShadow.layers,
753
+ )
734
754
  }
735
755
  continue
736
756
  }
737
757
  const v = styleValueFor(flat, key)
738
- if (v !== undefined) styleResting[key] = v
758
+ // A static `style` may legitimately say `'transparent'`; the slot
759
+ // it seeds must not, or the next animation off that value dies.
760
+ if (v !== undefined) {
761
+ styleResting[key] = COLOR_KEY_SET.has(key)
762
+ ? normalizeAnimatableColor(v)
763
+ : v
764
+ }
739
765
  }
740
766
  }
741
767
  }
@@ -754,14 +780,13 @@ export function createMotionComponent<C extends ComponentType<any>>(
754
780
  boxShadowSeedRef.current =
755
781
  source !== undefined
756
782
  ? normalizeBoxShadow(source)
757
- : (styleRestingShadow ?? {
758
- layers: [...NO_BOX_SHADOW],
759
- insets: null,
760
- })
783
+ : (styleRestingShadow ?? { layers: [], insets: null })
761
784
  }
762
785
 
763
786
  const sharedValues = useAnimatableSharedValues((key) => {
764
- if (key === 'boxShadow') return boxShadowSeedRef.current!.layers
787
+ if (key === 'boxShadow') {
788
+ return layersToPayload(boxShadowSeedRef.current!.layers)
789
+ }
765
790
  // Shadow offset synthetics seed from the corresponding axis on the
766
791
  // `shadowOffset: { width, height }` source — the consumer doesn't write
767
792
  // `shadowOffsetWidth` / `shadowOffsetHeight` directly. Fall back to the
@@ -788,16 +813,21 @@ export function createMotionComponent<C extends ComponentType<any>>(
788
813
  DEFAULT_RESTING[key]
789
814
  )
790
815
  }
816
+ // Colors are normalized on the way in: `initial` and `animate` are
817
+ // consumer strings and may say `'transparent'`, which Reanimated
818
+ // cannot animate *away from* (see `internal/color.ts`). The two
819
+ // fallbacks below are already normalized.
791
820
  if (initial === false) {
792
821
  const a = animateRecord[key]
793
- return restValue(a) ?? styleResting[key] ?? DEFAULT_RESTING[key]
822
+ const seed = restValue(a) ?? styleResting[key] ?? DEFAULT_RESTING[key]
823
+ return COLOR_KEY_SET.has(key) ? normalizeAnimatableColor(seed) : seed
794
824
  }
795
- return (
825
+ const seed =
796
826
  initialRecord?.[key] ??
797
827
  restValue(animateRecord[key]) ??
798
828
  styleResting[key] ??
799
829
  DEFAULT_RESTING[key]
800
- )
830
+ return COLOR_KEY_SET.has(key) ? normalizeAnimatableColor(seed) : seed
801
831
  })
802
832
 
803
833
  // Keep never-driven keys tracking the *live* static style. The seed above
@@ -1014,8 +1044,17 @@ export function createMotionComponent<C extends ComponentType<any>>(
1014
1044
  isExiting ? onSettle : undefined,
1015
1045
  TRANSFORM_KEY_SET.has(key) ? transformGroup : undefined,
1016
1046
  )
1047
+ // Normalized for the driver only, and after `targetEndValue` above:
1048
+ // `onAnimationEnd` reports the target the consumer declared, not our
1049
+ // rewriting of it. A color target of `'transparent'` animates fine
1050
+ // — Reanimated dispatches on the *source* — but under
1051
+ // `'no-animation'` (and so under reduced motion) it is assigned
1052
+ // straight into the slot, and the next animation would start from a
1053
+ // value that can't be parsed.
1017
1054
  sharedValues[key].value = resolveAnimatableValue(
1018
- target,
1055
+ COLOR_KEY_SET.has(key)
1056
+ ? normalizeAnimatableColorTarget(target)
1057
+ : target,
1019
1058
  cfg,
1020
1059
  factory,
1021
1060
  ) as never
@@ -1243,17 +1282,19 @@ export function createMotionComponent<C extends ComponentType<any>>(
1243
1282
  out.shadowOffset = { width: shadowOffsetW, height: shadowOffsetH }
1244
1283
  }
1245
1284
  if (hasBoxShadow) {
1246
- const layers = sharedValues.boxShadow
1247
- .value as readonly AnimatedBoxShadowLayer[]
1285
+ // The slot holds the layers keyed by index (Reanimated recurses into
1286
+ // objects, not arrays — see `BoxShadowPayload`); RN wants an array,
1287
+ // so this is where the two shapes meet.
1288
+ const payload = sharedValues.boxShadow.value as BoxShadowPayload
1248
1289
  const insets = boxShadowInsets.value
1249
- // Fast path — no inset layer anywhere, so the animated array is
1250
- // already exactly what RN wants and crosses over untouched.
1290
+ // Fast path — no inset layer anywhere, so each layer crosses over by
1291
+ // reference and only the array itself is allocated.
1251
1292
  if (insets === null) {
1252
- out.boxShadow = layers
1293
+ out.boxShadow = payloadToLayers(payload)
1253
1294
  } else {
1254
1295
  const withInset = []
1255
- for (let i = 0; i < layers.length; i++) {
1256
- withInset.push({ ...layers[i], inset: insets[i] })
1296
+ for (let i = 0; payload[i] !== undefined; i++) {
1297
+ withInset.push({ ...payload[i], inset: insets[i] })
1257
1298
  }
1258
1299
  out.boxShadow = withInset
1259
1300
  }
@@ -1486,6 +1527,13 @@ function makeKeyCallbackFactory(
1486
1527
  const reportedIteration = state.iteration
1487
1528
  if (phase === 'sequence' || phase === 'repeat') state.iteration++
1488
1529
 
1530
+ // `boxShadow`'s slot is keyed by index so Reanimated will recurse into it;
1531
+ // that is an internal shape, and the consumer gets the layer list.
1532
+ const reportedValue =
1533
+ key === 'boxShadow' && value !== undefined
1534
+ ? payloadToLayers(value as BoxShadowPayload)
1535
+ : value
1536
+
1489
1537
  const fn = onAnimationEndRef.current
1490
1538
  if (fn) {
1491
1539
  // Transform-group coalescing: a multi-axis translate / scale /
@@ -1501,7 +1549,7 @@ function makeKeyCallbackFactory(
1501
1549
  fn({
1502
1550
  key: 'transform' as never,
1503
1551
  finished,
1504
- value,
1552
+ value: reportedValue,
1505
1553
  target,
1506
1554
  phase,
1507
1555
  step,
@@ -1512,7 +1560,7 @@ function makeKeyCallbackFactory(
1512
1560
  fn({
1513
1561
  key: key as never,
1514
1562
  finished,
1515
- value,
1563
+ value: reportedValue,
1516
1564
  target,
1517
1565
  phase,
1518
1566
  step,
@@ -1609,20 +1657,18 @@ function driveBoxShadow(
1609
1657
  cfg: TransitionConfig | undefined,
1610
1658
  factory: CallbackFactory | undefined,
1611
1659
  ): void {
1612
- const currentLayers = Array.isArray(slot.value)
1613
- ? (slot.value as readonly AnimatedBoxShadowLayer[])
1614
- : NO_BOX_SHADOW
1660
+ const currentLayers = payloadToLayers(slot.value as BoxShadowPayload)
1615
1661
  const { from, to, insets } = prepareBoxShadowAnimation(
1616
- { layers: [...currentLayers], insets: insetSlot.value },
1662
+ { layers: currentLayers, insets: insetSlot.value },
1617
1663
  target,
1618
1664
  )
1619
1665
  insetSlot.value = insets
1620
- if (from.length !== currentLayers.length) slot.value = from
1666
+ if (from.length !== currentLayers.length) slot.value = layersToPayload(from)
1621
1667
  // `resolveTransition` is typed for the scalar surface it was written for;
1622
1668
  // Reanimated itself accepts the structured target and recurses into it.
1623
1669
  slot.value = resolveTransition(
1624
1670
  cfg,
1625
- to as unknown as number,
1671
+ layersToPayload(to) as unknown as number,
1626
1672
  factory?.('animation', undefined),
1627
1673
  ) as AnimatableSlotValue
1628
1674
  }
@@ -1,8 +0,0 @@
1
- 'use strict';
2
-
3
- var chunkVQ5D35UA_js = require('./chunk-VQ5D35UA.js');
4
- var reactNative = require('react-native');
5
-
6
- var MotionText = chunkVQ5D35UA_js.createMotionComponent(reactNative.Text);
7
-
8
- exports.MotionText = MotionText;
@@ -1,8 +0,0 @@
1
- 'use strict';
2
-
3
- var chunkVQ5D35UA_js = require('./chunk-VQ5D35UA.js');
4
- var reactNative = require('react-native');
5
-
6
- var MotionImage = chunkVQ5D35UA_js.createMotionComponent(reactNative.Image);
7
-
8
- exports.MotionImage = MotionImage;
@@ -1,8 +0,0 @@
1
- 'use strict';
2
-
3
- var chunkVQ5D35UA_js = require('./chunk-VQ5D35UA.js');
4
- var reactNative = require('react-native');
5
-
6
- var MotionView = chunkVQ5D35UA_js.createMotionComponent(reactNative.View);
7
-
8
- exports.MotionView = MotionView;