@rootnative/inertia 0.0.9 → 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.
- package/CHANGELOG.md +95 -1
- package/README.md +8 -8
- package/dist/{chunk-MFAFB4K7.mjs → chunk-2Q6JMDYZ.mjs} +8 -10
- package/dist/chunk-2UREDR4Z.js +8 -0
- package/dist/{chunk-ZBHQPVWE.js → chunk-3PB27UIE.js} +2 -2
- package/dist/{chunk-IX6SEOSK.mjs → chunk-5EYIZG72.mjs} +60 -45
- package/dist/{chunk-EN4PN7X3.mjs → chunk-7RMSLKQQ.mjs} +1 -1
- package/dist/{chunk-6YIUETBF.mjs → chunk-DT2KVAAO.mjs} +1 -1
- package/dist/{chunk-L4JFECXU.mjs → chunk-FKJLPYGY.mjs} +6 -3
- package/dist/{chunk-E3ALFSH2.js → chunk-HBURWGCZ.js} +86 -71
- package/dist/chunk-JAJHKJC5.js +8 -0
- package/dist/chunk-JBMTTICC.js +8 -0
- package/dist/{chunk-GSUC4HWP.mjs → chunk-JGQ6YRDN.mjs} +1 -1
- package/dist/chunk-KFHZMFAX.js +23 -0
- package/dist/{chunk-M4766VUV.mjs → chunk-LHW4VNWD.mjs} +1 -1
- package/dist/{chunk-MUT6BTZS.js → chunk-NTG6PSGZ.js} +10 -7
- package/dist/chunk-OH46TGEA.mjs +20 -0
- package/dist/{chunk-6AN5X3YE.js → chunk-QDMSFRQR.js} +10 -11
- package/dist/{chunk-W7NTRSPD.mjs → chunk-QIRKPDXA.mjs} +118 -68
- package/dist/{chunk-WNVHPMBI.js → chunk-QX5JHRZ2.js} +118 -66
- package/dist/{chunk-6NOCY764.js → chunk-R7YD6L3D.js} +2 -2
- package/dist/{chunk-JBK5ZLMH.mjs → chunk-RO4XFTQ6.mjs} +1 -1
- package/dist/{chunk-QNLSCOR5.mjs → chunk-UGYBFAVY.mjs} +1 -1
- package/dist/{chunk-IHVMNOS4.js → chunk-XLKI2MSD.js} +2 -2
- package/dist/gestureLayer/index.d.mts +3 -3
- package/dist/gestureLayer/index.d.ts +3 -3
- package/dist/gestureLayer/index.js +12 -18
- package/dist/gestureLayer/index.mjs +5 -11
- package/dist/index.d.mts +108 -49
- package/dist/index.d.ts +108 -49
- package/dist/index.js +114 -91
- package/dist/index.mjs +66 -59
- package/dist/motion/FlatList.d.mts +2 -1
- package/dist/motion/FlatList.d.ts +2 -1
- package/dist/motion/FlatList.js +5 -5
- package/dist/motion/FlatList.mjs +4 -4
- package/dist/motion/Image.d.mts +2 -1
- package/dist/motion/Image.d.ts +2 -1
- package/dist/motion/Image.js +5 -5
- package/dist/motion/Image.mjs +4 -4
- package/dist/motion/Pressable.d.mts +2 -1
- package/dist/motion/Pressable.d.ts +2 -1
- package/dist/motion/Pressable.js +5 -5
- package/dist/motion/Pressable.mjs +4 -4
- package/dist/motion/ScrollView.d.mts +2 -1
- package/dist/motion/ScrollView.d.ts +2 -1
- package/dist/motion/ScrollView.js +5 -5
- package/dist/motion/ScrollView.mjs +4 -4
- package/dist/motion/Text.d.mts +2 -1
- package/dist/motion/Text.d.ts +2 -1
- package/dist/motion/Text.js +5 -5
- package/dist/motion/Text.mjs +4 -4
- package/dist/motion/View.d.mts +2 -1
- package/dist/motion/View.d.ts +2 -1
- package/dist/motion/View.js +5 -5
- package/dist/motion/View.mjs +4 -4
- package/dist/touch/index.d.mts +5 -3
- package/dist/touch/index.d.ts +5 -3
- package/dist/touch/index.js +16 -31
- package/dist/touch/index.mjs +11 -29
- package/dist/{types-DyJpG64F.d.mts → types-C5Zbs9MC.d.mts} +22 -4
- package/dist/{types-DyJpG64F.d.ts → types-C5Zbs9MC.d.ts} +22 -4
- package/dist/{useGesture-DbH46EBp.d.ts → useGesture-Be0ZhCWj.d.ts} +1 -1
- package/dist/{useGesture-CnZQnYHH.d.mts → useGesture-Ve8EGjTr.d.mts} +1 -1
- package/dist/useTranslateStyle-BDrOD97O.d.mts +58 -0
- package/dist/useTranslateStyle-BDrOD97O.d.ts +58 -0
- package/jest-preset.js +31 -2
- package/llms.txt +2 -1
- package/package.json +15 -12
- package/src/config/namedTransitions.ts +7 -9
- package/src/gestureLayer/index.ts +3 -0
- package/src/gestureLayer/useGestureLayer.ts +6 -9
- package/src/gestures/focusVisibility.ts +19 -17
- package/src/gestures/index.ts +1 -1
- package/src/index.ts +12 -0
- package/src/internal/boxShadow.ts +24 -2
- package/src/layout/resolveLayout.ts +4 -1
- package/src/layout/useSharedLayout.ts +2 -1
- package/src/motion/createMotionComponent.tsx +92 -44
- package/src/presence/Presence.tsx +5 -7
- package/src/touch/applyBounds.ts +30 -0
- package/src/touch/index.ts +1 -0
- package/src/touch/useTouchDrag.ts +18 -46
- package/src/transitions/constants.ts +14 -0
- package/src/transitions/easing.ts +23 -11
- package/src/transitions/index.ts +5 -0
- package/src/transitions/keys.ts +0 -2
- package/src/transitions/resolve.ts +86 -11
- package/src/transitions/resolveSequence.ts +45 -3
- package/src/transitions/runtime.ts +4 -7
- package/src/transitions/spring.ts +7 -2
- package/src/types.ts +23 -3
- package/src/values/extrapolation.ts +21 -0
- package/src/values/index.ts +3 -0
- package/src/values/useColorCascade.ts +3 -3
- package/src/values/useColorTransition.ts +17 -2
- package/src/values/useGesture.ts +9 -2
- package/src/values/useInterpolatedStyle.ts +13 -13
- package/src/values/useShadow.ts +36 -9
- package/src/values/useSpring.ts +34 -25
- package/src/values/useTransform.ts +24 -20
- package/src/values/useTranslateStyle.ts +47 -0
- package/dist/chunk-24NUL23E.js +0 -8
- package/dist/chunk-4XBX7KOD.js +0 -8
- package/dist/chunk-Z5USXWTE.js +0 -8
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { ViewStyle } from 'react-native';
|
|
2
|
+
import { SharedValue } from 'react-native-reanimated';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Clamp `value` to `[min, max]`. When `elastic > 0` the overshoot past a
|
|
6
|
+
* bound is scaled by `elastic` instead of hard-clamped, giving a rubber-band
|
|
7
|
+
* feel. `min` / `max` may be `undefined` to leave that side unbounded.
|
|
8
|
+
*
|
|
9
|
+
* Worklet, so it runs both inside a gesture-handler pan handler on the UI
|
|
10
|
+
* thread (`useDrag`) and from a JS-thread `PanResponder` callback
|
|
11
|
+
* (`useTouchDrag`).
|
|
12
|
+
*
|
|
13
|
+
* ```ts
|
|
14
|
+
* applyBounds(120, -100, 100, 0) // 100
|
|
15
|
+
* applyBounds(120, -100, 100, 0.5) // 110
|
|
16
|
+
* applyBounds(50, undefined, 100, 0) // 50
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
declare function applyBounds(value: number, min: number | undefined, max: number | undefined, elastic: number): number;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* What {@link useTranslateStyle} returns: a plain style object that owns
|
|
23
|
+
* `transform`.
|
|
24
|
+
*
|
|
25
|
+
* Deliberately **not** `ReturnType<typeof useAnimatedStyle>`. Reanimated 4.5
|
|
26
|
+
* brands that value (`AnimatedStyleHandle`), and a branded type is rejected
|
|
27
|
+
* inside a `StyleProp<ViewStyle>` array — so every call site would need a cast,
|
|
28
|
+
* which is the exact defect `InterpolatedStyle` was introduced to remove in
|
|
29
|
+
* `0.0.9`. The brand is compile-time only, so the runtime value is unchanged:
|
|
30
|
+
* it is still the animated style Reanimated produced.
|
|
31
|
+
*/
|
|
32
|
+
type TranslateStyle = {
|
|
33
|
+
transform: NonNullable<ViewStyle['transform']>;
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Animated style that translates by two shared values:
|
|
37
|
+
* `transform: [{ translateX: x }, { translateY: y }]`.
|
|
38
|
+
*
|
|
39
|
+
* This is the style fragment every drag-style hook returns (`useTouchDrag`,
|
|
40
|
+
* and `useDrag` / `usePan` / `useSwipe` in `@rootnative/inertia-gestures`).
|
|
41
|
+
* Use it directly when a custom gesture owns its own translation values.
|
|
42
|
+
*
|
|
43
|
+
* The style owns the whole `transform` key. Do not stack a second transform
|
|
44
|
+
* style beside it — `transform` is one key in React Native, so the later
|
|
45
|
+
* style replaces this one instead of merging. Nest another animated view, or
|
|
46
|
+
* build one style from `x` / `y` with `useInterpolatedStyle`, to add a
|
|
47
|
+
* transform.
|
|
48
|
+
*
|
|
49
|
+
* ```tsx
|
|
50
|
+
* const x = useMotionValue(0)
|
|
51
|
+
* const y = useMotionValue(0)
|
|
52
|
+
* const style = useTranslateStyle(x, y)
|
|
53
|
+
* return <Motion.View style={style} />
|
|
54
|
+
* ```
|
|
55
|
+
*/
|
|
56
|
+
declare function useTranslateStyle(x: SharedValue<number>, y: SharedValue<number>): TranslateStyle;
|
|
57
|
+
|
|
58
|
+
export { type TranslateStyle as T, applyBounds as a, useTranslateStyle as u };
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { ViewStyle } from 'react-native';
|
|
2
|
+
import { SharedValue } from 'react-native-reanimated';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Clamp `value` to `[min, max]`. When `elastic > 0` the overshoot past a
|
|
6
|
+
* bound is scaled by `elastic` instead of hard-clamped, giving a rubber-band
|
|
7
|
+
* feel. `min` / `max` may be `undefined` to leave that side unbounded.
|
|
8
|
+
*
|
|
9
|
+
* Worklet, so it runs both inside a gesture-handler pan handler on the UI
|
|
10
|
+
* thread (`useDrag`) and from a JS-thread `PanResponder` callback
|
|
11
|
+
* (`useTouchDrag`).
|
|
12
|
+
*
|
|
13
|
+
* ```ts
|
|
14
|
+
* applyBounds(120, -100, 100, 0) // 100
|
|
15
|
+
* applyBounds(120, -100, 100, 0.5) // 110
|
|
16
|
+
* applyBounds(50, undefined, 100, 0) // 50
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
declare function applyBounds(value: number, min: number | undefined, max: number | undefined, elastic: number): number;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* What {@link useTranslateStyle} returns: a plain style object that owns
|
|
23
|
+
* `transform`.
|
|
24
|
+
*
|
|
25
|
+
* Deliberately **not** `ReturnType<typeof useAnimatedStyle>`. Reanimated 4.5
|
|
26
|
+
* brands that value (`AnimatedStyleHandle`), and a branded type is rejected
|
|
27
|
+
* inside a `StyleProp<ViewStyle>` array — so every call site would need a cast,
|
|
28
|
+
* which is the exact defect `InterpolatedStyle` was introduced to remove in
|
|
29
|
+
* `0.0.9`. The brand is compile-time only, so the runtime value is unchanged:
|
|
30
|
+
* it is still the animated style Reanimated produced.
|
|
31
|
+
*/
|
|
32
|
+
type TranslateStyle = {
|
|
33
|
+
transform: NonNullable<ViewStyle['transform']>;
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Animated style that translates by two shared values:
|
|
37
|
+
* `transform: [{ translateX: x }, { translateY: y }]`.
|
|
38
|
+
*
|
|
39
|
+
* This is the style fragment every drag-style hook returns (`useTouchDrag`,
|
|
40
|
+
* and `useDrag` / `usePan` / `useSwipe` in `@rootnative/inertia-gestures`).
|
|
41
|
+
* Use it directly when a custom gesture owns its own translation values.
|
|
42
|
+
*
|
|
43
|
+
* The style owns the whole `transform` key. Do not stack a second transform
|
|
44
|
+
* style beside it — `transform` is one key in React Native, so the later
|
|
45
|
+
* style replaces this one instead of merging. Nest another animated view, or
|
|
46
|
+
* build one style from `x` / `y` with `useInterpolatedStyle`, to add a
|
|
47
|
+
* transform.
|
|
48
|
+
*
|
|
49
|
+
* ```tsx
|
|
50
|
+
* const x = useMotionValue(0)
|
|
51
|
+
* const y = useMotionValue(0)
|
|
52
|
+
* const style = useTranslateStyle(x, y)
|
|
53
|
+
* return <Motion.View style={style} />
|
|
54
|
+
* ```
|
|
55
|
+
*/
|
|
56
|
+
declare function useTranslateStyle(x: SharedValue<number>, y: SharedValue<number>): TranslateStyle;
|
|
57
|
+
|
|
58
|
+
export { type TranslateStyle as T, applyBounds as a, useTranslateStyle as u };
|
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
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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",
|
|
@@ -125,27 +125,30 @@
|
|
|
125
125
|
"!**/*.test.*"
|
|
126
126
|
],
|
|
127
127
|
"peerDependencies": {
|
|
128
|
-
"react": ">=
|
|
129
|
-
"react
|
|
130
|
-
"react-native
|
|
131
|
-
"react-native-
|
|
128
|
+
"@testing-library/react-native": ">=13.0.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"
|
|
132
133
|
},
|
|
133
134
|
"peerDependenciesMeta": {
|
|
134
|
-
"react-native
|
|
135
|
+
"@testing-library/react-native": {
|
|
135
136
|
"optional": true
|
|
136
137
|
}
|
|
137
138
|
},
|
|
138
139
|
"devDependencies": {
|
|
139
|
-
"@react-native/babel-preset": "^0.
|
|
140
|
+
"@react-native/babel-preset": "^0.86.3",
|
|
141
|
+
"@react-native/jest-preset": "^0.86.3",
|
|
140
142
|
"@size-limit/preset-small-lib": "^11.1.0",
|
|
141
143
|
"@testing-library/react-native": "^13.3.3",
|
|
142
144
|
"@types/jest": "^29.5.14",
|
|
143
|
-
"@types/react": "^19.
|
|
145
|
+
"@types/react": "^19.2.0",
|
|
144
146
|
"jest": "^29.7.0",
|
|
145
|
-
"react": "19.
|
|
146
|
-
"react-native": "0.
|
|
147
|
-
"react-native-reanimated": "
|
|
148
|
-
"react-
|
|
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",
|
|
149
152
|
"size-limit": "^11.1.0",
|
|
150
153
|
"tsup": "^8.3.5",
|
|
151
154
|
"typescript": "^5.7.3"
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { warnOnce } from '../internal/warnOnce'
|
|
1
2
|
import { isTopLevelTransition } from '../transitions/keys'
|
|
2
3
|
import {
|
|
3
4
|
type NamedTransitions,
|
|
@@ -6,8 +7,6 @@ import {
|
|
|
6
7
|
type TransitionInput,
|
|
7
8
|
} from '../types'
|
|
8
9
|
|
|
9
|
-
declare const __DEV__: boolean
|
|
10
|
-
|
|
11
10
|
/**
|
|
12
11
|
* Fallback used when a name isn't found in the registry: the library default
|
|
13
12
|
* spring, matching what an omitted transition resolves to everywhere else.
|
|
@@ -26,13 +25,12 @@ export function lookupNamedTransition(
|
|
|
26
25
|
): TransitionConfig {
|
|
27
26
|
const cfg = registry[name]
|
|
28
27
|
if (cfg) return cfg
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
}
|
|
28
|
+
warnOnce(
|
|
29
|
+
`unknown-transition:${name}`,
|
|
30
|
+
`[inertia] Unknown transition name "${name}" — falling back to the ` +
|
|
31
|
+
`default spring. Register it on a provider: ` +
|
|
32
|
+
`<MotionConfig transitions={{ '${name}': { ... } }}>.`,
|
|
33
|
+
)
|
|
36
34
|
return UNKNOWN_NAME_FALLBACK
|
|
37
35
|
}
|
|
38
36
|
|
|
@@ -20,3 +20,6 @@ export type {
|
|
|
20
20
|
UseGestureLayerOptions,
|
|
21
21
|
UseGestureLayerResult,
|
|
22
22
|
} from './useGestureLayer'
|
|
23
|
+
// The per-layer form of `UseGestureLayerOptions.transition`. Re-exported here
|
|
24
|
+
// so a consumer of this subpath can name it without also importing the root.
|
|
25
|
+
export type { GestureLayerTransitions } from '../types'
|
|
@@ -210,7 +210,10 @@ export function useGestureLayer(
|
|
|
210
210
|
// JS-thread precompute: union of keys across all layers, per-key type
|
|
211
211
|
// (number vs color), and a rest-fallback table. The worklet body reads
|
|
212
212
|
// from `meta` instead of probing each layer per frame — the type check
|
|
213
|
-
// only runs when
|
|
213
|
+
// only runs when the layers change structurally. Keyed on a signature, not
|
|
214
|
+
// on object identity: `states` is usually an inline literal, and a fresh
|
|
215
|
+
// `meta` each render would rebuild the animated style's worklet.
|
|
216
|
+
const statesSig = stableSig(states)
|
|
214
217
|
const meta = useMemo(() => {
|
|
215
218
|
const layers = {
|
|
216
219
|
rest: states.rest,
|
|
@@ -251,14 +254,8 @@ export function useGestureLayer(
|
|
|
251
254
|
restRaw !== undefined ? restRaw : isColor ? 'transparent' : 0
|
|
252
255
|
}
|
|
253
256
|
return { layers, keys, types, restValues }
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
states.hovered,
|
|
257
|
-
states.focused,
|
|
258
|
-
states.focusVisible,
|
|
259
|
-
states.pressed,
|
|
260
|
-
states.disabled,
|
|
261
|
-
])
|
|
257
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
258
|
+
}, [statesSig])
|
|
262
259
|
|
|
263
260
|
const style = useAnimatedStyle(() => {
|
|
264
261
|
const { layers, keys, types, restValues } = meta
|
|
@@ -12,15 +12,16 @@ import { Platform } from 'react-native'
|
|
|
12
12
|
* arrives via D-pad, screen reader, or hardware keyboard, all of which are
|
|
13
13
|
* keyboard-equivalent — so `isFocusVisible()` is unconditionally `true`.
|
|
14
14
|
*
|
|
15
|
-
* The web listeners attach
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
15
|
+
* The web listeners attach when the first component that tracks
|
|
16
|
+
* `focusVisible` mounts (`installFocusVisibility`, called from a mount
|
|
17
|
+
* effect), in the capture phase so they run before the focus event reaches
|
|
18
|
+
* the focused element, and they stay installed for the lifetime of the
|
|
19
|
+
* document. Mount-time installation is early enough: the click that focuses
|
|
20
|
+
* a gesture-wired element can only land on an element that is already
|
|
21
|
+
* mounted, so its `mousedown` is observed. The listeners are not attached at
|
|
22
|
+
* import time — `@rootnative/inertia` declares `sideEffects: false`, and an
|
|
23
|
+
* import-time listener would make that declaration false. They are passive
|
|
24
|
+
* and idle-cheap; the cost is one boolean read per `onFocus` dispatch.
|
|
24
25
|
*/
|
|
25
26
|
|
|
26
27
|
type InputModality = 'keyboard' | 'pointer'
|
|
@@ -38,7 +39,14 @@ function setPointer() {
|
|
|
38
39
|
modality = 'pointer'
|
|
39
40
|
}
|
|
40
41
|
|
|
41
|
-
|
|
42
|
+
/**
|
|
43
|
+
* Attach the document listeners that track input modality on web. Idempotent
|
|
44
|
+
* and a no-op on native or without a `document`. Call it from a mount effect
|
|
45
|
+
* of any component that reads `isFocusVisible()`, so the pointer event that
|
|
46
|
+
* precedes the first focus is observed (see module doc above).
|
|
47
|
+
* `isFocusVisible` also calls it as a safety net.
|
|
48
|
+
*/
|
|
49
|
+
export function installFocusVisibility(): void {
|
|
42
50
|
if (installed) return
|
|
43
51
|
if (Platform.OS !== 'web') return
|
|
44
52
|
if (typeof document === 'undefined') return
|
|
@@ -49,12 +57,6 @@ function ensureInstalled(): void {
|
|
|
49
57
|
installed = true
|
|
50
58
|
}
|
|
51
59
|
|
|
52
|
-
// Install at import time so the pointer event that precedes the first focus
|
|
53
|
-
// is observed (see module doc above). `ensureInstalled` stays in
|
|
54
|
-
// `isFocusVisible` as a safety net for environments where `document` appears
|
|
55
|
-
// after import.
|
|
56
|
-
ensureInstalled()
|
|
57
|
-
|
|
58
60
|
/**
|
|
59
61
|
* `true` if the next `onFocus` should be treated as "focus-visible" (keyboard
|
|
60
62
|
* focus). On native, always `true`. On web, reflects the most recent user
|
|
@@ -62,7 +64,7 @@ ensureInstalled()
|
|
|
62
64
|
*/
|
|
63
65
|
export function isFocusVisible(): boolean {
|
|
64
66
|
if (Platform.OS !== 'web') return true
|
|
65
|
-
|
|
67
|
+
installFocusVisibility()
|
|
66
68
|
return modality === 'keyboard'
|
|
67
69
|
}
|
|
68
70
|
|
package/src/gestures/index.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { isFocusVisible } from './focusVisibility'
|
|
1
|
+
export { installFocusVisibility, isFocusVisible } from './focusVisibility'
|
package/src/index.ts
CHANGED
|
@@ -42,12 +42,18 @@ export {
|
|
|
42
42
|
resolveTransition,
|
|
43
43
|
resolveAnimatableValue,
|
|
44
44
|
ensureWorkletEasing,
|
|
45
|
+
isTopLevelTransition,
|
|
46
|
+
stableSig,
|
|
45
47
|
type AnimationCallback,
|
|
48
|
+
type CallbackFactory,
|
|
46
49
|
} from './transitions'
|
|
47
50
|
// Seed value for any color shared value a custom animated component drives
|
|
48
51
|
// through `resolveTransition`. Exported because the obvious choice —
|
|
49
52
|
// `'transparent'` — cannot be animated away from; see the symbol's own docs.
|
|
50
53
|
export { TRANSPARENT } from './internal/color'
|
|
54
|
+
// Worklet-safe clamp shared by `useTouchDrag` and the gesture adapters'
|
|
55
|
+
// `useDrag`; exported so a custom drag can apply the same rubber-band rule.
|
|
56
|
+
export { applyBounds } from './touch/applyBounds'
|
|
51
57
|
export {
|
|
52
58
|
useAnimation,
|
|
53
59
|
useAnimator,
|
|
@@ -61,6 +67,7 @@ export {
|
|
|
61
67
|
useShadow,
|
|
62
68
|
useSpring,
|
|
63
69
|
useTransform,
|
|
70
|
+
useTranslateStyle,
|
|
64
71
|
useVariants,
|
|
65
72
|
} from './values'
|
|
66
73
|
// The value-layer hooks above all return Reanimated `SharedValue`s — export
|
|
@@ -72,12 +79,15 @@ export type {
|
|
|
72
79
|
Animator,
|
|
73
80
|
BoxShadowLayer,
|
|
74
81
|
ColorCascadeLayer,
|
|
82
|
+
ColorStyle,
|
|
75
83
|
ColorStyleKey,
|
|
76
84
|
ExtrapolationMode,
|
|
77
85
|
InterpolatedStyle,
|
|
78
86
|
InterpolatedStyleMap,
|
|
87
|
+
TranslateStyle,
|
|
79
88
|
NumericStyleKey,
|
|
80
89
|
ShadowConfig,
|
|
90
|
+
ShadowStyle,
|
|
81
91
|
TransformKey,
|
|
82
92
|
UseColorCascadeOptions,
|
|
83
93
|
UseColorTransitionOptions,
|
|
@@ -97,8 +107,10 @@ export type {
|
|
|
97
107
|
EasingFunction,
|
|
98
108
|
EasingFunctionFactory,
|
|
99
109
|
EasingInput,
|
|
110
|
+
GestureLayerTransitions,
|
|
100
111
|
GestureSubStates,
|
|
101
112
|
MotionComponent,
|
|
113
|
+
MotionComponentProps,
|
|
102
114
|
MotionProps,
|
|
103
115
|
NamedTransitions,
|
|
104
116
|
NoAnimationTransition,
|
|
@@ -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
|
|
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
|
-
|
|
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
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { LinearTransition } from 'react-native-reanimated'
|
|
2
|
+
import { DEFAULT_LAYOUT_DURATION } from '../transitions/constants'
|
|
2
3
|
import { ensureWorkletEasing } from '../transitions/easing'
|
|
3
4
|
import { DEFAULT_SPRING, springToReanimated } from '../transitions/spring'
|
|
4
5
|
import { type TransitionConfig } from '../types'
|
|
@@ -34,7 +35,9 @@ export function resolveLayoutTransition(
|
|
|
34
35
|
if (cfg.type === 'no-animation') return undefined
|
|
35
36
|
|
|
36
37
|
if (cfg.type === 'timing') {
|
|
37
|
-
let builder = LinearTransition.duration(
|
|
38
|
+
let builder = LinearTransition.duration(
|
|
39
|
+
cfg.duration ?? DEFAULT_LAYOUT_DURATION,
|
|
40
|
+
)
|
|
38
41
|
const easing = ensureWorkletEasing(cfg.easing)
|
|
39
42
|
if (easing) builder = builder.easing(easing)
|
|
40
43
|
if (cfg.delay) builder = builder.delay(cfg.delay)
|
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
withSpring,
|
|
16
16
|
withTiming,
|
|
17
17
|
} from 'react-native-reanimated'
|
|
18
|
+
import { DEFAULT_LAYOUT_DURATION } from '../transitions/constants'
|
|
18
19
|
import { DEFAULT_SPRING, springToReanimated } from '../transitions/spring'
|
|
19
20
|
import { type SpringTransition, type TransitionConfig } from '../types'
|
|
20
21
|
import { measureWindowRect } from './measureWindow'
|
|
@@ -334,7 +335,7 @@ function legBuilder(
|
|
|
334
335
|
transition: TransitionConfig | undefined,
|
|
335
336
|
): (from: number, to: number) => number {
|
|
336
337
|
if (transition?.type === 'timing') {
|
|
337
|
-
const duration = transition.duration ??
|
|
338
|
+
const duration = transition.duration ?? DEFAULT_LAYOUT_DURATION
|
|
338
339
|
return (from, to) =>
|
|
339
340
|
withSequence(
|
|
340
341
|
withTiming(from, { duration: 0 }),
|