@rootnative/inertia 0.0.2 → 0.0.4
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 +63 -5
- package/README.md +5 -3
- package/dist/{chunk-ZN3PNGHQ.mjs → chunk-4ASOELLA.mjs} +1 -1
- package/dist/{chunk-LYMLQ6WQ.mjs → chunk-55W7BNO2.mjs} +604 -106
- package/dist/chunk-5SRD2WCO.js +8 -0
- package/dist/{chunk-PTRF47DA.js → chunk-7A6WTXNK.js} +10 -3
- package/dist/{chunk-NKA6XR77.js → chunk-CENTULW6.js} +2 -2
- package/dist/{chunk-4PEHWDAZ.js → chunk-D5KAHWMC.js} +7 -7
- package/dist/{chunk-2DHKV2XY.mjs → chunk-J5Q46OPW.mjs} +1 -1
- package/dist/{chunk-RGNX6UZN.mjs → chunk-K3CLP74S.mjs} +2 -2
- package/dist/{chunk-IWLU3VDE.js → chunk-MYE5NMXW.js} +623 -123
- package/dist/{chunk-HFDZAHWP.mjs → chunk-NAOXQJ7U.mjs} +1 -1
- package/dist/{chunk-R63GIUNU.mjs → chunk-QKC3APP4.mjs} +1 -1
- package/dist/{chunk-V6BTXHZK.mjs → chunk-RF7J5FRM.mjs} +1 -1
- package/dist/{chunk-CY7Y64C3.mjs → chunk-TU2XFPAK.mjs} +10 -4
- package/dist/chunk-TVFNJ25Z.js +8 -0
- package/dist/chunk-VLDIFL3F.js +8 -0
- package/dist/{chunk-7AOERN53.js → chunk-WVRCKOPO.js} +3 -3
- package/dist/{chunk-TUYHTHVV.js → chunk-XMEVPEMH.js} +2 -2
- package/dist/{chunk-TUMNV4P7.mjs → chunk-YKGY22SB.mjs} +1 -1
- package/dist/gestureLayer/index.d.mts +2 -2
- package/dist/gestureLayer/index.d.ts +2 -2
- package/dist/gestureLayer/index.js +10 -10
- package/dist/gestureLayer/index.mjs +3 -3
- package/dist/index.d.mts +14 -12
- package/dist/index.d.ts +14 -12
- package/dist/index.js +68 -176
- package/dist/index.mjs +40 -147
- package/dist/motion/Image.d.mts +1 -1
- package/dist/motion/Image.d.ts +1 -1
- package/dist/motion/Image.js +5 -5
- package/dist/motion/Image.mjs +4 -4
- package/dist/motion/Pressable.d.mts +1 -1
- package/dist/motion/Pressable.d.ts +1 -1
- package/dist/motion/Pressable.js +5 -5
- package/dist/motion/Pressable.mjs +4 -4
- package/dist/motion/ScrollView.d.mts +1 -1
- package/dist/motion/ScrollView.d.ts +1 -1
- package/dist/motion/ScrollView.js +5 -5
- package/dist/motion/ScrollView.mjs +4 -4
- package/dist/motion/Text.d.mts +1 -1
- package/dist/motion/Text.d.ts +1 -1
- package/dist/motion/Text.js +5 -5
- package/dist/motion/Text.mjs +4 -4
- package/dist/motion/View.d.mts +1 -1
- package/dist/motion/View.d.ts +1 -1
- package/dist/motion/View.js +5 -5
- package/dist/motion/View.mjs +4 -4
- package/dist/touch/index.d.mts +1 -1
- package/dist/touch/index.d.ts +1 -1
- package/dist/touch/index.js +3 -3
- package/dist/touch/index.mjs +1 -1
- package/dist/{types-G1dBU2IQ.d.mts → types-37pN82yh.d.mts} +49 -14
- package/dist/{types-G1dBU2IQ.d.ts → types-37pN82yh.d.ts} +49 -14
- package/dist/{useGesture-D1e4ixJo.d.ts → useGesture-Cl4uzZIr.d.ts} +1 -1
- package/dist/{useGesture-RFY6kTL3.d.mts → useGesture-_IKkS8Dv.d.mts} +1 -1
- package/llms.txt +3 -1
- package/package.json +1 -1
- package/src/index.ts +2 -1
- package/src/internal/boxShadow.ts +177 -6
- package/src/internal/nonWorkletWarning.ts +4 -6
- package/src/internal/warnOnce.ts +28 -0
- package/src/layout/index.ts +11 -0
- package/src/layout/measureWindow.ts +135 -0
- package/src/layout/sharedRegistry.ts +156 -18
- package/src/layout/useSharedLayout.ts +256 -91
- package/src/motion/createMotionComponent.tsx +550 -55
- package/src/motion/installCheck.ts +1 -1
- package/src/presence/Presence.tsx +73 -10
- package/src/transitions/index.ts +1 -1
- package/src/types.ts +52 -12
- package/src/values/useAnimator.ts +25 -14
- package/src/values/useColorCascade.ts +34 -16
- package/src/values/useColorTransition.ts +1 -1
- package/dist/chunk-34Q4UM6V.js +0 -8
- package/dist/chunk-KYJROYCG.js +0 -8
- package/dist/chunk-X5J5M3K3.js +0 -8
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { useNamedTransitions, resolveNamedTransitionProp, lookupNamedTransition, useShouldReduceMotion, isFocusVisible } from './chunk-
|
|
2
|
-
import { stableSig, resolveAnimatableValue, isTopLevelTransition, resolveTransition, ensureWorkletEasing, springToReanimated, DEFAULT_SPRING } from './chunk-
|
|
1
|
+
import { useNamedTransitions, resolveNamedTransitionProp, lookupNamedTransition, useShouldReduceMotion, isFocusVisible } from './chunk-QKC3APP4.mjs';
|
|
2
|
+
import { warnOnce, stableSig, resolveAnimatableValue, isTopLevelTransition, resolveTransition, ensureWorkletEasing, springToReanimated, DEFAULT_SPRING } from './chunk-TU2XFPAK.mjs';
|
|
3
3
|
import { createContext, useContext, useMemo, Children, isValidElement, useState, useRef, useCallback, forwardRef, useEffect } from 'react';
|
|
4
4
|
import { jsx, Fragment } from 'react/jsx-runtime';
|
|
5
5
|
import Animated, { useSharedValue, cancelAnimation, useAnimatedStyle, interpolateColor, reanimatedVersion, runOnJS, LinearTransition, withSequence, withTiming, withSpring } from 'react-native-reanimated';
|
|
6
|
+
import { StyleSheet } from 'react-native';
|
|
6
7
|
|
|
7
8
|
var PresenceContext = createContext(null);
|
|
8
9
|
function usePresence() {
|
|
@@ -29,6 +30,8 @@ function Presence({ children }) {
|
|
|
29
30
|
() => /* @__PURE__ */ new Map()
|
|
30
31
|
);
|
|
31
32
|
const prevIncomingRef = useRef(incoming);
|
|
33
|
+
const orderRef = useRef([]);
|
|
34
|
+
let pendingExiting = null;
|
|
32
35
|
if (prevIncomingRef.current !== incoming) {
|
|
33
36
|
const prev = prevIncomingRef.current;
|
|
34
37
|
prevIncomingRef.current = incoming;
|
|
@@ -50,8 +53,12 @@ function Presence({ children }) {
|
|
|
50
53
|
ensureMutable().delete(key);
|
|
51
54
|
}
|
|
52
55
|
}
|
|
53
|
-
if (next)
|
|
56
|
+
if (next) {
|
|
57
|
+
pendingExiting = next;
|
|
58
|
+
setExiting(next);
|
|
59
|
+
}
|
|
54
60
|
}
|
|
61
|
+
const activeExiting = pendingExiting ?? exiting;
|
|
55
62
|
const handleRemove = useCallback((key) => {
|
|
56
63
|
setExiting((prev) => {
|
|
57
64
|
if (!prev.has(key)) return prev;
|
|
@@ -60,19 +67,42 @@ function Presence({ children }) {
|
|
|
60
67
|
return next;
|
|
61
68
|
});
|
|
62
69
|
}, []);
|
|
63
|
-
const
|
|
70
|
+
const byKey = /* @__PURE__ */ new Map();
|
|
71
|
+
const presentKeys = /* @__PURE__ */ new Set();
|
|
72
|
+
const order = [];
|
|
64
73
|
for (const el of incoming) {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
});
|
|
74
|
+
const key = el.key;
|
|
75
|
+
byKey.set(key, el);
|
|
76
|
+
presentKeys.add(key);
|
|
77
|
+
order.push(key);
|
|
70
78
|
}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
79
|
+
const prevOrder = orderRef.current;
|
|
80
|
+
for (let i = 0; i < prevOrder.length; i++) {
|
|
81
|
+
const key = prevOrder[i];
|
|
82
|
+
const departing = activeExiting.get(key);
|
|
83
|
+
if (!departing || byKey.has(key)) continue;
|
|
84
|
+
let insertAt = 0;
|
|
85
|
+
for (let j = i - 1; j >= 0; j--) {
|
|
86
|
+
const anchor = order.indexOf(prevOrder[j]);
|
|
87
|
+
if (anchor !== -1) {
|
|
88
|
+
insertAt = anchor + 1;
|
|
89
|
+
break;
|
|
90
|
+
}
|
|
74
91
|
}
|
|
92
|
+
byKey.set(key, departing);
|
|
93
|
+
order.splice(insertAt, 0, key);
|
|
75
94
|
}
|
|
95
|
+
for (const [key, el] of activeExiting) {
|
|
96
|
+
if (byKey.has(key)) continue;
|
|
97
|
+
byKey.set(key, el);
|
|
98
|
+
order.push(key);
|
|
99
|
+
}
|
|
100
|
+
orderRef.current = order;
|
|
101
|
+
const renderList = order.map((key) => ({
|
|
102
|
+
key,
|
|
103
|
+
element: byKey.get(key),
|
|
104
|
+
isPresent: presentKeys.has(key)
|
|
105
|
+
}));
|
|
76
106
|
return /* @__PURE__ */ jsx(Fragment, { children: renderList.map(({ key, element, isPresent }) => /* @__PURE__ */ jsx(
|
|
77
107
|
PresenceItem,
|
|
78
108
|
{
|
|
@@ -99,6 +129,175 @@ function PresenceItem({
|
|
|
99
129
|
);
|
|
100
130
|
return /* @__PURE__ */ jsx(PresenceContext.Provider, { value, children });
|
|
101
131
|
}
|
|
132
|
+
|
|
133
|
+
// src/internal/boxShadow.ts
|
|
134
|
+
var LENGTH = /^[+-]?(\d+\.?\d*|\.\d+)(px)?$/i;
|
|
135
|
+
var UNIT_LIKE = /^[+-]?(\d+\.?\d*|\.\d+)[a-z%]+$/i;
|
|
136
|
+
function invalid(input, reason) {
|
|
137
|
+
return new Error(
|
|
138
|
+
`[inertia] parseBoxShadow: ${reason} in ${JSON.stringify(input)}. Expected CSS box-shadow syntax with px lengths: '[inset] <offset-x> <offset-y> [blur] [spread] [color], ...'`
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
function splitLayers(input) {
|
|
142
|
+
const layers = [];
|
|
143
|
+
let depth = 0;
|
|
144
|
+
let start = 0;
|
|
145
|
+
for (let i = 0; i < input.length; i++) {
|
|
146
|
+
const ch = input[i];
|
|
147
|
+
if (ch === "(") depth++;
|
|
148
|
+
else if (ch === ")") depth--;
|
|
149
|
+
else if (ch === "," && depth === 0) {
|
|
150
|
+
layers.push(input.slice(start, i));
|
|
151
|
+
start = i + 1;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
layers.push(input.slice(start));
|
|
155
|
+
return layers;
|
|
156
|
+
}
|
|
157
|
+
function tokenize(layer) {
|
|
158
|
+
const tokens = [];
|
|
159
|
+
let depth = 0;
|
|
160
|
+
let current = "";
|
|
161
|
+
for (const ch of layer) {
|
|
162
|
+
if (ch === "(") depth++;
|
|
163
|
+
else if (ch === ")") depth--;
|
|
164
|
+
if (depth === 0 && /\s/.test(ch)) {
|
|
165
|
+
if (current) tokens.push(current);
|
|
166
|
+
current = "";
|
|
167
|
+
} else {
|
|
168
|
+
current += ch;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
if (current) tokens.push(current);
|
|
172
|
+
return tokens;
|
|
173
|
+
}
|
|
174
|
+
function parseBoxShadow(input) {
|
|
175
|
+
const trimmed = input.trim();
|
|
176
|
+
if (trimmed === "") throw invalid(input, "empty string");
|
|
177
|
+
if (trimmed.toLowerCase() === "none") return [];
|
|
178
|
+
return splitLayers(trimmed).map((layerString) => {
|
|
179
|
+
const tokens = tokenize(layerString.trim());
|
|
180
|
+
if (tokens.length === 0) throw invalid(input, "empty layer");
|
|
181
|
+
const lengths = [];
|
|
182
|
+
let color;
|
|
183
|
+
let inset = false;
|
|
184
|
+
for (const token of tokens) {
|
|
185
|
+
if (token.toLowerCase() === "inset") {
|
|
186
|
+
if (inset) throw invalid(input, "duplicate 'inset'");
|
|
187
|
+
inset = true;
|
|
188
|
+
} else if (LENGTH.test(token)) {
|
|
189
|
+
lengths.push(parseFloat(token));
|
|
190
|
+
} else if (UNIT_LIKE.test(token)) {
|
|
191
|
+
throw invalid(input, `unsupported unit in ${JSON.stringify(token)}`);
|
|
192
|
+
} else {
|
|
193
|
+
if (color !== void 0) throw invalid(input, "multiple colors");
|
|
194
|
+
color = token;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
if (lengths.length < 2 || lengths.length > 4) {
|
|
198
|
+
throw invalid(input, `expected 2-4 lengths, got ${lengths.length}`);
|
|
199
|
+
}
|
|
200
|
+
const [offsetX = 0, offsetY = 0, blurRadius = 0, spreadDistance = 0] = lengths;
|
|
201
|
+
if (blurRadius < 0) throw invalid(input, "negative blur radius");
|
|
202
|
+
return {
|
|
203
|
+
offsetX,
|
|
204
|
+
offsetY,
|
|
205
|
+
blurRadius,
|
|
206
|
+
spreadDistance,
|
|
207
|
+
color: color ?? "black",
|
|
208
|
+
inset
|
|
209
|
+
};
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
function resolveBoxShadowInput(input) {
|
|
213
|
+
if (input === void 0) return [];
|
|
214
|
+
if (typeof input === "string") return parseBoxShadow(input);
|
|
215
|
+
return input.map((layer) => ({
|
|
216
|
+
offsetX: coerceLength(layer.offsetX, "offsetX"),
|
|
217
|
+
offsetY: coerceLength(layer.offsetY, "offsetY"),
|
|
218
|
+
blurRadius: coerceLength(layer.blurRadius, "blurRadius"),
|
|
219
|
+
spreadDistance: coerceLength(layer.spreadDistance, "spreadDistance"),
|
|
220
|
+
color: layer.color ?? "black",
|
|
221
|
+
inset: layer.inset ?? false
|
|
222
|
+
}));
|
|
223
|
+
}
|
|
224
|
+
function pairBoxShadowLayers(from, to) {
|
|
225
|
+
const count = Math.max(from.length, to.length);
|
|
226
|
+
const pairs = [];
|
|
227
|
+
for (let i = 0; i < count; i++) {
|
|
228
|
+
const a = from[i];
|
|
229
|
+
const b = to[i];
|
|
230
|
+
const fromLayer = a ?? invisibleLayer(b.inset);
|
|
231
|
+
const toLayer = b ?? invisibleLayer(a.inset);
|
|
232
|
+
if (fromLayer.inset !== toLayer.inset) {
|
|
233
|
+
throw new Error(
|
|
234
|
+
`[inertia] useShadow: boxShadow layer ${i} is 'inset' on one side but not the other \u2014 inset cannot be interpolated. Give both sides the same inset-ness (pad with a transparent layer if needed).`
|
|
235
|
+
);
|
|
236
|
+
}
|
|
237
|
+
pairs.push({ from: fromLayer, to: toLayer });
|
|
238
|
+
}
|
|
239
|
+
return pairs;
|
|
240
|
+
}
|
|
241
|
+
function invisibleLayer(inset) {
|
|
242
|
+
return {
|
|
243
|
+
offsetX: 0,
|
|
244
|
+
offsetY: 0,
|
|
245
|
+
blurRadius: 0,
|
|
246
|
+
spreadDistance: 0,
|
|
247
|
+
color: "transparent",
|
|
248
|
+
inset
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
function coerceLength(value, field) {
|
|
252
|
+
if (value === void 0) return 0;
|
|
253
|
+
if (typeof value === "number") return value;
|
|
254
|
+
const trimmed = typeof value === "string" ? value.trim() : "";
|
|
255
|
+
if (!LENGTH.test(trimmed)) {
|
|
256
|
+
throw new Error(
|
|
257
|
+
`[inertia] boxShadow: ${field} must be a number or a px length, got ${JSON.stringify(value)}.`
|
|
258
|
+
);
|
|
259
|
+
}
|
|
260
|
+
return parseFloat(trimmed);
|
|
261
|
+
}
|
|
262
|
+
function strip(layer) {
|
|
263
|
+
const { inset: _inset, ...rest } = layer;
|
|
264
|
+
return rest;
|
|
265
|
+
}
|
|
266
|
+
function markInset(insets, i, count) {
|
|
267
|
+
const list = insets ?? new Array(count).fill(false);
|
|
268
|
+
list[i] = true;
|
|
269
|
+
return list;
|
|
270
|
+
}
|
|
271
|
+
function normalizeBoxShadow(input) {
|
|
272
|
+
const resolved = resolveBoxShadowInput(input);
|
|
273
|
+
const layers = [];
|
|
274
|
+
let insets = null;
|
|
275
|
+
for (let i = 0; i < resolved.length; i++) {
|
|
276
|
+
const layer = resolved[i];
|
|
277
|
+
layers.push(strip(layer));
|
|
278
|
+
if (layer.inset) insets = markInset(insets, i, resolved.length);
|
|
279
|
+
}
|
|
280
|
+
return { layers, insets };
|
|
281
|
+
}
|
|
282
|
+
function prepareBoxShadowAnimation(current, target) {
|
|
283
|
+
const pairs = pairBoxShadowLayers(
|
|
284
|
+
current.layers.map((layer, i) => ({
|
|
285
|
+
...layer,
|
|
286
|
+
inset: current.insets?.[i] ?? false
|
|
287
|
+
})),
|
|
288
|
+
resolveBoxShadowInput(target)
|
|
289
|
+
);
|
|
290
|
+
const from = [];
|
|
291
|
+
const to = [];
|
|
292
|
+
let insets = null;
|
|
293
|
+
for (let i = 0; i < pairs.length; i++) {
|
|
294
|
+
const pair = pairs[i];
|
|
295
|
+
from.push(strip(pair.from));
|
|
296
|
+
to.push(strip(pair.to));
|
|
297
|
+
if (pair.to.inset) insets = markInset(insets, i, pairs.length);
|
|
298
|
+
}
|
|
299
|
+
return { from, to, insets };
|
|
300
|
+
}
|
|
102
301
|
function resolveLayoutTransition(layout) {
|
|
103
302
|
if (!layout) return void 0;
|
|
104
303
|
const cfg = layout === true ? { type: "spring" } : layout;
|
|
@@ -124,68 +323,146 @@ function resolveLayoutTransition(layout) {
|
|
|
124
323
|
var REGISTRY = /* @__PURE__ */ new Map();
|
|
125
324
|
var SHARED_LAYOUT_TTL_MS = 1e3;
|
|
126
325
|
var now = () => Date.now();
|
|
127
|
-
|
|
128
|
-
|
|
326
|
+
var lastSweep = 0;
|
|
327
|
+
function sweepExpired(at) {
|
|
328
|
+
if (at - lastSweep < SHARED_LAYOUT_TTL_MS) return;
|
|
329
|
+
lastSweep = at;
|
|
330
|
+
for (const [id, entry] of REGISTRY) {
|
|
331
|
+
if (entry.expiresAt < at) REGISTRY.delete(id);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
function registerLayout(id, rect, remeasure, readStyles) {
|
|
335
|
+
const at = now();
|
|
336
|
+
sweepExpired(at);
|
|
337
|
+
REGISTRY.set(id, {
|
|
338
|
+
rect,
|
|
339
|
+
expiresAt: at + SHARED_LAYOUT_TTL_MS,
|
|
340
|
+
remeasure,
|
|
341
|
+
readStyles
|
|
342
|
+
});
|
|
129
343
|
}
|
|
130
|
-
function releaseLayout(id, rect) {
|
|
131
|
-
|
|
344
|
+
function releaseLayout(id, rect, styles) {
|
|
345
|
+
const at = now();
|
|
346
|
+
sweepExpired(at);
|
|
347
|
+
REGISTRY.set(id, { rect, expiresAt: at + SHARED_LAYOUT_TTL_MS, styles });
|
|
132
348
|
}
|
|
133
349
|
function consumeLayout(id) {
|
|
134
350
|
const entry = REGISTRY.get(id);
|
|
135
351
|
if (!entry) return void 0;
|
|
136
352
|
REGISTRY.delete(id);
|
|
137
353
|
if (entry.expiresAt < now()) return void 0;
|
|
138
|
-
return
|
|
354
|
+
return {
|
|
355
|
+
rect: entry.rect,
|
|
356
|
+
remeasure: entry.remeasure,
|
|
357
|
+
styles: entry.styles,
|
|
358
|
+
readStyles: entry.readStyles
|
|
359
|
+
};
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
// src/layout/measureWindow.ts
|
|
363
|
+
var defaultMeasurer = (node) => {
|
|
364
|
+
const measure = node?.measureInWindow;
|
|
365
|
+
if (typeof measure !== "function") return void 0;
|
|
366
|
+
let result;
|
|
367
|
+
let returned = false;
|
|
368
|
+
try {
|
|
369
|
+
measure.call(node, (x, y, width, height) => {
|
|
370
|
+
if (returned) return;
|
|
371
|
+
result = { x, y, width, height };
|
|
372
|
+
});
|
|
373
|
+
} catch {
|
|
374
|
+
returned = true;
|
|
375
|
+
return void 0;
|
|
376
|
+
}
|
|
377
|
+
returned = true;
|
|
378
|
+
return result;
|
|
379
|
+
};
|
|
380
|
+
var measurer = defaultMeasurer;
|
|
381
|
+
function usable(rect) {
|
|
382
|
+
const { x, y, width, height } = rect;
|
|
383
|
+
for (const v of [x, y, width, height]) {
|
|
384
|
+
if (typeof v !== "number" || !Number.isFinite(v)) return false;
|
|
385
|
+
}
|
|
386
|
+
return width !== 0 || height !== 0;
|
|
387
|
+
}
|
|
388
|
+
function measureWindowRect(node) {
|
|
389
|
+
const measured = measurer(node);
|
|
390
|
+
if (!measured || !usable(measured)) return void 0;
|
|
391
|
+
return toSharedRect(measured, "window");
|
|
392
|
+
}
|
|
393
|
+
function toSharedRect(rect, space) {
|
|
394
|
+
return { ...rect, space };
|
|
139
395
|
}
|
|
140
396
|
function useSharedLayout(options) {
|
|
141
|
-
const {
|
|
397
|
+
const {
|
|
398
|
+
layoutId,
|
|
399
|
+
userRef,
|
|
400
|
+
transition,
|
|
401
|
+
shouldReduceMotion,
|
|
402
|
+
userOnLayout,
|
|
403
|
+
readStyles
|
|
404
|
+
} = options;
|
|
142
405
|
const dx = useSharedValue(0);
|
|
143
406
|
const dy = useSharedValue(0);
|
|
144
407
|
const sx = useSharedValue(1);
|
|
145
408
|
const sy = useSharedValue(1);
|
|
409
|
+
const snapshot = useSharedValue(null);
|
|
410
|
+
const carryProgress = useSharedValue(0);
|
|
146
411
|
const lastRectRef = useRef(null);
|
|
147
412
|
const consumedRef = useRef(false);
|
|
413
|
+
const nodeRef = useRef(null);
|
|
148
414
|
const transitionRef = useRef(transition);
|
|
149
415
|
transitionRef.current = transition;
|
|
150
416
|
const reducedMotionRef = useRef(shouldReduceMotion);
|
|
151
417
|
reducedMotionRef.current = shouldReduceMotion;
|
|
418
|
+
const readStylesRef = useRef(readStyles);
|
|
419
|
+
readStylesRef.current = readStyles;
|
|
152
420
|
const setRef = useCallback(
|
|
153
421
|
(node) => {
|
|
422
|
+
nodeRef.current = node;
|
|
154
423
|
if (typeof userRef === "function") userRef(node);
|
|
155
424
|
else if (userRef) userRef.current = node;
|
|
156
425
|
},
|
|
157
426
|
[userRef]
|
|
158
427
|
);
|
|
428
|
+
const remeasure = useCallback(() => measureWindowRect(nodeRef.current), []);
|
|
429
|
+
const snapshotStyles = useCallback(() => readStylesRef.current?.(), []);
|
|
430
|
+
const startTransition = useCallback(
|
|
431
|
+
(source, sourceRect, target) => {
|
|
432
|
+
if (sourceRect.space !== target.space) return;
|
|
433
|
+
const shared = {
|
|
434
|
+
transition: transitionRef.current,
|
|
435
|
+
shouldReduceMotion: reducedMotionRef.current
|
|
436
|
+
};
|
|
437
|
+
applyFlip({ source: sourceRect, target, dx, dy, sx, sy, ...shared });
|
|
438
|
+
applyStyleCarry({
|
|
439
|
+
snapshot,
|
|
440
|
+
progress: carryProgress,
|
|
441
|
+
// Prefer a live read for the same reason the rect is re-measured: a
|
|
442
|
+
// still-mounted source may have moved on since its last layout.
|
|
443
|
+
styles: source.readStyles?.() ?? source.styles,
|
|
444
|
+
...shared
|
|
445
|
+
});
|
|
446
|
+
},
|
|
447
|
+
[dx, dy, sx, sy, snapshot, carryProgress]
|
|
448
|
+
);
|
|
159
449
|
const onLayout = useCallback(
|
|
160
450
|
(event) => {
|
|
161
451
|
userOnLayout?.(event);
|
|
162
452
|
if (!layoutId) return;
|
|
163
453
|
const { x, y, width, height } = event.nativeEvent.layout;
|
|
164
|
-
const rect = { x, y, width, height };
|
|
165
|
-
lastRectRef.current = rect;
|
|
166
454
|
let source;
|
|
167
455
|
if (!consumedRef.current) {
|
|
168
456
|
consumedRef.current = true;
|
|
169
457
|
source = consumeLayout(layoutId);
|
|
170
458
|
}
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
dx,
|
|
177
|
-
dy,
|
|
178
|
-
sx,
|
|
179
|
-
sy,
|
|
180
|
-
transition: transitionRef.current,
|
|
181
|
-
shouldReduceMotion: reducedMotionRef.current
|
|
182
|
-
});
|
|
183
|
-
}
|
|
459
|
+
const rect = measureWindowRect(nodeRef.current) ?? { x, y, width, height, space: "parent" };
|
|
460
|
+
lastRectRef.current = rect;
|
|
461
|
+
registerLayout(layoutId, rect, remeasure, snapshotStyles);
|
|
462
|
+
if (!source) return;
|
|
463
|
+
startTransition(source, source.remeasure?.() ?? source.rect, rect);
|
|
184
464
|
},
|
|
185
|
-
|
|
186
|
-
// exhaustive-deps would flag them — including them is harmless and
|
|
187
|
-
// silences the warning.
|
|
188
|
-
[layoutId, userOnLayout, dx, dy, sx, sy]
|
|
465
|
+
[layoutId, userOnLayout, remeasure, snapshotStyles, startTransition]
|
|
189
466
|
);
|
|
190
467
|
useEffect(() => {
|
|
191
468
|
consumedRef.current = false;
|
|
@@ -195,18 +472,49 @@ function useSharedLayout(options) {
|
|
|
195
472
|
if (!layoutId) return;
|
|
196
473
|
const rect = lastRectRef.current;
|
|
197
474
|
if (!rect) return;
|
|
198
|
-
releaseLayout(layoutId, rect);
|
|
475
|
+
releaseLayout(layoutId, rect, snapshotStyles());
|
|
199
476
|
};
|
|
200
|
-
}, [layoutId]);
|
|
477
|
+
}, [layoutId, snapshotStyles]);
|
|
478
|
+
useEffect(
|
|
479
|
+
() => () => {
|
|
480
|
+
cancelAnimation(dx);
|
|
481
|
+
cancelAnimation(dy);
|
|
482
|
+
cancelAnimation(sx);
|
|
483
|
+
cancelAnimation(sy);
|
|
484
|
+
cancelAnimation(carryProgress);
|
|
485
|
+
},
|
|
486
|
+
// The FLIP shared values are identity-stable per hook instance.
|
|
487
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
488
|
+
[]
|
|
489
|
+
);
|
|
201
490
|
return useMemo(
|
|
202
491
|
() => ({
|
|
203
492
|
flip: { dx, dy, sx, sy },
|
|
493
|
+
carry: { snapshot, progress: carryProgress },
|
|
204
494
|
setRef,
|
|
205
495
|
onLayout
|
|
206
496
|
}),
|
|
207
|
-
[dx, dy, sx, sy, setRef, onLayout]
|
|
497
|
+
[dx, dy, sx, sy, snapshot, carryProgress, setRef, onLayout]
|
|
498
|
+
);
|
|
499
|
+
}
|
|
500
|
+
function legBuilder(transition) {
|
|
501
|
+
if (transition?.type === "timing") {
|
|
502
|
+
const duration = transition.duration ?? 300;
|
|
503
|
+
return (from, to) => withSequence(
|
|
504
|
+
withTiming(from, { duration: 0 }),
|
|
505
|
+
withTiming(to, { duration })
|
|
506
|
+
);
|
|
507
|
+
}
|
|
508
|
+
const springCfg = transition?.type === "spring" ? { ...DEFAULT_SPRING, ...transition } : { type: "spring", ...DEFAULT_SPRING };
|
|
509
|
+
const springParams = springToReanimated(springCfg);
|
|
510
|
+
return (from, to) => withSequence(
|
|
511
|
+
withTiming(from, { duration: 0 }),
|
|
512
|
+
withSpring(to, springParams)
|
|
208
513
|
);
|
|
209
514
|
}
|
|
515
|
+
function shouldSnap(transition, shouldReduceMotion) {
|
|
516
|
+
return shouldReduceMotion || transition?.type === "no-animation";
|
|
517
|
+
}
|
|
210
518
|
function applyFlip(args) {
|
|
211
519
|
const { source, target, dx, dy, sx, sy, transition, shouldReduceMotion } = args;
|
|
212
520
|
const sourceCenterX = source.x + source.width / 2;
|
|
@@ -217,58 +525,29 @@ function applyFlip(args) {
|
|
|
217
525
|
const deltaY = sourceCenterY - targetCenterY;
|
|
218
526
|
const scaleX = target.width > 0 ? source.width / target.width : 1;
|
|
219
527
|
const scaleY = target.height > 0 ? source.height / target.height : 1;
|
|
220
|
-
if (shouldReduceMotion) {
|
|
221
|
-
dx.value = 0;
|
|
222
|
-
dy.value = 0;
|
|
223
|
-
sx.value = 1;
|
|
224
|
-
sy.value = 1;
|
|
225
|
-
return;
|
|
226
|
-
}
|
|
227
|
-
if (transition?.type === "no-animation") {
|
|
528
|
+
if (shouldSnap(transition, shouldReduceMotion)) {
|
|
228
529
|
dx.value = 0;
|
|
229
530
|
dy.value = 0;
|
|
230
531
|
sx.value = 1;
|
|
231
532
|
sy.value = 1;
|
|
232
533
|
return;
|
|
233
534
|
}
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
withTiming(1, { duration })
|
|
247
|
-
);
|
|
248
|
-
sy.value = withSequence(
|
|
249
|
-
withTiming(scaleY, { duration: 0 }),
|
|
250
|
-
withTiming(1, { duration })
|
|
251
|
-
);
|
|
535
|
+
const leg = legBuilder(transition);
|
|
536
|
+
dx.value = leg(deltaX, 0);
|
|
537
|
+
dy.value = leg(deltaY, 0);
|
|
538
|
+
sx.value = leg(scaleX, 1);
|
|
539
|
+
sy.value = leg(scaleY, 1);
|
|
540
|
+
}
|
|
541
|
+
function applyStyleCarry(args) {
|
|
542
|
+
const { snapshot, progress, styles, transition, shouldReduceMotion } = args;
|
|
543
|
+
if (!styles) return;
|
|
544
|
+
if (shouldSnap(transition, shouldReduceMotion)) {
|
|
545
|
+
snapshot.value = null;
|
|
546
|
+
progress.value = 0;
|
|
252
547
|
return;
|
|
253
548
|
}
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
dx.value = withSequence(
|
|
257
|
-
withTiming(deltaX, { duration: 0 }),
|
|
258
|
-
withSpring(0, springParams)
|
|
259
|
-
);
|
|
260
|
-
dy.value = withSequence(
|
|
261
|
-
withTiming(deltaY, { duration: 0 }),
|
|
262
|
-
withSpring(0, springParams)
|
|
263
|
-
);
|
|
264
|
-
sx.value = withSequence(
|
|
265
|
-
withTiming(scaleX, { duration: 0 }),
|
|
266
|
-
withSpring(1, springParams)
|
|
267
|
-
);
|
|
268
|
-
sy.value = withSequence(
|
|
269
|
-
withTiming(scaleY, { duration: 0 }),
|
|
270
|
-
withSpring(1, springParams)
|
|
271
|
-
);
|
|
549
|
+
snapshot.value = styles;
|
|
550
|
+
progress.value = legBuilder(transition)(1, 0);
|
|
272
551
|
}
|
|
273
552
|
var alreadyChecked = false;
|
|
274
553
|
function ensureReanimatedInstalled() {
|
|
@@ -282,7 +561,7 @@ function ensureReanimatedInstalled() {
|
|
|
282
561
|
const major = parseInt(version.split(".")[0] ?? "0", 10);
|
|
283
562
|
if (major < 4) {
|
|
284
563
|
console.error(
|
|
285
|
-
`[inertia] react-native-reanimated
|
|
564
|
+
`[inertia] react-native-reanimated ${version} is installed, but @rootnative/inertia requires 4.0.0 or later. Upgrade with \`pnpm add react-native-reanimated@^4\` (or your package manager's equivalent).`
|
|
286
565
|
);
|
|
287
566
|
return;
|
|
288
567
|
}
|
|
@@ -325,15 +604,28 @@ var COLOR_KEYS = [
|
|
|
325
604
|
"shadowColor"
|
|
326
605
|
];
|
|
327
606
|
var SHADOW_OFFSET_KEYS = ["shadowOffsetWidth", "shadowOffsetHeight"];
|
|
607
|
+
var STRUCTURED_KEYS = ["boxShadow"];
|
|
608
|
+
var SHARED_STYLE_KEYS = [
|
|
609
|
+
"opacity",
|
|
610
|
+
"borderRadius",
|
|
611
|
+
"backgroundColor",
|
|
612
|
+
"borderColor",
|
|
613
|
+
"color",
|
|
614
|
+
"tintColor"
|
|
615
|
+
];
|
|
328
616
|
var ALL_KEYS = [
|
|
329
617
|
...TRANSFORM_KEYS,
|
|
330
618
|
...NUMERIC_TOP_LEVEL_KEYS,
|
|
331
619
|
...COLOR_KEYS,
|
|
332
|
-
...SHADOW_OFFSET_KEYS
|
|
620
|
+
...SHADOW_OFFSET_KEYS,
|
|
621
|
+
...STRUCTURED_KEYS
|
|
333
622
|
];
|
|
334
623
|
var TRANSFORM_KEY_SET = new Set(TRANSFORM_KEYS);
|
|
335
624
|
var COLOR_KEY_SET = new Set(COLOR_KEYS);
|
|
336
625
|
var SHADOW_OFFSET_KEY_SET = new Set(SHADOW_OFFSET_KEYS);
|
|
626
|
+
var STRUCTURED_KEY_SET = new Set(STRUCTURED_KEYS);
|
|
627
|
+
var SHARED_STYLE_KEY_SET = new Set(SHARED_STYLE_KEYS);
|
|
628
|
+
var NO_BOX_SHADOW = Object.freeze([]);
|
|
337
629
|
var GESTURE_LAYER_NAMES = [
|
|
338
630
|
"hovered",
|
|
339
631
|
"focused",
|
|
@@ -368,7 +660,8 @@ var DEFAULT_RESTING = {
|
|
|
368
660
|
tintColor: "transparent",
|
|
369
661
|
shadowColor: "transparent",
|
|
370
662
|
shadowOffsetWidth: 0,
|
|
371
|
-
shadowOffsetHeight: 0
|
|
663
|
+
shadowOffsetHeight: 0,
|
|
664
|
+
boxShadow: NO_BOX_SHADOW
|
|
372
665
|
};
|
|
373
666
|
function transitionFor(prop, transition) {
|
|
374
667
|
if (!transition) return void 0;
|
|
@@ -459,6 +752,16 @@ function createMotionComponent(Component) {
|
|
|
459
752
|
const [focused, setFocused] = useState(false);
|
|
460
753
|
const [focusVisible, setFocusVisible] = useState(false);
|
|
461
754
|
const [hovered, setHovered] = useState(false);
|
|
755
|
+
const hasLayoutId = layoutId !== void 0;
|
|
756
|
+
let flatStyle;
|
|
757
|
+
let flatStyleRead = false;
|
|
758
|
+
const getFlatStyle = () => {
|
|
759
|
+
if (!flatStyleRead) {
|
|
760
|
+
flatStyleRead = true;
|
|
761
|
+
flatStyle = StyleSheet.flatten(style);
|
|
762
|
+
}
|
|
763
|
+
return flatStyle;
|
|
764
|
+
};
|
|
462
765
|
const touched = /* @__PURE__ */ new Set();
|
|
463
766
|
collectTouchedKeys(touched, animateRecord);
|
|
464
767
|
if (initialRecord) collectTouchedKeys(touched, initialRecord);
|
|
@@ -480,9 +783,19 @@ function createMotionComponent(Component) {
|
|
|
480
783
|
}
|
|
481
784
|
}
|
|
482
785
|
if (exitRecord) collectTouchedKeys(touched, exitRecord);
|
|
786
|
+
if (hasLayoutId) {
|
|
787
|
+
const flat = getFlatStyle();
|
|
788
|
+
if (flat) {
|
|
789
|
+
for (const key of SHARED_STYLE_KEYS) {
|
|
790
|
+
if (touched.has(key)) continue;
|
|
791
|
+
if (styleValueFor(flat, key) !== void 0) touched.add(key);
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
}
|
|
483
795
|
const activeKeysRef = useRef(null);
|
|
484
796
|
const hasTransformRef = useRef(false);
|
|
485
797
|
const hasShadowOffsetRef = useRef(false);
|
|
798
|
+
const hasBoxShadowRef = useRef(false);
|
|
486
799
|
const prevActive = activeKeysRef.current;
|
|
487
800
|
let grew = prevActive === null;
|
|
488
801
|
if (!grew && prevActive) {
|
|
@@ -503,24 +816,83 @@ function createMotionComponent(Component) {
|
|
|
503
816
|
hasShadowOffsetRef.current = activeKeysRef.current.some(
|
|
504
817
|
(k) => SHADOW_OFFSET_KEY_SET.has(k)
|
|
505
818
|
);
|
|
819
|
+
hasBoxShadowRef.current = activeKeysRef.current.includes("boxShadow");
|
|
820
|
+
if (__DEV__ && hasBoxShadowRef.current && activeKeysRef.current.some(
|
|
821
|
+
(k) => k !== "boxShadow" && k.startsWith("shadow")
|
|
822
|
+
)) {
|
|
823
|
+
warnOnce(
|
|
824
|
+
"boxShadow-with-native-shadow",
|
|
825
|
+
"[inertia] `boxShadow` is animated alongside the native `shadow*` keys on the same element, which applies two shadow systems at once. Pick one: `boxShadow` for the cross-platform CSS form, or the native `shadow*` keys."
|
|
826
|
+
);
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
const drivenNow = /* @__PURE__ */ new Set();
|
|
830
|
+
collectTouchedKeys(drivenNow, animateRecord);
|
|
831
|
+
if (initialRecord) collectTouchedKeys(drivenNow, initialRecord);
|
|
832
|
+
if (isExiting && exitRecord) collectTouchedKeys(drivenNow, exitRecord);
|
|
833
|
+
const everDrivenRef = useRef(/* @__PURE__ */ new Set());
|
|
834
|
+
for (const k of drivenNow) everDrivenRef.current.add(k);
|
|
835
|
+
const activeKeys = activeKeysRef.current;
|
|
836
|
+
const everDriven = everDrivenRef.current;
|
|
837
|
+
const styleResting = {};
|
|
838
|
+
let styleRestingShadow = null;
|
|
839
|
+
if (activeKeys.some((k) => !everDriven.has(k))) {
|
|
840
|
+
const flat = getFlatStyle();
|
|
841
|
+
if (flat) {
|
|
842
|
+
for (const key of activeKeys) {
|
|
843
|
+
if (everDriven.has(key)) continue;
|
|
844
|
+
if (key === "boxShadow") {
|
|
845
|
+
const raw = flat.boxShadow;
|
|
846
|
+
if (raw !== void 0) {
|
|
847
|
+
styleRestingShadow = normalizeBoxShadow(raw);
|
|
848
|
+
styleResting.boxShadow = styleRestingShadow.layers;
|
|
849
|
+
}
|
|
850
|
+
continue;
|
|
851
|
+
}
|
|
852
|
+
const v = styleValueFor(flat, key);
|
|
853
|
+
if (v !== void 0) styleResting[key] = v;
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
const boxShadowSeedRef = useRef(null);
|
|
858
|
+
if (boxShadowSeedRef.current === null) {
|
|
859
|
+
const source = initial === false ? animateRecord.boxShadow : initialRecord?.boxShadow ?? animateRecord.boxShadow;
|
|
860
|
+
boxShadowSeedRef.current = source !== void 0 ? normalizeBoxShadow(source) : styleRestingShadow ?? {
|
|
861
|
+
layers: [...NO_BOX_SHADOW],
|
|
862
|
+
insets: null
|
|
863
|
+
};
|
|
506
864
|
}
|
|
507
865
|
const sharedValues = useAnimatableSharedValues((key) => {
|
|
866
|
+
if (key === "boxShadow") return boxShadowSeedRef.current.layers;
|
|
508
867
|
if (SHADOW_OFFSET_KEY_SET.has(key)) {
|
|
509
868
|
const axis = shadowOffsetAxisFor(key);
|
|
510
869
|
if (initial === false) {
|
|
511
|
-
return shadowOffsetAxisValue(animateRecord.shadowOffset, axis) ?? DEFAULT_RESTING[key];
|
|
870
|
+
return shadowOffsetAxisValue(animateRecord.shadowOffset, axis) ?? styleResting[key] ?? DEFAULT_RESTING[key];
|
|
512
871
|
}
|
|
513
872
|
return shadowOffsetAxisValue(
|
|
514
873
|
initialRecord?.shadowOffset,
|
|
515
874
|
axis
|
|
516
|
-
) ?? shadowOffsetAxisValue(animateRecord.shadowOffset, axis) ?? DEFAULT_RESTING[key];
|
|
875
|
+
) ?? shadowOffsetAxisValue(animateRecord.shadowOffset, axis) ?? styleResting[key] ?? DEFAULT_RESTING[key];
|
|
517
876
|
}
|
|
518
877
|
if (initial === false) {
|
|
519
878
|
const a = animateRecord[key];
|
|
520
|
-
return restValue(a) ?? DEFAULT_RESTING[key];
|
|
879
|
+
return restValue(a) ?? styleResting[key] ?? DEFAULT_RESTING[key];
|
|
521
880
|
}
|
|
522
|
-
return initialRecord?.[key] ?? restValue(animateRecord[key]) ?? DEFAULT_RESTING[key];
|
|
881
|
+
return initialRecord?.[key] ?? restValue(animateRecord[key]) ?? styleResting[key] ?? DEFAULT_RESTING[key];
|
|
523
882
|
});
|
|
883
|
+
const boxShadowInsets = useSharedValue(
|
|
884
|
+
boxShadowSeedRef.current.insets
|
|
885
|
+
);
|
|
886
|
+
const styleRestingSig = stableSig(styleResting);
|
|
887
|
+
useEffect(() => {
|
|
888
|
+
for (const key of activeKeysRef.current) {
|
|
889
|
+
if (everDrivenRef.current.has(key)) continue;
|
|
890
|
+
sharedValues[key].value = styleResting[key] ?? DEFAULT_RESTING[key];
|
|
891
|
+
if (key === "boxShadow") {
|
|
892
|
+
boxShadowInsets.value = styleRestingShadow?.insets ?? null;
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
}, [styleRestingSig]);
|
|
524
896
|
const pressedProgress = useSharedValue(0);
|
|
525
897
|
const focusedProgress = useSharedValue(0);
|
|
526
898
|
const focusVisibleProgress = useSharedValue(0);
|
|
@@ -563,24 +935,49 @@ function createMotionComponent(Component) {
|
|
|
563
935
|
if (isExiting) safeToRemoveRef.current?.();
|
|
564
936
|
}
|
|
565
937
|
};
|
|
938
|
+
const configFor = (key) => shouldReduceMotion ? { type: "no-animation" } : transitionFor(
|
|
939
|
+
SHADOW_OFFSET_KEY_SET.has(key) ? "shadowOffset" : key,
|
|
940
|
+
transition
|
|
941
|
+
);
|
|
566
942
|
let transformPending = 0;
|
|
567
943
|
for (const k of ALL_KEYS) {
|
|
568
|
-
if (TRANSFORM_KEY_SET.has(k)
|
|
569
|
-
|
|
570
|
-
|
|
944
|
+
if (!TRANSFORM_KEY_SET.has(k) || baseRecord[k] === void 0) continue;
|
|
945
|
+
if (!reachesTerminalPhase(configFor(k))) continue;
|
|
946
|
+
transformPending++;
|
|
571
947
|
}
|
|
572
948
|
const transformGroup = transformPending > 0 ? { remaining: transformPending } : void 0;
|
|
573
949
|
for (const key of ALL_KEYS) {
|
|
950
|
+
if (key === "boxShadow") {
|
|
951
|
+
const shadowTarget = baseRecord.boxShadow;
|
|
952
|
+
if (shadowTarget === void 0) continue;
|
|
953
|
+
const cfg2 = configFor("boxShadow");
|
|
954
|
+
if (isExiting && reachesTerminalPhase(cfg2)) pending++;
|
|
955
|
+
driveBoxShadow(
|
|
956
|
+
sharedValues.boxShadow,
|
|
957
|
+
boxShadowInsets,
|
|
958
|
+
shadowTarget,
|
|
959
|
+
cfg2,
|
|
960
|
+
makeKeyCallbackFactory(
|
|
961
|
+
"boxShadow",
|
|
962
|
+
sharedValues.boxShadow,
|
|
963
|
+
// The declared target, not the padded one — the padding is an
|
|
964
|
+
// implementation detail of getting Reanimated to interpolate.
|
|
965
|
+
shadowTarget,
|
|
966
|
+
onAnimationEndRef,
|
|
967
|
+
{ stepCount: 1, totalIterations: totalIterationsOf(cfg2) },
|
|
968
|
+
isExiting ? onSettle : void 0,
|
|
969
|
+
void 0
|
|
970
|
+
)
|
|
971
|
+
);
|
|
972
|
+
continue;
|
|
973
|
+
}
|
|
574
974
|
const target = SHADOW_OFFSET_KEY_SET.has(key) ? shadowOffsetAxisValue(
|
|
575
975
|
baseRecord.shadowOffset,
|
|
576
976
|
shadowOffsetAxisFor(key)
|
|
577
977
|
) : baseRecord[key];
|
|
578
978
|
if (target === void 0) continue;
|
|
579
|
-
const cfg =
|
|
580
|
-
|
|
581
|
-
transition
|
|
582
|
-
);
|
|
583
|
-
if (isExiting) pending++;
|
|
979
|
+
const cfg = configFor(key);
|
|
980
|
+
if (isExiting && reachesTerminalPhase(cfg)) pending++;
|
|
584
981
|
const factory = makeKeyCallbackFactory(
|
|
585
982
|
key,
|
|
586
983
|
sharedValues[key],
|
|
@@ -639,19 +1036,34 @@ function createMotionComponent(Component) {
|
|
|
639
1036
|
isExiting,
|
|
640
1037
|
shouldReduceMotion
|
|
641
1038
|
);
|
|
1039
|
+
const readSharedStyles = () => {
|
|
1040
|
+
if (!hasLayoutId) return void 0;
|
|
1041
|
+
const out = {};
|
|
1042
|
+
let any = false;
|
|
1043
|
+
for (const key of activeKeysRef.current) {
|
|
1044
|
+
if (!SHARED_STYLE_KEY_SET.has(key)) continue;
|
|
1045
|
+
const v = sharedValues[key].value;
|
|
1046
|
+
if (typeof v !== "number" && typeof v !== "string") continue;
|
|
1047
|
+
out[key] = v;
|
|
1048
|
+
any = true;
|
|
1049
|
+
}
|
|
1050
|
+
return any ? out : void 0;
|
|
1051
|
+
};
|
|
642
1052
|
const sharedLayout = useSharedLayout({
|
|
643
1053
|
layoutId,
|
|
644
1054
|
userRef: ref,
|
|
645
1055
|
transition: isTopLevelTransition(transition) ? transition : void 0,
|
|
646
1056
|
shouldReduceMotion,
|
|
647
|
-
userOnLayout
|
|
1057
|
+
userOnLayout,
|
|
1058
|
+
readStyles: readSharedStyles
|
|
648
1059
|
});
|
|
649
1060
|
const flip = sharedLayout.flip;
|
|
650
|
-
const
|
|
1061
|
+
const carry = sharedLayout.carry;
|
|
651
1062
|
const animatedStyle = useAnimatedStyle(() => {
|
|
652
|
-
const
|
|
1063
|
+
const activeKeys2 = activeKeysRef.current;
|
|
653
1064
|
const hasTransform = hasTransformRef.current;
|
|
654
1065
|
const hasShadowOffset = hasShadowOffsetRef.current;
|
|
1066
|
+
const hasBoxShadow = hasBoxShadowRef.current;
|
|
655
1067
|
const out = {};
|
|
656
1068
|
const transform = [];
|
|
657
1069
|
let shadowOffsetW = 0;
|
|
@@ -660,12 +1072,15 @@ function createMotionComponent(Component) {
|
|
|
660
1072
|
const pf = focusedProgress.value;
|
|
661
1073
|
const pfv = focusVisibleProgress.value;
|
|
662
1074
|
const pp = pressedProgress.value;
|
|
1075
|
+
const carried = carry.snapshot.value;
|
|
1076
|
+
const pc = carry.progress.value;
|
|
663
1077
|
const layers = gestureSV.value;
|
|
664
1078
|
const hoveredLayer = layers ? layers.hovered : null;
|
|
665
1079
|
const focusedLayer = layers ? layers.focused : null;
|
|
666
1080
|
const focusVisibleLayer = layers ? layers.focusVisible : null;
|
|
667
1081
|
const pressedLayer = layers ? layers.pressed : null;
|
|
668
|
-
for (const key of
|
|
1082
|
+
for (const key of activeKeys2) {
|
|
1083
|
+
if (key === "boxShadow") continue;
|
|
669
1084
|
let v = sharedValues[key].value;
|
|
670
1085
|
const isColor = COLOR_KEY_SET.has(key);
|
|
671
1086
|
if (hoveredLayer && ph > 0 && hoveredLayer[key] !== void 0) {
|
|
@@ -684,6 +1099,12 @@ function createMotionComponent(Component) {
|
|
|
684
1099
|
const t = pressedLayer[key];
|
|
685
1100
|
v = isColor ? interpolateColor(pp, [0, 1], [v, t]) : v + (t - v) * pp;
|
|
686
1101
|
}
|
|
1102
|
+
if (carried !== null && pc > 0) {
|
|
1103
|
+
const s = carried[key];
|
|
1104
|
+
if (s !== void 0) {
|
|
1105
|
+
v = isColor ? interpolateColor(pc, [0, 1], [v, s]) : v + (s - v) * pc;
|
|
1106
|
+
}
|
|
1107
|
+
}
|
|
687
1108
|
if (TRANSFORM_KEY_SET.has(key)) {
|
|
688
1109
|
transform.push(
|
|
689
1110
|
ROTATION_KEYS.has(key) ? { [key]: `${v}deg` } : { [key]: v }
|
|
@@ -706,6 +1127,19 @@ function createMotionComponent(Component) {
|
|
|
706
1127
|
if (hasShadowOffset) {
|
|
707
1128
|
out.shadowOffset = { width: shadowOffsetW, height: shadowOffsetH };
|
|
708
1129
|
}
|
|
1130
|
+
if (hasBoxShadow) {
|
|
1131
|
+
const layers2 = sharedValues.boxShadow.value;
|
|
1132
|
+
const insets = boxShadowInsets.value;
|
|
1133
|
+
if (insets === null) {
|
|
1134
|
+
out.boxShadow = layers2;
|
|
1135
|
+
} else {
|
|
1136
|
+
const withInset = [];
|
|
1137
|
+
for (let i = 0; i < layers2.length; i++) {
|
|
1138
|
+
withInset.push({ ...layers2[i], inset: insets[i] });
|
|
1139
|
+
}
|
|
1140
|
+
out.boxShadow = withInset;
|
|
1141
|
+
}
|
|
1142
|
+
}
|
|
709
1143
|
return out;
|
|
710
1144
|
});
|
|
711
1145
|
const mergedStyle = useMemo(
|
|
@@ -780,7 +1214,9 @@ function useAnimatableSharedValues(init) {
|
|
|
780
1214
|
const width = useSharedValue(init("width"));
|
|
781
1215
|
const height = useSharedValue(init("height"));
|
|
782
1216
|
const borderRadius = useSharedValue(init("borderRadius"));
|
|
783
|
-
const shadowOpacity = useSharedValue(
|
|
1217
|
+
const shadowOpacity = useSharedValue(
|
|
1218
|
+
init("shadowOpacity")
|
|
1219
|
+
);
|
|
784
1220
|
const shadowRadius = useSharedValue(init("shadowRadius"));
|
|
785
1221
|
const elevation = useSharedValue(init("elevation"));
|
|
786
1222
|
const backgroundColor = useSharedValue(
|
|
@@ -796,6 +1232,7 @@ function useAnimatableSharedValues(init) {
|
|
|
796
1232
|
const shadowOffsetHeight = useSharedValue(
|
|
797
1233
|
init("shadowOffsetHeight")
|
|
798
1234
|
);
|
|
1235
|
+
const boxShadow = useSharedValue(init("boxShadow"));
|
|
799
1236
|
const ref = useRef(null);
|
|
800
1237
|
if (ref.current === null) {
|
|
801
1238
|
ref.current = {
|
|
@@ -820,7 +1257,8 @@ function useAnimatableSharedValues(init) {
|
|
|
820
1257
|
tintColor,
|
|
821
1258
|
shadowColor,
|
|
822
1259
|
shadowOffsetWidth,
|
|
823
|
-
shadowOffsetHeight
|
|
1260
|
+
shadowOffsetHeight,
|
|
1261
|
+
boxShadow
|
|
824
1262
|
};
|
|
825
1263
|
}
|
|
826
1264
|
const map = ref.current;
|
|
@@ -905,6 +1343,54 @@ function shadowOffsetAxisFor(key) {
|
|
|
905
1343
|
function shadowOffsetAxisValue(source, axis) {
|
|
906
1344
|
return source?.[axis];
|
|
907
1345
|
}
|
|
1346
|
+
function driveBoxShadow(slot, insetSlot, target, cfg, factory) {
|
|
1347
|
+
const currentLayers = Array.isArray(slot.value) ? slot.value : NO_BOX_SHADOW;
|
|
1348
|
+
const { from, to, insets } = prepareBoxShadowAnimation(
|
|
1349
|
+
{ layers: [...currentLayers], insets: insetSlot.value },
|
|
1350
|
+
target
|
|
1351
|
+
);
|
|
1352
|
+
insetSlot.value = insets;
|
|
1353
|
+
if (from.length !== currentLayers.length) slot.value = from;
|
|
1354
|
+
slot.value = resolveTransition(
|
|
1355
|
+
cfg,
|
|
1356
|
+
to,
|
|
1357
|
+
factory?.("animation", void 0)
|
|
1358
|
+
);
|
|
1359
|
+
}
|
|
1360
|
+
function styleValueFor(flat, key) {
|
|
1361
|
+
if (SHADOW_OFFSET_KEY_SET.has(key)) {
|
|
1362
|
+
return shadowOffsetAxisValue(
|
|
1363
|
+
flat.shadowOffset,
|
|
1364
|
+
shadowOffsetAxisFor(key)
|
|
1365
|
+
);
|
|
1366
|
+
}
|
|
1367
|
+
if (TRANSFORM_KEY_SET.has(key)) {
|
|
1368
|
+
const list = flat.transform;
|
|
1369
|
+
if (!Array.isArray(list)) return void 0;
|
|
1370
|
+
for (const entry of list) {
|
|
1371
|
+
if (!entry || typeof entry !== "object") continue;
|
|
1372
|
+
const v2 = entry[key];
|
|
1373
|
+
if (v2 === void 0) continue;
|
|
1374
|
+
if (ROTATION_KEYS.has(key)) return parseAngleDegrees(v2);
|
|
1375
|
+
return typeof v2 === "number" ? v2 : void 0;
|
|
1376
|
+
}
|
|
1377
|
+
return void 0;
|
|
1378
|
+
}
|
|
1379
|
+
const v = flat[key];
|
|
1380
|
+
if (COLOR_KEY_SET.has(key)) {
|
|
1381
|
+
return typeof v === "string" || typeof v === "number" ? v : void 0;
|
|
1382
|
+
}
|
|
1383
|
+
return typeof v === "number" ? v : void 0;
|
|
1384
|
+
}
|
|
1385
|
+
function parseAngleDegrees(v) {
|
|
1386
|
+
if (typeof v === "number") return v;
|
|
1387
|
+
if (typeof v !== "string") return void 0;
|
|
1388
|
+
const deg = /^(-?\d*\.?\d+)deg$/.exec(v);
|
|
1389
|
+
if (deg) return Number(deg[1]);
|
|
1390
|
+
const rad = /^(-?\d*\.?\d+)rad$/.exec(v);
|
|
1391
|
+
if (rad) return Number(rad[1]) * 180 / Math.PI;
|
|
1392
|
+
return void 0;
|
|
1393
|
+
}
|
|
908
1394
|
function collectTouchedKeys(touched, record) {
|
|
909
1395
|
for (const k of ALL_KEYS) {
|
|
910
1396
|
if (k in record) touched.add(k);
|
|
@@ -928,6 +1414,9 @@ function totalIterationsOf(cfg) {
|
|
|
928
1414
|
if (r.count === "infinite") return Number.POSITIVE_INFINITY;
|
|
929
1415
|
return r.count;
|
|
930
1416
|
}
|
|
1417
|
+
function reachesTerminalPhase(cfg) {
|
|
1418
|
+
return Number.isFinite(totalIterationsOf(cfg));
|
|
1419
|
+
}
|
|
931
1420
|
function targetEndValue(v) {
|
|
932
1421
|
if (v === void 0) return void 0;
|
|
933
1422
|
if (typeof v === "number" || typeof v === "string") return v;
|
|
@@ -984,6 +1473,15 @@ function resolveGestureLayers(gesture) {
|
|
|
984
1473
|
if (!subState) continue;
|
|
985
1474
|
const resolved = {};
|
|
986
1475
|
for (const key of ALL_KEYS) {
|
|
1476
|
+
if (STRUCTURED_KEY_SET.has(key)) {
|
|
1477
|
+
if (__DEV__ && subState[key] !== void 0) {
|
|
1478
|
+
warnOnce(
|
|
1479
|
+
`gesture-structured-${layer}-${key}`,
|
|
1480
|
+
`[inertia] \`${key}\` is not supported inside \`gesture.${layer}\` and will be ignored. Drive it from \`animate\` (optionally via a variant keyed off the same state), or interpolate it yourself with \`useShadow\`.`
|
|
1481
|
+
);
|
|
1482
|
+
}
|
|
1483
|
+
continue;
|
|
1484
|
+
}
|
|
987
1485
|
if (SHADOW_OFFSET_KEY_SET.has(key)) {
|
|
988
1486
|
const axis = shadowOffsetAxisFor(key);
|
|
989
1487
|
const so = subState.shadowOffset;
|
|
@@ -1073,4 +1571,4 @@ function compose(user, ours) {
|
|
|
1073
1571
|
};
|
|
1074
1572
|
}
|
|
1075
1573
|
|
|
1076
|
-
export { Presence, createMotionComponent, usePresence };
|
|
1574
|
+
export { Presence, createMotionComponent, pairBoxShadowLayers, resolveBoxShadowInput, usePresence };
|