@onlynative/inertia 0.0.1-alpha.7 → 0.0.1-alpha.9
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 +1 -1
- 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 +114 -74
- package/dist/index.d.ts +114 -74
- package/dist/index.js +388 -1542
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +388 -1545
- 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 +244 -1462
- package/dist/motion/Image.js.map +1 -1
- package/dist/motion/Image.mjs +247 -1465
- 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 +244 -1462
- package/dist/motion/Pressable.js.map +1 -1
- package/dist/motion/Pressable.mjs +247 -1465
- 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 +244 -1462
- package/dist/motion/ScrollView.js.map +1 -1
- package/dist/motion/ScrollView.mjs +247 -1465
- 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 +244 -1462
- package/dist/motion/Text.js.map +1 -1
- package/dist/motion/Text.mjs +247 -1465
- 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 +244 -1462
- package/dist/motion/View.js.map +1 -1
- package/dist/motion/View.mjs +247 -1465
- package/dist/motion/View.mjs.map +1 -1
- 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-NmNeJjo1.d.mts → types-cU43dEmH.d.mts} +64 -17
- package/dist/{types-NmNeJjo1.d.ts → types-cU43dEmH.d.ts} +64 -17
- package/dist/useGesture-B7A_1DVg.d.ts +84 -0
- package/dist/useGesture-cimMrzC1.d.mts +84 -0
- package/jest-setup.js +4 -0
- package/llms.txt +12 -3
- package/package.json +22 -2
- package/src/__type-tests__/variants.test-d.tsx +67 -0
- package/src/gestureLayer/index.ts +21 -0
- package/src/gestureLayer/useGestureLayer.ts +285 -0
- package/src/index.ts +7 -0
- package/src/layout/index.ts +15 -0
- package/src/layout/sharedRegistry.ts +111 -0
- package/src/layout/useSharedLayout.ts +289 -0
- package/src/motion/createMotionComponent.tsx +123 -37
- package/src/motion/installCheck.ts +7 -11
- package/src/touch/index.ts +18 -0
- package/src/touch/useTouchDrag.ts +289 -0
- package/src/types.ts +79 -20
- package/src/values/index.ts +11 -0
- package/src/values/useBooleanSpring.ts +33 -0
- package/src/values/useColorTransition.ts +72 -0
- package/src/values/useShadow.ts +116 -0
package/README.md
CHANGED
|
@@ -88,7 +88,7 @@ Plus, on any transition: `delay`, `repeat`. Per-property transitions take preced
|
|
|
88
88
|
|
|
89
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
90
|
|
|
91
|
-
SVG path morphing ships in the [`@onlynative/inertia-svg`](../svg) adapter (`MotionPath`).
|
|
91
|
+
SVG path morphing ships in the [`@onlynative/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
92
|
|
|
93
93
|
## When not to use the core package alone
|
|
94
94
|
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { AnimatedStyle } from 'react-native-reanimated';
|
|
2
|
+
import { U as UseGestureHandlers } from '../useGesture-cimMrzC1.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 '@onlynative/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-B7A_1DVg.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 '@onlynative/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,346 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var react = require('react');
|
|
4
|
+
var reactNativeReanimated = require('react-native-reanimated');
|
|
5
|
+
var reactNativeWorklets = require('react-native-worklets');
|
|
6
|
+
var reactNative = require('react-native');
|
|
7
|
+
|
|
8
|
+
// src/gestureLayer/useGestureLayer.ts
|
|
9
|
+
var DEFAULT_MOTION_CONFIG = {
|
|
10
|
+
reducedMotion: "user"
|
|
11
|
+
};
|
|
12
|
+
var MotionConfigContext = react.createContext(
|
|
13
|
+
DEFAULT_MOTION_CONFIG
|
|
14
|
+
);
|
|
15
|
+
function useMotionConfig() {
|
|
16
|
+
return react.useContext(MotionConfigContext);
|
|
17
|
+
}
|
|
18
|
+
function useShouldReduceMotion() {
|
|
19
|
+
const { reducedMotion } = useMotionConfig();
|
|
20
|
+
const osReduced = reactNativeReanimated.useReducedMotion();
|
|
21
|
+
if (reducedMotion === "never") return false;
|
|
22
|
+
if (reducedMotion === "always") return true;
|
|
23
|
+
return osReduced;
|
|
24
|
+
}
|
|
25
|
+
function ensureWorkletEasing(easing) {
|
|
26
|
+
if (!easing) return void 0;
|
|
27
|
+
const fn = isEasingFactory(easing) ? easing.factory() : easing;
|
|
28
|
+
if (reactNativeWorklets.isWorkletFunction(fn)) return fn;
|
|
29
|
+
const wrapped = (t) => {
|
|
30
|
+
"worklet";
|
|
31
|
+
return fn(t);
|
|
32
|
+
};
|
|
33
|
+
return wrapped;
|
|
34
|
+
}
|
|
35
|
+
function isEasingFactory(value) {
|
|
36
|
+
return typeof value === "object" && value !== null && "factory" in value && typeof value.factory === "function";
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// src/transitions/spring.ts
|
|
40
|
+
var DEFAULT_SPRING = {
|
|
41
|
+
tension: 170,
|
|
42
|
+
friction: 26,
|
|
43
|
+
mass: 1
|
|
44
|
+
};
|
|
45
|
+
function springToReanimated(t) {
|
|
46
|
+
"worklet";
|
|
47
|
+
return {
|
|
48
|
+
stiffness: t.tension ?? DEFAULT_SPRING.tension,
|
|
49
|
+
damping: t.friction ?? DEFAULT_SPRING.friction,
|
|
50
|
+
mass: t.mass ?? DEFAULT_SPRING.mass,
|
|
51
|
+
velocity: t.velocity,
|
|
52
|
+
restSpeedThreshold: t.restSpeedThreshold,
|
|
53
|
+
restDisplacementThreshold: t.restDisplacementThreshold
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// src/transitions/resolve.ts
|
|
58
|
+
var DEFAULT_TIMING_DURATION = 250;
|
|
59
|
+
function buildSpring(cfg, toValue, cb) {
|
|
60
|
+
return reactNativeReanimated.withSpring(toValue, springToReanimated(cfg), cb);
|
|
61
|
+
}
|
|
62
|
+
function buildTiming(cfg, toValue, cb) {
|
|
63
|
+
return reactNativeReanimated.withTiming(
|
|
64
|
+
toValue,
|
|
65
|
+
{
|
|
66
|
+
duration: cfg.duration ?? DEFAULT_TIMING_DURATION,
|
|
67
|
+
easing: ensureWorkletEasing(cfg.easing) ?? reactNativeReanimated.Easing.inOut(reactNativeReanimated.Easing.ease)
|
|
68
|
+
},
|
|
69
|
+
cb
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
function buildDecay(cfg, cb) {
|
|
73
|
+
return reactNativeReanimated.withDecay(
|
|
74
|
+
{
|
|
75
|
+
velocity: cfg.velocity ?? 0,
|
|
76
|
+
deceleration: cfg.deceleration,
|
|
77
|
+
clamp: cfg.clamp
|
|
78
|
+
},
|
|
79
|
+
cb
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
function buildOne(cfg, toValue, cb) {
|
|
83
|
+
if (cfg.type === "no-animation") {
|
|
84
|
+
return toValue;
|
|
85
|
+
}
|
|
86
|
+
if (cfg.type === "decay") return buildDecay(cfg, cb);
|
|
87
|
+
if (cfg.type === "timing") return buildTiming(cfg, toValue, cb);
|
|
88
|
+
return buildSpring(cfg, toValue, cb);
|
|
89
|
+
}
|
|
90
|
+
function applyRepeat(animation, repeat) {
|
|
91
|
+
if (repeat === void 0) return animation;
|
|
92
|
+
if (repeat === "infinite") {
|
|
93
|
+
return reactNativeReanimated.withRepeat(animation, -1, true);
|
|
94
|
+
}
|
|
95
|
+
if (typeof repeat === "number") {
|
|
96
|
+
return reactNativeReanimated.withRepeat(animation, repeat, true);
|
|
97
|
+
}
|
|
98
|
+
const count = repeat.count === "infinite" ? -1 : repeat.count;
|
|
99
|
+
const alternate = repeat.alternate ?? true;
|
|
100
|
+
return reactNativeReanimated.withRepeat(animation, count, alternate);
|
|
101
|
+
}
|
|
102
|
+
function applyDelay(animation, delay) {
|
|
103
|
+
if (!delay || delay <= 0) return animation;
|
|
104
|
+
return reactNativeReanimated.withDelay(delay, animation);
|
|
105
|
+
}
|
|
106
|
+
function resolveTransition(config, toValue, callback) {
|
|
107
|
+
const cfg = config ?? { type: "spring" };
|
|
108
|
+
const base = buildOne(cfg, toValue, callback);
|
|
109
|
+
const repeated = applyRepeat(base, repeatOf(cfg));
|
|
110
|
+
return applyDelay(repeated, delayOf(cfg));
|
|
111
|
+
}
|
|
112
|
+
function repeatOf(cfg) {
|
|
113
|
+
if (cfg.type === "no-animation" || cfg.type === "decay") return void 0;
|
|
114
|
+
return cfg.repeat;
|
|
115
|
+
}
|
|
116
|
+
function delayOf(cfg) {
|
|
117
|
+
if (cfg.type === "no-animation") return void 0;
|
|
118
|
+
return cfg.delay;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// src/transitions/keys.ts
|
|
122
|
+
var TRANSITION_CONFIG_KEYS = /* @__PURE__ */ new Set([
|
|
123
|
+
"type",
|
|
124
|
+
"tension",
|
|
125
|
+
"friction",
|
|
126
|
+
"mass",
|
|
127
|
+
"velocity",
|
|
128
|
+
"restSpeedThreshold",
|
|
129
|
+
"restDisplacementThreshold",
|
|
130
|
+
"duration",
|
|
131
|
+
"easing",
|
|
132
|
+
"delay",
|
|
133
|
+
"repeat",
|
|
134
|
+
"deceleration",
|
|
135
|
+
"clamp"
|
|
136
|
+
]);
|
|
137
|
+
function isTopLevelTransition(t) {
|
|
138
|
+
if (t === null || typeof t !== "object") return false;
|
|
139
|
+
const keys = Object.keys(t);
|
|
140
|
+
if (keys.length === 0) return false;
|
|
141
|
+
return keys.every((k) => TRANSITION_CONFIG_KEYS.has(k));
|
|
142
|
+
}
|
|
143
|
+
var modality = "keyboard";
|
|
144
|
+
var installed = false;
|
|
145
|
+
function setKeyboard() {
|
|
146
|
+
modality = "keyboard";
|
|
147
|
+
}
|
|
148
|
+
function setPointer() {
|
|
149
|
+
modality = "pointer";
|
|
150
|
+
}
|
|
151
|
+
function ensureInstalled() {
|
|
152
|
+
if (installed) return;
|
|
153
|
+
if (reactNative.Platform.OS !== "web") return;
|
|
154
|
+
if (typeof document === "undefined") return;
|
|
155
|
+
document.addEventListener("keydown", setKeyboard, true);
|
|
156
|
+
document.addEventListener("mousedown", setPointer, true);
|
|
157
|
+
document.addEventListener("pointerdown", setPointer, true);
|
|
158
|
+
document.addEventListener("touchstart", setPointer, true);
|
|
159
|
+
installed = true;
|
|
160
|
+
}
|
|
161
|
+
function isFocusVisible() {
|
|
162
|
+
if (reactNative.Platform.OS !== "web") return true;
|
|
163
|
+
ensureInstalled();
|
|
164
|
+
return modality === "keyboard";
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// src/values/useGesture.ts
|
|
168
|
+
function useGesture(transition) {
|
|
169
|
+
const pressed = reactNativeReanimated.useSharedValue(0);
|
|
170
|
+
const focused = reactNativeReanimated.useSharedValue(0);
|
|
171
|
+
const focusVisible = reactNativeReanimated.useSharedValue(0);
|
|
172
|
+
const hovered = reactNativeReanimated.useSharedValue(0);
|
|
173
|
+
const shouldReduceMotion = useShouldReduceMotion();
|
|
174
|
+
const setLayer = react.useCallback(
|
|
175
|
+
(sv, layer, target) => {
|
|
176
|
+
const cfg = shouldReduceMotion ? { type: "no-animation" } : layerTransition(layer, transition) ?? { type: "spring" };
|
|
177
|
+
sv.value = resolveTransition(cfg, target);
|
|
178
|
+
},
|
|
179
|
+
// The transition is intentionally read on every call rather than cooked
|
|
180
|
+
// into the dep array — a fresh literal each render would otherwise
|
|
181
|
+
// rebuild the handler bag and break composing consumers that key off
|
|
182
|
+
// handler identity. `transition` is read inside the callback closure;
|
|
183
|
+
// shared values are stable so the only dep that matters is the reduce-
|
|
184
|
+
// motion flag.
|
|
185
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
186
|
+
[shouldReduceMotion]
|
|
187
|
+
);
|
|
188
|
+
const handlers = react.useMemo(
|
|
189
|
+
() => ({
|
|
190
|
+
onPressIn: () => setLayer(pressed, "pressed", 1),
|
|
191
|
+
onPressOut: () => setLayer(pressed, "pressed", 0),
|
|
192
|
+
onHoverIn: () => setLayer(hovered, "hovered", 1),
|
|
193
|
+
onHoverOut: () => setLayer(hovered, "hovered", 0),
|
|
194
|
+
onFocus: () => {
|
|
195
|
+
setLayer(focused, "focused", 1);
|
|
196
|
+
if (isFocusVisible()) setLayer(focusVisible, "focusVisible", 1);
|
|
197
|
+
},
|
|
198
|
+
onBlur: () => {
|
|
199
|
+
setLayer(focused, "focused", 0);
|
|
200
|
+
setLayer(focusVisible, "focusVisible", 0);
|
|
201
|
+
}
|
|
202
|
+
}),
|
|
203
|
+
[setLayer, pressed, focused, focusVisible, hovered]
|
|
204
|
+
);
|
|
205
|
+
return { pressed, focused, focusVisible, hovered, handlers };
|
|
206
|
+
}
|
|
207
|
+
function layerTransition(layer, transition) {
|
|
208
|
+
if (!transition) return void 0;
|
|
209
|
+
if (isTopLevelTransition(transition)) return transition;
|
|
210
|
+
return transition[layer];
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// src/gestureLayer/useGestureLayer.ts
|
|
214
|
+
function useGestureLayer(states, options = {}) {
|
|
215
|
+
const { disabled: isDisabled = false, transition } = options;
|
|
216
|
+
const shouldReduceMotion = useShouldReduceMotion();
|
|
217
|
+
const gesture = useGesture(transition);
|
|
218
|
+
const disabledProgress = reactNativeReanimated.useSharedValue(0);
|
|
219
|
+
react.useEffect(() => {
|
|
220
|
+
const target = isDisabled ? 1 : 0;
|
|
221
|
+
const cfg = shouldReduceMotion ? { type: "no-animation" } : disabledTransition(transition) ?? { type: "spring" };
|
|
222
|
+
disabledProgress.value = resolveTransition(cfg, target);
|
|
223
|
+
}, [isDisabled, shouldReduceMotion, transition, disabledProgress]);
|
|
224
|
+
const meta = react.useMemo(() => {
|
|
225
|
+
const layers = {
|
|
226
|
+
rest: states.rest,
|
|
227
|
+
hovered: states.hovered,
|
|
228
|
+
focused: states.focused,
|
|
229
|
+
focusVisible: states.focusVisible,
|
|
230
|
+
pressed: states.pressed,
|
|
231
|
+
disabled: states.disabled
|
|
232
|
+
};
|
|
233
|
+
const sources = [
|
|
234
|
+
layers.rest,
|
|
235
|
+
layers.hovered,
|
|
236
|
+
layers.focused,
|
|
237
|
+
layers.focusVisible,
|
|
238
|
+
layers.pressed,
|
|
239
|
+
layers.disabled
|
|
240
|
+
];
|
|
241
|
+
const keySet = /* @__PURE__ */ new Set();
|
|
242
|
+
for (const src of sources) {
|
|
243
|
+
if (!src) continue;
|
|
244
|
+
for (const k in src) if (src[k] !== void 0) keySet.add(k);
|
|
245
|
+
}
|
|
246
|
+
const keys = Array.from(keySet);
|
|
247
|
+
const types = {};
|
|
248
|
+
const restValues = {};
|
|
249
|
+
for (const k of keys) {
|
|
250
|
+
let firstDefined;
|
|
251
|
+
for (const src of sources) {
|
|
252
|
+
if (src && src[k] !== void 0) {
|
|
253
|
+
firstDefined = src[k];
|
|
254
|
+
break;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
const isColor = typeof firstDefined === "string";
|
|
258
|
+
types[k] = isColor ? "color" : "number";
|
|
259
|
+
const restRaw = layers.rest ? layers.rest[k] : void 0;
|
|
260
|
+
restValues[k] = restRaw !== void 0 ? restRaw : isColor ? "transparent" : 0;
|
|
261
|
+
}
|
|
262
|
+
return { layers, keys, types, restValues };
|
|
263
|
+
}, [
|
|
264
|
+
states.rest,
|
|
265
|
+
states.hovered,
|
|
266
|
+
states.focused,
|
|
267
|
+
states.focusVisible,
|
|
268
|
+
states.pressed,
|
|
269
|
+
states.disabled
|
|
270
|
+
]);
|
|
271
|
+
const style = reactNativeReanimated.useAnimatedStyle(() => {
|
|
272
|
+
const { layers, keys, types, restValues } = meta;
|
|
273
|
+
const ph = gesture.hovered.value;
|
|
274
|
+
const pf = gesture.focused.value;
|
|
275
|
+
const pfv = gesture.focusVisible.value;
|
|
276
|
+
const pp = gesture.pressed.value;
|
|
277
|
+
const pd = disabledProgress.value;
|
|
278
|
+
const hoveredLayer = layers.hovered;
|
|
279
|
+
const focusedLayer = layers.focused;
|
|
280
|
+
const focusVisibleLayer = layers.focusVisible;
|
|
281
|
+
const pressedLayer = layers.pressed;
|
|
282
|
+
const disabledLayer = layers.disabled;
|
|
283
|
+
const out = {};
|
|
284
|
+
for (let i = 0; i < keys.length; i++) {
|
|
285
|
+
const k = keys[i];
|
|
286
|
+
const isColor = types[k] === "color";
|
|
287
|
+
const rest = restValues[k];
|
|
288
|
+
if (isColor) {
|
|
289
|
+
let v = rest;
|
|
290
|
+
if (hoveredLayer && ph > 0 && hoveredLayer[k] !== void 0) {
|
|
291
|
+
v = reactNativeReanimated.interpolateColor(ph, [0, 1], [v, hoveredLayer[k]]);
|
|
292
|
+
}
|
|
293
|
+
if (focusedLayer && pf > 0 && focusedLayer[k] !== void 0) {
|
|
294
|
+
v = reactNativeReanimated.interpolateColor(pf, [0, 1], [v, focusedLayer[k]]);
|
|
295
|
+
}
|
|
296
|
+
if (focusVisibleLayer && pfv > 0 && focusVisibleLayer[k] !== void 0) {
|
|
297
|
+
v = reactNativeReanimated.interpolateColor(pfv, [0, 1], [v, focusVisibleLayer[k]]);
|
|
298
|
+
}
|
|
299
|
+
if (pressedLayer && pp > 0 && pressedLayer[k] !== void 0) {
|
|
300
|
+
v = reactNativeReanimated.interpolateColor(pp, [0, 1], [v, pressedLayer[k]]);
|
|
301
|
+
}
|
|
302
|
+
if (disabledLayer && pd > 0 && disabledLayer[k] !== void 0) {
|
|
303
|
+
v = reactNativeReanimated.interpolateColor(pd, [0, 1], [v, disabledLayer[k]]);
|
|
304
|
+
}
|
|
305
|
+
out[k] = v;
|
|
306
|
+
} else {
|
|
307
|
+
const base = rest;
|
|
308
|
+
let m = base;
|
|
309
|
+
if (hoveredLayer && ph > 0 && hoveredLayer[k] !== void 0) {
|
|
310
|
+
const c = base + (hoveredLayer[k] - base) * ph;
|
|
311
|
+
if (c > m) m = c;
|
|
312
|
+
}
|
|
313
|
+
if (focusedLayer && pf > 0 && focusedLayer[k] !== void 0) {
|
|
314
|
+
const c = base + (focusedLayer[k] - base) * pf;
|
|
315
|
+
if (c > m) m = c;
|
|
316
|
+
}
|
|
317
|
+
if (focusVisibleLayer && pfv > 0 && focusVisibleLayer[k] !== void 0) {
|
|
318
|
+
const c = base + (focusVisibleLayer[k] - base) * pfv;
|
|
319
|
+
if (c > m) m = c;
|
|
320
|
+
}
|
|
321
|
+
if (pressedLayer && pp > 0 && pressedLayer[k] !== void 0) {
|
|
322
|
+
const c = base + (pressedLayer[k] - base) * pp;
|
|
323
|
+
if (c > m) m = c;
|
|
324
|
+
}
|
|
325
|
+
if (disabledLayer && pd > 0 && disabledLayer[k] !== void 0) {
|
|
326
|
+
m = m + (disabledLayer[k] - m) * pd;
|
|
327
|
+
}
|
|
328
|
+
out[k] = m;
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
return out;
|
|
332
|
+
});
|
|
333
|
+
return {
|
|
334
|
+
style,
|
|
335
|
+
handlers: gesture.handlers
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
function disabledTransition(transition) {
|
|
339
|
+
if (!transition) return void 0;
|
|
340
|
+
if (isTopLevelTransition(transition)) return transition;
|
|
341
|
+
return void 0;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
exports.useGestureLayer = useGestureLayer;
|
|
345
|
+
//# sourceMappingURL=index.js.map
|
|
346
|
+
//# sourceMappingURL=index.js.map
|