@rootnative/inertia 0.0.0-alpha.0
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 +33 -0
- package/LICENSE +21 -0
- package/README.md +131 -0
- package/dist/gestureLayer/index.d.mts +119 -0
- package/dist/gestureLayer/index.d.ts +119 -0
- package/dist/gestureLayer/index.js +346 -0
- package/dist/gestureLayer/index.js.map +1 -0
- package/dist/gestureLayer/index.mjs +344 -0
- package/dist/gestureLayer/index.mjs.map +1 -0
- package/dist/index.d.mts +507 -0
- package/dist/index.d.ts +507 -0
- package/dist/index.js +1546 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +1515 -0
- package/dist/index.mjs.map +1 -0
- package/dist/motion/Image.d.mts +12 -0
- package/dist/motion/Image.d.ts +12 -0
- package/dist/motion/Image.js +1138 -0
- package/dist/motion/Image.js.map +1 -0
- package/dist/motion/Image.mjs +1132 -0
- package/dist/motion/Image.mjs.map +1 -0
- package/dist/motion/Pressable.d.mts +15 -0
- package/dist/motion/Pressable.d.ts +15 -0
- package/dist/motion/Pressable.js +1138 -0
- package/dist/motion/Pressable.js.map +1 -0
- package/dist/motion/Pressable.mjs +1132 -0
- package/dist/motion/Pressable.mjs.map +1 -0
- package/dist/motion/ScrollView.d.mts +12 -0
- package/dist/motion/ScrollView.d.ts +12 -0
- package/dist/motion/ScrollView.js +1138 -0
- package/dist/motion/ScrollView.js.map +1 -0
- package/dist/motion/ScrollView.mjs +1132 -0
- package/dist/motion/ScrollView.mjs.map +1 -0
- package/dist/motion/Text.d.mts +11 -0
- package/dist/motion/Text.d.ts +11 -0
- package/dist/motion/Text.js +1138 -0
- package/dist/motion/Text.js.map +1 -0
- package/dist/motion/Text.mjs +1132 -0
- package/dist/motion/Text.mjs.map +1 -0
- package/dist/motion/View.d.mts +11 -0
- package/dist/motion/View.d.ts +11 -0
- package/dist/motion/View.js +1138 -0
- package/dist/motion/View.js.map +1 -0
- package/dist/motion/View.mjs +1132 -0
- package/dist/motion/View.mjs.map +1 -0
- package/dist/testing/index.d.mts +57 -0
- package/dist/testing/index.d.ts +57 -0
- package/dist/testing/index.js +19 -0
- package/dist/testing/index.js.map +1 -0
- package/dist/testing/index.mjs +16 -0
- package/dist/testing/index.mjs.map +1 -0
- package/dist/touch/index.d.mts +146 -0
- package/dist/touch/index.d.ts +146 -0
- package/dist/touch/index.js +166 -0
- package/dist/touch/index.js.map +1 -0
- package/dist/touch/index.mjs +164 -0
- package/dist/touch/index.mjs.map +1 -0
- package/dist/types-cU43dEmH.d.mts +339 -0
- package/dist/types-cU43dEmH.d.ts +339 -0
- package/dist/useGesture-BnY65PlQ.d.ts +84 -0
- package/dist/useGesture-DxtXdz-K.d.mts +84 -0
- package/jest-preset.js +33 -0
- package/jest-setup.js +213 -0
- package/llms.txt +142 -0
- package/package.json +151 -0
- package/src/__type-tests__/animate.test-d.tsx +88 -0
- package/src/__type-tests__/variants.test-d.tsx +67 -0
- package/src/config/MotionConfig.tsx +30 -0
- package/src/config/MotionConfigContext.ts +53 -0
- package/src/config/index.ts +9 -0
- package/src/gestureLayer/index.ts +21 -0
- package/src/gestureLayer/useGestureLayer.ts +285 -0
- package/src/gestures/focusVisibility.ts +61 -0
- package/src/gestures/index.ts +1 -0
- package/src/index.ts +72 -0
- package/src/layout/index.ts +16 -0
- package/src/layout/resolveLayout.ts +54 -0
- package/src/layout/sharedRegistry.ts +111 -0
- package/src/layout/useSharedLayout.ts +289 -0
- package/src/motion/Image.tsx +9 -0
- package/src/motion/Pressable.tsx +12 -0
- package/src/motion/ScrollView.tsx +9 -0
- package/src/motion/Text.tsx +8 -0
- package/src/motion/View.tsx +8 -0
- package/src/motion/createMotionComponent.tsx +1306 -0
- package/src/motion/index.ts +26 -0
- package/src/motion/installCheck.ts +65 -0
- package/src/presence/Presence.tsx +165 -0
- package/src/presence/PresenceContext.ts +28 -0
- package/src/presence/index.ts +6 -0
- package/src/testing/index.ts +78 -0
- package/src/touch/index.ts +18 -0
- package/src/touch/useTouchDrag.ts +289 -0
- package/src/transitions/easing.ts +53 -0
- package/src/transitions/index.ts +7 -0
- package/src/transitions/keys.ts +32 -0
- package/src/transitions/resolve.ts +169 -0
- package/src/transitions/resolveSequence.ts +87 -0
- package/src/transitions/runtime.ts +63 -0
- package/src/transitions/sig.ts +40 -0
- package/src/transitions/spring.ts +42 -0
- package/src/types.ts +383 -0
- package/src/values/index.ts +26 -0
- package/src/values/useAnimation.ts +69 -0
- package/src/values/useBooleanSpring.ts +33 -0
- package/src/values/useColorTransition.ts +72 -0
- package/src/values/useGesture.ts +144 -0
- package/src/values/useMotionValue.ts +33 -0
- package/src/values/useScroll.ts +72 -0
- package/src/values/useShadow.ts +116 -0
- package/src/values/useSpring.ts +98 -0
- package/src/values/useTransform.ts +132 -0
- package/src/values/useVariants.ts +60 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Changelog
|
|
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.
|
|
4
|
+
|
|
5
|
+
## [Unreleased]
|
|
6
|
+
|
|
7
|
+
## [0.0.0-alpha.0]
|
|
8
|
+
|
|
9
|
+
Initial alpha publish. The full v0.1 surface is in place; APIs are still subject to change before `0.1.0`.
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- **Primitives** — `Motion.View`, `Motion.Text`, `Motion.Image`, `Motion.Pressable`, `Motion.ScrollView` with per-primitive style inference (no shared `ViewStyle & TextStyle & ImageStyle` fallback).
|
|
14
|
+
- **Subpath imports** — `@rootnative/inertia/view`, `/text`, `/image`, `/pressable`, `/scroll-view` for per-primitive tree-shaking. Bundle-size baselines recorded via `size-limit`.
|
|
15
|
+
- **Transitions** — `spring` (default, react-spring vocabulary `tension`/`friction`/`mass`/`velocity`), `timing` (with auto-worklet-wrapped easing functions), `decay`, `no-animation`. Per-property `transition` shape takes precedence over top-level.
|
|
16
|
+
- **Sequences and keyframes** — `animate={{ x: [0, 100, 0] }}` and `[{ to, ...override }]` step shape with per-step transition overrides.
|
|
17
|
+
- **Repeat config** — unified `repeat: number | 'infinite' | { count, alternate }` shape; `alternate` defaults to `true`.
|
|
18
|
+
- **Variants** — `variants={{ open, closed }}` + `animate="open"` props; `useVariants` hook returning `{ current, transitionTo, subscribe }` for programmatic flows; `controller` prop wires the hook back to the component.
|
|
19
|
+
- **Gestures** — single `gesture` prop on every primitive: `pressed`, `focused`, `hovered` (web) sub-states. Pressable-based, zero overhead when omitted.
|
|
20
|
+
- **`<Presence>`** — mount/unmount transitions on top of Reanimated's `entering` / `exiting`. Exiting children automatically receive `pointerEvents: 'none'`.
|
|
21
|
+
- **`<MotionConfig reducedMotion>`** — `'user' | 'never' | 'always'` provider with `'user'` as the default. `useMotionConfig` and `useShouldReduceMotion` exposed for custom integrations.
|
|
22
|
+
- **`onAnimationEnd`** — `{ key, finished, value, target, phase, step, iteration }` payload. Transform-group keys (`translateX`/`translateY`, `scaleX`/`scaleY`) coalesce so a single `translate` step fires once, not once per axis.
|
|
23
|
+
- **Stable worklets, JS-thread resolver** — animate/transition objects compile to baked `withSpring` / `withTiming` / `withDecay` calls on the JS thread. Worklet bodies never iterate `Object.keys(...)` at frame time, and re-renders with unchanged values produce zero new UI-thread closures (regression-tested).
|
|
24
|
+
- **`createMotionComponent<C>()`** — public factory for custom primitives, with style inference from `C`'s `style` prop.
|
|
25
|
+
- **Docs** — Docusaurus site at `rootnative.github.io/inertia`; `llms.txt` and `llms-full.txt` shipped both on the docs site and in the npm tarball.
|
|
26
|
+
|
|
27
|
+
### Known limitations
|
|
28
|
+
|
|
29
|
+
- SVG path morphing, gradient interpolation, and shared-element transitions across screens are out of scope until `0.2.x` / `1.x` per the roadmap.
|
|
30
|
+
- `react-native-gesture-handler` integration (drag, pan, swipe sub-states) lands in `0.2` via the optional `@rootnative/inertia-gestures` adapter.
|
|
31
|
+
|
|
32
|
+
[unreleased]: https://github.com/rootnative/inertia/compare/v0.0.0-alpha.0...HEAD
|
|
33
|
+
[0.0.0-alpha.0]: https://github.com/rootnative/inertia/releases/tag/v0.0.0-alpha.0
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 RootNative
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
# @rootnative/inertia
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/@rootnative/inertia)
|
|
4
|
+
[](https://docs.swmansion.com/react-native-reanimated/)
|
|
5
|
+
[](./LICENSE)
|
|
6
|
+
|
|
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
|
+
|
|
9
|
+
> **Status:** `0.0.0-alpha`. Pre-1.0 minor versions may break — see the root [README](https://github.com/rootnative/inertia#versioning--release).
|
|
10
|
+
|
|
11
|
+
## Install
|
|
12
|
+
|
|
13
|
+
```sh
|
|
14
|
+
pnpm add @rootnative/inertia react-native-reanimated
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Then enable the [Reanimated Babel plugin](https://docs.swmansion.com/react-native-reanimated/docs/fundamentals/installation).
|
|
18
|
+
|
|
19
|
+
**Peer dependencies:** `react >=19.0.0`, `react-native >=0.81.0`, `react-native-reanimated >=4.0.0`.
|
|
20
|
+
|
|
21
|
+
## Quick start
|
|
22
|
+
|
|
23
|
+
```tsx
|
|
24
|
+
import { Motion, Presence } from '@rootnative/inertia'
|
|
25
|
+
|
|
26
|
+
export function FadeIn() {
|
|
27
|
+
return (
|
|
28
|
+
<Motion.View
|
|
29
|
+
initial={{ opacity: 0, translateY: 20 }}
|
|
30
|
+
animate={{ opacity: 1, translateY: 0 }}
|
|
31
|
+
transition={{
|
|
32
|
+
opacity: { type: 'timing', duration: 200 },
|
|
33
|
+
translateY: { type: 'spring', tension: 180, friction: 12 },
|
|
34
|
+
}}
|
|
35
|
+
/>
|
|
36
|
+
)
|
|
37
|
+
}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## What ships
|
|
41
|
+
|
|
42
|
+
- **Primitives** — `Motion.View`, `Motion.Text`, `Motion.Image`, `Motion.Pressable`, `Motion.ScrollView`. Per-primitive style inference (no shared `ViewStyle & TextStyle & ImageStyle` fallback).
|
|
43
|
+
- **Sequences and keyframes** — `animate={{ x: [0, 100, 0] }}` with per-step transitions; unified `repeat: number | 'infinite' | { count, alternate }`.
|
|
44
|
+
- **Variants** — `variants={{ open, closed }}` with `animate="open"`. Programmatic control via `useVariants` + `controller={...}`.
|
|
45
|
+
- **Gestures** — single `gesture` prop on every primitive: `gesture={{ pressed, focused, focusVisible, hovered }}`. Sub-states layer **additively** in priority order (`hovered → focused → focusVisible → pressed`); each layer fades in/out on its own progress so MD3 release-while-hovered cross-fades correctly. Per-layer transitions via `transition.<stateName>`. `focusVisible` engages only on keyboard focus (W3C `:focus-visible`) so click-focus on web doesn't flash a ring; on native it tracks `focused`. Zero overhead when omitted.
|
|
46
|
+
- **`<Presence>`** — mount/unmount transitions; exiting children automatically receive `pointerEvents: 'none'`.
|
|
47
|
+
- **`<MotionConfig reducedMotion>`** — OS reduce-motion honored end-to-end (`'user' | 'never' | 'always'`).
|
|
48
|
+
- **`layout` prop** — auto-layout transitions on every primitive, bridging Reanimated's `LinearTransition` via the same react-spring vocab (`tension`/`friction`/`mass`). Accepts `boolean | TransitionConfig`.
|
|
49
|
+
- **Value-layer hooks** — `useMotionValue`, `useSpring`, `useAnimation`, `useTransform`, `useScroll`, `useVariants`, `useGesture`. The escape hatch for animations the prop surface can't express — sibling overlays driven by a parent gesture, indeterminate loops on standalone shared values, gesture-smoothed inputs, etc.
|
|
50
|
+
- **Per-primitive subpath imports** — `@rootnative/inertia/view`, `/text`, `/image`, `/pressable`, `/scroll-view`.
|
|
51
|
+
- **JS-thread resolver, memoized worklets** — animate/transition objects compile to baked `withSpring` / `withTiming` / `withDecay` calls on the JS thread; the worklet body never iterates `Object.keys(...)` at frame time.
|
|
52
|
+
|
|
53
|
+
## Subpath imports
|
|
54
|
+
|
|
55
|
+
```ts
|
|
56
|
+
import { MotionView } from '@rootnative/inertia/view'
|
|
57
|
+
import { MotionText } from '@rootnative/inertia/text'
|
|
58
|
+
import { MotionImage } from '@rootnative/inertia/image'
|
|
59
|
+
import { MotionPressable } from '@rootnative/inertia/pressable'
|
|
60
|
+
import { MotionScrollView } from '@rootnative/inertia/scroll-view'
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Or the barrel — same primitives, named imports tree-shake cleanly because the package is `sideEffects: false`:
|
|
64
|
+
|
|
65
|
+
```ts
|
|
66
|
+
import { MotionView } from '@rootnative/inertia'
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Both forms land at ~4.1–4.2 kB brotlied for a single primitive (peers excluded). The full namespace (`import { Motion } from '@rootnative/inertia'`, then `Motion.View`) cannot tree-shake — accessing one property of a literal object holds the whole object live — and lands at ~4.8 kB. CI checks all three forms via `size-limit` so the gap doesn't drift.
|
|
70
|
+
|
|
71
|
+
## Transitions
|
|
72
|
+
|
|
73
|
+
| `type` | Public config keys | Maps to |
|
|
74
|
+
| -------------------- | -------------------------------------------------------------------------------------------- | ----------------------------------- |
|
|
75
|
+
| `'spring'` (default) | `tension`, `friction`, `mass`, `velocity`, `restSpeedThreshold`, `restDisplacementThreshold` | `withSpring` |
|
|
76
|
+
| `'timing'` | `duration`, `easing`, `delay` | `withTiming` |
|
|
77
|
+
| `'decay'` | `velocity`, `deceleration`, `clamp` | `withDecay` |
|
|
78
|
+
| `'no-animation'` | — | direct assignment, no interpolation |
|
|
79
|
+
|
|
80
|
+
Plus, on any transition: `delay`, `repeat`. Per-property transitions take precedence over the top-level transition. Spring config uses **react-spring vocabulary** (`tension`/`friction`); Reanimated's raw `stiffness`/`damping` is never on the public surface.
|
|
81
|
+
|
|
82
|
+
## Caveats
|
|
83
|
+
|
|
84
|
+
- **`Motion.Pressable` does not support function-form `style`.** RN's `Pressable` accepts `style={({ pressed }) => ...}` and re-runs it per state change; Inertia inherits Reanimated's `createAnimatedComponent` wrapper, which silently drops that form (no error, no warning). Drive press/focus/hover styling through `gesture` instead, or compute conditional styles once in render. See [primitives/pressable](https://rootnative.github.io/inertia/docs/primitives/pressable#style-must-be-a-value-not-a-function).
|
|
85
|
+
- **`initial` is read once on mount.** Mutating `initial` after first render does nothing — change the component `key`, remount via `<Presence>`, or drive the value through a controller. Pass `initial={false}` to skip the initial-mount animation entirely.
|
|
86
|
+
|
|
87
|
+
## Animatable properties
|
|
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.
|
|
90
|
+
|
|
91
|
+
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.
|
|
92
|
+
|
|
93
|
+
## When not to use the core package alone
|
|
94
|
+
|
|
95
|
+
Three sibling packages extend Inertia for capabilities that need extra peer dependencies. The core stays minimal so apps that don't need these don't pay for them.
|
|
96
|
+
|
|
97
|
+
**Continuous gestures** — the `gesture` prop in `@rootnative/inertia` covers `pressed` / `focused` / `focusVisible` / `hovered` (the Pressable-shaped sub-states). For drag, pan, or swipe, use [`@rootnative/inertia-gestures`](../gestures):
|
|
98
|
+
|
|
99
|
+
- `useDrag` — one- or two-axis drag with optional constraints and rubber-band elasticity
|
|
100
|
+
- `usePan` — camera-style pan with momentum on release
|
|
101
|
+
- `useSwipe` — directional commit-or-snap-back (distance + velocity thresholds)
|
|
102
|
+
|
|
103
|
+
```sh
|
|
104
|
+
pnpm add @rootnative/inertia-gestures react-native-gesture-handler
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
**Animated gradients** — colors / start / end / locations interpolation on a linear gradient lives in [`@rootnative/inertia-gradients`](../gradients), wrapping `expo-linear-gradient`:
|
|
108
|
+
|
|
109
|
+
```sh
|
|
110
|
+
pnpm add @rootnative/inertia-gradients expo-linear-gradient
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
**SVG path morphing** — `MotionPath` lives in [`@rootnative/inertia-svg`](../svg), wrapping `react-native-svg`. Animates path data (`d`) on structurally-compatible paths via element-wise scalar interpolation, plus `fill` / `stroke` / `strokeWidth` / opacities / `strokeDashoffset`:
|
|
114
|
+
|
|
115
|
+
```sh
|
|
116
|
+
pnpm add @rootnative/inertia-svg react-native-svg
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Keeping `react-native-gesture-handler`, `expo-linear-gradient`, and `react-native-svg` out of the core peer set means apps that animate buttons, sheets, and basic styles don't pay for capabilities they never invoke.
|
|
120
|
+
|
|
121
|
+
## Documentation
|
|
122
|
+
|
|
123
|
+
Full docs, every primitive's example screen, and an `llms-full.txt` reference live at:
|
|
124
|
+
|
|
125
|
+
- [https://rootnative.github.io/inertia/](https://rootnative.github.io/inertia/)
|
|
126
|
+
- [llms.txt](https://rootnative.github.io/inertia/llms.txt) — concise overview
|
|
127
|
+
- [llms-full.txt](https://rootnative.github.io/inertia/llms-full.txt) — full API reference
|
|
128
|
+
|
|
129
|
+
## License
|
|
130
|
+
|
|
131
|
+
[MIT](./LICENSE) © RootNative
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { AnimatedStyle } from 'react-native-reanimated';
|
|
2
|
+
import { U as UseGestureHandlers } from '../useGesture-DxtXdz-K.mjs';
|
|
3
|
+
import { T as TransitionConfig, h as GestureLayerTransitions } from '../types-cU43dEmH.mjs';
|
|
4
|
+
import 'react';
|
|
5
|
+
import 'react-native';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* A single gesture-layer style — a flat map of style keys to a value. Numeric
|
|
9
|
+
* values participate in clamped-max composition (the "strongest active layer
|
|
10
|
+
* wins" model used by MD3 state-layer haloes); string values are treated as
|
|
11
|
+
* colors and composed via priority cascade with `interpolateColor`.
|
|
12
|
+
*
|
|
13
|
+
* The hook does not validate that string values are valid colors — passing
|
|
14
|
+
* something like `borderStyle: 'solid'` will crash inside the worklet. Keep
|
|
15
|
+
* string values to color strings.
|
|
16
|
+
*/
|
|
17
|
+
type GestureLayerStyle = {
|
|
18
|
+
[key: string]: number | string | undefined;
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Per-state style maps. Every key is optional; missing layers default to
|
|
22
|
+
* `rest` (or `0` / `'transparent'` if `rest` is also absent for that key).
|
|
23
|
+
*
|
|
24
|
+
* - `rest` — base values, applied when no other layer is active.
|
|
25
|
+
* - `hovered` / `focused` / `focusVisible` / `pressed` — gesture-driven
|
|
26
|
+
* states tracked via the underlying `useGesture` hook. Each owns an
|
|
27
|
+
* independent 0↔1 progress that fades the layer in/out per the configured
|
|
28
|
+
* transition.
|
|
29
|
+
* - `disabled` — gated by the JS-side `options.disabled` flag rather than a
|
|
30
|
+
* gesture. Sits at the top of the priority cascade and overrides every
|
|
31
|
+
* gesture layer when active.
|
|
32
|
+
*/
|
|
33
|
+
interface GestureLayerStates {
|
|
34
|
+
rest?: GestureLayerStyle;
|
|
35
|
+
hovered?: GestureLayerStyle;
|
|
36
|
+
focused?: GestureLayerStyle;
|
|
37
|
+
focusVisible?: GestureLayerStyle;
|
|
38
|
+
pressed?: GestureLayerStyle;
|
|
39
|
+
disabled?: GestureLayerStyle;
|
|
40
|
+
}
|
|
41
|
+
interface UseGestureLayerOptions {
|
|
42
|
+
/**
|
|
43
|
+
* When `true`, the `disabled` layer becomes active (or `rest` if `disabled`
|
|
44
|
+
* is undefined). Animates via the top-level transition or the library
|
|
45
|
+
* default spring; per-layer transitions (`GestureLayerTransitions`) do not
|
|
46
|
+
* apply to `disabled`.
|
|
47
|
+
*/
|
|
48
|
+
disabled?: boolean;
|
|
49
|
+
/**
|
|
50
|
+
* Transition forwarded to the underlying `useGesture` hook. Either a single
|
|
51
|
+
* `TransitionConfig` for every gesture layer, or a `GestureLayerTransitions`
|
|
52
|
+
* map for per-layer fades. Reduced motion collapses every transition to
|
|
53
|
+
* `no-animation`.
|
|
54
|
+
*/
|
|
55
|
+
transition?: TransitionConfig | GestureLayerTransitions;
|
|
56
|
+
}
|
|
57
|
+
interface UseGestureLayerResult {
|
|
58
|
+
/**
|
|
59
|
+
* Animated style produced by `useAnimatedStyle` — spread on an
|
|
60
|
+
* `Animated.View` or pass through `<Motion.View style={...} />`.
|
|
61
|
+
*/
|
|
62
|
+
style: AnimatedStyle<Record<string, unknown>>;
|
|
63
|
+
/** Handlers to spread on the receiving `Pressable`. */
|
|
64
|
+
handlers: UseGestureHandlers;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* A "strongest active layer wins" interactive-feedback primitive. Sits one
|
|
68
|
+
* step above `useGesture()` — the consumer supplies the per-state target
|
|
69
|
+
* values, the hook handles the four gesture progress shared values, the
|
|
70
|
+
* disabled override, the worklet, and the transition.
|
|
71
|
+
*
|
|
72
|
+
* Composition model:
|
|
73
|
+
*
|
|
74
|
+
* - **Numeric keys** (opacity, scale, borderWidth, etc.) compose via
|
|
75
|
+
* clamped-max with `rest` as the floor:
|
|
76
|
+
* `out = max(rest, ...for each active gesture layer: lerp(rest, layer, progress))`.
|
|
77
|
+
* This matches the MD3 state-layer halo pattern — multiple states active
|
|
78
|
+
* simultaneously raise the value to the strongest, not the sum.
|
|
79
|
+
* - **Color keys** (any string value) compose via priority cascade with
|
|
80
|
+
* `interpolateColor`, lowest priority first: `hovered → focused →
|
|
81
|
+
* focusVisible → pressed`. Clamped-max doesn't apply to colors; this
|
|
82
|
+
* matches the cascade used by the declarative `gesture` prop.
|
|
83
|
+
* - **Disabled** sits at the top of the cascade for both numeric and color
|
|
84
|
+
* keys — when active, it lerps the composed value toward the `disabled`
|
|
85
|
+
* target.
|
|
86
|
+
*
|
|
87
|
+
* Reach for this when you want MD3 / iOS-translucent state-layer overlays
|
|
88
|
+
* without rewriting the worklet by hand for every consumer; reach for plain
|
|
89
|
+
* `useGesture()` when you need a composition model this hook doesn't
|
|
90
|
+
* express (additive, multiply, per-key custom blends).
|
|
91
|
+
*
|
|
92
|
+
* @example MD3 state-layer halo
|
|
93
|
+
* ```tsx
|
|
94
|
+
* import { useGestureLayer } from '@rootnative/inertia/gesture-layer'
|
|
95
|
+
* import Animated from 'react-native-reanimated'
|
|
96
|
+
* import { Pressable } from 'react-native'
|
|
97
|
+
*
|
|
98
|
+
* function SwitchHalo({ disabled }: { disabled?: boolean }) {
|
|
99
|
+
* const { style, handlers } = useGestureLayer(
|
|
100
|
+
* {
|
|
101
|
+
* rest: { opacity: 0, backgroundColor: 'transparent' },
|
|
102
|
+
* hovered: { opacity: 0.08, backgroundColor: '#000' },
|
|
103
|
+
* focused: { opacity: 0.10, backgroundColor: '#000' },
|
|
104
|
+
* pressed: { opacity: 0.12, backgroundColor: '#000' },
|
|
105
|
+
* },
|
|
106
|
+
* { disabled, transition: { type: 'timing', duration: 150 } },
|
|
107
|
+
* )
|
|
108
|
+
*
|
|
109
|
+
* return (
|
|
110
|
+
* <Pressable {...handlers}>
|
|
111
|
+
* <Animated.View style={style} />
|
|
112
|
+
* </Pressable>
|
|
113
|
+
* )
|
|
114
|
+
* }
|
|
115
|
+
* ```
|
|
116
|
+
*/
|
|
117
|
+
declare function useGestureLayer(states: GestureLayerStates, options?: UseGestureLayerOptions): UseGestureLayerResult;
|
|
118
|
+
|
|
119
|
+
export { type GestureLayerStates, type GestureLayerStyle, type UseGestureLayerOptions, type UseGestureLayerResult, useGestureLayer };
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { AnimatedStyle } from 'react-native-reanimated';
|
|
2
|
+
import { U as UseGestureHandlers } from '../useGesture-BnY65PlQ.js';
|
|
3
|
+
import { T as TransitionConfig, h as GestureLayerTransitions } from '../types-cU43dEmH.js';
|
|
4
|
+
import 'react';
|
|
5
|
+
import 'react-native';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* A single gesture-layer style — a flat map of style keys to a value. Numeric
|
|
9
|
+
* values participate in clamped-max composition (the "strongest active layer
|
|
10
|
+
* wins" model used by MD3 state-layer haloes); string values are treated as
|
|
11
|
+
* colors and composed via priority cascade with `interpolateColor`.
|
|
12
|
+
*
|
|
13
|
+
* The hook does not validate that string values are valid colors — passing
|
|
14
|
+
* something like `borderStyle: 'solid'` will crash inside the worklet. Keep
|
|
15
|
+
* string values to color strings.
|
|
16
|
+
*/
|
|
17
|
+
type GestureLayerStyle = {
|
|
18
|
+
[key: string]: number | string | undefined;
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Per-state style maps. Every key is optional; missing layers default to
|
|
22
|
+
* `rest` (or `0` / `'transparent'` if `rest` is also absent for that key).
|
|
23
|
+
*
|
|
24
|
+
* - `rest` — base values, applied when no other layer is active.
|
|
25
|
+
* - `hovered` / `focused` / `focusVisible` / `pressed` — gesture-driven
|
|
26
|
+
* states tracked via the underlying `useGesture` hook. Each owns an
|
|
27
|
+
* independent 0↔1 progress that fades the layer in/out per the configured
|
|
28
|
+
* transition.
|
|
29
|
+
* - `disabled` — gated by the JS-side `options.disabled` flag rather than a
|
|
30
|
+
* gesture. Sits at the top of the priority cascade and overrides every
|
|
31
|
+
* gesture layer when active.
|
|
32
|
+
*/
|
|
33
|
+
interface GestureLayerStates {
|
|
34
|
+
rest?: GestureLayerStyle;
|
|
35
|
+
hovered?: GestureLayerStyle;
|
|
36
|
+
focused?: GestureLayerStyle;
|
|
37
|
+
focusVisible?: GestureLayerStyle;
|
|
38
|
+
pressed?: GestureLayerStyle;
|
|
39
|
+
disabled?: GestureLayerStyle;
|
|
40
|
+
}
|
|
41
|
+
interface UseGestureLayerOptions {
|
|
42
|
+
/**
|
|
43
|
+
* When `true`, the `disabled` layer becomes active (or `rest` if `disabled`
|
|
44
|
+
* is undefined). Animates via the top-level transition or the library
|
|
45
|
+
* default spring; per-layer transitions (`GestureLayerTransitions`) do not
|
|
46
|
+
* apply to `disabled`.
|
|
47
|
+
*/
|
|
48
|
+
disabled?: boolean;
|
|
49
|
+
/**
|
|
50
|
+
* Transition forwarded to the underlying `useGesture` hook. Either a single
|
|
51
|
+
* `TransitionConfig` for every gesture layer, or a `GestureLayerTransitions`
|
|
52
|
+
* map for per-layer fades. Reduced motion collapses every transition to
|
|
53
|
+
* `no-animation`.
|
|
54
|
+
*/
|
|
55
|
+
transition?: TransitionConfig | GestureLayerTransitions;
|
|
56
|
+
}
|
|
57
|
+
interface UseGestureLayerResult {
|
|
58
|
+
/**
|
|
59
|
+
* Animated style produced by `useAnimatedStyle` — spread on an
|
|
60
|
+
* `Animated.View` or pass through `<Motion.View style={...} />`.
|
|
61
|
+
*/
|
|
62
|
+
style: AnimatedStyle<Record<string, unknown>>;
|
|
63
|
+
/** Handlers to spread on the receiving `Pressable`. */
|
|
64
|
+
handlers: UseGestureHandlers;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* A "strongest active layer wins" interactive-feedback primitive. Sits one
|
|
68
|
+
* step above `useGesture()` — the consumer supplies the per-state target
|
|
69
|
+
* values, the hook handles the four gesture progress shared values, the
|
|
70
|
+
* disabled override, the worklet, and the transition.
|
|
71
|
+
*
|
|
72
|
+
* Composition model:
|
|
73
|
+
*
|
|
74
|
+
* - **Numeric keys** (opacity, scale, borderWidth, etc.) compose via
|
|
75
|
+
* clamped-max with `rest` as the floor:
|
|
76
|
+
* `out = max(rest, ...for each active gesture layer: lerp(rest, layer, progress))`.
|
|
77
|
+
* This matches the MD3 state-layer halo pattern — multiple states active
|
|
78
|
+
* simultaneously raise the value to the strongest, not the sum.
|
|
79
|
+
* - **Color keys** (any string value) compose via priority cascade with
|
|
80
|
+
* `interpolateColor`, lowest priority first: `hovered → focused →
|
|
81
|
+
* focusVisible → pressed`. Clamped-max doesn't apply to colors; this
|
|
82
|
+
* matches the cascade used by the declarative `gesture` prop.
|
|
83
|
+
* - **Disabled** sits at the top of the cascade for both numeric and color
|
|
84
|
+
* keys — when active, it lerps the composed value toward the `disabled`
|
|
85
|
+
* target.
|
|
86
|
+
*
|
|
87
|
+
* Reach for this when you want MD3 / iOS-translucent state-layer overlays
|
|
88
|
+
* without rewriting the worklet by hand for every consumer; reach for plain
|
|
89
|
+
* `useGesture()` when you need a composition model this hook doesn't
|
|
90
|
+
* express (additive, multiply, per-key custom blends).
|
|
91
|
+
*
|
|
92
|
+
* @example MD3 state-layer halo
|
|
93
|
+
* ```tsx
|
|
94
|
+
* import { useGestureLayer } from '@rootnative/inertia/gesture-layer'
|
|
95
|
+
* import Animated from 'react-native-reanimated'
|
|
96
|
+
* import { Pressable } from 'react-native'
|
|
97
|
+
*
|
|
98
|
+
* function SwitchHalo({ disabled }: { disabled?: boolean }) {
|
|
99
|
+
* const { style, handlers } = useGestureLayer(
|
|
100
|
+
* {
|
|
101
|
+
* rest: { opacity: 0, backgroundColor: 'transparent' },
|
|
102
|
+
* hovered: { opacity: 0.08, backgroundColor: '#000' },
|
|
103
|
+
* focused: { opacity: 0.10, backgroundColor: '#000' },
|
|
104
|
+
* pressed: { opacity: 0.12, backgroundColor: '#000' },
|
|
105
|
+
* },
|
|
106
|
+
* { disabled, transition: { type: 'timing', duration: 150 } },
|
|
107
|
+
* )
|
|
108
|
+
*
|
|
109
|
+
* return (
|
|
110
|
+
* <Pressable {...handlers}>
|
|
111
|
+
* <Animated.View style={style} />
|
|
112
|
+
* </Pressable>
|
|
113
|
+
* )
|
|
114
|
+
* }
|
|
115
|
+
* ```
|
|
116
|
+
*/
|
|
117
|
+
declare function useGestureLayer(states: GestureLayerStates, options?: UseGestureLayerOptions): UseGestureLayerResult;
|
|
118
|
+
|
|
119
|
+
export { type GestureLayerStates, type GestureLayerStyle, type UseGestureLayerOptions, type UseGestureLayerResult, useGestureLayer };
|