@yorozu/animations 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +199 -0
- package/index.d.ts +1 -0
- package/index.js +2229 -0
- package/package.json +21 -0
- package/playground/src/catalog.d.ts +8 -0
- package/playground/src/level-switch.d.ts +1 -0
- package/playground/src/level.d.ts +7 -0
- package/playground/src/main.d.ts +0 -0
- package/playground/src/overlay.d.ts +2 -0
- package/playground/src/testers/digit-flip.d.ts +1 -0
- package/playground/src/testers/dock.d.ts +1 -0
- package/playground/src/testers/fade.d.ts +1 -0
- package/playground/src/testers/list-layer.d.ts +1 -0
- package/playground/src/testers/list-reorder.d.ts +1 -0
- package/playground/src/testers/pinch-zoom.d.ts +1 -0
- package/playground/src/testers/popover.d.ts +1 -0
- package/playground/src/testers/presence-pop.d.ts +1 -0
- package/playground/src/testers/ripple.d.ts +1 -0
- package/playground/src/testers/scroll-tween.d.ts +1 -0
- package/playground/src/testers/send-flight.d.ts +1 -0
- package/playground/src/testers/shared-element.d.ts +1 -0
- package/playground/src/testers/sliding-indicator.d.ts +1 -0
- package/playground/src/testers/spoiler.d.ts +1 -0
- package/playground/src/testers/swipe-reveal.d.ts +1 -0
- package/playground/src/testers/tabs.d.ts +1 -0
- package/playground/src/testers/view-slide-modes.d.ts +3 -0
- package/playground/src/testers/view-slide.d.ts +1 -0
- package/playground/src/testers/waveform.d.ts +1 -0
- package/src/core/level.d.ts +10 -0
- package/src/core/playback.d.ts +7 -0
- package/src/core/raf.d.ts +1 -0
- package/src/core/reduced-motion.d.ts +1 -0
- package/src/core/styles.d.ts +2 -0
- package/src/core/tween.d.ts +11 -0
- package/src/core/types.d.ts +9 -0
- package/src/digit-flip/play.d.ts +12 -0
- package/src/digit-flip/slots.d.ts +14 -0
- package/src/dock/dock.d.ts +23 -0
- package/src/dock/transforms.d.ts +14 -0
- package/src/fade/fade.d.ts +11 -0
- package/src/index.d.ts +45 -0
- package/src/list-reorder/classify.d.ts +10 -0
- package/src/list-reorder/reorder.d.ts +19 -0
- package/src/pinch-zoom/math.d.ts +21 -0
- package/src/pinch-zoom/pinch.d.ts +15 -0
- package/src/popover/popover.d.ts +14 -0
- package/src/rect/fit.d.ts +5 -0
- package/src/rect/types.d.ts +17 -0
- package/src/ripple/ripple.d.ts +13 -0
- package/src/scroll-tween/scroll.d.ts +8 -0
- package/src/send-flight/flight.d.ts +14 -0
- package/src/shared-element/math.d.ts +45 -0
- package/src/shared-element/player.d.ts +52 -0
- package/src/sliding-indicator/indicator.d.ts +15 -0
- package/src/spoiler/spoiler.d.ts +14 -0
- package/src/swipe-reveal/swipe.d.ts +16 -0
- package/src/view-slide/session.d.ts +27 -0
- package/src/view-slide/transforms.d.ts +34 -0
- package/src/waveform/decode.d.ts +2 -0
package/index.js
ADDED
|
@@ -0,0 +1,2229 @@
|
|
|
1
|
+
//#region src/core/reduced-motion.ts
|
|
2
|
+
function prefersReducedMotion() {
|
|
3
|
+
if (typeof window === "undefined" || typeof window.matchMedia !== "function") return false;
|
|
4
|
+
return window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
5
|
+
}
|
|
6
|
+
//#endregion
|
|
7
|
+
//#region src/core/raf.ts
|
|
8
|
+
function dualRaf() {
|
|
9
|
+
return new Promise((resolve) => {
|
|
10
|
+
requestAnimationFrame(() => {
|
|
11
|
+
requestAnimationFrame(() => resolve());
|
|
12
|
+
});
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
//#endregion
|
|
16
|
+
//#region src/core/level.ts
|
|
17
|
+
var ANIMATION_LEVELS = [
|
|
18
|
+
"low",
|
|
19
|
+
"med",
|
|
20
|
+
"high"
|
|
21
|
+
];
|
|
22
|
+
var DEFAULT_ANIMATION_LEVEL = "high";
|
|
23
|
+
function isAnimationLevel(value) {
|
|
24
|
+
return value === "low" || value === "med" || value === "high";
|
|
25
|
+
}
|
|
26
|
+
function parseAnimationLevel(raw) {
|
|
27
|
+
if (typeof raw !== "string") return null;
|
|
28
|
+
let next = raw.trim().toLowerCase();
|
|
29
|
+
return isAnimationLevel(next) ? next : null;
|
|
30
|
+
}
|
|
31
|
+
function defaultAnimationLevel(prefersReduced) {
|
|
32
|
+
return prefersReduced ? "med" : DEFAULT_ANIMATION_LEVEL;
|
|
33
|
+
}
|
|
34
|
+
function cycleAnimationLevel(level) {
|
|
35
|
+
let idx = ANIMATION_LEVELS.indexOf(level);
|
|
36
|
+
if (idx < 0) return DEFAULT_ANIMATION_LEVEL;
|
|
37
|
+
return ANIMATION_LEVELS[(idx + 1) % ANIMATION_LEVELS.length];
|
|
38
|
+
}
|
|
39
|
+
function canAnimate(level) {
|
|
40
|
+
return level !== "low";
|
|
41
|
+
}
|
|
42
|
+
function pickAnimationLevelFromRatio(t) {
|
|
43
|
+
if (t < 1 / 3) return "low";
|
|
44
|
+
if (t < 2 / 3) return "med";
|
|
45
|
+
return "high";
|
|
46
|
+
}
|
|
47
|
+
function stepAnimationLevel(level, delta) {
|
|
48
|
+
let idx = ANIMATION_LEVELS.indexOf(level);
|
|
49
|
+
if (idx < 0) return DEFAULT_ANIMATION_LEVEL;
|
|
50
|
+
let next = idx + delta;
|
|
51
|
+
if (next < 0) return ANIMATION_LEVELS[0];
|
|
52
|
+
if (next >= ANIMATION_LEVELS.length) return ANIMATION_LEVELS[ANIMATION_LEVELS.length - 1];
|
|
53
|
+
return ANIMATION_LEVELS[next];
|
|
54
|
+
}
|
|
55
|
+
//#endregion
|
|
56
|
+
//#region src/core/playback.ts
|
|
57
|
+
function createPlayback() {
|
|
58
|
+
let cancelled = false;
|
|
59
|
+
let settled = false;
|
|
60
|
+
let resolveDone;
|
|
61
|
+
let done = new Promise((resolve) => {
|
|
62
|
+
resolveDone = resolve;
|
|
63
|
+
});
|
|
64
|
+
let resolve = (ran) => {
|
|
65
|
+
if (settled) return;
|
|
66
|
+
settled = true;
|
|
67
|
+
resolveDone(cancelled ? false : ran);
|
|
68
|
+
};
|
|
69
|
+
return {
|
|
70
|
+
playback: {
|
|
71
|
+
done,
|
|
72
|
+
cancel: () => {
|
|
73
|
+
cancelled = true;
|
|
74
|
+
resolve(false);
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
resolve,
|
|
78
|
+
isCancelled: () => cancelled
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
function animateElement(el, keyframes, options) {
|
|
82
|
+
if (typeof el.animate !== "function") return null;
|
|
83
|
+
return el.animate(keyframes, options);
|
|
84
|
+
}
|
|
85
|
+
//#endregion
|
|
86
|
+
//#region src/core/tween.ts
|
|
87
|
+
function easeOutCubic(t) {
|
|
88
|
+
return 1 - (1 - (t < 0 ? 0 : t > 1 ? 1 : t)) ** 3;
|
|
89
|
+
}
|
|
90
|
+
function lerp(from, to, t) {
|
|
91
|
+
return from + (to - from) * t;
|
|
92
|
+
}
|
|
93
|
+
function tween(options) {
|
|
94
|
+
let { playback, resolve, isCancelled } = createPlayback();
|
|
95
|
+
let ease = options.easing ?? easeOutCubic;
|
|
96
|
+
let durationMs = Math.max(0, options.durationMs);
|
|
97
|
+
if (durationMs <= 0) {
|
|
98
|
+
options.onUpdate(options.to);
|
|
99
|
+
resolve(true);
|
|
100
|
+
return playback;
|
|
101
|
+
}
|
|
102
|
+
let start = performance.now();
|
|
103
|
+
let frame = 0;
|
|
104
|
+
let tick = (now) => {
|
|
105
|
+
if (isCancelled()) {
|
|
106
|
+
resolve(false);
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
let t = Math.min(1, (now - start) / durationMs);
|
|
110
|
+
options.onUpdate(lerp(options.from, options.to, ease(t)));
|
|
111
|
+
if (t >= 1) {
|
|
112
|
+
resolve(true);
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
frame = requestAnimationFrame(tick);
|
|
116
|
+
};
|
|
117
|
+
frame = requestAnimationFrame(tick);
|
|
118
|
+
let cancel = playback.cancel;
|
|
119
|
+
playback.cancel = () => {
|
|
120
|
+
cancelAnimationFrame(frame);
|
|
121
|
+
cancel();
|
|
122
|
+
};
|
|
123
|
+
return playback;
|
|
124
|
+
}
|
|
125
|
+
//#endregion
|
|
126
|
+
//#region src/rect/fit.ts
|
|
127
|
+
/** Fit natural size into box without upscaling past natural (CSS contain-like). */
|
|
128
|
+
function fitContain(natural, box) {
|
|
129
|
+
let natW = natural.width;
|
|
130
|
+
let natH = natural.height;
|
|
131
|
+
let boxW = box.width;
|
|
132
|
+
let boxH = box.height;
|
|
133
|
+
if (!(natW > 0) || !(natH > 0) || !(boxW > 0) || !(boxH > 0)) return null;
|
|
134
|
+
let scale = Math.min(boxW / natW, boxH / natH, 1);
|
|
135
|
+
return {
|
|
136
|
+
width: Math.max(1, Math.round(natW * scale)),
|
|
137
|
+
height: Math.max(1, Math.round(natH * scale))
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
/** Center a fitted size inside a padded viewport. */
|
|
141
|
+
function centerFitInViewport(fit, viewport, insets) {
|
|
142
|
+
let availW = Math.max(0, viewport.width - insets.left - insets.right);
|
|
143
|
+
let availH = Math.max(0, viewport.height - insets.top - insets.bottom);
|
|
144
|
+
return {
|
|
145
|
+
top: insets.top + (availH - fit.height) / 2,
|
|
146
|
+
left: insets.left + (availW - fit.width) / 2,
|
|
147
|
+
width: fit.width,
|
|
148
|
+
height: fit.height
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
//#endregion
|
|
152
|
+
//#region src/shared-element/math.ts
|
|
153
|
+
/** Expand a cover-cropped thumb so its aspect matches destination aspect. */
|
|
154
|
+
function uncoverByAspect(aspectWidth, aspectHeight, thumb) {
|
|
155
|
+
let { top, left, width, height } = thumb;
|
|
156
|
+
let realW = aspectWidth;
|
|
157
|
+
let realH = aspectHeight;
|
|
158
|
+
if (!(realW > 0) || !(realH > 0) || !(width > 0) || !(height > 0)) return { ...thumb };
|
|
159
|
+
if (realW === realH) {
|
|
160
|
+
let size = Math.max(width, height);
|
|
161
|
+
left -= (size - width) / 2;
|
|
162
|
+
top -= (size - height) / 2;
|
|
163
|
+
width = size;
|
|
164
|
+
height = size;
|
|
165
|
+
} else if (realW > realH) {
|
|
166
|
+
let srcWidth = width;
|
|
167
|
+
width = height * (realW / realH);
|
|
168
|
+
left -= (width - srcWidth) / 2;
|
|
169
|
+
} else {
|
|
170
|
+
let srcHeight = height;
|
|
171
|
+
height = width * (realH / realW);
|
|
172
|
+
top -= (height - srcHeight) / 2;
|
|
173
|
+
}
|
|
174
|
+
return {
|
|
175
|
+
top,
|
|
176
|
+
left,
|
|
177
|
+
width,
|
|
178
|
+
height
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
/** Center-based translate/scale so an element at `to` visually matches `from`. */
|
|
182
|
+
function computeFlight(from, to) {
|
|
183
|
+
if (!(to.width > 0) || !(to.height > 0) || !(from.width > 0) || !(from.height > 0)) return null;
|
|
184
|
+
let fromCx = from.left + from.width / 2;
|
|
185
|
+
let fromCy = from.top + from.height / 2;
|
|
186
|
+
let toCx = to.left + to.width / 2;
|
|
187
|
+
let toCy = to.top + to.height / 2;
|
|
188
|
+
return {
|
|
189
|
+
to: { ...to },
|
|
190
|
+
fromTranslateX: fromCx - toCx,
|
|
191
|
+
fromTranslateY: fromCy - toCy,
|
|
192
|
+
fromScaleX: from.width / to.width,
|
|
193
|
+
fromScaleY: from.height / to.height
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
/** Prefer natural size; fall back to thumb so fit is never empty. */
|
|
197
|
+
function resolveNaturalSize(naturalWidth, naturalHeight, thumb) {
|
|
198
|
+
if (naturalWidth && naturalHeight && naturalWidth > 0 && naturalHeight > 0) return {
|
|
199
|
+
width: naturalWidth,
|
|
200
|
+
height: naturalHeight
|
|
201
|
+
};
|
|
202
|
+
if (thumb.width > 0 && thumb.height > 0) return {
|
|
203
|
+
width: thumb.width,
|
|
204
|
+
height: thumb.height
|
|
205
|
+
};
|
|
206
|
+
return {
|
|
207
|
+
width: 1,
|
|
208
|
+
height: 1
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Open flight: (possibly uncovered) thumb → stage fit box (or explicit live `to`).
|
|
213
|
+
* Cover thumbs uncover against destination aspect so scale is uniform.
|
|
214
|
+
*/
|
|
215
|
+
function computeOpenFlight(opts) {
|
|
216
|
+
let natural = resolveNaturalSize(opts.naturalWidth, opts.naturalHeight, opts.thumb);
|
|
217
|
+
let to = opts.to && opts.to.width > 0 && opts.to.height > 0 ? { ...opts.to } : null;
|
|
218
|
+
if (!to) {
|
|
219
|
+
if (!opts.insets) return null;
|
|
220
|
+
let fit = fitContain(natural, {
|
|
221
|
+
width: Math.max(0, opts.viewport.width - opts.insets.left - opts.insets.right),
|
|
222
|
+
height: Math.max(0, opts.viewport.height - opts.insets.top - opts.insets.bottom)
|
|
223
|
+
});
|
|
224
|
+
if (!fit) return null;
|
|
225
|
+
to = centerFitInViewport(fit, opts.viewport, opts.insets);
|
|
226
|
+
}
|
|
227
|
+
let from;
|
|
228
|
+
if (opts.objectFit === "cover") from = uncoverByAspect(to.width, to.height, opts.thumb);
|
|
229
|
+
else from = { ...opts.thumb };
|
|
230
|
+
return computeFlight(from, to);
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* Close flight: stage box → thumb.
|
|
234
|
+
* Cover uses Math.min(sx, sy) so the land crop matches.
|
|
235
|
+
*/
|
|
236
|
+
function computeCloseFlight(opts) {
|
|
237
|
+
let flight = computeFlight(opts.fromStage, opts.thumb);
|
|
238
|
+
if (!flight) return null;
|
|
239
|
+
if (opts.objectFit === "cover") {
|
|
240
|
+
let s = Math.min(flight.fromScaleX, flight.fromScaleY);
|
|
241
|
+
flight = {
|
|
242
|
+
...flight,
|
|
243
|
+
fromScaleX: s,
|
|
244
|
+
fromScaleY: s
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
return flight;
|
|
248
|
+
}
|
|
249
|
+
/** Whether rect is fully inside clip (within epsilon). */
|
|
250
|
+
function isRectFullyVisibleIn(rect, clip, epsilon = 1) {
|
|
251
|
+
if (!(rect.width > 0) || !(rect.height > 0) || !(clip.width > 0) || !(clip.height > 0)) return false;
|
|
252
|
+
let right = rect.left + rect.width;
|
|
253
|
+
let bottom = rect.top + rect.height;
|
|
254
|
+
let clipRight = clip.left + clip.width;
|
|
255
|
+
let clipBottom = clip.top + clip.height;
|
|
256
|
+
return rect.left >= clip.left - epsilon && rect.top >= clip.top - epsilon && right <= clipRight + epsilon && bottom <= clipBottom + epsilon;
|
|
257
|
+
}
|
|
258
|
+
/** Whether rect intersects the viewport enough to land on. */
|
|
259
|
+
function isRectInViewport(rect, viewport, minVisiblePx = 8) {
|
|
260
|
+
let right = rect.left + rect.width;
|
|
261
|
+
let bottom = rect.top + rect.height;
|
|
262
|
+
let visW = Math.min(right, viewport.width) - Math.max(rect.left, 0);
|
|
263
|
+
let visH = Math.min(bottom, viewport.height) - Math.max(rect.top, 0);
|
|
264
|
+
return visW >= minVisiblePx && visH >= minVisiblePx;
|
|
265
|
+
}
|
|
266
|
+
/** Off-viewport landing: fly above or below toward the thumb's side. */
|
|
267
|
+
function offViewportLandingRect(from, thumb, viewportHeight) {
|
|
268
|
+
return {
|
|
269
|
+
top: thumb.top + thumb.height / 2 < from.top + from.height / 2 ? -thumb.height : viewportHeight,
|
|
270
|
+
left: thumb.left,
|
|
271
|
+
width: thumb.width,
|
|
272
|
+
height: thumb.height
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
//#endregion
|
|
276
|
+
//#region src/core/styles.ts
|
|
277
|
+
function applyStyles(el, styles) {
|
|
278
|
+
for (let key in styles) el.style.setProperty(key, styles[key]);
|
|
279
|
+
}
|
|
280
|
+
function clearStyles(el, keys) {
|
|
281
|
+
for (let key of keys) el.style.removeProperty(key);
|
|
282
|
+
}
|
|
283
|
+
//#endregion
|
|
284
|
+
//#region src/shared-element/player.ts
|
|
285
|
+
var SHARED_ELEMENT_MS = 200;
|
|
286
|
+
var SHARED_ELEMENT_EASING = "ease";
|
|
287
|
+
var SHARED_ELEMENT_END_MS = 16;
|
|
288
|
+
function defaultViewport(override) {
|
|
289
|
+
if (override) return override;
|
|
290
|
+
if (typeof window === "undefined") return {
|
|
291
|
+
width: 0,
|
|
292
|
+
height: 0
|
|
293
|
+
};
|
|
294
|
+
return {
|
|
295
|
+
width: window.innerWidth,
|
|
296
|
+
height: window.innerHeight
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
function createClone(imageUrl, objectFit, roundedStart) {
|
|
300
|
+
let el = document.createElement("div");
|
|
301
|
+
el.setAttribute("aria-hidden", "true");
|
|
302
|
+
applyStyles(el, {
|
|
303
|
+
position: "fixed",
|
|
304
|
+
"z-index": "2147483646",
|
|
305
|
+
overflow: "hidden",
|
|
306
|
+
"pointer-events": "none",
|
|
307
|
+
"transform-origin": "center center",
|
|
308
|
+
"will-change": "transform, opacity",
|
|
309
|
+
"border-radius": roundedStart ? "12px" : "0"
|
|
310
|
+
});
|
|
311
|
+
let img = document.createElement("img");
|
|
312
|
+
img.draggable = false;
|
|
313
|
+
img.alt = "";
|
|
314
|
+
applyStyles(img, {
|
|
315
|
+
display: "block",
|
|
316
|
+
width: "100%",
|
|
317
|
+
height: "100%",
|
|
318
|
+
"object-fit": objectFit,
|
|
319
|
+
"pointer-events": "none",
|
|
320
|
+
"user-select": "none"
|
|
321
|
+
});
|
|
322
|
+
if (imageUrl) img.src = imageUrl;
|
|
323
|
+
el.appendChild(img);
|
|
324
|
+
return el;
|
|
325
|
+
}
|
|
326
|
+
function applyStartPose(el, flight) {
|
|
327
|
+
applyStyles(el, {
|
|
328
|
+
top: `${flight.to.top}px`,
|
|
329
|
+
left: `${flight.to.left}px`,
|
|
330
|
+
width: `${flight.to.width}px`,
|
|
331
|
+
height: `${flight.to.height}px`,
|
|
332
|
+
transform: startTransform$1(flight)
|
|
333
|
+
});
|
|
334
|
+
}
|
|
335
|
+
function startTransform$1(flight) {
|
|
336
|
+
return `translate3d(${flight.fromTranslateX}px, ${flight.fromTranslateY}px, 0) scale(${flight.fromScaleX}, ${flight.fromScaleY})`;
|
|
337
|
+
}
|
|
338
|
+
function createSharedElement() {
|
|
339
|
+
let current = null;
|
|
340
|
+
let teardownDom = (active) => {
|
|
341
|
+
if (active.timer != null) {
|
|
342
|
+
clearTimeout(active.timer);
|
|
343
|
+
active.timer = null;
|
|
344
|
+
}
|
|
345
|
+
if (active.clone.parentNode) active.clone.remove();
|
|
346
|
+
if (active.hideTarget) active.hideTarget.style.visibility = active.hidePrev;
|
|
347
|
+
};
|
|
348
|
+
let abort = (active) => {
|
|
349
|
+
if (current === active) current = null;
|
|
350
|
+
active.anim?.cancel();
|
|
351
|
+
active.anim = null;
|
|
352
|
+
teardownDom(active);
|
|
353
|
+
active.settleCancel();
|
|
354
|
+
};
|
|
355
|
+
let cancel = () => {
|
|
356
|
+
if (!current) return;
|
|
357
|
+
abort(current);
|
|
358
|
+
};
|
|
359
|
+
let start = (opts, flight) => {
|
|
360
|
+
cancel();
|
|
361
|
+
let { playback, resolve, isCancelled } = createPlayback();
|
|
362
|
+
let objectFit = opts.objectFit ?? "contain";
|
|
363
|
+
let clone = createClone(opts.imageUrl, objectFit, !!opts.roundedStart);
|
|
364
|
+
let hideTarget = opts.hideTarget ?? null;
|
|
365
|
+
let hidePrev = hideTarget?.style.visibility ?? "";
|
|
366
|
+
let durationMs = opts.durationMs ?? 200;
|
|
367
|
+
let easing = opts.easing ?? "ease";
|
|
368
|
+
let active = {
|
|
369
|
+
clone,
|
|
370
|
+
hideTarget,
|
|
371
|
+
hidePrev,
|
|
372
|
+
settleCancel: playback.cancel,
|
|
373
|
+
resolve,
|
|
374
|
+
isCancelled,
|
|
375
|
+
anim: null,
|
|
376
|
+
timer: null,
|
|
377
|
+
finishing: false
|
|
378
|
+
};
|
|
379
|
+
current = active;
|
|
380
|
+
applyStartPose(clone, flight);
|
|
381
|
+
opts.host.appendChild(clone);
|
|
382
|
+
if (hideTarget) hideTarget.style.visibility = "hidden";
|
|
383
|
+
let handle = {
|
|
384
|
+
done: playback.done,
|
|
385
|
+
cancel: () => {
|
|
386
|
+
if (current === active) abort(active);
|
|
387
|
+
else playback.cancel();
|
|
388
|
+
}
|
|
389
|
+
};
|
|
390
|
+
let finish = async (ran) => {
|
|
391
|
+
if (current !== active || active.finishing) return;
|
|
392
|
+
if (isCancelled()) {
|
|
393
|
+
abort(active);
|
|
394
|
+
return;
|
|
395
|
+
}
|
|
396
|
+
active.finishing = true;
|
|
397
|
+
if (active.timer != null) {
|
|
398
|
+
clearTimeout(active.timer);
|
|
399
|
+
active.timer = null;
|
|
400
|
+
}
|
|
401
|
+
if (opts.onLand) try {
|
|
402
|
+
await opts.onLand();
|
|
403
|
+
} catch {}
|
|
404
|
+
if (isCancelled() || current !== active) return;
|
|
405
|
+
current = null;
|
|
406
|
+
teardownDom(active);
|
|
407
|
+
resolve(ran);
|
|
408
|
+
};
|
|
409
|
+
(async () => {
|
|
410
|
+
await dualRaf();
|
|
411
|
+
if (current !== active) return;
|
|
412
|
+
if (isCancelled()) {
|
|
413
|
+
abort(active);
|
|
414
|
+
return;
|
|
415
|
+
}
|
|
416
|
+
let fromFrame = { transform: startTransform$1(flight) };
|
|
417
|
+
let toFrame = { transform: "translate3d(0, 0, 0) scale(1, 1)" };
|
|
418
|
+
if (opts.fadeOut) {
|
|
419
|
+
fromFrame.opacity = "1";
|
|
420
|
+
toFrame.opacity = "0";
|
|
421
|
+
}
|
|
422
|
+
if (opts.roundedStart || opts.roundedEnd) {
|
|
423
|
+
fromFrame.borderRadius = opts.roundedStart ? "12px" : "0px";
|
|
424
|
+
toFrame.borderRadius = opts.roundedEnd ? "12px" : "0px";
|
|
425
|
+
}
|
|
426
|
+
active.anim = animateElement(clone, [fromFrame, toFrame], {
|
|
427
|
+
duration: durationMs,
|
|
428
|
+
easing,
|
|
429
|
+
fill: "forwards"
|
|
430
|
+
});
|
|
431
|
+
active.timer = setTimeout(() => {
|
|
432
|
+
finish(true);
|
|
433
|
+
}, durationMs + 16);
|
|
434
|
+
if (active.anim) active.anim.finished.then(() => finish(true), () => void 0);
|
|
435
|
+
})();
|
|
436
|
+
return handle;
|
|
437
|
+
};
|
|
438
|
+
let play = (opts) => {
|
|
439
|
+
let flight = computeFlight(opts.from, opts.to);
|
|
440
|
+
if (!flight) {
|
|
441
|
+
cancel();
|
|
442
|
+
return null;
|
|
443
|
+
}
|
|
444
|
+
return start(opts, flight);
|
|
445
|
+
};
|
|
446
|
+
let playOpen = (opts) => {
|
|
447
|
+
let flight = computeOpenFlight({
|
|
448
|
+
thumb: opts.seed.rect,
|
|
449
|
+
naturalWidth: opts.seed.naturalWidth,
|
|
450
|
+
naturalHeight: opts.seed.naturalHeight,
|
|
451
|
+
objectFit: opts.seed.objectFit ?? "contain",
|
|
452
|
+
viewport: defaultViewport(opts.viewport),
|
|
453
|
+
insets: opts.insets,
|
|
454
|
+
to: opts.to
|
|
455
|
+
});
|
|
456
|
+
if (!flight) {
|
|
457
|
+
cancel();
|
|
458
|
+
return null;
|
|
459
|
+
}
|
|
460
|
+
return start({
|
|
461
|
+
host: opts.host,
|
|
462
|
+
from: opts.seed.rect,
|
|
463
|
+
to: flight.to,
|
|
464
|
+
imageUrl: opts.seed.imageUrl,
|
|
465
|
+
objectFit: "contain",
|
|
466
|
+
durationMs: opts.durationMs,
|
|
467
|
+
roundedStart: true,
|
|
468
|
+
roundedEnd: false,
|
|
469
|
+
hideTarget: opts.hideTarget,
|
|
470
|
+
onLand: opts.onLand
|
|
471
|
+
}, flight);
|
|
472
|
+
};
|
|
473
|
+
let playClose = (opts) => {
|
|
474
|
+
let imageUrl = opts.imageUrl ?? opts.target?.imageUrl ?? null;
|
|
475
|
+
if (!opts.target) return start({
|
|
476
|
+
host: opts.host,
|
|
477
|
+
from: opts.fromStage,
|
|
478
|
+
to: opts.fromStage,
|
|
479
|
+
imageUrl,
|
|
480
|
+
objectFit: "contain",
|
|
481
|
+
durationMs: opts.durationMs,
|
|
482
|
+
fadeOut: true,
|
|
483
|
+
roundedStart: false,
|
|
484
|
+
roundedEnd: false,
|
|
485
|
+
hideTarget: opts.hideTarget
|
|
486
|
+
}, {
|
|
487
|
+
to: { ...opts.fromStage },
|
|
488
|
+
fromTranslateX: 0,
|
|
489
|
+
fromTranslateY: 0,
|
|
490
|
+
fromScaleX: 1,
|
|
491
|
+
fromScaleY: 1
|
|
492
|
+
});
|
|
493
|
+
let thumb = opts.target.rect;
|
|
494
|
+
let viewport = defaultViewport(opts.viewport);
|
|
495
|
+
if (!isRectInViewport(thumb, viewport)) thumb = offViewportLandingRect(opts.fromStage, thumb, viewport.height);
|
|
496
|
+
let objectFit = opts.target.objectFit ?? "contain";
|
|
497
|
+
let flight = computeCloseFlight({
|
|
498
|
+
fromStage: opts.fromStage,
|
|
499
|
+
thumb,
|
|
500
|
+
objectFit
|
|
501
|
+
});
|
|
502
|
+
if (!flight) {
|
|
503
|
+
cancel();
|
|
504
|
+
return null;
|
|
505
|
+
}
|
|
506
|
+
return start({
|
|
507
|
+
host: opts.host,
|
|
508
|
+
from: opts.fromStage,
|
|
509
|
+
to: flight.to,
|
|
510
|
+
imageUrl,
|
|
511
|
+
objectFit,
|
|
512
|
+
durationMs: opts.durationMs,
|
|
513
|
+
fadeOut: opts.fadeOut ?? false,
|
|
514
|
+
roundedStart: false,
|
|
515
|
+
roundedEnd: true,
|
|
516
|
+
hideTarget: opts.hideTarget
|
|
517
|
+
}, flight);
|
|
518
|
+
};
|
|
519
|
+
return {
|
|
520
|
+
play,
|
|
521
|
+
playOpen,
|
|
522
|
+
playClose,
|
|
523
|
+
cancel
|
|
524
|
+
};
|
|
525
|
+
}
|
|
526
|
+
var defaultController = null;
|
|
527
|
+
function playSharedElement(opts) {
|
|
528
|
+
if (!defaultController) defaultController = createSharedElement();
|
|
529
|
+
return defaultController.play(opts);
|
|
530
|
+
}
|
|
531
|
+
//#endregion
|
|
532
|
+
//#region src/view-slide/transforms.ts
|
|
533
|
+
var VIEW_SLIDE_FADE_OFFSET = "1.5rem";
|
|
534
|
+
var VIEW_SLIDE_MS = 300;
|
|
535
|
+
var VIEW_SLIDE_EASING = "cubic-bezier(0.25, 1, 0.5, 1)";
|
|
536
|
+
var VIEW_SLIDE_COVER_MS = 250;
|
|
537
|
+
var VIEW_SLIDE_COVER_EASING = "ease-in-out";
|
|
538
|
+
var VIEW_SLIDE_ZOOM_MS = 150;
|
|
539
|
+
var VIEW_SLIDE_ZOOM_EASING = "ease";
|
|
540
|
+
var VIEW_SLIDE_REVEAL_MS = 350;
|
|
541
|
+
var VIEW_SLIDE_REVEAL_EASING = "ease-in";
|
|
542
|
+
function slideDirectionByIndex(from, to, items) {
|
|
543
|
+
let fromIndex = items.findIndex((item) => item.id === from);
|
|
544
|
+
let toIndex = items.findIndex((item) => item.id === to);
|
|
545
|
+
if (fromIndex < 0 || toIndex < 0 || fromIndex === toIndex) return null;
|
|
546
|
+
return toIndex > fromIndex ? "forward" : "back";
|
|
547
|
+
}
|
|
548
|
+
function resolveViewSlideMode(level, kind) {
|
|
549
|
+
if (level === "low") return "none";
|
|
550
|
+
if (level === "med") return "crossfade";
|
|
551
|
+
return kind === "layer" ? "cover" : "push";
|
|
552
|
+
}
|
|
553
|
+
function viewSlideDurationMs(mode) {
|
|
554
|
+
if (mode === "cover") return 250;
|
|
555
|
+
if (mode === "zoom") return 150;
|
|
556
|
+
if (mode === "reveal") return 350;
|
|
557
|
+
return 300;
|
|
558
|
+
}
|
|
559
|
+
function viewSlideEasing(mode) {
|
|
560
|
+
if (mode === "cover") return VIEW_SLIDE_COVER_EASING;
|
|
561
|
+
if (mode === "zoom") return VIEW_SLIDE_ZOOM_EASING;
|
|
562
|
+
if (mode === "reveal") return VIEW_SLIDE_REVEAL_EASING;
|
|
563
|
+
return VIEW_SLIDE_EASING;
|
|
564
|
+
}
|
|
565
|
+
function viewSlideTransforms(direction, mode) {
|
|
566
|
+
if (mode === "crossfade") {
|
|
567
|
+
let leave = direction === "forward" ? `-${VIEW_SLIDE_FADE_OFFSET}` : VIEW_SLIDE_FADE_OFFSET;
|
|
568
|
+
let enter = direction === "forward" ? VIEW_SLIDE_FADE_OFFSET : `-${VIEW_SLIDE_FADE_OFFSET}`;
|
|
569
|
+
return {
|
|
570
|
+
fromStart: {
|
|
571
|
+
transform: "translate3d(0, 0, 0)",
|
|
572
|
+
opacity: "1"
|
|
573
|
+
},
|
|
574
|
+
fromEnd: {
|
|
575
|
+
transform: `translate3d(${leave}, 0, 0)`,
|
|
576
|
+
opacity: "0"
|
|
577
|
+
},
|
|
578
|
+
toStart: {
|
|
579
|
+
transform: `translate3d(${enter}, 0, 0)`,
|
|
580
|
+
opacity: "0"
|
|
581
|
+
},
|
|
582
|
+
toEnd: {
|
|
583
|
+
transform: "translate3d(0, 0, 0)",
|
|
584
|
+
opacity: "1"
|
|
585
|
+
}
|
|
586
|
+
};
|
|
587
|
+
}
|
|
588
|
+
if (mode === "cover") {
|
|
589
|
+
if (direction === "forward") return {
|
|
590
|
+
fromStart: {
|
|
591
|
+
transform: "scale(1)",
|
|
592
|
+
opacity: "1"
|
|
593
|
+
},
|
|
594
|
+
fromEnd: {
|
|
595
|
+
transform: "scale(0.7)",
|
|
596
|
+
opacity: "0"
|
|
597
|
+
},
|
|
598
|
+
toStart: {
|
|
599
|
+
transform: "translateX(200%)",
|
|
600
|
+
opacity: "1"
|
|
601
|
+
},
|
|
602
|
+
toEnd: {
|
|
603
|
+
transform: "translateX(0)",
|
|
604
|
+
opacity: "1"
|
|
605
|
+
}
|
|
606
|
+
};
|
|
607
|
+
return {
|
|
608
|
+
fromStart: {
|
|
609
|
+
transform: "translateX(0)",
|
|
610
|
+
opacity: "1"
|
|
611
|
+
},
|
|
612
|
+
fromEnd: {
|
|
613
|
+
transform: "translateX(200%)",
|
|
614
|
+
opacity: "1"
|
|
615
|
+
},
|
|
616
|
+
toStart: {
|
|
617
|
+
transform: "scale(0.7)",
|
|
618
|
+
opacity: "0"
|
|
619
|
+
},
|
|
620
|
+
toEnd: {
|
|
621
|
+
transform: "scale(1)",
|
|
622
|
+
opacity: "1"
|
|
623
|
+
}
|
|
624
|
+
};
|
|
625
|
+
}
|
|
626
|
+
if (mode === "peek") {
|
|
627
|
+
if (direction === "forward") return {
|
|
628
|
+
fromStart: {
|
|
629
|
+
transform: "translate3d(0, 0, 0)",
|
|
630
|
+
opacity: "1"
|
|
631
|
+
},
|
|
632
|
+
fromEnd: {
|
|
633
|
+
transform: "translate3d(-20%, 0, 0)",
|
|
634
|
+
opacity: "0.7"
|
|
635
|
+
},
|
|
636
|
+
toStart: {
|
|
637
|
+
transform: "translate3d(100%, 0, 0)",
|
|
638
|
+
opacity: "1"
|
|
639
|
+
},
|
|
640
|
+
toEnd: {
|
|
641
|
+
transform: "translate3d(0, 0, 0)",
|
|
642
|
+
opacity: "1"
|
|
643
|
+
}
|
|
644
|
+
};
|
|
645
|
+
return {
|
|
646
|
+
fromStart: {
|
|
647
|
+
transform: "translate3d(0, 0, 0)",
|
|
648
|
+
opacity: "1"
|
|
649
|
+
},
|
|
650
|
+
fromEnd: {
|
|
651
|
+
transform: "translate3d(100%, 0, 0)",
|
|
652
|
+
opacity: "1"
|
|
653
|
+
},
|
|
654
|
+
toStart: {
|
|
655
|
+
transform: "translate3d(-20%, 0, 0)",
|
|
656
|
+
opacity: "0.7"
|
|
657
|
+
},
|
|
658
|
+
toEnd: {
|
|
659
|
+
transform: "translate3d(0, 0, 0)",
|
|
660
|
+
opacity: "1"
|
|
661
|
+
}
|
|
662
|
+
};
|
|
663
|
+
}
|
|
664
|
+
if (mode === "lift") {
|
|
665
|
+
if (direction === "forward") return {
|
|
666
|
+
fromStart: {
|
|
667
|
+
transform: "translate3d(0, 0, 0)",
|
|
668
|
+
opacity: "1"
|
|
669
|
+
},
|
|
670
|
+
fromEnd: {
|
|
671
|
+
transform: "translate3d(0, -100%, 0)",
|
|
672
|
+
opacity: "1"
|
|
673
|
+
},
|
|
674
|
+
toStart: {
|
|
675
|
+
transform: "translate3d(0, 100%, 0)",
|
|
676
|
+
opacity: "1"
|
|
677
|
+
},
|
|
678
|
+
toEnd: {
|
|
679
|
+
transform: "translate3d(0, 0, 0)",
|
|
680
|
+
opacity: "1"
|
|
681
|
+
}
|
|
682
|
+
};
|
|
683
|
+
return {
|
|
684
|
+
fromStart: {
|
|
685
|
+
transform: "translate3d(0, 0, 0)",
|
|
686
|
+
opacity: "1"
|
|
687
|
+
},
|
|
688
|
+
fromEnd: {
|
|
689
|
+
transform: "translate3d(0, 100%, 0)",
|
|
690
|
+
opacity: "1"
|
|
691
|
+
},
|
|
692
|
+
toStart: {
|
|
693
|
+
transform: "translate3d(0, -100%, 0)",
|
|
694
|
+
opacity: "1"
|
|
695
|
+
},
|
|
696
|
+
toEnd: {
|
|
697
|
+
transform: "translate3d(0, 0, 0)",
|
|
698
|
+
opacity: "1"
|
|
699
|
+
}
|
|
700
|
+
};
|
|
701
|
+
}
|
|
702
|
+
if (mode === "zoom") {
|
|
703
|
+
if (direction === "forward") return {
|
|
704
|
+
fromStart: {
|
|
705
|
+
transform: "scale(1)",
|
|
706
|
+
opacity: "1"
|
|
707
|
+
},
|
|
708
|
+
fromEnd: {
|
|
709
|
+
transform: "scale(0.95)",
|
|
710
|
+
opacity: "0"
|
|
711
|
+
},
|
|
712
|
+
toStart: {
|
|
713
|
+
transform: "scale(1.1)",
|
|
714
|
+
opacity: "0"
|
|
715
|
+
},
|
|
716
|
+
toEnd: {
|
|
717
|
+
transform: "scale(1)",
|
|
718
|
+
opacity: "1"
|
|
719
|
+
}
|
|
720
|
+
};
|
|
721
|
+
return {
|
|
722
|
+
fromStart: {
|
|
723
|
+
transform: "scale(1)",
|
|
724
|
+
opacity: "1"
|
|
725
|
+
},
|
|
726
|
+
fromEnd: {
|
|
727
|
+
transform: "scale(1.1)",
|
|
728
|
+
opacity: "0"
|
|
729
|
+
},
|
|
730
|
+
toStart: {
|
|
731
|
+
transform: "scale(0.95)",
|
|
732
|
+
opacity: "0"
|
|
733
|
+
},
|
|
734
|
+
toEnd: {
|
|
735
|
+
transform: "scale(1)",
|
|
736
|
+
opacity: "1"
|
|
737
|
+
}
|
|
738
|
+
};
|
|
739
|
+
}
|
|
740
|
+
if (mode === "reveal") {
|
|
741
|
+
if (direction === "forward") return {
|
|
742
|
+
fromStart: {
|
|
743
|
+
transform: "translate3d(0, 0, 0)",
|
|
744
|
+
opacity: "1"
|
|
745
|
+
},
|
|
746
|
+
fromEnd: {
|
|
747
|
+
transform: "translate3d(0, 0, 0)",
|
|
748
|
+
opacity: "1"
|
|
749
|
+
},
|
|
750
|
+
toStart: {
|
|
751
|
+
transform: "translate3d(0, 0, 0)",
|
|
752
|
+
opacity: "1",
|
|
753
|
+
clipPath: "inset(0 100% 0 0)"
|
|
754
|
+
},
|
|
755
|
+
toEnd: {
|
|
756
|
+
transform: "translate3d(0, 0, 0)",
|
|
757
|
+
opacity: "1",
|
|
758
|
+
clipPath: "inset(0 0 0 0)"
|
|
759
|
+
}
|
|
760
|
+
};
|
|
761
|
+
return {
|
|
762
|
+
fromStart: {
|
|
763
|
+
transform: "translate3d(0, 0, 0)",
|
|
764
|
+
opacity: "1",
|
|
765
|
+
clipPath: "inset(0 0 0 0)"
|
|
766
|
+
},
|
|
767
|
+
fromEnd: {
|
|
768
|
+
transform: "translate3d(0, 0, 0)",
|
|
769
|
+
opacity: "1",
|
|
770
|
+
clipPath: "inset(0 100% 0 0)"
|
|
771
|
+
},
|
|
772
|
+
toStart: {
|
|
773
|
+
transform: "translate3d(0, 0, 0)",
|
|
774
|
+
opacity: "1"
|
|
775
|
+
},
|
|
776
|
+
toEnd: {
|
|
777
|
+
transform: "translate3d(0, 0, 0)",
|
|
778
|
+
opacity: "1"
|
|
779
|
+
}
|
|
780
|
+
};
|
|
781
|
+
}
|
|
782
|
+
if (direction === "forward") return {
|
|
783
|
+
fromStart: {
|
|
784
|
+
transform: "translate3d(0, 0, 0)",
|
|
785
|
+
opacity: "1"
|
|
786
|
+
},
|
|
787
|
+
fromEnd: {
|
|
788
|
+
transform: "translate3d(-100%, 0, 0)",
|
|
789
|
+
opacity: "1"
|
|
790
|
+
},
|
|
791
|
+
toStart: {
|
|
792
|
+
transform: "translate3d(100%, 0, 0)",
|
|
793
|
+
opacity: "1"
|
|
794
|
+
},
|
|
795
|
+
toEnd: {
|
|
796
|
+
transform: "translate3d(0, 0, 0)",
|
|
797
|
+
opacity: "1"
|
|
798
|
+
}
|
|
799
|
+
};
|
|
800
|
+
return {
|
|
801
|
+
fromStart: {
|
|
802
|
+
transform: "translate3d(0, 0, 0)",
|
|
803
|
+
opacity: "1"
|
|
804
|
+
},
|
|
805
|
+
fromEnd: {
|
|
806
|
+
transform: "translate3d(100%, 0, 0)",
|
|
807
|
+
opacity: "1"
|
|
808
|
+
},
|
|
809
|
+
toStart: {
|
|
810
|
+
transform: "translate3d(-100%, 0, 0)",
|
|
811
|
+
opacity: "1"
|
|
812
|
+
},
|
|
813
|
+
toEnd: {
|
|
814
|
+
transform: "translate3d(0, 0, 0)",
|
|
815
|
+
opacity: "1"
|
|
816
|
+
}
|
|
817
|
+
};
|
|
818
|
+
}
|
|
819
|
+
//#endregion
|
|
820
|
+
//#region src/view-slide/session.ts
|
|
821
|
+
var VIEW_SLIDE_SETTLE_SLACK_MS = 80;
|
|
822
|
+
var PANEL_STYLE_KEYS$1 = [
|
|
823
|
+
"transform",
|
|
824
|
+
"opacity",
|
|
825
|
+
"will-change",
|
|
826
|
+
"clip-path"
|
|
827
|
+
];
|
|
828
|
+
function applyPanel(el, state) {
|
|
829
|
+
let styles = {
|
|
830
|
+
transform: state.transform,
|
|
831
|
+
opacity: state.opacity,
|
|
832
|
+
"will-change": state.clipPath != null ? "transform, opacity, clip-path" : "transform, opacity"
|
|
833
|
+
};
|
|
834
|
+
if (state.clipPath != null) styles["clip-path"] = state.clipPath;
|
|
835
|
+
applyStyles(el, styles);
|
|
836
|
+
}
|
|
837
|
+
function clearPanel(el) {
|
|
838
|
+
clearStyles(el, PANEL_STYLE_KEYS$1);
|
|
839
|
+
}
|
|
840
|
+
function createViewSlide(config) {
|
|
841
|
+
let mountPolicy = config.mountPolicy ?? "keep-visited";
|
|
842
|
+
let settleSlackMs = config.settleSlackMs ?? 80;
|
|
843
|
+
let resolveTransforms = config.transforms ?? viewSlideTransforms;
|
|
844
|
+
let resolveDuration = config.durationMs ?? viewSlideDurationMs;
|
|
845
|
+
let resolveEasing = config.easing ?? viewSlideEasing;
|
|
846
|
+
let mountedKeys = [];
|
|
847
|
+
let panelEls = /* @__PURE__ */ new Map();
|
|
848
|
+
let animating = false;
|
|
849
|
+
let leavingKey;
|
|
850
|
+
let lastRequestedKey;
|
|
851
|
+
let pending = null;
|
|
852
|
+
let pair = null;
|
|
853
|
+
let slideGen = 0;
|
|
854
|
+
let destroyed = false;
|
|
855
|
+
let isMounted = (key) => mountedKeys.includes(key);
|
|
856
|
+
let ensureMounted = (key) => {
|
|
857
|
+
if (!mountedKeys.includes(key)) mountedKeys = [...mountedKeys, key];
|
|
858
|
+
};
|
|
859
|
+
let retainOnly = (keys) => {
|
|
860
|
+
let wanted = keys.filter((key, index) => keys.indexOf(key) === index);
|
|
861
|
+
let ordered = mountedKeys.filter((key) => wanted.includes(key));
|
|
862
|
+
for (let key of wanted) if (!ordered.includes(key)) ordered.push(key);
|
|
863
|
+
mountedKeys = ordered;
|
|
864
|
+
};
|
|
865
|
+
let applyMountAfterSettle = (active) => {
|
|
866
|
+
if (mountPolicy === "keep-visited") return;
|
|
867
|
+
retainOnly([active]);
|
|
868
|
+
};
|
|
869
|
+
let isVisible = (key) => {
|
|
870
|
+
if (key === lastRequestedKey) return true;
|
|
871
|
+
return animating && key === leavingKey;
|
|
872
|
+
};
|
|
873
|
+
let role = (key) => {
|
|
874
|
+
if (animating) {
|
|
875
|
+
if (key === lastRequestedKey) return "entering";
|
|
876
|
+
if (key === leavingKey) return "leaving";
|
|
877
|
+
return "idle";
|
|
878
|
+
}
|
|
879
|
+
return key === lastRequestedKey ? "active" : "idle";
|
|
880
|
+
};
|
|
881
|
+
let detachPair = (active) => {
|
|
882
|
+
if (active.settleTimer != null) {
|
|
883
|
+
clearTimeout(active.settleTimer);
|
|
884
|
+
active.settleTimer = null;
|
|
885
|
+
}
|
|
886
|
+
active.fromAnim?.cancel();
|
|
887
|
+
active.toAnim?.cancel();
|
|
888
|
+
active.fromAnim = null;
|
|
889
|
+
active.toAnim = null;
|
|
890
|
+
active.fromEl.removeEventListener("transitionend", active.onEnd);
|
|
891
|
+
active.toEl.removeEventListener("transitionend", active.onEnd);
|
|
892
|
+
clearPanel(active.fromEl);
|
|
893
|
+
clearPanel(active.toEl);
|
|
894
|
+
};
|
|
895
|
+
let abortInFlight = () => {
|
|
896
|
+
pending = null;
|
|
897
|
+
if (!pair) return;
|
|
898
|
+
detachPair(pair);
|
|
899
|
+
pair = null;
|
|
900
|
+
};
|
|
901
|
+
let finishSlide = (gen, to) => {
|
|
902
|
+
if (gen !== slideGen || destroyed) return;
|
|
903
|
+
if (pair && pair.gen === gen) detachPair(pair);
|
|
904
|
+
pair = null;
|
|
905
|
+
pending = null;
|
|
906
|
+
animating = false;
|
|
907
|
+
leavingKey = void 0;
|
|
908
|
+
applyMountAfterSettle(to);
|
|
909
|
+
};
|
|
910
|
+
let abortToInstant = (from, to, gen) => {
|
|
911
|
+
if (gen !== slideGen || destroyed) return;
|
|
912
|
+
let fromEl = panelEls.get(from);
|
|
913
|
+
let toEl = panelEls.get(to);
|
|
914
|
+
if (fromEl) clearPanel(fromEl);
|
|
915
|
+
if (toEl) clearPanel(toEl);
|
|
916
|
+
animating = false;
|
|
917
|
+
leavingKey = void 0;
|
|
918
|
+
pending = null;
|
|
919
|
+
applyMountAfterSettle(to);
|
|
920
|
+
};
|
|
921
|
+
let performSlide = async (from, to, fromEl, toEl, dir, mode, gen) => {
|
|
922
|
+
if (gen !== slideGen || destroyed) return;
|
|
923
|
+
let tx = resolveTransforms(dir, mode);
|
|
924
|
+
let ms = resolveDuration(mode);
|
|
925
|
+
animating = true;
|
|
926
|
+
leavingKey = from;
|
|
927
|
+
applyPanel(fromEl, tx.fromStart);
|
|
928
|
+
applyPanel(toEl, tx.toStart);
|
|
929
|
+
fromEl.offsetWidth;
|
|
930
|
+
toEl.offsetWidth;
|
|
931
|
+
let onEnd = (event) => {
|
|
932
|
+
if (event.target !== toEl) return;
|
|
933
|
+
let propertyName = event.propertyName;
|
|
934
|
+
if (propertyName && propertyName !== "transform") return;
|
|
935
|
+
finishSlide(gen, to);
|
|
936
|
+
};
|
|
937
|
+
fromEl.addEventListener("transitionend", onEnd);
|
|
938
|
+
toEl.addEventListener("transitionend", onEnd);
|
|
939
|
+
let active = {
|
|
940
|
+
gen,
|
|
941
|
+
from,
|
|
942
|
+
to,
|
|
943
|
+
fromEl,
|
|
944
|
+
toEl,
|
|
945
|
+
fromAnim: null,
|
|
946
|
+
toAnim: null,
|
|
947
|
+
settleTimer: null,
|
|
948
|
+
onEnd
|
|
949
|
+
};
|
|
950
|
+
pair = active;
|
|
951
|
+
active.settleTimer = setTimeout(() => {
|
|
952
|
+
finishSlide(gen, to);
|
|
953
|
+
}, ms + settleSlackMs);
|
|
954
|
+
await dualRaf();
|
|
955
|
+
if (gen !== slideGen || destroyed || pair !== active) return;
|
|
956
|
+
let easing = resolveEasing(mode);
|
|
957
|
+
active.fromAnim = animateElement(fromEl, [tx.fromStart, tx.fromEnd], {
|
|
958
|
+
duration: ms,
|
|
959
|
+
easing,
|
|
960
|
+
fill: "forwards"
|
|
961
|
+
});
|
|
962
|
+
active.toAnim = animateElement(toEl, [tx.toStart, tx.toEnd], {
|
|
963
|
+
duration: ms,
|
|
964
|
+
easing,
|
|
965
|
+
fill: "forwards"
|
|
966
|
+
});
|
|
967
|
+
if (active.toAnim) active.toAnim.finished.then(() => finishSlide(gen, to), () => void 0);
|
|
968
|
+
};
|
|
969
|
+
/** Start the pair only after both panels have an attached node. */
|
|
970
|
+
let tryRunPending = () => {
|
|
971
|
+
if (!pending || destroyed) return;
|
|
972
|
+
let fromEl = panelEls.get(pending.from);
|
|
973
|
+
let toEl = panelEls.get(pending.to);
|
|
974
|
+
if (!fromEl || !toEl) return;
|
|
975
|
+
let { from, to, gen } = pending;
|
|
976
|
+
pending = null;
|
|
977
|
+
let mode = config.getMode();
|
|
978
|
+
let dir = config.getDirection(from, to);
|
|
979
|
+
if (!dir || mode === "none") {
|
|
980
|
+
abortToInstant(from, to, gen);
|
|
981
|
+
return;
|
|
982
|
+
}
|
|
983
|
+
performSlide(from, to, fromEl, toEl, dir, mode, gen);
|
|
984
|
+
};
|
|
985
|
+
let setActive = (key) => {
|
|
986
|
+
if (destroyed) return;
|
|
987
|
+
if (key === lastRequestedKey) return;
|
|
988
|
+
if (lastRequestedKey == null) {
|
|
989
|
+
lastRequestedKey = key;
|
|
990
|
+
if (key != null) ensureMounted(key);
|
|
991
|
+
return;
|
|
992
|
+
}
|
|
993
|
+
let from = lastRequestedKey;
|
|
994
|
+
lastRequestedKey = key;
|
|
995
|
+
slideGen++;
|
|
996
|
+
abortInFlight();
|
|
997
|
+
if (key == null) {
|
|
998
|
+
animating = false;
|
|
999
|
+
leavingKey = void 0;
|
|
1000
|
+
if (mountPolicy !== "keep-visited") retainOnly([]);
|
|
1001
|
+
return;
|
|
1002
|
+
}
|
|
1003
|
+
ensureMounted(from);
|
|
1004
|
+
ensureMounted(key);
|
|
1005
|
+
animating = true;
|
|
1006
|
+
leavingKey = from;
|
|
1007
|
+
pending = {
|
|
1008
|
+
from,
|
|
1009
|
+
to: key,
|
|
1010
|
+
gen: slideGen
|
|
1011
|
+
};
|
|
1012
|
+
tryRunPending();
|
|
1013
|
+
};
|
|
1014
|
+
let attach = (el, key) => {
|
|
1015
|
+
if (!destroyed) {
|
|
1016
|
+
panelEls.set(key, el);
|
|
1017
|
+
tryRunPending();
|
|
1018
|
+
}
|
|
1019
|
+
return {
|
|
1020
|
+
update: (next) => {
|
|
1021
|
+
if (destroyed || next === key) return;
|
|
1022
|
+
if (pair && (pair.from === key || pair.to === key) || pending && (pending.from === key || pending.to === key)) {
|
|
1023
|
+
slideGen++;
|
|
1024
|
+
abortInFlight();
|
|
1025
|
+
animating = false;
|
|
1026
|
+
leavingKey = void 0;
|
|
1027
|
+
}
|
|
1028
|
+
if (panelEls.get(key) === el) panelEls.delete(key);
|
|
1029
|
+
key = next;
|
|
1030
|
+
panelEls.set(key, el);
|
|
1031
|
+
tryRunPending();
|
|
1032
|
+
},
|
|
1033
|
+
destroy: () => {
|
|
1034
|
+
if (panelEls.get(key) === el) panelEls.delete(key);
|
|
1035
|
+
}
|
|
1036
|
+
};
|
|
1037
|
+
};
|
|
1038
|
+
let cancel = () => {
|
|
1039
|
+
if (destroyed) return;
|
|
1040
|
+
slideGen++;
|
|
1041
|
+
abortInFlight();
|
|
1042
|
+
animating = false;
|
|
1043
|
+
leavingKey = void 0;
|
|
1044
|
+
if (lastRequestedKey != null && mountPolicy !== "keep-visited") applyMountAfterSettle(lastRequestedKey);
|
|
1045
|
+
};
|
|
1046
|
+
let destroy = () => {
|
|
1047
|
+
if (destroyed) return;
|
|
1048
|
+
destroyed = true;
|
|
1049
|
+
slideGen++;
|
|
1050
|
+
abortInFlight();
|
|
1051
|
+
for (let el of panelEls.values()) clearPanel(el);
|
|
1052
|
+
animating = false;
|
|
1053
|
+
leavingKey = void 0;
|
|
1054
|
+
lastRequestedKey = void 0;
|
|
1055
|
+
pending = null;
|
|
1056
|
+
pair = null;
|
|
1057
|
+
panelEls.clear();
|
|
1058
|
+
mountedKeys = [];
|
|
1059
|
+
};
|
|
1060
|
+
return {
|
|
1061
|
+
get mountedKeys() {
|
|
1062
|
+
return mountedKeys;
|
|
1063
|
+
},
|
|
1064
|
+
get animating() {
|
|
1065
|
+
return animating;
|
|
1066
|
+
},
|
|
1067
|
+
get leavingKey() {
|
|
1068
|
+
return leavingKey;
|
|
1069
|
+
},
|
|
1070
|
+
setActive,
|
|
1071
|
+
attach,
|
|
1072
|
+
isMounted,
|
|
1073
|
+
isVisible,
|
|
1074
|
+
role,
|
|
1075
|
+
cancel,
|
|
1076
|
+
destroy
|
|
1077
|
+
};
|
|
1078
|
+
}
|
|
1079
|
+
//#endregion
|
|
1080
|
+
//#region src/sliding-indicator/indicator.ts
|
|
1081
|
+
var INDICATOR_MS = 300;
|
|
1082
|
+
var INDICATOR_EASING = "cubic-bezier(0.25, 1, 0.5, 1)";
|
|
1083
|
+
function translate3d(left, top) {
|
|
1084
|
+
return `translate3d(${left}px, ${top}px, 0)`;
|
|
1085
|
+
}
|
|
1086
|
+
function createSlidingIndicator(options) {
|
|
1087
|
+
let durationMs = options.durationMs ?? 300;
|
|
1088
|
+
let easing = options.easing ?? "cubic-bezier(0.25, 1, 0.5, 1)";
|
|
1089
|
+
let laidOut = false;
|
|
1090
|
+
let prevLeft = 0;
|
|
1091
|
+
let prevTop = 0;
|
|
1092
|
+
let currentAnim = null;
|
|
1093
|
+
let destroyed = false;
|
|
1094
|
+
let observedTrack = null;
|
|
1095
|
+
let cancelAnim = () => {
|
|
1096
|
+
if (!currentAnim) return;
|
|
1097
|
+
currentAnim.cancel();
|
|
1098
|
+
currentAnim = null;
|
|
1099
|
+
};
|
|
1100
|
+
let measure = () => {
|
|
1101
|
+
if (destroyed) return;
|
|
1102
|
+
let track = options.getTrack();
|
|
1103
|
+
if (track && track !== observedTrack) {
|
|
1104
|
+
if (observedTrack) observer.unobserve(observedTrack);
|
|
1105
|
+
observer.observe(track);
|
|
1106
|
+
observedTrack = track;
|
|
1107
|
+
}
|
|
1108
|
+
let indicator = options.getIndicator();
|
|
1109
|
+
let active = options.getActive();
|
|
1110
|
+
if (!indicator || !active) return;
|
|
1111
|
+
let left = active.offsetLeft;
|
|
1112
|
+
let top = active.offsetTop;
|
|
1113
|
+
let width = active.offsetWidth;
|
|
1114
|
+
let height = active.offsetHeight;
|
|
1115
|
+
let next = translate3d(left, top);
|
|
1116
|
+
let shouldAnimate = laidOut && (options.enabled?.() ?? true);
|
|
1117
|
+
applyStyles(indicator, {
|
|
1118
|
+
width: `${width}px`,
|
|
1119
|
+
height: `${height}px`
|
|
1120
|
+
});
|
|
1121
|
+
if (!shouldAnimate) {
|
|
1122
|
+
cancelAnim();
|
|
1123
|
+
applyStyles(indicator, { transform: next });
|
|
1124
|
+
prevLeft = left;
|
|
1125
|
+
prevTop = top;
|
|
1126
|
+
laidOut = true;
|
|
1127
|
+
return;
|
|
1128
|
+
}
|
|
1129
|
+
let from = translate3d(prevLeft, prevTop);
|
|
1130
|
+
cancelAnim();
|
|
1131
|
+
applyStyles(indicator, { transform: next });
|
|
1132
|
+
currentAnim = animateElement(indicator, [{ transform: from }, { transform: next }], {
|
|
1133
|
+
duration: durationMs,
|
|
1134
|
+
easing,
|
|
1135
|
+
fill: "forwards"
|
|
1136
|
+
});
|
|
1137
|
+
prevLeft = left;
|
|
1138
|
+
prevTop = top;
|
|
1139
|
+
};
|
|
1140
|
+
let observer = new ResizeObserver(() => {
|
|
1141
|
+
measure();
|
|
1142
|
+
});
|
|
1143
|
+
let initialTrack = options.getTrack();
|
|
1144
|
+
if (initialTrack) {
|
|
1145
|
+
observer.observe(initialTrack);
|
|
1146
|
+
observedTrack = initialTrack;
|
|
1147
|
+
}
|
|
1148
|
+
let destroy = () => {
|
|
1149
|
+
if (destroyed) return;
|
|
1150
|
+
destroyed = true;
|
|
1151
|
+
cancelAnim();
|
|
1152
|
+
observer.disconnect();
|
|
1153
|
+
observedTrack = null;
|
|
1154
|
+
};
|
|
1155
|
+
return {
|
|
1156
|
+
measure,
|
|
1157
|
+
destroy
|
|
1158
|
+
};
|
|
1159
|
+
}
|
|
1160
|
+
//#endregion
|
|
1161
|
+
//#region src/list-reorder/classify.ts
|
|
1162
|
+
/** orderDiff = newIndex − oldIndex for keys in curr; new keys → −Infinity. */
|
|
1163
|
+
function buildOrderDiff(prev, curr) {
|
|
1164
|
+
let diff = /* @__PURE__ */ new Map();
|
|
1165
|
+
for (let [key, newIdx] of curr) {
|
|
1166
|
+
let oldIdx = prev.get(key);
|
|
1167
|
+
if (oldIdx === void 0) {
|
|
1168
|
+
diff.set(key, -Infinity);
|
|
1169
|
+
continue;
|
|
1170
|
+
}
|
|
1171
|
+
diff.set(key, newIdx - oldIdx);
|
|
1172
|
+
}
|
|
1173
|
+
return diff;
|
|
1174
|
+
}
|
|
1175
|
+
/**
|
|
1176
|
+
* Majority vertical direction → move; minority / non-finite → opacity; zero → none.
|
|
1177
|
+
* Adjacent swap (1 up, 1 down): up is opacity, down is move.
|
|
1178
|
+
*/
|
|
1179
|
+
function classifyReorderAnim(orderDiffByKey, key) {
|
|
1180
|
+
let orderDiff = orderDiffByKey.get(key);
|
|
1181
|
+
if (orderDiff === void 0 || orderDiff === 0) return "none";
|
|
1182
|
+
if (!Number.isFinite(orderDiff)) return "opacity";
|
|
1183
|
+
let numberOfUp = 0;
|
|
1184
|
+
let numberOfDown = 0;
|
|
1185
|
+
for (let d of orderDiffByKey.values()) {
|
|
1186
|
+
if (!Number.isFinite(d) || d === 0) continue;
|
|
1187
|
+
if (d < 0) numberOfUp++;
|
|
1188
|
+
else if (d > 0) numberOfDown++;
|
|
1189
|
+
}
|
|
1190
|
+
if (numberOfUp <= numberOfDown && orderDiff < 0 || numberOfDown < numberOfUp && orderDiff > 0) return "opacity";
|
|
1191
|
+
return "move";
|
|
1192
|
+
}
|
|
1193
|
+
//#endregion
|
|
1194
|
+
//#region src/list-reorder/reorder.ts
|
|
1195
|
+
var LIST_REORDER_MS = 200;
|
|
1196
|
+
var LIST_REORDER_EASING = "ease-out";
|
|
1197
|
+
var LIST_REORDER_EPSILON_PX = 1;
|
|
1198
|
+
function createListReorder(options) {
|
|
1199
|
+
let durationMs = options.durationMs ?? 200;
|
|
1200
|
+
let easing = options.easing ?? "ease-out";
|
|
1201
|
+
let isEnabled = options.isEnabled ?? (() => true);
|
|
1202
|
+
let isReduced = options.isReduced ?? (() => false);
|
|
1203
|
+
let isSuppressed = options.isSuppressed ?? (() => false);
|
|
1204
|
+
let itemEls = /* @__PURE__ */ new Map();
|
|
1205
|
+
let activeAnims = /* @__PURE__ */ new Map();
|
|
1206
|
+
let prevIndexByKey = null;
|
|
1207
|
+
let destroyed = false;
|
|
1208
|
+
let buildIndexMap = (items) => {
|
|
1209
|
+
let map = /* @__PURE__ */ new Map();
|
|
1210
|
+
for (let i = 0; i < items.length; i++) map.set(options.getKey(items[i], i), i);
|
|
1211
|
+
return map;
|
|
1212
|
+
};
|
|
1213
|
+
let cancelAnim = (key) => {
|
|
1214
|
+
let anim = activeAnims.get(key);
|
|
1215
|
+
if (!anim) return;
|
|
1216
|
+
anim.cancel();
|
|
1217
|
+
activeAnims.delete(key);
|
|
1218
|
+
};
|
|
1219
|
+
let cancelAll = () => {
|
|
1220
|
+
for (let key of [...activeAnims.keys()]) cancelAnim(key);
|
|
1221
|
+
};
|
|
1222
|
+
let register = (el, key) => {
|
|
1223
|
+
if (destroyed) return {
|
|
1224
|
+
update: () => {},
|
|
1225
|
+
destroy: () => {}
|
|
1226
|
+
};
|
|
1227
|
+
itemEls.set(key, el);
|
|
1228
|
+
return {
|
|
1229
|
+
update: (next) => {
|
|
1230
|
+
if (destroyed || next === key) return;
|
|
1231
|
+
cancelAnim(key);
|
|
1232
|
+
if (itemEls.get(key) === el) itemEls.delete(key);
|
|
1233
|
+
key = next;
|
|
1234
|
+
itemEls.set(key, el);
|
|
1235
|
+
},
|
|
1236
|
+
destroy: () => {
|
|
1237
|
+
if (itemEls.get(key) === el) itemEls.delete(key);
|
|
1238
|
+
cancelAnim(key);
|
|
1239
|
+
}
|
|
1240
|
+
};
|
|
1241
|
+
};
|
|
1242
|
+
let sync = (items) => {
|
|
1243
|
+
if (destroyed) return;
|
|
1244
|
+
if (!isEnabled() || isReduced()) {
|
|
1245
|
+
prevIndexByKey = null;
|
|
1246
|
+
return;
|
|
1247
|
+
}
|
|
1248
|
+
let curr = buildIndexMap(items);
|
|
1249
|
+
let prev = prevIndexByKey;
|
|
1250
|
+
let suppressed = isSuppressed();
|
|
1251
|
+
if (prev && !suppressed) {
|
|
1252
|
+
let orderDiffByKey = buildOrderDiff(prev, curr);
|
|
1253
|
+
let itemHeight = options.getItemHeight();
|
|
1254
|
+
for (let [key, el] of itemEls) {
|
|
1255
|
+
let kind = classifyReorderAnim(orderDiffByKey, key);
|
|
1256
|
+
if (kind === "none") continue;
|
|
1257
|
+
cancelAnim(key);
|
|
1258
|
+
let anim;
|
|
1259
|
+
if (kind === "opacity") anim = animateElement(el, [{ opacity: 0 }, { opacity: 1 }], {
|
|
1260
|
+
duration: durationMs,
|
|
1261
|
+
easing
|
|
1262
|
+
});
|
|
1263
|
+
else {
|
|
1264
|
+
let delta = -(orderDiffByKey.get(key) ?? 0) * itemHeight;
|
|
1265
|
+
if (Math.abs(delta) <= 1) continue;
|
|
1266
|
+
anim = animateElement(el, [{ transform: `translateY(${delta}px)` }, { transform: "translateY(0)" }], {
|
|
1267
|
+
duration: durationMs,
|
|
1268
|
+
easing
|
|
1269
|
+
});
|
|
1270
|
+
}
|
|
1271
|
+
if (!anim) continue;
|
|
1272
|
+
activeAnims.set(key, anim);
|
|
1273
|
+
let captured = anim;
|
|
1274
|
+
anim.finished.then(() => {
|
|
1275
|
+
if (activeAnims.get(key) === captured) activeAnims.delete(key);
|
|
1276
|
+
}).catch(() => {});
|
|
1277
|
+
}
|
|
1278
|
+
}
|
|
1279
|
+
prevIndexByKey = curr;
|
|
1280
|
+
};
|
|
1281
|
+
let cancel = () => {
|
|
1282
|
+
cancelAll();
|
|
1283
|
+
};
|
|
1284
|
+
let destroy = () => {
|
|
1285
|
+
if (destroyed) return;
|
|
1286
|
+
destroyed = true;
|
|
1287
|
+
cancelAll();
|
|
1288
|
+
itemEls.clear();
|
|
1289
|
+
prevIndexByKey = null;
|
|
1290
|
+
};
|
|
1291
|
+
return {
|
|
1292
|
+
register,
|
|
1293
|
+
sync,
|
|
1294
|
+
cancel,
|
|
1295
|
+
destroy
|
|
1296
|
+
};
|
|
1297
|
+
}
|
|
1298
|
+
//#endregion
|
|
1299
|
+
//#region src/fade/fade.ts
|
|
1300
|
+
var FADE_MS = 120;
|
|
1301
|
+
var FADE_EASING = "ease-out";
|
|
1302
|
+
var STYLE_KEYS$1 = ["will-change"];
|
|
1303
|
+
function createFade(el, options) {
|
|
1304
|
+
let durationMs = options?.durationMs ?? 120;
|
|
1305
|
+
let easing = options?.easing ?? "ease-out";
|
|
1306
|
+
let lastVisible;
|
|
1307
|
+
let current = null;
|
|
1308
|
+
let stopCurrent = () => {
|
|
1309
|
+
if (!current) return;
|
|
1310
|
+
current.anim?.cancel();
|
|
1311
|
+
current.resolve(false);
|
|
1312
|
+
current = null;
|
|
1313
|
+
};
|
|
1314
|
+
let setVisible = (visible) => {
|
|
1315
|
+
if (lastVisible === visible && !current) {
|
|
1316
|
+
let { playback, resolve } = createPlayback();
|
|
1317
|
+
resolve(true);
|
|
1318
|
+
return playback;
|
|
1319
|
+
}
|
|
1320
|
+
stopCurrent();
|
|
1321
|
+
lastVisible = visible;
|
|
1322
|
+
let { playback, resolve, isCancelled } = createPlayback();
|
|
1323
|
+
let from = visible ? "0" : "1";
|
|
1324
|
+
let to = visible ? "1" : "0";
|
|
1325
|
+
if (durationMs <= 0) {
|
|
1326
|
+
applyStyles(el, { opacity: to });
|
|
1327
|
+
resolve(true);
|
|
1328
|
+
return playback;
|
|
1329
|
+
}
|
|
1330
|
+
applyStyles(el, {
|
|
1331
|
+
opacity: from,
|
|
1332
|
+
"will-change": "opacity"
|
|
1333
|
+
});
|
|
1334
|
+
let anim = animateElement(el, [{ opacity: from }, { opacity: to }], {
|
|
1335
|
+
duration: durationMs,
|
|
1336
|
+
easing,
|
|
1337
|
+
fill: "forwards"
|
|
1338
|
+
});
|
|
1339
|
+
let run = {
|
|
1340
|
+
anim,
|
|
1341
|
+
resolve
|
|
1342
|
+
};
|
|
1343
|
+
current = run;
|
|
1344
|
+
let finish = (ran) => {
|
|
1345
|
+
if (current !== run) return;
|
|
1346
|
+
current = null;
|
|
1347
|
+
clearStyles(el, STYLE_KEYS$1);
|
|
1348
|
+
applyStyles(el, { opacity: to });
|
|
1349
|
+
resolve(ran);
|
|
1350
|
+
};
|
|
1351
|
+
let cancel = playback.cancel;
|
|
1352
|
+
playback.cancel = () => {
|
|
1353
|
+
if (current === run) {
|
|
1354
|
+
anim?.cancel();
|
|
1355
|
+
current = null;
|
|
1356
|
+
clearStyles(el, STYLE_KEYS$1);
|
|
1357
|
+
}
|
|
1358
|
+
cancel();
|
|
1359
|
+
};
|
|
1360
|
+
if (!anim) {
|
|
1361
|
+
finish(true);
|
|
1362
|
+
return playback;
|
|
1363
|
+
}
|
|
1364
|
+
anim.finished.then(() => finish(true), () => {
|
|
1365
|
+
if (!isCancelled() && current === run) finish(false);
|
|
1366
|
+
});
|
|
1367
|
+
return playback;
|
|
1368
|
+
};
|
|
1369
|
+
return { setVisible };
|
|
1370
|
+
}
|
|
1371
|
+
//#endregion
|
|
1372
|
+
//#region src/dock/transforms.ts
|
|
1373
|
+
var DOCK_FADE_OFFSET = "1.5rem";
|
|
1374
|
+
var DOCK_MS = 300;
|
|
1375
|
+
var DOCK_EASING = "cubic-bezier(0.25, 1, 0.5, 1)";
|
|
1376
|
+
function isVertical(edge) {
|
|
1377
|
+
return edge === "top" || edge === "bottom";
|
|
1378
|
+
}
|
|
1379
|
+
function isNegative(edge) {
|
|
1380
|
+
return edge === "left" || edge === "top";
|
|
1381
|
+
}
|
|
1382
|
+
function translate(edge, value) {
|
|
1383
|
+
return `translate${isVertical(edge) ? "Y" : "X"}(${value})`;
|
|
1384
|
+
}
|
|
1385
|
+
function dockTransforms(mode, edge) {
|
|
1386
|
+
let axisZero = translate(edge, "0");
|
|
1387
|
+
if (mode === "fade") return {
|
|
1388
|
+
closed: {
|
|
1389
|
+
transform: translate(edge, isNegative(edge) ? `-${DOCK_FADE_OFFSET}` : DOCK_FADE_OFFSET),
|
|
1390
|
+
opacity: "0"
|
|
1391
|
+
},
|
|
1392
|
+
open: {
|
|
1393
|
+
transform: axisZero,
|
|
1394
|
+
opacity: "1"
|
|
1395
|
+
}
|
|
1396
|
+
};
|
|
1397
|
+
return {
|
|
1398
|
+
closed: {
|
|
1399
|
+
transform: translate(edge, isNegative(edge) ? "-100%" : "100%"),
|
|
1400
|
+
opacity: "1"
|
|
1401
|
+
},
|
|
1402
|
+
open: {
|
|
1403
|
+
transform: axisZero,
|
|
1404
|
+
opacity: "1"
|
|
1405
|
+
}
|
|
1406
|
+
};
|
|
1407
|
+
}
|
|
1408
|
+
//#endregion
|
|
1409
|
+
//#region src/dock/dock.ts
|
|
1410
|
+
var PANEL_STYLE_KEYS = [
|
|
1411
|
+
"transform",
|
|
1412
|
+
"opacity",
|
|
1413
|
+
"will-change"
|
|
1414
|
+
];
|
|
1415
|
+
var BACKDROP_STYLE_KEYS = ["opacity", "will-change"];
|
|
1416
|
+
function createDock(config) {
|
|
1417
|
+
let edge = config.edge ?? "right";
|
|
1418
|
+
let durationMs = config.durationMs ?? 300;
|
|
1419
|
+
let easing = config.easing ?? "cubic-bezier(0.25, 1, 0.5, 1)";
|
|
1420
|
+
let mounted = false;
|
|
1421
|
+
let leaving = false;
|
|
1422
|
+
let animating = false;
|
|
1423
|
+
let wantOpen = false;
|
|
1424
|
+
let destroyed = false;
|
|
1425
|
+
let panel = null;
|
|
1426
|
+
let backdrop = null;
|
|
1427
|
+
let active = null;
|
|
1428
|
+
let applyPanel = (el, state) => {
|
|
1429
|
+
applyStyles(el, {
|
|
1430
|
+
transform: state.transform,
|
|
1431
|
+
opacity: state.opacity,
|
|
1432
|
+
"will-change": "transform, opacity"
|
|
1433
|
+
});
|
|
1434
|
+
};
|
|
1435
|
+
let clearPanel = () => {
|
|
1436
|
+
if (panel) clearStyles(panel, PANEL_STYLE_KEYS);
|
|
1437
|
+
};
|
|
1438
|
+
let clearBackdrop = () => {
|
|
1439
|
+
if (backdrop) clearStyles(backdrop, BACKDROP_STYLE_KEYS);
|
|
1440
|
+
};
|
|
1441
|
+
let abortActive = () => {
|
|
1442
|
+
if (!active) return;
|
|
1443
|
+
active.panelAnim?.cancel();
|
|
1444
|
+
active.backdropAnim?.cancel();
|
|
1445
|
+
let resolve = active.resolve;
|
|
1446
|
+
active = null;
|
|
1447
|
+
resolve(false);
|
|
1448
|
+
};
|
|
1449
|
+
let settleInstant = (open) => {
|
|
1450
|
+
wantOpen = open;
|
|
1451
|
+
mounted = open;
|
|
1452
|
+
leaving = false;
|
|
1453
|
+
animating = false;
|
|
1454
|
+
if (open) {
|
|
1455
|
+
if (backdrop) applyStyles(backdrop, { opacity: "1" });
|
|
1456
|
+
return;
|
|
1457
|
+
}
|
|
1458
|
+
clearPanel();
|
|
1459
|
+
clearBackdrop();
|
|
1460
|
+
};
|
|
1461
|
+
let finish = (run, ran) => {
|
|
1462
|
+
if (active !== run) return;
|
|
1463
|
+
active = null;
|
|
1464
|
+
animating = false;
|
|
1465
|
+
leaving = false;
|
|
1466
|
+
if (wantOpen) {
|
|
1467
|
+
mounted = true;
|
|
1468
|
+
if (panel) {
|
|
1469
|
+
applyPanel(panel, dockTransforms(run.mode, edge).open);
|
|
1470
|
+
clearStyles(panel, ["will-change"]);
|
|
1471
|
+
}
|
|
1472
|
+
if (backdrop) {
|
|
1473
|
+
applyStyles(backdrop, { opacity: "1" });
|
|
1474
|
+
clearStyles(backdrop, ["will-change"]);
|
|
1475
|
+
}
|
|
1476
|
+
} else {
|
|
1477
|
+
mounted = false;
|
|
1478
|
+
clearPanel();
|
|
1479
|
+
clearBackdrop();
|
|
1480
|
+
}
|
|
1481
|
+
run.resolve(ran);
|
|
1482
|
+
};
|
|
1483
|
+
let animatePair = (run) => {
|
|
1484
|
+
let tx = dockTransforms(run.mode, edge);
|
|
1485
|
+
let from = run.opening ? tx.closed : tx.open;
|
|
1486
|
+
let to = run.opening ? tx.open : tx.closed;
|
|
1487
|
+
if (panel) run.panelAnim = animateElement(panel, [from, to], {
|
|
1488
|
+
duration: durationMs,
|
|
1489
|
+
easing,
|
|
1490
|
+
fill: "forwards"
|
|
1491
|
+
});
|
|
1492
|
+
if (backdrop) run.backdropAnim = animateElement(backdrop, run.opening ? [{ opacity: "0" }, { opacity: "1" }] : [{ opacity: "1" }, { opacity: "0" }], {
|
|
1493
|
+
duration: durationMs,
|
|
1494
|
+
easing,
|
|
1495
|
+
fill: "forwards"
|
|
1496
|
+
});
|
|
1497
|
+
let waiter = run.panelAnim ?? run.backdropAnim;
|
|
1498
|
+
if (!waiter) {
|
|
1499
|
+
finish(run, true);
|
|
1500
|
+
return;
|
|
1501
|
+
}
|
|
1502
|
+
waiter.finished.then(() => finish(run, true), () => void 0);
|
|
1503
|
+
};
|
|
1504
|
+
let performOpen = async (run) => {
|
|
1505
|
+
await dualRaf();
|
|
1506
|
+
if (active !== run || destroyed || run.isCancelled()) return;
|
|
1507
|
+
animatePair(run);
|
|
1508
|
+
};
|
|
1509
|
+
let kick = (run) => {
|
|
1510
|
+
if (run.started || run !== active || destroyed) return;
|
|
1511
|
+
if (run.opening) {
|
|
1512
|
+
if (!panel && !backdrop) return;
|
|
1513
|
+
run.started = true;
|
|
1514
|
+
let tx = dockTransforms(run.mode, edge);
|
|
1515
|
+
if (panel) {
|
|
1516
|
+
applyPanel(panel, tx.closed);
|
|
1517
|
+
panel.offsetWidth;
|
|
1518
|
+
}
|
|
1519
|
+
if (backdrop) applyStyles(backdrop, {
|
|
1520
|
+
opacity: "0",
|
|
1521
|
+
"will-change": "opacity"
|
|
1522
|
+
});
|
|
1523
|
+
performOpen(run);
|
|
1524
|
+
return;
|
|
1525
|
+
}
|
|
1526
|
+
if (!panel && !backdrop) {
|
|
1527
|
+
finish(run, true);
|
|
1528
|
+
return;
|
|
1529
|
+
}
|
|
1530
|
+
run.started = true;
|
|
1531
|
+
if (panel) {
|
|
1532
|
+
applyPanel(panel, dockTransforms(run.mode, edge).open);
|
|
1533
|
+
panel.offsetWidth;
|
|
1534
|
+
}
|
|
1535
|
+
if (backdrop) applyStyles(backdrop, {
|
|
1536
|
+
opacity: "1",
|
|
1537
|
+
"will-change": "opacity"
|
|
1538
|
+
});
|
|
1539
|
+
animatePair(run);
|
|
1540
|
+
};
|
|
1541
|
+
let bindCancel = (run) => {
|
|
1542
|
+
let cancel = run.playback.cancel;
|
|
1543
|
+
run.playback.cancel = () => {
|
|
1544
|
+
if (active === run) {
|
|
1545
|
+
run.panelAnim?.cancel();
|
|
1546
|
+
run.backdropAnim?.cancel();
|
|
1547
|
+
active = null;
|
|
1548
|
+
animating = false;
|
|
1549
|
+
leaving = false;
|
|
1550
|
+
if (!wantOpen) {
|
|
1551
|
+
mounted = false;
|
|
1552
|
+
clearPanel();
|
|
1553
|
+
clearBackdrop();
|
|
1554
|
+
}
|
|
1555
|
+
}
|
|
1556
|
+
cancel();
|
|
1557
|
+
};
|
|
1558
|
+
};
|
|
1559
|
+
let setOpen = (open) => {
|
|
1560
|
+
if (destroyed) {
|
|
1561
|
+
let { playback, resolve } = createPlayback();
|
|
1562
|
+
resolve(false);
|
|
1563
|
+
return playback;
|
|
1564
|
+
}
|
|
1565
|
+
if (open === wantOpen && mounted === open && !animating) {
|
|
1566
|
+
let { playback, resolve } = createPlayback();
|
|
1567
|
+
resolve(true);
|
|
1568
|
+
return playback;
|
|
1569
|
+
}
|
|
1570
|
+
abortActive();
|
|
1571
|
+
wantOpen = open;
|
|
1572
|
+
let mode = config.getMode();
|
|
1573
|
+
if (mode === "none" || durationMs <= 0) {
|
|
1574
|
+
let { playback, resolve } = createPlayback();
|
|
1575
|
+
settleInstant(open);
|
|
1576
|
+
resolve(true);
|
|
1577
|
+
return playback;
|
|
1578
|
+
}
|
|
1579
|
+
let { playback, resolve, isCancelled } = createPlayback();
|
|
1580
|
+
mounted = true;
|
|
1581
|
+
leaving = !open;
|
|
1582
|
+
animating = true;
|
|
1583
|
+
let run = {
|
|
1584
|
+
opening: open,
|
|
1585
|
+
mode,
|
|
1586
|
+
playback,
|
|
1587
|
+
resolve,
|
|
1588
|
+
isCancelled,
|
|
1589
|
+
panelAnim: null,
|
|
1590
|
+
backdropAnim: null,
|
|
1591
|
+
started: false
|
|
1592
|
+
};
|
|
1593
|
+
active = run;
|
|
1594
|
+
bindCancel(run);
|
|
1595
|
+
kick(run);
|
|
1596
|
+
return playback;
|
|
1597
|
+
};
|
|
1598
|
+
let attach = (el) => {
|
|
1599
|
+
if (!destroyed) {
|
|
1600
|
+
panel = el;
|
|
1601
|
+
if (active) kick(active);
|
|
1602
|
+
}
|
|
1603
|
+
return { destroy: () => {
|
|
1604
|
+
if (panel === el) panel = null;
|
|
1605
|
+
} };
|
|
1606
|
+
};
|
|
1607
|
+
let attachBackdrop = (el) => {
|
|
1608
|
+
if (!destroyed) {
|
|
1609
|
+
backdrop = el;
|
|
1610
|
+
if (active) kick(active);
|
|
1611
|
+
}
|
|
1612
|
+
return { destroy: () => {
|
|
1613
|
+
if (backdrop === el) backdrop = null;
|
|
1614
|
+
} };
|
|
1615
|
+
};
|
|
1616
|
+
let destroy = () => {
|
|
1617
|
+
if (destroyed) return;
|
|
1618
|
+
destroyed = true;
|
|
1619
|
+
abortActive();
|
|
1620
|
+
clearPanel();
|
|
1621
|
+
clearBackdrop();
|
|
1622
|
+
mounted = false;
|
|
1623
|
+
leaving = false;
|
|
1624
|
+
animating = false;
|
|
1625
|
+
wantOpen = false;
|
|
1626
|
+
panel = null;
|
|
1627
|
+
backdrop = null;
|
|
1628
|
+
};
|
|
1629
|
+
return {
|
|
1630
|
+
get mounted() {
|
|
1631
|
+
return mounted;
|
|
1632
|
+
},
|
|
1633
|
+
get leaving() {
|
|
1634
|
+
return leaving;
|
|
1635
|
+
},
|
|
1636
|
+
get animating() {
|
|
1637
|
+
return animating;
|
|
1638
|
+
},
|
|
1639
|
+
setOpen,
|
|
1640
|
+
attach,
|
|
1641
|
+
attachBackdrop,
|
|
1642
|
+
destroy
|
|
1643
|
+
};
|
|
1644
|
+
}
|
|
1645
|
+
//#endregion
|
|
1646
|
+
//#region src/popover/popover.ts
|
|
1647
|
+
var POPOVER_MS = 120;
|
|
1648
|
+
var POPOVER_EASING = "ease-out";
|
|
1649
|
+
var POPOVER_ORIGIN = "center top";
|
|
1650
|
+
var CLOSED = {
|
|
1651
|
+
transform: "scale(0.92)",
|
|
1652
|
+
opacity: "0"
|
|
1653
|
+
};
|
|
1654
|
+
var OPEN = {
|
|
1655
|
+
transform: "scale(1)",
|
|
1656
|
+
opacity: "1"
|
|
1657
|
+
};
|
|
1658
|
+
var STYLE_KEYS = ["will-change"];
|
|
1659
|
+
function createPopover() {
|
|
1660
|
+
let lastOrigin = POPOVER_ORIGIN;
|
|
1661
|
+
let current = null;
|
|
1662
|
+
let stopCurrent = () => {
|
|
1663
|
+
if (!current) return;
|
|
1664
|
+
current.anim?.cancel();
|
|
1665
|
+
current.resolve(false);
|
|
1666
|
+
current = null;
|
|
1667
|
+
};
|
|
1668
|
+
let play = (el, opening, options) => {
|
|
1669
|
+
stopCurrent();
|
|
1670
|
+
let { playback, resolve, isCancelled } = createPlayback();
|
|
1671
|
+
let durationMs = options?.durationMs ?? 120;
|
|
1672
|
+
let easing = options?.easing ?? "ease-out";
|
|
1673
|
+
if (options?.origin != null) lastOrigin = options.origin;
|
|
1674
|
+
applyStyles(el, { "transform-origin": options?.origin ?? lastOrigin });
|
|
1675
|
+
let from = opening ? CLOSED : OPEN;
|
|
1676
|
+
let to = opening ? OPEN : CLOSED;
|
|
1677
|
+
if (durationMs <= 0) {
|
|
1678
|
+
applyStyles(el, {
|
|
1679
|
+
transform: to.transform,
|
|
1680
|
+
opacity: to.opacity
|
|
1681
|
+
});
|
|
1682
|
+
resolve(true);
|
|
1683
|
+
return playback;
|
|
1684
|
+
}
|
|
1685
|
+
applyStyles(el, {
|
|
1686
|
+
transform: from.transform,
|
|
1687
|
+
opacity: from.opacity,
|
|
1688
|
+
"will-change": "transform, opacity"
|
|
1689
|
+
});
|
|
1690
|
+
let anim = animateElement(el, [from, to], {
|
|
1691
|
+
duration: durationMs,
|
|
1692
|
+
easing,
|
|
1693
|
+
fill: "forwards"
|
|
1694
|
+
});
|
|
1695
|
+
let run = {
|
|
1696
|
+
anim,
|
|
1697
|
+
resolve
|
|
1698
|
+
};
|
|
1699
|
+
current = run;
|
|
1700
|
+
let finish = (ran) => {
|
|
1701
|
+
if (current !== run) return;
|
|
1702
|
+
current = null;
|
|
1703
|
+
clearStyles(el, STYLE_KEYS);
|
|
1704
|
+
applyStyles(el, {
|
|
1705
|
+
transform: to.transform,
|
|
1706
|
+
opacity: to.opacity
|
|
1707
|
+
});
|
|
1708
|
+
resolve(ran);
|
|
1709
|
+
};
|
|
1710
|
+
let cancel = playback.cancel;
|
|
1711
|
+
playback.cancel = () => {
|
|
1712
|
+
if (current === run) {
|
|
1713
|
+
anim?.cancel();
|
|
1714
|
+
current = null;
|
|
1715
|
+
clearStyles(el, STYLE_KEYS);
|
|
1716
|
+
}
|
|
1717
|
+
cancel();
|
|
1718
|
+
};
|
|
1719
|
+
if (!anim) {
|
|
1720
|
+
finish(true);
|
|
1721
|
+
return playback;
|
|
1722
|
+
}
|
|
1723
|
+
anim.finished.then(() => finish(true), () => {
|
|
1724
|
+
if (!isCancelled() && current === run) finish(false);
|
|
1725
|
+
});
|
|
1726
|
+
return playback;
|
|
1727
|
+
};
|
|
1728
|
+
return {
|
|
1729
|
+
playOpen: (el, options) => play(el, true, options),
|
|
1730
|
+
playClose: (el, options) => play(el, false, options)
|
|
1731
|
+
};
|
|
1732
|
+
}
|
|
1733
|
+
//#endregion
|
|
1734
|
+
//#region src/digit-flip/slots.ts
|
|
1735
|
+
var DIGIT_FLIP_MS = 200;
|
|
1736
|
+
var MAX_SIMULTANEOUS_DIGIT_FLIPS = 10;
|
|
1737
|
+
function buildDigitSlots(text, prevText, shouldAnimate) {
|
|
1738
|
+
if (!shouldAnimate || prevText === void 0 || prevText === text) return [...text].map((char) => ({
|
|
1739
|
+
kind: "static",
|
|
1740
|
+
char
|
|
1741
|
+
}));
|
|
1742
|
+
let slots = [];
|
|
1743
|
+
let textLength = text.length;
|
|
1744
|
+
let prevLength = prevText.length;
|
|
1745
|
+
for (let i = 0; i < textLength; i++) {
|
|
1746
|
+
let charIndex = textLength - 1 - i;
|
|
1747
|
+
let prevIndex = prevLength - 1 - i;
|
|
1748
|
+
let char = text[charIndex];
|
|
1749
|
+
let prevChar = prevIndex >= 0 ? prevText[prevIndex] : "";
|
|
1750
|
+
if (char !== prevChar) slots.unshift({
|
|
1751
|
+
kind: "flip",
|
|
1752
|
+
char,
|
|
1753
|
+
prevChar
|
|
1754
|
+
});
|
|
1755
|
+
else slots.unshift({
|
|
1756
|
+
kind: "static",
|
|
1757
|
+
char
|
|
1758
|
+
});
|
|
1759
|
+
}
|
|
1760
|
+
return slots;
|
|
1761
|
+
}
|
|
1762
|
+
function formatCounterText(value) {
|
|
1763
|
+
let n = Math.max(0, Math.floor(value));
|
|
1764
|
+
return n >= 1e3 ? "999+" : String(n);
|
|
1765
|
+
}
|
|
1766
|
+
function shouldPresencePop(prev, next) {
|
|
1767
|
+
return prev === 0 && next > 0;
|
|
1768
|
+
}
|
|
1769
|
+
var scheduled = 0;
|
|
1770
|
+
var resetQueued = false;
|
|
1771
|
+
function scheduleDigitFlip(condition) {
|
|
1772
|
+
if (!condition || scheduled >= 10) return false;
|
|
1773
|
+
if (!resetQueued) {
|
|
1774
|
+
resetQueued = true;
|
|
1775
|
+
setTimeout(() => {
|
|
1776
|
+
scheduled = 0;
|
|
1777
|
+
resetQueued = false;
|
|
1778
|
+
}, 0);
|
|
1779
|
+
}
|
|
1780
|
+
scheduled += 1;
|
|
1781
|
+
return true;
|
|
1782
|
+
}
|
|
1783
|
+
//#endregion
|
|
1784
|
+
//#region src/digit-flip/play.ts
|
|
1785
|
+
var DIGIT_FLIP_EASING = "ease";
|
|
1786
|
+
var PRESENCE_POP_MS = 200;
|
|
1787
|
+
var PRESENCE_POP_EASING = "ease";
|
|
1788
|
+
function playTransform(el, frames, options, fallbackMs = 200, fallbackEasing = DIGIT_FLIP_EASING) {
|
|
1789
|
+
let { playback, resolve } = createPlayback();
|
|
1790
|
+
let anim = animateElement(el, frames, {
|
|
1791
|
+
duration: options?.durationMs ?? fallbackMs,
|
|
1792
|
+
easing: options?.easing ?? fallbackEasing,
|
|
1793
|
+
fill: "forwards"
|
|
1794
|
+
});
|
|
1795
|
+
if (!anim) {
|
|
1796
|
+
resolve(true);
|
|
1797
|
+
return playback;
|
|
1798
|
+
}
|
|
1799
|
+
let cancel = playback.cancel;
|
|
1800
|
+
playback.cancel = () => {
|
|
1801
|
+
anim.cancel();
|
|
1802
|
+
cancel();
|
|
1803
|
+
};
|
|
1804
|
+
anim.finished.then(() => resolve(true), () => resolve(false));
|
|
1805
|
+
return playback;
|
|
1806
|
+
}
|
|
1807
|
+
function playDigitFlip(el, options) {
|
|
1808
|
+
return playTransform(el, [{ transform: "rotateX(90deg)" }, { transform: "rotateX(0deg)" }], options);
|
|
1809
|
+
}
|
|
1810
|
+
function playPresencePop(el, options) {
|
|
1811
|
+
return playTransform(el, [{
|
|
1812
|
+
transform: "scale(0.6)",
|
|
1813
|
+
opacity: "0"
|
|
1814
|
+
}, {
|
|
1815
|
+
transform: "scale(1)",
|
|
1816
|
+
opacity: "1"
|
|
1817
|
+
}], options, 200, PRESENCE_POP_EASING);
|
|
1818
|
+
}
|
|
1819
|
+
//#endregion
|
|
1820
|
+
//#region src/send-flight/flight.ts
|
|
1821
|
+
var SEND_FLIGHT_MS = 200;
|
|
1822
|
+
var SEND_FLIGHT_EASING = "ease";
|
|
1823
|
+
function startTransform(tx, ty, sx, sy) {
|
|
1824
|
+
return `translate3d(${tx}px, ${ty}px, 0) scale(${sx}, ${sy})`;
|
|
1825
|
+
}
|
|
1826
|
+
function createImageClone(imageUrl) {
|
|
1827
|
+
let img = document.createElement("img");
|
|
1828
|
+
img.src = imageUrl;
|
|
1829
|
+
img.alt = "";
|
|
1830
|
+
img.draggable = false;
|
|
1831
|
+
return img;
|
|
1832
|
+
}
|
|
1833
|
+
function playSendFlight(options) {
|
|
1834
|
+
let flight = computeFlight(options.from, options.to);
|
|
1835
|
+
if (!flight) return null;
|
|
1836
|
+
let clone = options.node ? options.node.cloneNode(true) : createImageClone(options.imageUrl ?? "");
|
|
1837
|
+
applyStyles(clone, {
|
|
1838
|
+
position: "fixed",
|
|
1839
|
+
top: `${flight.to.top}px`,
|
|
1840
|
+
left: `${flight.to.left}px`,
|
|
1841
|
+
width: `${flight.to.width}px`,
|
|
1842
|
+
height: `${flight.to.height}px`,
|
|
1843
|
+
"transform-origin": "center center",
|
|
1844
|
+
"pointer-events": "none",
|
|
1845
|
+
"z-index": "2147483646",
|
|
1846
|
+
transform: startTransform(flight.fromTranslateX, flight.fromTranslateY, flight.fromScaleX, flight.fromScaleY)
|
|
1847
|
+
});
|
|
1848
|
+
options.host.appendChild(clone);
|
|
1849
|
+
let { playback, resolve } = createPlayback();
|
|
1850
|
+
let durationMs = options.durationMs ?? 200;
|
|
1851
|
+
let easing = options.easing ?? "ease";
|
|
1852
|
+
let anim = animateElement(clone, [{ transform: startTransform(flight.fromTranslateX, flight.fromTranslateY, flight.fromScaleX, flight.fromScaleY) }, { transform: "translate3d(0, 0, 0) scale(1, 1)" }], {
|
|
1853
|
+
duration: durationMs,
|
|
1854
|
+
easing,
|
|
1855
|
+
fill: "forwards"
|
|
1856
|
+
});
|
|
1857
|
+
let teardown = () => {
|
|
1858
|
+
if (clone.parentNode) clone.remove();
|
|
1859
|
+
};
|
|
1860
|
+
let cancel = playback.cancel;
|
|
1861
|
+
playback.cancel = () => {
|
|
1862
|
+
anim?.cancel();
|
|
1863
|
+
teardown();
|
|
1864
|
+
cancel();
|
|
1865
|
+
};
|
|
1866
|
+
let finish = (ran) => {
|
|
1867
|
+
teardown();
|
|
1868
|
+
resolve(ran);
|
|
1869
|
+
};
|
|
1870
|
+
if (!anim) {
|
|
1871
|
+
finish(true);
|
|
1872
|
+
return playback;
|
|
1873
|
+
}
|
|
1874
|
+
anim.finished.then(() => finish(true), () => finish(false));
|
|
1875
|
+
return playback;
|
|
1876
|
+
}
|
|
1877
|
+
//#endregion
|
|
1878
|
+
//#region src/swipe-reveal/swipe.ts
|
|
1879
|
+
var SWIPE_THRESHOLD = 56;
|
|
1880
|
+
var SWIPE_MAX = 80;
|
|
1881
|
+
var SWIPE_TWEEN_MS = 200;
|
|
1882
|
+
function shouldCommitSwipe(offset, threshold) {
|
|
1883
|
+
return offset >= threshold;
|
|
1884
|
+
}
|
|
1885
|
+
function rubberSwipeOffset(delta, max) {
|
|
1886
|
+
if (delta <= 0) return 0;
|
|
1887
|
+
if (delta <= max) return delta;
|
|
1888
|
+
let overflow = delta - max;
|
|
1889
|
+
return max + overflow / (1 + overflow / max);
|
|
1890
|
+
}
|
|
1891
|
+
function applyOffset(el, offset) {
|
|
1892
|
+
el.style.setProperty("transform", `translateX(${offset}px)`);
|
|
1893
|
+
}
|
|
1894
|
+
function createSwipeReveal(el, options) {
|
|
1895
|
+
let threshold = options.threshold ?? 56;
|
|
1896
|
+
let max = options.max ?? 80;
|
|
1897
|
+
let dragging = false;
|
|
1898
|
+
let startX = 0;
|
|
1899
|
+
let current = 0;
|
|
1900
|
+
let playback = null;
|
|
1901
|
+
let stopTween = () => {
|
|
1902
|
+
if (!playback) return;
|
|
1903
|
+
playback.cancel();
|
|
1904
|
+
playback = null;
|
|
1905
|
+
};
|
|
1906
|
+
let settleTo = (target) => {
|
|
1907
|
+
stopTween();
|
|
1908
|
+
let run = tween({
|
|
1909
|
+
from: current,
|
|
1910
|
+
to: target,
|
|
1911
|
+
durationMs: options.durationMs ?? 200,
|
|
1912
|
+
onUpdate: (value) => {
|
|
1913
|
+
current = value;
|
|
1914
|
+
applyOffset(el, value);
|
|
1915
|
+
}
|
|
1916
|
+
});
|
|
1917
|
+
playback = run;
|
|
1918
|
+
run.done.then(() => {
|
|
1919
|
+
if (playback === run) playback = null;
|
|
1920
|
+
});
|
|
1921
|
+
};
|
|
1922
|
+
let onPointerDown = (event) => {
|
|
1923
|
+
stopTween();
|
|
1924
|
+
dragging = true;
|
|
1925
|
+
startX = event.clientX - current;
|
|
1926
|
+
if (typeof el.setPointerCapture === "function") el.setPointerCapture(event.pointerId);
|
|
1927
|
+
};
|
|
1928
|
+
let onPointerMove = (event) => {
|
|
1929
|
+
if (!dragging) return;
|
|
1930
|
+
current = rubberSwipeOffset(event.clientX - startX, max);
|
|
1931
|
+
applyOffset(el, current);
|
|
1932
|
+
};
|
|
1933
|
+
let onPointerUp = (event) => {
|
|
1934
|
+
if (!dragging) return;
|
|
1935
|
+
dragging = false;
|
|
1936
|
+
if (typeof el.releasePointerCapture === "function") try {
|
|
1937
|
+
el.releasePointerCapture(event.pointerId);
|
|
1938
|
+
} catch {}
|
|
1939
|
+
if (shouldCommitSwipe(current, threshold)) {
|
|
1940
|
+
options.onCommit();
|
|
1941
|
+
settleTo(max);
|
|
1942
|
+
} else settleTo(0);
|
|
1943
|
+
};
|
|
1944
|
+
el.addEventListener("pointerdown", onPointerDown);
|
|
1945
|
+
el.addEventListener("pointermove", onPointerMove);
|
|
1946
|
+
el.addEventListener("pointerup", onPointerUp);
|
|
1947
|
+
el.addEventListener("pointercancel", onPointerUp);
|
|
1948
|
+
return { destroy: () => {
|
|
1949
|
+
stopTween();
|
|
1950
|
+
dragging = false;
|
|
1951
|
+
el.removeEventListener("pointerdown", onPointerDown);
|
|
1952
|
+
el.removeEventListener("pointermove", onPointerMove);
|
|
1953
|
+
el.removeEventListener("pointerup", onPointerUp);
|
|
1954
|
+
el.removeEventListener("pointercancel", onPointerUp);
|
|
1955
|
+
} };
|
|
1956
|
+
}
|
|
1957
|
+
//#endregion
|
|
1958
|
+
//#region src/scroll-tween/scroll.ts
|
|
1959
|
+
var SCROLL_TWEEN_MS = 200;
|
|
1960
|
+
function playScrollTween(el, options) {
|
|
1961
|
+
let fromLeft = el.scrollLeft;
|
|
1962
|
+
let fromTop = el.scrollTop;
|
|
1963
|
+
let toLeft = options.left ?? fromLeft;
|
|
1964
|
+
let toTop = options.top ?? fromTop;
|
|
1965
|
+
return tween({
|
|
1966
|
+
from: 0,
|
|
1967
|
+
to: 1,
|
|
1968
|
+
durationMs: options.durationMs ?? 200,
|
|
1969
|
+
onUpdate: (t) => {
|
|
1970
|
+
if (options.left != null) el.scrollLeft = lerp(fromLeft, toLeft, t);
|
|
1971
|
+
if (options.top != null) el.scrollTop = lerp(fromTop, toTop, t);
|
|
1972
|
+
}
|
|
1973
|
+
});
|
|
1974
|
+
}
|
|
1975
|
+
//#endregion
|
|
1976
|
+
//#region src/ripple/ripple.ts
|
|
1977
|
+
var RIPPLE_MS = 400;
|
|
1978
|
+
var RIPPLE_EASING = "ease-out";
|
|
1979
|
+
var RIPPLE_COLOR = "currentColor";
|
|
1980
|
+
var RIPPLE_SIZE_PX = 48;
|
|
1981
|
+
function playRipple(host, options) {
|
|
1982
|
+
let { playback, resolve } = createPlayback();
|
|
1983
|
+
let durationMs = options.durationMs ?? 400;
|
|
1984
|
+
let easing = options.easing ?? "ease-out";
|
|
1985
|
+
let ink = document.createElement("span");
|
|
1986
|
+
applyStyles(ink, {
|
|
1987
|
+
position: "absolute",
|
|
1988
|
+
left: `${options.x}px`,
|
|
1989
|
+
top: `${options.y}px`,
|
|
1990
|
+
width: `48px`,
|
|
1991
|
+
height: `48px`,
|
|
1992
|
+
"border-radius": "50%",
|
|
1993
|
+
background: options.color ?? "currentColor",
|
|
1994
|
+
"pointer-events": "none",
|
|
1995
|
+
"transform-origin": "center center"
|
|
1996
|
+
});
|
|
1997
|
+
host.appendChild(ink);
|
|
1998
|
+
let anim = animateElement(ink, [{
|
|
1999
|
+
transform: "translate(-50%, -50%) scale(0)",
|
|
2000
|
+
opacity: "1"
|
|
2001
|
+
}, {
|
|
2002
|
+
transform: "translate(-50%, -50%) scale(1)",
|
|
2003
|
+
opacity: "0"
|
|
2004
|
+
}], {
|
|
2005
|
+
duration: durationMs,
|
|
2006
|
+
easing,
|
|
2007
|
+
fill: "forwards"
|
|
2008
|
+
});
|
|
2009
|
+
let teardown = () => {
|
|
2010
|
+
if (ink.parentNode) ink.remove();
|
|
2011
|
+
};
|
|
2012
|
+
let cancel = playback.cancel;
|
|
2013
|
+
playback.cancel = () => {
|
|
2014
|
+
anim?.cancel();
|
|
2015
|
+
teardown();
|
|
2016
|
+
cancel();
|
|
2017
|
+
};
|
|
2018
|
+
let finish = (ran) => {
|
|
2019
|
+
teardown();
|
|
2020
|
+
resolve(ran);
|
|
2021
|
+
};
|
|
2022
|
+
if (!anim) {
|
|
2023
|
+
finish(true);
|
|
2024
|
+
return playback;
|
|
2025
|
+
}
|
|
2026
|
+
anim.finished.then(() => finish(true), () => finish(false));
|
|
2027
|
+
return playback;
|
|
2028
|
+
}
|
|
2029
|
+
//#endregion
|
|
2030
|
+
//#region src/pinch-zoom/math.ts
|
|
2031
|
+
var PINCH_MIN_SCALE = 1;
|
|
2032
|
+
var PINCH_MAX_SCALE = 20;
|
|
2033
|
+
var PINCH_WHEEL_FACTOR = .01;
|
|
2034
|
+
function clampScale(scale, min, max) {
|
|
2035
|
+
if (!Number.isFinite(scale)) return min;
|
|
2036
|
+
if (scale < min) return min;
|
|
2037
|
+
if (scale > max) return max;
|
|
2038
|
+
return scale;
|
|
2039
|
+
}
|
|
2040
|
+
function clampNumber(value, min, max) {
|
|
2041
|
+
let next = value;
|
|
2042
|
+
if (next < min) next = min;
|
|
2043
|
+
if (next > max) next = max;
|
|
2044
|
+
return next === 0 ? 0 : next;
|
|
2045
|
+
}
|
|
2046
|
+
function maxTranslate(scale, layoutW, layoutH, viewportW, viewportH) {
|
|
2047
|
+
return {
|
|
2048
|
+
translateX: Math.max(0, (layoutW * scale - viewportW) / 2),
|
|
2049
|
+
translateY: Math.max(0, (layoutH * scale - viewportH) / 2)
|
|
2050
|
+
};
|
|
2051
|
+
}
|
|
2052
|
+
function boundTranslate(translateX, translateY, scale, layoutW, layoutH, viewportW, viewportH) {
|
|
2053
|
+
let bounds = maxTranslate(scale, layoutW, layoutH, viewportW, viewportH);
|
|
2054
|
+
return {
|
|
2055
|
+
translateX: clampNumber(translateX, -bounds.translateX, bounds.translateX),
|
|
2056
|
+
translateY: clampNumber(translateY, -bounds.translateY, bounds.translateY)
|
|
2057
|
+
};
|
|
2058
|
+
}
|
|
2059
|
+
function boundState(state, layoutW, layoutH, viewportW, viewportH) {
|
|
2060
|
+
let bounded = boundTranslate(state.translateX, state.translateY, state.scale, layoutW, layoutH, viewportW, viewportH);
|
|
2061
|
+
return {
|
|
2062
|
+
scale: state.scale,
|
|
2063
|
+
translateX: bounded.translateX,
|
|
2064
|
+
translateY: bounded.translateY
|
|
2065
|
+
};
|
|
2066
|
+
}
|
|
2067
|
+
function zoomAtOrigin(state, nextScale, origin, layoutW, layoutH, viewportW, viewportH, minScale = 1, maxScale = 20) {
|
|
2068
|
+
let scale = clampScale(nextScale, minScale, maxScale);
|
|
2069
|
+
if (scale === state.scale) return boundState(state, layoutW, layoutH, viewportW, viewportH);
|
|
2070
|
+
let originX = origin?.offsetX ?? 0;
|
|
2071
|
+
let originY = origin?.offsetY ?? 0;
|
|
2072
|
+
let ratio = scale / state.scale;
|
|
2073
|
+
return boundState({
|
|
2074
|
+
scale,
|
|
2075
|
+
translateX: state.translateX + originX * (1 - ratio),
|
|
2076
|
+
translateY: state.translateY + originY * (1 - ratio)
|
|
2077
|
+
}, layoutW, layoutH, viewportW, viewportH);
|
|
2078
|
+
}
|
|
2079
|
+
function resetZoom() {
|
|
2080
|
+
return {
|
|
2081
|
+
scale: 1,
|
|
2082
|
+
translateX: 0,
|
|
2083
|
+
translateY: 0
|
|
2084
|
+
};
|
|
2085
|
+
}
|
|
2086
|
+
function zoomTransform(state) {
|
|
2087
|
+
return `translate3d(${state.translateX}px, ${state.translateY}px, 0) scale(${state.scale})`;
|
|
2088
|
+
}
|
|
2089
|
+
function nextScaleFromWheel(scale, deltaY, minScale = 1, maxScale = 20) {
|
|
2090
|
+
return clampScale(scale * Math.exp(-deltaY * PINCH_WHEEL_FACTOR), minScale, maxScale);
|
|
2091
|
+
}
|
|
2092
|
+
//#endregion
|
|
2093
|
+
//#region src/pinch-zoom/pinch.ts
|
|
2094
|
+
function applyState(el, state) {
|
|
2095
|
+
applyStyles(el, {
|
|
2096
|
+
transform: zoomTransform(state),
|
|
2097
|
+
"transform-origin": "0 0"
|
|
2098
|
+
});
|
|
2099
|
+
}
|
|
2100
|
+
function createPinchZoom(config) {
|
|
2101
|
+
let state = resetZoom();
|
|
2102
|
+
let el = config.getEl();
|
|
2103
|
+
let sizes = () => ({
|
|
2104
|
+
layout: config.getLayout(),
|
|
2105
|
+
viewport: config.getViewport()
|
|
2106
|
+
});
|
|
2107
|
+
let commit = (next) => {
|
|
2108
|
+
let { layout, viewport } = sizes();
|
|
2109
|
+
let scale = clampScale(next.scale, 1, 20);
|
|
2110
|
+
let bounded = boundTranslate(next.translateX, next.translateY, scale, layout.width, layout.height, viewport.width, viewport.height);
|
|
2111
|
+
state = {
|
|
2112
|
+
scale,
|
|
2113
|
+
translateX: bounded.translateX,
|
|
2114
|
+
translateY: bounded.translateY
|
|
2115
|
+
};
|
|
2116
|
+
let node = config.getEl();
|
|
2117
|
+
if (node) applyState(node, state);
|
|
2118
|
+
return state;
|
|
2119
|
+
};
|
|
2120
|
+
let onWheel = (event) => {
|
|
2121
|
+
let node = config.getEl();
|
|
2122
|
+
if (!node) return;
|
|
2123
|
+
let { layout, viewport } = sizes();
|
|
2124
|
+
if (event.ctrlKey || event.metaKey) {
|
|
2125
|
+
event.preventDefault();
|
|
2126
|
+
state = zoomAtOrigin(state, nextScaleFromWheel(state.scale, event.deltaY), {
|
|
2127
|
+
offsetX: event.offsetX,
|
|
2128
|
+
offsetY: event.offsetY
|
|
2129
|
+
}, layout.width, layout.height, viewport.width, viewport.height);
|
|
2130
|
+
applyState(node, state);
|
|
2131
|
+
return;
|
|
2132
|
+
}
|
|
2133
|
+
if (state.scale <= 1) return;
|
|
2134
|
+
event.preventDefault();
|
|
2135
|
+
let pan = boundTranslate(state.translateX - event.deltaX, state.translateY - event.deltaY, state.scale, layout.width, layout.height, viewport.width, viewport.height);
|
|
2136
|
+
state = {
|
|
2137
|
+
scale: state.scale,
|
|
2138
|
+
translateX: pan.translateX,
|
|
2139
|
+
translateY: pan.translateY
|
|
2140
|
+
};
|
|
2141
|
+
applyState(node, state);
|
|
2142
|
+
};
|
|
2143
|
+
if (el) el.addEventListener("wheel", onWheel, { passive: false });
|
|
2144
|
+
return {
|
|
2145
|
+
setState: (next) => commit(next),
|
|
2146
|
+
reset: () => commit(resetZoom()),
|
|
2147
|
+
destroy: () => {
|
|
2148
|
+
if (el) el.removeEventListener("wheel", onWheel);
|
|
2149
|
+
el = null;
|
|
2150
|
+
}
|
|
2151
|
+
};
|
|
2152
|
+
}
|
|
2153
|
+
//#endregion
|
|
2154
|
+
//#region src/waveform/decode.ts
|
|
2155
|
+
function decodeWaveform(encoded5bit) {
|
|
2156
|
+
let bitsCount = encoded5bit.length * 8;
|
|
2157
|
+
let valuesCount = Math.floor(bitsCount / 5);
|
|
2158
|
+
if (!valuesCount) return [];
|
|
2159
|
+
let result = [];
|
|
2160
|
+
for (let i = 0; i < valuesCount; i++) {
|
|
2161
|
+
let byteIndex = Math.floor(i * 5 / 8);
|
|
2162
|
+
let bitShift = i * 5 % 8;
|
|
2163
|
+
let lo = encoded5bit[byteIndex] ?? 0;
|
|
2164
|
+
let hi = encoded5bit[byteIndex + 1] ?? 0;
|
|
2165
|
+
result.push(lo + (hi << 8) >> bitShift & 31);
|
|
2166
|
+
}
|
|
2167
|
+
return result;
|
|
2168
|
+
}
|
|
2169
|
+
function fitWaveform(data, fitCount) {
|
|
2170
|
+
let count = Math.max(0, Math.floor(fitCount));
|
|
2171
|
+
if (count === 0) return [];
|
|
2172
|
+
if (data.length === 0) return Array.from({ length: count }, () => 0);
|
|
2173
|
+
let out = [];
|
|
2174
|
+
let spring = data.length / count;
|
|
2175
|
+
for (let i = 0; i < count; i++) {
|
|
2176
|
+
let idx = Math.min(data.length - 1, Math.floor(i * spring));
|
|
2177
|
+
out.push(data[idx] ?? 0);
|
|
2178
|
+
}
|
|
2179
|
+
return out;
|
|
2180
|
+
}
|
|
2181
|
+
//#endregion
|
|
2182
|
+
//#region src/spoiler/spoiler.ts
|
|
2183
|
+
var SPOILER_MS = 200;
|
|
2184
|
+
var SPOILER_EASING = "ease-out";
|
|
2185
|
+
var DOT_GAP = 6;
|
|
2186
|
+
var DOT_RADIUS = 1.15;
|
|
2187
|
+
function paintDots(canvas) {
|
|
2188
|
+
let ctx = canvas.getContext("2d");
|
|
2189
|
+
if (!ctx) return;
|
|
2190
|
+
let width = canvas.width;
|
|
2191
|
+
let height = canvas.height;
|
|
2192
|
+
ctx.clearRect(0, 0, width, height);
|
|
2193
|
+
ctx.fillStyle = "rgba(128, 128, 128, 0.9)";
|
|
2194
|
+
for (let y = DOT_GAP / 2; y < height; y += DOT_GAP) for (let x = DOT_GAP / 2; x < width; x += DOT_GAP) {
|
|
2195
|
+
ctx.beginPath();
|
|
2196
|
+
ctx.arc(x, y, DOT_RADIUS, 0, Math.PI * 2);
|
|
2197
|
+
ctx.fill();
|
|
2198
|
+
}
|
|
2199
|
+
}
|
|
2200
|
+
function createSpoiler(el, options) {
|
|
2201
|
+
let durationMs = options.durationMs ?? 200;
|
|
2202
|
+
let easing = options.easing ?? "ease-out";
|
|
2203
|
+
let canvas = document.createElement("canvas");
|
|
2204
|
+
applyStyles(canvas, {
|
|
2205
|
+
position: "absolute",
|
|
2206
|
+
inset: "0",
|
|
2207
|
+
width: "100%",
|
|
2208
|
+
height: "100%",
|
|
2209
|
+
"pointer-events": "none"
|
|
2210
|
+
});
|
|
2211
|
+
canvas.width = Math.max(1, el.clientWidth || 1);
|
|
2212
|
+
canvas.height = Math.max(1, el.clientHeight || 1);
|
|
2213
|
+
paintDots(canvas);
|
|
2214
|
+
el.appendChild(canvas);
|
|
2215
|
+
let fade = createFade(canvas, {
|
|
2216
|
+
durationMs,
|
|
2217
|
+
easing
|
|
2218
|
+
});
|
|
2219
|
+
if (options.revealed()) applyStyles(canvas, { opacity: "0" });
|
|
2220
|
+
return {
|
|
2221
|
+
reveal: () => fade.setVisible(false),
|
|
2222
|
+
reset: () => fade.setVisible(true),
|
|
2223
|
+
destroy: () => {
|
|
2224
|
+
if (canvas.parentNode) canvas.remove();
|
|
2225
|
+
}
|
|
2226
|
+
};
|
|
2227
|
+
}
|
|
2228
|
+
//#endregion
|
|
2229
|
+
export { ANIMATION_LEVELS, DEFAULT_ANIMATION_LEVEL, DIGIT_FLIP_EASING, DIGIT_FLIP_MS, DOCK_EASING, DOCK_FADE_OFFSET, DOCK_MS, FADE_EASING, FADE_MS, INDICATOR_EASING, INDICATOR_MS, LIST_REORDER_EASING, LIST_REORDER_EPSILON_PX, LIST_REORDER_MS, MAX_SIMULTANEOUS_DIGIT_FLIPS, PINCH_MAX_SCALE, PINCH_MIN_SCALE, PINCH_WHEEL_FACTOR, POPOVER_EASING, POPOVER_MS, POPOVER_ORIGIN, PRESENCE_POP_EASING, PRESENCE_POP_MS, RIPPLE_COLOR, RIPPLE_EASING, RIPPLE_MS, RIPPLE_SIZE_PX, SCROLL_TWEEN_MS, SEND_FLIGHT_EASING, SEND_FLIGHT_MS, SHARED_ELEMENT_EASING, SHARED_ELEMENT_END_MS, SHARED_ELEMENT_MS, SPOILER_EASING, SPOILER_MS, SWIPE_MAX, SWIPE_THRESHOLD, SWIPE_TWEEN_MS, VIEW_SLIDE_COVER_EASING, VIEW_SLIDE_COVER_MS, VIEW_SLIDE_EASING, VIEW_SLIDE_FADE_OFFSET, VIEW_SLIDE_MS, VIEW_SLIDE_REVEAL_EASING, VIEW_SLIDE_REVEAL_MS, VIEW_SLIDE_SETTLE_SLACK_MS, VIEW_SLIDE_ZOOM_EASING, VIEW_SLIDE_ZOOM_MS, boundTranslate, buildDigitSlots, buildOrderDiff, canAnimate, centerFitInViewport, clampScale, classifyReorderAnim, computeCloseFlight, computeFlight, computeOpenFlight, createDock, createFade, createListReorder, createPinchZoom, createPopover, createSharedElement, createSlidingIndicator, createSpoiler, createSwipeReveal, createViewSlide, cycleAnimationLevel, decodeWaveform, defaultAnimationLevel, dockTransforms, dualRaf, easeOutCubic, fitContain, fitWaveform, formatCounterText, isAnimationLevel, isRectFullyVisibleIn, isRectInViewport, lerp, maxTranslate, nextScaleFromWheel, offViewportLandingRect, parseAnimationLevel, pickAnimationLevelFromRatio, playDigitFlip, playPresencePop, playRipple, playScrollTween, playSendFlight, playSharedElement, prefersReducedMotion, resetZoom, resolveNaturalSize, resolveViewSlideMode, rubberSwipeOffset, scheduleDigitFlip, shouldCommitSwipe, shouldPresencePop, slideDirectionByIndex, stepAnimationLevel, tween, uncoverByAspect, viewSlideDurationMs, viewSlideEasing, viewSlideTransforms, zoomAtOrigin, zoomTransform };
|