@rootnative/inertia 0.0.1 → 0.0.3
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 +36 -2
- package/dist/{chunk-TDSO63CJ.js → chunk-3XTVY34H.js} +2 -2
- package/dist/{chunk-NXDJZD6A.mjs → chunk-46P57VMY.mjs} +1 -1
- package/dist/{chunk-7UDYEFBU.js → chunk-BP3Y2SHQ.js} +477 -298
- package/dist/{chunk-6SMPIOIC.mjs → chunk-BQQTHG2V.mjs} +1 -1
- package/dist/{chunk-DWCLIBYO.mjs → chunk-CSODMRJ7.mjs} +478 -299
- package/dist/chunk-FNVFV4EY.js +8 -0
- package/dist/chunk-FWQOXA43.js +8 -0
- package/dist/chunk-KBP4LR75.js +8 -0
- package/dist/{chunk-ALRHDFZE.mjs → chunk-O22NXXCZ.mjs} +1 -1
- package/dist/{chunk-CWLFUYIY.mjs → chunk-OQV66TBQ.mjs} +1 -1
- package/dist/{chunk-JVBXPF2G.mjs → chunk-SGUHE5CX.mjs} +1 -1
- package/dist/{chunk-2HYD2ZBK.js → chunk-W5MC3P4N.js} +2 -2
- package/dist/index.d.mts +231 -43
- package/dist/index.d.ts +231 -43
- package/dist/index.js +212 -23
- package/dist/index.mjs +204 -18
- package/dist/motion/Image.js +3 -3
- package/dist/motion/Image.mjs +2 -2
- package/dist/motion/Pressable.js +3 -3
- package/dist/motion/Pressable.mjs +2 -2
- package/dist/motion/ScrollView.js +3 -3
- package/dist/motion/ScrollView.mjs +2 -2
- package/dist/motion/Text.js +3 -3
- package/dist/motion/Text.mjs +2 -2
- package/dist/motion/View.js +3 -3
- package/dist/motion/View.mjs +2 -2
- package/llms.txt +5 -0
- package/package.json +1 -1
- package/src/index.ts +10 -0
- package/src/layout/index.ts +1 -0
- package/src/layout/sharedRegistry.ts +51 -2
- package/src/motion/createMotionComponent.tsx +781 -502
- package/src/presence/Presence.tsx +73 -10
- package/src/values/index.ts +13 -0
- package/src/values/useAnimation.ts +15 -1
- package/src/values/useAnimator.ts +83 -0
- package/src/values/useColorCascade.ts +125 -0
- package/src/values/useInterpolatedStyle.ts +319 -0
- package/src/values/useMotionValue.ts +20 -2
- package/src/values/useSpring.ts +12 -0
- package/dist/chunk-3UTJJ4A3.js +0 -8
- package/dist/chunk-4QGXK6TF.js +0 -8
- package/dist/chunk-Z7HIOFKQ.js +0 -8
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,34 @@ All notable changes to `@rootnative/inertia` are documented here. The format fol
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [0.0.3] - 2026-07-25
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- **Properties mentioned only by `gesture` / `exit` / a non-current variant no longer stomp the static `style`.** A key touched by any of those joins the animated key set — the worklet has to know about it so the layer or branch can drive it later — but until something actually drove it, it rested at a generic default and the animated style, which merges _after_ `style`, silently overrode the element's own stylesheet. `<Motion.View style={styles.field} gesture={{ focused: { borderColor: '#4f46e5' } }} />` rendered `borderColor: 'transparent'` on the very first frame, before any interaction; `exit={{ width: 0 }}` collapsed the element to zero width while it was still on screen; a `width` present in one variant branch collapsed the element whenever a different branch was active. Affected every property whose default isn't an identity — `width`, `height`, `borderRadius`, `shadowOpacity`, `shadowRadius`, `elevation`, `shadowOffset`, and all five color keys. Transforms and `opacity` were unaffected, which is why this survived so long: their defaults (`0` / `1`) happen to be no-ops. Undriven properties now rest at the value the static `style` sets — read through `StyleSheet.flatten`, so style arrays and registered stylesheet IDs resolve, transform entries are read out of the `transform` array, `shadowOffset` decomposes into both axes, and unit-suffixed rotations (`'45deg'`, `'0.5rad'`) convert to the plain degrees the shared value holds — falling back to the type default only when the style is silent too. Gesture layers now blend from that style-derived base and return to it on release. The base also tracks a `style` that changes after mount (a theme swap no longer gets pinned to the value captured at mount). Non-numeric style values such as `width: '100%'` are deliberately not seeded into numeric slots, since a later `withTiming` can't interpolate them. Values a record has already driven are untouched: switching to a variant branch that omits a key still leaves it where the previous branch put it. Guarded by `style-resting.test.tsx` (18 cases).
|
|
12
|
+
|
|
13
|
+
- **`<Presence>` children now animate out in place instead of jumping to the end of their siblings.** The render list was built as "every present entry, then every exiting entry". React reconciles that array by key, so appending physically moved the node: removing the middle of a three-row list rendered `a, c, b` and the departing row visibly jumped to the bottom before it had finished fading. Absolutely-positioned overlays — popovers, sheets, toasts, the cases `<Presence>` gets used for most and the ones the existing tests covered — never showed it; every list and column did. Exiting entries are now spliced back in at the position they held, anchored immediately after their nearest surviving predecessor (or at the front if there isn't one), so adjacent departures keep their relative order and siblings added or reordered mid-exit are placed around them. `<Presence>` remembers the full previous render order, including entries that were already exiting, since an exiting child is by definition absent from `children`. Note for anyone touching this code: the ordering pass has to read the exiting map that the _current_ render computed, not the `useState` value — on the render that first detects a departure the state update hasn't applied yet, and using the stale map drops the child from the remembered order entirely on the following render, losing the exit animation rather than merely misplacing it. Guarded by `Presence.order.test.tsx` (12 cases; 7 fail against the previous implementation).
|
|
14
|
+
|
|
15
|
+
- **An endless (`repeat: 'infinite'`) animation no longer blocks completion counters.** A callback is only promoted to the terminal `'animation'` phase once `iteration >= totalIterations - 1`, which is unreachable against `Infinity`. Two counters were gated behind that check and both broke in the presence of an endless animation. **(1) The transform group** — a multi-axis transform coalesces its terminal callbacks into one `onAnimationEnd({ key: 'transform' })`, counting the participating axes and firing when the last settles. An endless axis pinned that count above zero forever, so `animate={{ translateX: 100, translateY: 50 }}` with `transition={{ translateX: { repeat: 'infinite' }, translateY: {} }}` settled `translateY`, decremented the counter, and fired **nothing** — the completion was silently swallowed. Endless axes are now excluded from the group, so the finite axes still report; when every transform axis is endless no terminal callback fires at all, which is correct since nothing finishes. **(2) `<Presence>`'s settle counter** — the same unreachable check gated `safeToRemove`, so an endless exit animation left the child **mounted forever**. A top-level `transition={{ repeat: 'infinite' }}` is inherited by `exit`, so a pulsing element inside `<Presence>` would never unmount — a permanent leak, not just a missed callback. Exit keys that can never settle are no longer counted: finite keys still gate the unmount, and if every exiting property is endless the child is released immediately. Scope the repeat to one property (`transition={{ opacity: { repeat: 'infinite' } }}`) when you want a loop _and_ a normal exit. Guarded by `endless-repeat.test.tsx` (8 cases; 4 fail against the previous implementation).
|
|
16
|
+
|
|
17
|
+
- **`useColorCascade` now notices when a layer's `progress` shared value is swapped.** The layer chain is memoized on a structural signature — key, base colour, layer count, layer colours — which can't contain the shared values themselves because they're objects, and they were excluded from change detection entirely. Swapping _which_ value drives a layer while its colour stayed the same therefore did nothing: the worklet went on reading the old shared value, and driving the one no longer referenced by any layer still moved the colour. A conditionally-sourced progress (`{ progress: isError ? errorSpring : hoverSpring, color }`) silently animated off the wrong input. Shared-value references are now compared directly and the array is rebuilt only when one actually differs, so the swap rewires the worklet while a fresh-but-equal `layers` literal still produces no new UI-thread closure.
|
|
18
|
+
|
|
19
|
+
- **`useAnimator()` is now identity-stable by construction, as documented.** Its JSDoc, the hooks reference, and the `0.0.2` changelog all promised an identity-stable setter safe to drop into memoized handlers, and all three explained it as reading the registry "live inside the body" — but the implementation listed `[registry, shouldReduceMotion]` as `useCallback` dependencies, so a provider republishing its `transitions` map or a reduced-motion change handed back a new function and churned every handler built on it. It stayed stable only by accident of `<MotionConfig>` memoizing. Both values now genuinely live in refs read at call time, which also makes writes resolve against the registry current _when the event fires_ rather than the one captured at render.
|
|
20
|
+
|
|
21
|
+
- **The `layoutId` measure registry now evicts expired entries.** Rects only ever left the module-level map through `consumeLayout` / `peekSharedLayout`, so a `layoutId` that unmounted and was never remounted kept its entry for the lifetime of the process. Harmless for a handful of hero images; a slow leak for per-item ids in a long-lived list (`layoutId={`photo-${item.id}`}`), where the release is never consumed. Writes now run an expiry sweep, amortized to at most one full scan per TTL window so a burst of layout events doesn't become a burst of scans. Sweeping a still-mounted element's entry is safe — it re-registers on its next `onLayout`, and unmount re-publishes the rect with a fresh TTL either way.
|
|
22
|
+
|
|
23
|
+
## [0.0.2] - 2026-07-24
|
|
24
|
+
|
|
25
|
+
### Added
|
|
26
|
+
|
|
27
|
+
- **`useAnimator()` — imperative writes that resolve named transitions and respect reduced motion.** The imperative counterpart to `useAnimation`: returns an identity-stable `(value, to, transition?)` setter to call from event handlers (hover-in, focus, press) rather than from an effect. It closes the two footguns of the hand-written `value.value = resolveTransition(config, to)` escape hatch: a registered `TransitionName` now resolves through the nearest `<MotionConfig transitions>` (a bare `resolveTransition` can't reach the context registry, so imperative call sites otherwise rebuild configs the provider already owns — surfaced by the RootNative UI Slider rebuilding `state-hover` / `state-focus` by hand), and the write routes through the same `no-animation` reduced-motion downgrade `useAnimation` applies (raw `resolveTransition` writes silently bypass `<MotionConfig reducedMotion>` — a correctness fix). Not a new animation API — the hooks-layer equivalent of `useMotionValue` + `resolveTransition`, minus the footguns. New `Animator` type exported from the root barrel.
|
|
28
|
+
- **`useColorCascade(rest, layers, options?)` — priority-ordered layered color crossfade.** Composites a stack of color layers over a base `rest` color; each layer carries its own `progress` shared value and blends toward its color as that progress rises, later layers winning over earlier ones (the values-layer form of the `gesture` prop's fixed-priority cascade, Decision 5). Exactly equivalent to the hand-chained nested-`interpolateColor` shape `focus(error(hover(rest)))`, collapsed into one hook and one worklet — the shape the RootNative UI TextField hand-writes three times (label color, filled indicator, outlined border). `options.key` reuses `ColorStyleKey` (default `backgroundColor`). Memoized on a colors+key signature so an equal-but-fresh `layers` literal produces no new UI-thread closure. Color-only by design; cascade a numeric key via `useInterpolatedStyle` + `useTransform` max. `useColorTransition` remains the single-layer fast path. New `ColorCascadeLayer` / `UseColorCascadeOptions` types exported from the root barrel.
|
|
29
|
+
|
|
30
|
+
### Changed
|
|
31
|
+
|
|
32
|
+
- **A prop-less `Motion.*` primitive is now a zero-cost plain host.** An instance carrying none of the animation-driving props (`initial` / `animate` / `exit` / `transition` / `variants` / `controller` / `gesture` / `layout` / `layoutId` / `onAnimationEnd`) previously still allocated the full animated body — ~25 per-key shared values, four gesture-progress shared values, gesture `useState`s, and a `useAnimatedStyle` worklet — every render. It now renders straight through `Animated.createAnimatedComponent(Component)` with `style` / `ref` / `onLayout` forwarded and no animation allocation, so a prop-less `Motion.View` is a genuine drop-in for `Animated.View` when hosting an animated-style fragment from `useColorTransition` / `useShadow` / `useInterpolatedStyle` / `useColorCascade` (no `/reanimated` import needed — closes proposal item 5). The plain host stays `<Presence>`-aware (it self-removes on exit via a context read + unmount effect, no worklet). Adding an animation prop opts the instance back into the full body; because the two paths are distinct component types, crossing that boundary remounts the element (give it a stable `key` if the remount matters). New guard test (`plain-host.test.tsx`) pins that a prop-less primitive calls neither `useSharedValue` nor `useAnimatedStyle`.
|
|
33
|
+
- **Value hooks and the `Motion.*` factory now cancel in-flight animations on unmount.** `useMotionValue`, `useSpring`, `useBooleanSpring`, and `useAnimation` — and every per-key / gesture-layer shared value inside a `Motion.*` primitive — register an unmount cleanup that calls `cancelAnimation` on the shared value they own. Previously a mid-flight (or `repeat: 'infinite'`) `withSpring` / `withTiming` / `withDecay` kept ticking its worklet for frames after the owning component was gone; consumers who cared had to import `cancelAnimation` from the `/reanimated` interop subpath and hand-write the effect (surfaced by the RootNative UI Slider's 22-line 8-value teardown). The shared values are identity-stable and component-owned, so cancelling on unmount is always safe. `cancelAnimation` stays exported from the interop subpath for _mid-life_ cancellation — this change only covers the unmount case. Behavior change (no API change).
|
|
34
|
+
|
|
7
35
|
## [0.0.1] - 2026-07-23
|
|
8
36
|
|
|
9
37
|
**First stable release.** Graduates the `0.0.0-alpha.x` line, absorbing Milestones 1–3 in a single tag: the declarative core (primitives, per-property transitions, sequences/keyframes, unified `repeat`, variants, `gesture` prop, `<Presence>`, `<MotionConfig>` with reduced-motion + named transitions), the value-layer hooks, the `layout` prop and rect-only `layoutId` shared-element transitions, and the `inertia-gestures` / `inertia-gradients` / `inertia-svg` adapter packages (released in lockstep). See the alpha entries below for the per-change history.
|
|
@@ -77,7 +105,13 @@ Initial alpha publish. The full v0.1 surface is in place; APIs are still subject
|
|
|
77
105
|
- SVG path morphing, gradient interpolation, and shared-element transitions across screens are out of scope until `0.2.x` / `1.x` per the roadmap.
|
|
78
106
|
- `react-native-gesture-handler` integration (drag, pan, swipe sub-states) lands in `0.2` via the optional `@rootnative/inertia-gestures` adapter.
|
|
79
107
|
|
|
80
|
-
[unreleased]: https://github.com/rootnative/inertia/compare/core+gestures+gradients+svg@0.0.
|
|
108
|
+
[unreleased]: https://github.com/rootnative/inertia/compare/core+gestures+gradients+svg@0.0.3...HEAD
|
|
109
|
+
[0.0.3]: https://github.com/rootnative/inertia/releases/tag/core+gestures+gradients+svg@0.0.3
|
|
110
|
+
[0.0.2]: https://github.com/rootnative/inertia/releases/tag/core+gestures+gradients+svg@0.0.2
|
|
111
|
+
[0.0.1]: https://github.com/rootnative/inertia/releases/tag/core+gestures+gradients+svg@0.0.1
|
|
112
|
+
[0.0.0-alpha.5]: https://github.com/rootnative/inertia/releases/tag/core+gestures+gradients+svg@0.0.0-alpha.5
|
|
113
|
+
[0.0.0-alpha.4]: https://github.com/rootnative/inertia/releases/tag/core+gestures+gradients+svg@0.0.0-alpha.4
|
|
114
|
+
[0.0.0-alpha.3]: https://github.com/rootnative/inertia/releases/tag/core+gestures+gradients+svg@0.0.0-alpha.3
|
|
81
115
|
[0.0.0-alpha.2]: https://github.com/rootnative/inertia/releases/tag/core+gestures+gradients+svg@0.0.0-alpha.2
|
|
82
116
|
[0.0.0-alpha.1]: https://github.com/rootnative/inertia/releases/tag/core+gestures+gradients+svg@0.0.0-alpha.1
|
|
83
|
-
[0.0.0-alpha.0]: https://github.com/rootnative/inertia/releases/tag/
|
|
117
|
+
[0.0.0-alpha.0]: https://github.com/rootnative/inertia/releases/tag/core+gestures+gradients+svg@0.0.0-alpha.0
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var chunkBP3Y2SHQ_js = require('./chunk-BP3Y2SHQ.js');
|
|
4
4
|
var reactNative = require('react-native');
|
|
5
5
|
|
|
6
|
-
var MotionPressable =
|
|
6
|
+
var MotionPressable = chunkBP3Y2SHQ_js.createMotionComponent(reactNative.Pressable);
|
|
7
7
|
|
|
8
8
|
exports.MotionPressable = MotionPressable;
|