@rootnative/inertia 0.0.3 → 0.0.4
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.
- package/CHANGELOG.md +41 -5
- package/README.md +5 -3
- package/dist/{chunk-SGUHE5CX.mjs → chunk-4ASOELLA.mjs} +1 -1
- package/dist/{chunk-CSODMRJ7.mjs → chunk-55W7BNO2.mjs} +475 -83
- package/dist/chunk-5SRD2WCO.js +8 -0
- package/dist/{chunk-PTRF47DA.js → chunk-7A6WTXNK.js} +10 -3
- package/dist/{chunk-3XTVY34H.js → chunk-CENTULW6.js} +2 -2
- package/dist/{chunk-4PEHWDAZ.js → chunk-D5KAHWMC.js} +7 -7
- package/dist/{chunk-O22NXXCZ.mjs → chunk-J5Q46OPW.mjs} +1 -1
- package/dist/{chunk-RGNX6UZN.mjs → chunk-K3CLP74S.mjs} +2 -2
- package/dist/{chunk-BP3Y2SHQ.js → chunk-MYE5NMXW.js} +495 -101
- package/dist/{chunk-OQV66TBQ.mjs → chunk-NAOXQJ7U.mjs} +1 -1
- package/dist/{chunk-R63GIUNU.mjs → chunk-QKC3APP4.mjs} +1 -1
- package/dist/{chunk-46P57VMY.mjs → chunk-RF7J5FRM.mjs} +1 -1
- package/dist/{chunk-CY7Y64C3.mjs → chunk-TU2XFPAK.mjs} +10 -4
- package/dist/chunk-TVFNJ25Z.js +8 -0
- package/dist/chunk-VLDIFL3F.js +8 -0
- package/dist/{chunk-7AOERN53.js → chunk-WVRCKOPO.js} +3 -3
- package/dist/{chunk-W5MC3P4N.js → chunk-XMEVPEMH.js} +2 -2
- package/dist/{chunk-BQQTHG2V.mjs → chunk-YKGY22SB.mjs} +1 -1
- package/dist/gestureLayer/index.d.mts +2 -2
- package/dist/gestureLayer/index.d.ts +2 -2
- package/dist/gestureLayer/index.js +10 -10
- package/dist/gestureLayer/index.mjs +3 -3
- package/dist/index.d.mts +4 -4
- package/dist/index.d.ts +4 -4
- package/dist/index.js +48 -169
- package/dist/index.mjs +17 -137
- package/dist/motion/Image.d.mts +1 -1
- package/dist/motion/Image.d.ts +1 -1
- package/dist/motion/Image.js +5 -5
- package/dist/motion/Image.mjs +4 -4
- package/dist/motion/Pressable.d.mts +1 -1
- package/dist/motion/Pressable.d.ts +1 -1
- package/dist/motion/Pressable.js +5 -5
- package/dist/motion/Pressable.mjs +4 -4
- package/dist/motion/ScrollView.d.mts +1 -1
- package/dist/motion/ScrollView.d.ts +1 -1
- package/dist/motion/ScrollView.js +5 -5
- package/dist/motion/ScrollView.mjs +4 -4
- package/dist/motion/Text.d.mts +1 -1
- package/dist/motion/Text.d.ts +1 -1
- package/dist/motion/Text.js +5 -5
- package/dist/motion/Text.mjs +4 -4
- package/dist/motion/View.d.mts +1 -1
- package/dist/motion/View.d.ts +1 -1
- package/dist/motion/View.js +5 -5
- package/dist/motion/View.mjs +4 -4
- package/dist/touch/index.d.mts +1 -1
- package/dist/touch/index.d.ts +1 -1
- package/dist/touch/index.js +3 -3
- package/dist/touch/index.mjs +1 -1
- package/dist/{types-G1dBU2IQ.d.mts → types-37pN82yh.d.mts} +49 -14
- package/dist/{types-G1dBU2IQ.d.ts → types-37pN82yh.d.ts} +49 -14
- package/dist/{useGesture-D1e4ixJo.d.ts → useGesture-Cl4uzZIr.d.ts} +1 -1
- package/dist/{useGesture-RFY6kTL3.d.mts → useGesture-_IKkS8Dv.d.mts} +1 -1
- package/llms.txt +3 -1
- package/package.json +1 -1
- package/src/index.ts +2 -1
- package/src/internal/boxShadow.ts +177 -6
- package/src/internal/nonWorkletWarning.ts +4 -6
- package/src/internal/warnOnce.ts +28 -0
- package/src/layout/index.ts +10 -0
- package/src/layout/measureWindow.ts +135 -0
- package/src/layout/sharedRegistry.ts +107 -18
- package/src/layout/useSharedLayout.ts +256 -91
- package/src/motion/createMotionComponent.tsx +378 -39
- package/src/motion/installCheck.ts +1 -1
- package/src/transitions/index.ts +1 -1
- package/src/types.ts +52 -12
- package/src/values/useColorTransition.ts +1 -1
- package/dist/chunk-FNVFV4EY.js +0 -8
- package/dist/chunk-FWQOXA43.js +0 -8
- package/dist/chunk-KBP4LR75.js +0 -8
package/CHANGELOG.md
CHANGED
|
@@ -1,9 +1,43 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
All notable changes to `@rootnative/inertia` are documented here. The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and the project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). Pre-`1.0`, breaking changes may land in minor versions and are called out under their release.
|
|
3
|
+
All notable changes to `@rootnative/inertia` are documented here. The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and the project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). Pre-`1.0.0`, breaking changes may land in minor versions and are called out under their release.
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [0.0.4] - 2026-07-29
|
|
8
|
+
|
|
9
|
+
**Feature release.** The three deferred items the `0.0.3` audit left on the roadmap land together: `boxShadow` joins the declarative `animate` surface, and `layoutId` shared-element transitions gain window-coordinate measurement and a style carry. No breaking changes; the three adapters are lockstep-bumped with no runtime changes of their own.
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- **`boxShadow` is now an animatable property on the `animate` surface.** The cross-platform CSS shadow form joins the declarative surface alongside the native `shadow*` keys, accepting either a CSS string (`'0px 4px 8px rgba(0,0,0,0.3)'` — the shape design systems store elevation tokens in) or React Native's own `BoxShadowValue[]`, whose lengths may be numbers or px strings. The key was already accepted at compile time and silently ignored at runtime, because `AnimateStyle<C>` maps over every key of the inferred style and RN's `ViewStyle` has carried `boxShadow` since 0.76 — so this makes an accepted key work rather than widening the type surface. Endpoints are normalized on the JS thread once per change and padded to a common layer count, so **layer counts may differ between `initial` and `animate`**: the short side gains transparent zero layers and the surplus layer fades rather than popping, matching CSS transition semantics. Every length and color interpolates per layer. `inset` is carried as a **static** per-layer flag rather than interpolated (there is no meaningful midpoint between an inner and an outer shadow, and a boolean driven down Reanimated's numeric path yields `0` mid-flight — a number where native expects a boolean); both endpoints may use it freely, but a layer that is `inset` on one side only throws, as it already did in `useShadow`. Only px and unitless lengths are accepted; `em` / `%` / `rem` throw rather than animating from a `NaN`. New `BoxShadowInput` type exported from the root barrel. Guarded by `boxShadow.test.tsx` (22 cases) and seven compile-time assertions in `animate.test-d.tsx`.
|
|
14
|
+
|
|
15
|
+
Worth knowing if you go looking at the implementation: the target has to reach Reanimated as the **structured array**, not the CSS string. `withSpring` / `withTiming` dispatch on the runtime shape of the value, and a box-shadow string is not a color, so it lands in the prefix-number-suffix branch built for values like `'100%'` and would pull a single number out of a four-value shadow. The array form instead recurses into each layer and animates every leaf. All string parsing therefore happens on the JS thread, and the worklet only emits what the slot already holds — no frame-time string or structural work.
|
|
16
|
+
|
|
17
|
+
Two deliberate limits, both matching the single-value contract `shadowOffset` has carried since `0.0.1`. **Sequences are unsupported on this key**: `boxShadow: [a, b]` means one two-layer shadow, not a two-step keyframe sequence, because nothing distinguishes the two shapes structurally and the array form is the one RN itself uses. Per-property transitions are unaffected. **`boxShadow` is not accepted inside `gesture` sub-states**: compositing a layer stack through the priority cascade would put per-layer, per-field interpolation on the UI thread for every primitive whether or not it animates a shadow. It dev-warns and is ignored — drive it from `animate` (optionally via a variant keyed off the same state), or interpolate it yourself with `useShadow`. Animating `boxShadow` alongside the native `shadow*` keys on one element also dev-warns: that applies two shadow systems at once and whichever the view resolves last wins.
|
|
18
|
+
|
|
19
|
+
**Bundle cost, measured:** +1.61 kB (+24%) per primitive subpath — 6.63 kB → 8.24 kB brotlied — and +0.82 kB on the root entry, which was already carrying the shadow parser via `useShadow`. The `size-limit` caps were raised deliberately to keep the ~25% band the config has always claimed; the full accounting, including the alternatives that were measured and rejected, is in `.size-limit.cjs`.
|
|
20
|
+
|
|
21
|
+
- **`layoutId` shared-element transitions now carry style across, not just the rect.** A hero card that changes background colour, corner radius, or opacity between screens used to snap on those props while its frame animated — the most visible half of the transition arriving instantly. The source element's values for `opacity`, `borderRadius`, `backgroundColor`, `borderColor`, `color`, and `tintColor` are now snapshotted alongside its rect and crossfaded out over the same transition that drives the FLIP, so the arriving element starts wearing its counterpart's style and ends wearing its own. Nothing to configure: it rides the existing `layoutId` + `transition` props.
|
|
22
|
+
|
|
23
|
+
**Which keys carry, and why not more.** Transform keys are deliberately excluded — they are the FLIP's job, and carrying them too would apply the same displacement twice. So is `shadowColor`, despite being a colour key: crossfading one of the four native shadow props over geometry that snapped looks worse than letting the whole shadow snap together. There is no configuration surface for the list and no `layoutStyles` prop; if you need a key that isn't carried, drive it from `animate` on both sides.
|
|
24
|
+
|
|
25
|
+
**A key only participates when the element already has a value for it** — from `animate` / `initial` / a variant / `gesture`, or from the static `style`, read through `StyleSheet.flatten` the same way undriven resting values are. Values are never invented: activating a key the element says nothing about would rest it at the generic type default, and for `color` on a `Motion.Text` that inherits its colour from a parent, that default is `'transparent'` — invisible text on an element the consumer only asked to move. This is the same failure mode as the `0.0.3` resting-value regression, approached from the other side. A key the source carried but the target has no value for is ignored, and vice versa.
|
|
26
|
+
|
|
27
|
+
**A still-mounted source is read live**, for the same reason its rect is re-measured: values move without a layout pass (an animation settling, a theme swap), so the stored snapshot is a floor rather than the truth. Once the source has unmounted, the snapshot taken at release is all that remains. Reduced motion and `transition={{ type: 'no-animation' }}` snap, matching the rect path — and a source and target in mismatched coordinate spaces skip the style carry along with the FLIP, since half a shared-element transition reads as a glitch rather than as graceful degradation. On the mount that consumes a source, the carry composites _above_ the base value, so it overrides `initial` for the carried keys on that mount.
|
|
28
|
+
|
|
29
|
+
Cheap by construction: one progress shared value drives however many keys are carried, and the blend reuses the worklet's existing lerp / `interpolateColor` branches — the same ones the `gesture` cascade uses — sitting one priority above every gesture layer. At rest the pair is `(null, 0)`, so a primitive that isn't mid-transition pays a single comparison. **Bundle cost: +0.56 kB (+6.8%) per primitive subpath**, 8.24 → 8.80 kB brotlied, +0.59 kB root; the `size-limit` caps did not move. Workspace-internal API changes (none of it is exported from the package root): `consumeLayout` returns `{ rect, remeasure?, styles?, readStyles? }`, `registerLayout` takes an optional style reader, `releaseLayout` takes an optional snapshot, and `useSharedLayout` returns a `carry` binding alongside `flip`. Guarded by 16 new cases in `sharedLayout.test.tsx`.
|
|
30
|
+
|
|
31
|
+
### Changed
|
|
32
|
+
|
|
33
|
+
- **`layoutId` shared-element rects are now measured in window coordinates.** Rects came from `onLayout`, which reports **parent-relative** coordinates, so a source and target sitting under containers at different screen offsets produced a FLIP short by exactly that offset — the element flew in from the wrong place. Rects are now measured with `measureInWindow` on the host node, which is what makes nested-parent setups line up. Where a synchronous measurement isn't available the parent-relative rect is used instead, so behavior is unchanged rather than broken; each entry records which space it is in, and a source and target that ended up in _different_ spaces skip the animation rather than play a wrong one.
|
|
34
|
+
|
|
35
|
+
**Synchronous or not at all**, and this is a design decision rather than a limitation. `measureInWindow` resolves synchronously on Fabric (a JSI call), asynchronously on the legacy architecture (over the bridge), and **never** on a detached node — `ReactFabricHostComponent.measureInWindow` looks the node up and simply returns when it's missing, and the Jest host mock behaves the same way. Awaiting the callback would put the same element in window space on one platform and parent space on another, and since the two aren't comparable, a source stored in one and a target measured in the other would silently cancel the transition. Reading the result synchronously or treating it as unavailable keeps the space coherent: fixed on Fabric, unchanged on legacy, never intermittent. A late callback is deliberately dropped. A measurement that is zero-sized or non-finite is also rejected — trusting it would fling the element in from the top-left corner, which is worse than not animating.
|
|
36
|
+
|
|
37
|
+
**A still-mounted source is re-measured when the target lays out.** This is the other half of the change, and without it window coordinates would have been a net regression. `onLayout` does not fire when an _ancestor_ scrolls, so a stored window rect drifts as the user scrolls a list while a parent-relative rect would have stayed valid — meaning the most common shared-element case (scroll a list of photos, tap one) would have been offset by the scroll distance. A registry entry now carries a re-measure hook while its owner is mounted, which a stack navigator's outgoing screen still is at the moment the incoming one lays out. `releaseLayout` drops the hook, since a detached node measures to nothing. Remaining caveat: if the navigator has already begun translating the outgoing screen, the re-measurement catches it mid-transition — bounded by how far the transition has progressed, far smaller than an unbounded scroll offset.
|
|
38
|
+
|
|
39
|
+
Also fixed while here: **the four FLIP shared values now cancel on unmount.** The `0.0.2` unmount-cancel pass covered the value-layer hooks and the factory's per-key values but missed these. Workspace-internal API changes (none of it is exported from the package root): `SharedRect` gains a required `space` field, `consumeLayout` returns a source object instead of a bare rect, `registerLayout` takes an optional re-measure hook, and a new `__setSharedLayoutMeasurer` test hook joins `__setSharedLayoutClock`. Guarded by 12 new cases in `sharedLayout.test.tsx` (49 in total for this release, counting the style carry below).
|
|
40
|
+
|
|
7
41
|
## [0.0.3] - 2026-07-25
|
|
8
42
|
|
|
9
43
|
### Fixed
|
|
@@ -80,9 +114,11 @@ All notable changes to `@rootnative/inertia` are documented here. The format fol
|
|
|
80
114
|
|
|
81
115
|
- **Nested `<MotionConfig>` now inherits `reducedMotion`** from its ancestor when the prop is omitted (previously an inner provider silently reset the subtree to the `'user'` default). A transitions-only inner provider no longer clobbers an outer `reducedMotion="never"` / `"always"`.
|
|
82
116
|
|
|
83
|
-
##
|
|
117
|
+
## 0.0.0-alpha.0
|
|
118
|
+
|
|
119
|
+
_No git tag was cut for this release; the published artifact is on npm as [`@rootnative/inertia@0.0.0-alpha.0`](https://www.npmjs.com/package/@rootnative/inertia/v/0.0.0-alpha.0). Unlinked here for that reason — every other heading resolves to a real tag._
|
|
84
120
|
|
|
85
|
-
Initial alpha publish. The full
|
|
121
|
+
Initial alpha publish. The full initial surface is in place; APIs are still subject to change before the stability lock at `1.0.0`.
|
|
86
122
|
|
|
87
123
|
### Added
|
|
88
124
|
|
|
@@ -105,7 +141,8 @@ Initial alpha publish. The full v0.1 surface is in place; APIs are still subject
|
|
|
105
141
|
- SVG path morphing, gradient interpolation, and shared-element transitions across screens are out of scope until `0.2.x` / `1.x` per the roadmap.
|
|
106
142
|
- `react-native-gesture-handler` integration (drag, pan, swipe sub-states) lands in `0.2` via the optional `@rootnative/inertia-gestures` adapter.
|
|
107
143
|
|
|
108
|
-
[unreleased]: https://github.com/rootnative/inertia/compare/core+gestures+gradients+svg@0.0.
|
|
144
|
+
[unreleased]: https://github.com/rootnative/inertia/compare/core+gestures+gradients+svg@0.0.4...HEAD
|
|
145
|
+
[0.0.4]: https://github.com/rootnative/inertia/releases/tag/core+gestures+gradients+svg@0.0.4
|
|
109
146
|
[0.0.3]: https://github.com/rootnative/inertia/releases/tag/core+gestures+gradients+svg@0.0.3
|
|
110
147
|
[0.0.2]: https://github.com/rootnative/inertia/releases/tag/core+gestures+gradients+svg@0.0.2
|
|
111
148
|
[0.0.1]: https://github.com/rootnative/inertia/releases/tag/core+gestures+gradients+svg@0.0.1
|
|
@@ -114,4 +151,3 @@ Initial alpha publish. The full v0.1 surface is in place; APIs are still subject
|
|
|
114
151
|
[0.0.0-alpha.3]: https://github.com/rootnative/inertia/releases/tag/core+gestures+gradients+svg@0.0.0-alpha.3
|
|
115
152
|
[0.0.0-alpha.2]: https://github.com/rootnative/inertia/releases/tag/core+gestures+gradients+svg@0.0.0-alpha.2
|
|
116
153
|
[0.0.0-alpha.1]: https://github.com/rootnative/inertia/releases/tag/core+gestures+gradients+svg@0.0.0-alpha.1
|
|
117
|
-
[0.0.0-alpha.0]: https://github.com/rootnative/inertia/releases/tag/core+gestures+gradients+svg@0.0.0-alpha.0
|
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
Declarative animation primitives for React Native, built as a thin wrapper around [`react-native-reanimated`](https://docs.swmansion.com/react-native-reanimated/). Inspired by Framer Motion (web) and react-spring (cross-platform).
|
|
8
8
|
|
|
9
|
-
> **Status:** `0.0.
|
|
9
|
+
> **Status:** `0.0.4` — stable. Pre-`1.0.0` minor versions may break — see the root [README](https://github.com/rootnative/inertia#versioning--release).
|
|
10
10
|
|
|
11
11
|
## Install
|
|
12
12
|
|
|
@@ -86,9 +86,11 @@ Plus, on any transition: `delay`, `repeat`. Per-property transitions take preced
|
|
|
86
86
|
|
|
87
87
|
## Animatable properties
|
|
88
88
|
|
|
89
|
-
Numeric: `opacity`, `translateX`, `translateY`, `scale`, `scaleX`, `scaleY`, `rotate`, `rotateX`, `rotateY`, `width`, `height`, `borderRadius`. Color: `backgroundColor`, `borderColor`, `color`, `tintColor` (Image only — `Motion.View` rejects it at compile time). Layout transforms via `transform: [...]`. Color targets are forwarded straight through `withSpring` / `withTiming`; Reanimated's value setter packs the string to RGBA and interpolates on the UI thread.
|
|
89
|
+
Numeric: `opacity`, `translateX`, `translateY`, `scale`, `scaleX`, `scaleY`, `rotate`, `rotateX`, `rotateY`, `width`, `height`, `borderRadius`, `shadowOpacity`, `shadowRadius`, `elevation`. Color: `backgroundColor`, `borderColor`, `color`, `shadowColor`, `tintColor` (Image only — `Motion.View` rejects it at compile time). Layout transforms via `transform: [...]`. Color targets are forwarded straight through `withSpring` / `withTiming`; Reanimated's value setter packs the string to RGBA and interpolates on the UI thread.
|
|
90
90
|
|
|
91
|
-
|
|
91
|
+
Nested object: `shadowOffset: { width, height }` — single-value form only (no sequences, no array keyframes). Structured: `boxShadow` — the cross-platform CSS shadow, as a string (`'0px 4px 8px rgba(0,0,0,0.3)'`) or RN's `BoxShadowValue[]`. Endpoints are padded to a common layer count, so layer counts may differ between `initial` and `animate`; sequences and `gesture` sub-states are not supported on this key.
|
|
92
|
+
|
|
93
|
+
SVG path morphing ships in the [`@rootnative/inertia-svg`](../svg) adapter (`MotionPath`). Shared-element transitions across screens are wired through the `layoutId` prop — pair the same id on a source and target `Motion.*` and Inertia FLIPs between them on mount, carrying `opacity`, `borderRadius`, and the color keys across alongside the rect.
|
|
92
94
|
|
|
93
95
|
## When not to use the core package alone
|
|
94
96
|
|