@onlynative/inertia 0.0.1-alpha.2 → 0.0.1-alpha.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/README.md +29 -2
- package/dist/index.d.mts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +170 -58
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +171 -59
- package/dist/index.mjs.map +1 -1
- package/dist/motion/Image.d.mts +1 -1
- package/dist/motion/Image.d.ts +1 -1
- package/dist/motion/Image.js +170 -58
- package/dist/motion/Image.js.map +1 -1
- package/dist/motion/Image.mjs +171 -59
- package/dist/motion/Image.mjs.map +1 -1
- package/dist/motion/Pressable.d.mts +1 -1
- package/dist/motion/Pressable.d.ts +1 -1
- package/dist/motion/Pressable.js +170 -58
- package/dist/motion/Pressable.js.map +1 -1
- package/dist/motion/Pressable.mjs +171 -59
- package/dist/motion/Pressable.mjs.map +1 -1
- package/dist/motion/ScrollView.d.mts +1 -1
- package/dist/motion/ScrollView.d.ts +1 -1
- package/dist/motion/ScrollView.js +170 -58
- package/dist/motion/ScrollView.js.map +1 -1
- package/dist/motion/ScrollView.mjs +171 -59
- package/dist/motion/ScrollView.mjs.map +1 -1
- package/dist/motion/Text.d.mts +1 -1
- package/dist/motion/Text.d.ts +1 -1
- package/dist/motion/Text.js +170 -58
- package/dist/motion/Text.js.map +1 -1
- package/dist/motion/Text.mjs +171 -59
- package/dist/motion/Text.mjs.map +1 -1
- package/dist/motion/View.d.mts +1 -1
- package/dist/motion/View.d.ts +1 -1
- package/dist/motion/View.js +170 -58
- package/dist/motion/View.js.map +1 -1
- package/dist/motion/View.mjs +171 -59
- package/dist/motion/View.mjs.map +1 -1
- package/dist/{types-DeZZzE_e.d.mts → types-DAhX3fC2.d.mts} +40 -16
- package/dist/{types-DeZZzE_e.d.ts → types-DAhX3fC2.d.ts} +40 -16
- package/llms.txt +25 -2
- package/package.json +1 -1
- package/src/motion/createMotionComponent.tsx +258 -97
- package/src/motion/installCheck.ts +69 -0
- package/src/types.ts +44 -16
package/README.md
CHANGED
|
@@ -42,7 +42,7 @@ export function FadeIn() {
|
|
|
42
42
|
- **Primitives** — `Motion.View`, `Motion.Text`, `Motion.Image`, `Motion.Pressable`, `Motion.ScrollView`. Per-primitive style inference (no shared `ViewStyle & TextStyle & ImageStyle` fallback).
|
|
43
43
|
- **Sequences and keyframes** — `animate={{ x: [0, 100, 0] }}` with per-step transitions; unified `repeat: number | 'infinite' | { count, alternate }`.
|
|
44
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 }}`. `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.
|
|
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
46
|
- **`<Presence>`** — mount/unmount transitions; exiting children automatically receive `pointerEvents: 'none'`.
|
|
47
47
|
- **`<MotionConfig reducedMotion>`** — OS reduce-motion honored end-to-end (`'user' | 'never' | 'always'`).
|
|
48
48
|
- **Per-primitive subpath imports** — `@onlynative/inertia/view`, `/text`, `/image`, `/pressable`, `/scroll-view`.
|
|
@@ -58,7 +58,13 @@ import { MotionPressable } from '@onlynative/inertia/pressable'
|
|
|
58
58
|
import { MotionScrollView } from '@onlynative/inertia/scroll-view'
|
|
59
59
|
```
|
|
60
60
|
|
|
61
|
-
|
|
61
|
+
Or the barrel — same primitives, named imports tree-shake cleanly because the package is `sideEffects: false`:
|
|
62
|
+
|
|
63
|
+
```ts
|
|
64
|
+
import { MotionView } from '@onlynative/inertia'
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Both forms land at ~4.1–4.2 kB brotlied for a single primitive (peers excluded). The full namespace (`import { Motion } from '@onlynative/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.
|
|
62
68
|
|
|
63
69
|
## Transitions
|
|
64
70
|
|
|
@@ -71,12 +77,33 @@ A `Motion.View`-only import currently bundles to ~3.2 kB brotlied (excluding pee
|
|
|
71
77
|
|
|
72
78
|
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.
|
|
73
79
|
|
|
80
|
+
## Caveats
|
|
81
|
+
|
|
82
|
+
- **`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://onlynative.github.io/inertia/docs/primitives/pressable#style-must-be-a-value-not-a-function).
|
|
83
|
+
- **`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.
|
|
84
|
+
|
|
74
85
|
## Animatable properties
|
|
75
86
|
|
|
76
87
|
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.
|
|
77
88
|
|
|
78
89
|
Out of scope for `0.x`: SVG path morphing, gradient interpolation, shared-element transitions across screens.
|
|
79
90
|
|
|
91
|
+
## When not to use the core package alone
|
|
92
|
+
|
|
93
|
+
The `gesture` prop in `@onlynative/inertia` covers `pressed` / `focused` / `focusVisible` / `hovered` — the Pressable-shaped sub-states. **Continuous, value-bearing gestures live in the adapter package** [`@onlynative/inertia-gestures`](../gestures), which wraps `react-native-gesture-handler` and ships:
|
|
94
|
+
|
|
95
|
+
- `useDrag` — one- or two-axis drag with optional constraints and rubber-band elasticity
|
|
96
|
+
- `usePan` — camera-style pan with momentum on release
|
|
97
|
+
- `useSwipe` — directional commit-or-snap-back (distance + velocity thresholds)
|
|
98
|
+
|
|
99
|
+
If your screen needs a thumb that follows the finger, a sheet that flicks closed, a carousel with momentum, or any gesture that produces a value other than "active / inactive" — install the adapter:
|
|
100
|
+
|
|
101
|
+
```sh
|
|
102
|
+
pnpm add @onlynative/inertia-gestures react-native-gesture-handler
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
A fully gesture-driven `Slider` (continuous thumb tracking + range clamping) is the canonical example: the core package can't build it on its own, the adapter can. This is intentional — keeping `react-native-gesture-handler` out of the core peer set means apps that animate buttons and sheets don't pay for a gesture engine they never invoke.
|
|
106
|
+
|
|
80
107
|
## Documentation
|
|
81
108
|
|
|
82
109
|
Full docs, every primitive's example screen, and an `llms-full.txt` reference live at:
|
package/dist/index.d.mts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
2
|
import { ComponentType, ReactNode } from 'react';
|
|
3
3
|
import * as react_native from 'react-native';
|
|
4
|
-
import { M as MotionComponent, A as AnimatableValue, T as TransitionConfig, V as VariantController } from './types-
|
|
5
|
-
export { a as AnimateStyle, b as AnimationCallbackInfo, D as DecayTransition, G as GestureSubStates, c as MotionProps, N as NoAnimationTransition, P as PerPropertyTransition, R as RepeatConfig, S as SequenceStep, d as SpringTransition, e as TimingTransition, f as Transition, g as VariantsMap } from './types-
|
|
4
|
+
import { M as MotionComponent, A as AnimatableValue, T as TransitionConfig, V as VariantController } from './types-DAhX3fC2.mjs';
|
|
5
|
+
export { a as AnimateStyle, b as AnimationCallbackInfo, D as DecayTransition, G as GestureSubStates, c as MotionProps, N as NoAnimationTransition, P as PerPropertyTransition, R as RepeatConfig, S as SequenceStep, d as SpringTransition, e as TimingTransition, f as Transition, g as VariantsMap } from './types-DAhX3fC2.mjs';
|
|
6
6
|
export { MotionImage } from './motion/Image.mjs';
|
|
7
7
|
export { MotionPressable } from './motion/Pressable.mjs';
|
|
8
8
|
export { MotionScrollView } from './motion/ScrollView.mjs';
|
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
2
|
import { ComponentType, ReactNode } from 'react';
|
|
3
3
|
import * as react_native from 'react-native';
|
|
4
|
-
import { M as MotionComponent, A as AnimatableValue, T as TransitionConfig, V as VariantController } from './types-
|
|
5
|
-
export { a as AnimateStyle, b as AnimationCallbackInfo, D as DecayTransition, G as GestureSubStates, c as MotionProps, N as NoAnimationTransition, P as PerPropertyTransition, R as RepeatConfig, S as SequenceStep, d as SpringTransition, e as TimingTransition, f as Transition, g as VariantsMap } from './types-
|
|
4
|
+
import { M as MotionComponent, A as AnimatableValue, T as TransitionConfig, V as VariantController } from './types-DAhX3fC2.js';
|
|
5
|
+
export { a as AnimateStyle, b as AnimationCallbackInfo, D as DecayTransition, G as GestureSubStates, c as MotionProps, N as NoAnimationTransition, P as PerPropertyTransition, R as RepeatConfig, S as SequenceStep, d as SpringTransition, e as TimingTransition, f as Transition, g as VariantsMap } from './types-DAhX3fC2.js';
|
|
6
6
|
export { MotionImage } from './motion/Image.js';
|
|
7
7
|
export { MotionPressable } from './motion/Pressable.js';
|
|
8
8
|
export { MotionScrollView } from './motion/ScrollView.js';
|
package/dist/index.js
CHANGED
|
@@ -9,7 +9,12 @@ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
|
9
9
|
|
|
10
10
|
var Animated__default = /*#__PURE__*/_interopDefault(Animated);
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
13
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
14
|
+
}) : x)(function(x) {
|
|
15
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
16
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
17
|
+
});
|
|
13
18
|
var DEFAULT_MOTION_CONFIG = {
|
|
14
19
|
reducedMotion: "user"
|
|
15
20
|
};
|
|
@@ -285,6 +290,40 @@ function mergeTransition(base, override) {
|
|
|
285
290
|
}
|
|
286
291
|
return { ...base ?? { type: "spring" }, ...override };
|
|
287
292
|
}
|
|
293
|
+
|
|
294
|
+
// src/motion/installCheck.ts
|
|
295
|
+
var alreadyChecked = false;
|
|
296
|
+
function ensureReanimatedInstalled() {
|
|
297
|
+
if (!__DEV__ || alreadyChecked) return;
|
|
298
|
+
if (typeof process !== "undefined" && process.env?.NODE_ENV === "test") {
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
alreadyChecked = true;
|
|
302
|
+
let version;
|
|
303
|
+
try {
|
|
304
|
+
const pkg = __require("react-native-reanimated/package.json");
|
|
305
|
+
version = pkg.version;
|
|
306
|
+
} catch {
|
|
307
|
+
}
|
|
308
|
+
if (version) {
|
|
309
|
+
const major = parseInt(version.split(".")[0] ?? "0", 10);
|
|
310
|
+
if (major < 4) {
|
|
311
|
+
console.error(
|
|
312
|
+
`[inertia] react-native-reanimated v${version} is installed, but @onlynative/inertia requires v4.0.0 or later. Upgrade with \`pnpm add react-native-reanimated@^4\` (or your package manager's equivalent).`
|
|
313
|
+
);
|
|
314
|
+
return;
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
const probe = function probe2() {
|
|
318
|
+
"worklet";
|
|
319
|
+
return 0;
|
|
320
|
+
};
|
|
321
|
+
if (typeof probe.__workletHash !== "number") {
|
|
322
|
+
console.error(
|
|
323
|
+
`[inertia] The Reanimated worklets babel plugin is not configured. Add \`'react-native-worklets/plugin'\` as the LAST entry in the \`plugins\` array of your \`babel.config.js\`, then restart Metro with a fresh cache: \`npx expo start -c\` or \`npx react-native start --reset-cache\`.`
|
|
324
|
+
);
|
|
325
|
+
}
|
|
326
|
+
}
|
|
288
327
|
var TRANSFORM_KEYS = [
|
|
289
328
|
"translateX",
|
|
290
329
|
"translateY",
|
|
@@ -311,6 +350,14 @@ var ALL_KEYS = [
|
|
|
311
350
|
...COLOR_KEYS
|
|
312
351
|
];
|
|
313
352
|
var TRANSFORM_KEY_SET = new Set(TRANSFORM_KEYS);
|
|
353
|
+
var COLOR_KEY_SET = new Set(COLOR_KEYS);
|
|
354
|
+
var GESTURE_LAYER_NAMES = [
|
|
355
|
+
"hovered",
|
|
356
|
+
"focused",
|
|
357
|
+
"focusVisible",
|
|
358
|
+
"pressed"
|
|
359
|
+
];
|
|
360
|
+
var GESTURE_LAYER_NAME_SET = new Set(GESTURE_LAYER_NAMES);
|
|
314
361
|
var EXITING_POINTER_EVENTS_STYLE = { pointerEvents: "none" };
|
|
315
362
|
var DEFAULT_RESTING = {
|
|
316
363
|
translateX: 0,
|
|
@@ -356,10 +403,18 @@ function isTopLevelTransition(t) {
|
|
|
356
403
|
function transitionFor(prop, transition) {
|
|
357
404
|
if (!transition) return void 0;
|
|
358
405
|
if (isTopLevelTransition(transition)) return transition;
|
|
406
|
+
if (GESTURE_LAYER_NAME_SET.has(prop)) return void 0;
|
|
359
407
|
return transition[prop];
|
|
360
408
|
}
|
|
409
|
+
function gestureLayerTransitionFor(layer, transition) {
|
|
410
|
+
if (!transition) return void 0;
|
|
411
|
+
if (isTopLevelTransition(transition)) return transition;
|
|
412
|
+
return transition[layer];
|
|
413
|
+
}
|
|
361
414
|
function createMotionComponent(Component) {
|
|
415
|
+
ensureReanimatedInstalled();
|
|
362
416
|
const AnimatedComponent = Animated__default.default.createAnimatedComponent(
|
|
417
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
363
418
|
Component
|
|
364
419
|
);
|
|
365
420
|
const Motion2 = react.forwardRef(function Motion3(props, ref) {
|
|
@@ -438,13 +493,19 @@ function createMotionComponent(Component) {
|
|
|
438
493
|
}
|
|
439
494
|
return initialRecord?.[key] ?? restValue(animateRecord[key]) ?? DEFAULT_RESTING[key];
|
|
440
495
|
});
|
|
441
|
-
const
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
496
|
+
const pressedProgress = Animated.useSharedValue(0);
|
|
497
|
+
const focusedProgress = Animated.useSharedValue(0);
|
|
498
|
+
const focusVisibleProgress = Animated.useSharedValue(0);
|
|
499
|
+
const hoveredProgress = Animated.useSharedValue(0);
|
|
500
|
+
const gestureSV = Animated.useSharedValue(
|
|
501
|
+
resolveGestureLayers(gesture)
|
|
502
|
+
);
|
|
503
|
+
const gestureTargetsSig = stableSig(gesture);
|
|
504
|
+
react.useEffect(() => {
|
|
505
|
+
gestureSV.value = resolveGestureLayers(gesture);
|
|
506
|
+
}, [gestureTargetsSig]);
|
|
507
|
+
const baseRecord = isExiting && exitRecord ? { ...animateRecord, ...exitRecord } : animateRecord;
|
|
508
|
+
const baseSig = stableSig(baseRecord) + (isExiting ? "|exit" : "") + (shouldReduceMotion ? "|rm" : "");
|
|
448
509
|
const transitionSig = stableSig(transition);
|
|
449
510
|
const safeToRemoveRef = react.useRef(void 0);
|
|
450
511
|
safeToRemoveRef.current = presence?.safeToRemove;
|
|
@@ -465,13 +526,13 @@ function createMotionComponent(Component) {
|
|
|
465
526
|
};
|
|
466
527
|
let transformPending = 0;
|
|
467
528
|
for (const k of ALL_KEYS) {
|
|
468
|
-
if (TRANSFORM_KEY_SET.has(k) &&
|
|
529
|
+
if (TRANSFORM_KEY_SET.has(k) && baseRecord[k] !== void 0) {
|
|
469
530
|
transformPending++;
|
|
470
531
|
}
|
|
471
532
|
}
|
|
472
533
|
const transformGroup = transformPending > 0 ? { remaining: transformPending } : void 0;
|
|
473
534
|
for (const key of ALL_KEYS) {
|
|
474
|
-
const target =
|
|
535
|
+
const target = baseRecord[key];
|
|
475
536
|
if (target === void 0) continue;
|
|
476
537
|
const cfg = shouldReduceMotion ? { type: "no-animation" } : transitionFor(key, transition);
|
|
477
538
|
if (isExiting) pending++;
|
|
@@ -496,14 +557,76 @@ function createMotionComponent(Component) {
|
|
|
496
557
|
if (isExiting && pending === 0) {
|
|
497
558
|
safeToRemoveRef.current?.();
|
|
498
559
|
}
|
|
499
|
-
}, [
|
|
560
|
+
}, [baseSig, transitionSig]);
|
|
561
|
+
useGestureLayerProgress(
|
|
562
|
+
pressedProgress,
|
|
563
|
+
pressed,
|
|
564
|
+
gesture?.pressed != null,
|
|
565
|
+
"pressed",
|
|
566
|
+
transition,
|
|
567
|
+
isExiting,
|
|
568
|
+
shouldReduceMotion
|
|
569
|
+
);
|
|
570
|
+
useGestureLayerProgress(
|
|
571
|
+
focusedProgress,
|
|
572
|
+
focused,
|
|
573
|
+
gesture?.focused != null,
|
|
574
|
+
"focused",
|
|
575
|
+
transition,
|
|
576
|
+
isExiting,
|
|
577
|
+
shouldReduceMotion
|
|
578
|
+
);
|
|
579
|
+
useGestureLayerProgress(
|
|
580
|
+
focusVisibleProgress,
|
|
581
|
+
focusVisible,
|
|
582
|
+
gesture?.focusVisible != null,
|
|
583
|
+
"focusVisible",
|
|
584
|
+
transition,
|
|
585
|
+
isExiting,
|
|
586
|
+
shouldReduceMotion
|
|
587
|
+
);
|
|
588
|
+
useGestureLayerProgress(
|
|
589
|
+
hoveredProgress,
|
|
590
|
+
hovered,
|
|
591
|
+
gesture?.hovered != null,
|
|
592
|
+
"hovered",
|
|
593
|
+
transition,
|
|
594
|
+
isExiting,
|
|
595
|
+
shouldReduceMotion
|
|
596
|
+
);
|
|
500
597
|
const animatedStyle = Animated.useAnimatedStyle(() => {
|
|
501
598
|
const activeKeys = activeKeysRef.current;
|
|
502
599
|
const hasTransform = hasTransformRef.current;
|
|
503
600
|
const out = {};
|
|
504
601
|
const transform = [];
|
|
602
|
+
const ph = hoveredProgress.value;
|
|
603
|
+
const pf = focusedProgress.value;
|
|
604
|
+
const pfv = focusVisibleProgress.value;
|
|
605
|
+
const pp = pressedProgress.value;
|
|
606
|
+
const layers = gestureSV.value;
|
|
607
|
+
const hoveredLayer = layers ? layers.hovered : null;
|
|
608
|
+
const focusedLayer = layers ? layers.focused : null;
|
|
609
|
+
const focusVisibleLayer = layers ? layers.focusVisible : null;
|
|
610
|
+
const pressedLayer = layers ? layers.pressed : null;
|
|
505
611
|
for (const key of activeKeys) {
|
|
506
|
-
|
|
612
|
+
let v = sharedValues[key].value;
|
|
613
|
+
const isColor = COLOR_KEY_SET.has(key);
|
|
614
|
+
if (hoveredLayer && ph > 0 && hoveredLayer[key] !== void 0) {
|
|
615
|
+
const t = hoveredLayer[key];
|
|
616
|
+
v = isColor ? Animated.interpolateColor(ph, [0, 1], [v, t]) : v + (t - v) * ph;
|
|
617
|
+
}
|
|
618
|
+
if (focusedLayer && pf > 0 && focusedLayer[key] !== void 0) {
|
|
619
|
+
const t = focusedLayer[key];
|
|
620
|
+
v = isColor ? Animated.interpolateColor(pf, [0, 1], [v, t]) : v + (t - v) * pf;
|
|
621
|
+
}
|
|
622
|
+
if (focusVisibleLayer && pfv > 0 && focusVisibleLayer[key] !== void 0) {
|
|
623
|
+
const t = focusVisibleLayer[key];
|
|
624
|
+
v = isColor ? Animated.interpolateColor(pfv, [0, 1], [v, t]) : v + (t - v) * pfv;
|
|
625
|
+
}
|
|
626
|
+
if (pressedLayer && pp > 0 && pressedLayer[key] !== void 0) {
|
|
627
|
+
const t = pressedLayer[key];
|
|
628
|
+
v = isColor ? Animated.interpolateColor(pp, [0, 1], [v, t]) : v + (t - v) * pp;
|
|
629
|
+
}
|
|
507
630
|
if (TRANSFORM_KEY_SET.has(key)) {
|
|
508
631
|
transform.push(
|
|
509
632
|
key === "rotate" ? { rotate: `${v}deg` } : { [key]: v }
|
|
@@ -722,52 +845,41 @@ function stableStringify(v) {
|
|
|
722
845
|
const keys = Object.keys(obj).sort();
|
|
723
846
|
return "{" + keys.map((k) => JSON.stringify(k) + ":" + stableStringify(obj[k])).join(",") + "}";
|
|
724
847
|
}
|
|
725
|
-
function
|
|
726
|
-
if (!gesture) return
|
|
727
|
-
const
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
if (!sub) continue;
|
|
738
|
-
for (const k of ALL_KEYS) {
|
|
739
|
-
if (k in sub && !(k in merged)) {
|
|
740
|
-
merged[k] = DEFAULT_RESTING[k];
|
|
741
|
-
}
|
|
848
|
+
function resolveGestureLayers(gesture) {
|
|
849
|
+
if (!gesture) return null;
|
|
850
|
+
const out = {};
|
|
851
|
+
for (const layer of GESTURE_LAYER_NAMES) {
|
|
852
|
+
const subState = gesture[layer];
|
|
853
|
+
if (!subState) continue;
|
|
854
|
+
const resolved = {};
|
|
855
|
+
for (const key of ALL_KEYS) {
|
|
856
|
+
const raw = subState[key];
|
|
857
|
+
if (raw === void 0) continue;
|
|
858
|
+
const t = targetEndValue(raw);
|
|
859
|
+
if (t !== void 0) resolved[key] = t;
|
|
742
860
|
}
|
|
861
|
+
out[layer] = resolved;
|
|
743
862
|
}
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
merged,
|
|
759
|
-
gesture.focusVisible
|
|
760
|
-
);
|
|
761
|
-
}
|
|
762
|
-
if (active.pressed && gesture.pressed) {
|
|
763
|
-
Object.assign(
|
|
764
|
-
merged,
|
|
765
|
-
gesture.pressed
|
|
766
|
-
);
|
|
767
|
-
}
|
|
768
|
-
return merged;
|
|
863
|
+
return out;
|
|
864
|
+
}
|
|
865
|
+
function useGestureLayerProgress(progress, active, declared, layer, transition, isExiting, shouldReduceMotion) {
|
|
866
|
+
const layerCfgSig = stableSig(gestureLayerTransitionFor(layer, transition));
|
|
867
|
+
react.useEffect(() => {
|
|
868
|
+
if (!declared) return;
|
|
869
|
+
if (isExiting) {
|
|
870
|
+
progress.value = 0;
|
|
871
|
+
return;
|
|
872
|
+
}
|
|
873
|
+
const target = active ? 1 : 0;
|
|
874
|
+
const cfg = shouldReduceMotion ? { type: "no-animation" } : gestureLayerTransitionFor(layer, transition) ?? { type: "spring" };
|
|
875
|
+
progress.value = resolveTransition(cfg, target);
|
|
876
|
+
}, [active, declared, isExiting, shouldReduceMotion, layerCfgSig]);
|
|
769
877
|
}
|
|
770
878
|
function useGestureHandlers(gesture, rest, setPressed, setFocused, setFocusVisible, setHovered) {
|
|
879
|
+
const hasPressed = gesture?.pressed ? 1 : 0;
|
|
880
|
+
const hasFocused = gesture?.focused ? 1 : 0;
|
|
881
|
+
const hasFocusVisible = gesture?.focusVisible ? 1 : 0;
|
|
882
|
+
const hasHovered = gesture?.hovered ? 1 : 0;
|
|
771
883
|
return react.useMemo(() => {
|
|
772
884
|
if (!gesture) return {};
|
|
773
885
|
const handlers = {};
|
|
@@ -800,10 +912,10 @@ function useGestureHandlers(gesture, rest, setPressed, setFocused, setFocusVisib
|
|
|
800
912
|
}
|
|
801
913
|
return handlers;
|
|
802
914
|
}, [
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
915
|
+
hasPressed,
|
|
916
|
+
hasFocused,
|
|
917
|
+
hasFocusVisible,
|
|
918
|
+
hasHovered,
|
|
807
919
|
rest.onTouchStart,
|
|
808
920
|
rest.onTouchEnd,
|
|
809
921
|
rest.onTouchCancel,
|