@surdeddd/bottom-sheet 0.23.4 → 0.24.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +513 -527
- package/README.ru.md +533 -545
- package/dist/{BottomSheetCore-C5tJYVMY.d.cts → BottomSheetCore-B_r4KCQL.d.cts} +48 -13
- package/dist/{BottomSheetCore-DxSrC4QC.d.ts → BottomSheetCore-B_r4KCQL.d.ts} +48 -13
- package/dist/{BottomSheetEngine-B3F9qukX.d.cts → BottomSheetEngine-0F7mjyvF.d.cts} +1 -1
- package/dist/{BottomSheetEngine-BchzgqGG.d.ts → BottomSheetEngine-CJV_DDrM.d.ts} +1 -1
- package/dist/core.cjs +810 -436
- package/dist/core.d.cts +2 -3
- package/dist/core.d.ts +2 -3
- package/dist/core.js +810 -436
- package/dist/debug.d.cts +0 -1
- package/dist/debug.d.ts +0 -1
- package/dist/{default-features-DwVTzDwP.d.ts → default-features-4Xwv6aVt.d.ts} +4 -2
- package/dist/{default-features-BGiCSmV1.d.cts → default-features-DX2NOxP6.d.cts} +4 -2
- package/dist/element.cjs +1421 -697
- package/dist/element.d.cts +11 -3
- package/dist/element.d.ts +11 -3
- package/dist/element.iife.global.js +2 -2
- package/dist/element.js +1421 -697
- package/dist/features.cjs +334 -190
- package/dist/features.d.cts +3 -4
- package/dist/features.d.ts +3 -4
- package/dist/features.js +334 -191
- package/dist/{index-C7TSQkVK.d.cts → index-2shlxhcG.d.cts} +1 -1
- package/dist/{index-CdM1tke5.d.ts → index-CNCRQnpc.d.ts} +1 -1
- package/dist/index.cjs +1129 -631
- package/dist/index.d.cts +11 -6
- package/dist/index.d.ts +11 -6
- package/dist/index.js +1129 -632
- package/dist/integrations/formik.cjs +21 -10
- package/dist/integrations/formik.d.cts +0 -1
- package/dist/integrations/formik.d.ts +0 -1
- package/dist/integrations/formik.js +22 -11
- package/dist/integrations/react-hook-form.cjs +21 -12
- package/dist/integrations/react-hook-form.d.cts +0 -1
- package/dist/integrations/react-hook-form.d.ts +0 -1
- package/dist/integrations/react-hook-form.js +21 -12
- package/dist/option-keys-DItuAjUw.d.cts +6 -0
- package/dist/option-keys-a5DYNwwO.d.ts +6 -0
- package/dist/overlay.cjs +386 -189
- package/dist/overlay.d.cts +1 -3
- package/dist/overlay.d.ts +1 -3
- package/dist/overlay.js +386 -189
- package/dist/preact.d.cts +3 -4
- package/dist/preact.d.ts +3 -4
- package/dist/qwik-lib/index.qwik.cjs +1306 -671
- package/dist/qwik-lib/index.qwik.mjs +1306 -671
- package/dist/qwik.cjs +1326 -701
- package/dist/qwik.d.cts +12 -30
- package/dist/qwik.d.ts +12 -30
- package/dist/qwik.js +1326 -701
- package/dist/react.cjs +1373 -750
- package/dist/react.d.cts +26 -12
- package/dist/react.d.ts +26 -12
- package/dist/react.js +1375 -752
- package/dist/solid.cjs +1332 -727
- package/dist/solid.d.cts +13 -438
- package/dist/solid.d.ts +13 -438
- package/dist/solid.js +1334 -729
- package/dist/solid.source.jsx +1374 -751
- package/dist/styles.css +20 -6
- package/dist/svelte-core.cjs +1209 -676
- package/dist/svelte-core.js +1207 -677
- package/dist/svelte-src/BottomSheet.svelte +101 -58
- package/dist/svelte.cjs +1323 -705
- package/dist/svelte.d.cts +45 -25
- package/dist/svelte.d.ts +45 -25
- package/dist/svelte.js +1323 -705
- package/dist/vue.cjs +1354 -773
- package/dist/vue.d.cts +20 -3
- package/dist/vue.d.ts +20 -3
- package/dist/vue.js +1354 -773
- package/dist/webgl.cjs +159 -53
- package/dist/webgl.d.cts +2 -2
- package/dist/webgl.d.ts +2 -2
- package/dist/webgl.js +159 -53
- package/llms.txt +330 -0
- package/package.json +42 -20
- package/dist/transform-jVfAikE9.d.cts +0 -3
- package/dist/transform-jVfAikE9.d.ts +0 -3
|
@@ -3,6 +3,13 @@ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { en
|
|
|
3
3
|
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
4
4
|
import { jsxs, jsx } from "@builder.io/qwik/jsx-runtime";
|
|
5
5
|
import { component$, useSignal, useStore, useVisibleTask$, noSerialize, useTask$, Slot } from "@builder.io/qwik";
|
|
6
|
+
const STORE_KEY = /* @__PURE__ */ Symbol.for("@surdeddd/bottom-sheet/shared/1");
|
|
7
|
+
function shared(name, init) {
|
|
8
|
+
const host = globalThis;
|
|
9
|
+
const store = host[STORE_KEY] ?? (host[STORE_KEY] = {});
|
|
10
|
+
if (!(name in store)) store[name] = init();
|
|
11
|
+
return store[name];
|
|
12
|
+
}
|
|
6
13
|
const BASE_Z = 100;
|
|
7
14
|
const STEP = 10;
|
|
8
15
|
class SheetStack {
|
|
@@ -69,7 +76,7 @@ class SheetStack {
|
|
|
69
76
|
});
|
|
70
77
|
}
|
|
71
78
|
}
|
|
72
|
-
const sheetStack = new SheetStack();
|
|
79
|
+
const sheetStack = shared("stack", () => new SheetStack());
|
|
73
80
|
function buildTransformTemplate(axis) {
|
|
74
81
|
switch (axis) {
|
|
75
82
|
case "bottom":
|
|
@@ -84,7 +91,7 @@ function buildTransformTemplate(axis) {
|
|
|
84
91
|
}
|
|
85
92
|
const offsetFromMatrix = (axis, m) => axis === "bottom" ? m.m42 : axis === "top" ? -m.m42 : axis === "left" ? -m.m41 : m.m41;
|
|
86
93
|
const layoutAxis = (axis) => axis === "left" || axis === "right" ? "width" : "height";
|
|
87
|
-
const counters = {};
|
|
94
|
+
const counters = shared("instanceIds", () => ({}));
|
|
88
95
|
function nextInstanceId(prefix) {
|
|
89
96
|
if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
|
|
90
97
|
return `${prefix}-${crypto.randomUUID()}`;
|
|
@@ -112,7 +119,13 @@ function createEventBus() {
|
|
|
112
119
|
for (const fn of [
|
|
113
120
|
...set
|
|
114
121
|
]) {
|
|
115
|
-
|
|
122
|
+
try {
|
|
123
|
+
fn(payload);
|
|
124
|
+
} catch (err) {
|
|
125
|
+
queueMicrotask(() => {
|
|
126
|
+
throw err;
|
|
127
|
+
});
|
|
128
|
+
}
|
|
116
129
|
}
|
|
117
130
|
},
|
|
118
131
|
listenerCount(event) {
|
|
@@ -123,7 +136,18 @@ function createEventBus() {
|
|
|
123
136
|
}
|
|
124
137
|
};
|
|
125
138
|
}
|
|
139
|
+
function devWarn(...args) {
|
|
140
|
+
if (typeof process !== "undefined" && process.env && process.env.NODE_ENV !== "production" && typeof console !== "undefined") {
|
|
141
|
+
console.warn(...args);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
126
144
|
let probe = null;
|
|
145
|
+
const rejected = /* @__PURE__ */ new Set();
|
|
146
|
+
const warnRejected = (value) => {
|
|
147
|
+
if (rejected.has(value)) return;
|
|
148
|
+
rejected.add(value);
|
|
149
|
+
devWarn(`[BottomSheet] snap size "${value}" is not a CSS length the browser accepts — it resolves to 0.`);
|
|
150
|
+
};
|
|
127
151
|
const PROBE_ATTR = "data-bs-probe";
|
|
128
152
|
const ensureProbe = (axis) => {
|
|
129
153
|
if (typeof document === "undefined") return null;
|
|
@@ -170,16 +194,13 @@ const resolveSnap = (point, mode, measureFit) => {
|
|
|
170
194
|
if (!el) return 0;
|
|
171
195
|
if (axis === "vertical") {
|
|
172
196
|
el.style.height = point;
|
|
197
|
+
if (el.style.height === "") warnRejected(point);
|
|
173
198
|
return Math.max(0, el.getBoundingClientRect().height);
|
|
174
199
|
}
|
|
175
200
|
el.style.width = point;
|
|
201
|
+
if (el.style.width === "") warnRejected(point);
|
|
176
202
|
return Math.max(0, el.getBoundingClientRect().width);
|
|
177
203
|
};
|
|
178
|
-
function devWarn(...args) {
|
|
179
|
-
if (typeof process !== "undefined" && process.env && process.env.NODE_ENV !== "production" && typeof console !== "undefined") {
|
|
180
|
-
console.warn(...args);
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
204
|
const resolveSnapList = (points, mode, measureFit) => points.map((p) => {
|
|
184
205
|
const raw = resolveSnap(p.size, mode, measureFit);
|
|
185
206
|
if (!Number.isFinite(raw) || raw < 0) {
|
|
@@ -194,6 +215,10 @@ const resolveSnapList = (points, mode, measureFit) => points.map((p) => {
|
|
|
194
215
|
size: raw
|
|
195
216
|
};
|
|
196
217
|
}).sort((a, b) => a.size - b.size);
|
|
218
|
+
const startsClosed = (points, initial, allowed) => {
|
|
219
|
+
const id = initial ?? allowed?.[0] ?? points[0]?.id;
|
|
220
|
+
return points.find((p) => p.id === id)?.size === 0;
|
|
221
|
+
};
|
|
197
222
|
const VH_NOT_DVH = /(^|[^a-z])vh\b/i;
|
|
198
223
|
const auditVhUsage = (points) => {
|
|
199
224
|
for (const p of points) {
|
|
@@ -304,6 +329,7 @@ class SnapResolver {
|
|
|
304
329
|
setMode(mode) {
|
|
305
330
|
if (mode === this.mode) return;
|
|
306
331
|
this.mode = mode;
|
|
332
|
+
this.notified = false;
|
|
307
333
|
this.recompute();
|
|
308
334
|
}
|
|
309
335
|
setAllowedIds(ids) {
|
|
@@ -313,14 +339,20 @@ class SnapResolver {
|
|
|
313
339
|
findById(id) {
|
|
314
340
|
return findById(id, this.resolved);
|
|
315
341
|
}
|
|
316
|
-
findDragSettleTarget(args) {
|
|
342
|
+
findDragSettleTarget(args, openOnly = false) {
|
|
317
343
|
return findDragSettleTarget({
|
|
318
344
|
...args,
|
|
319
345
|
resolved: this.resolved,
|
|
320
|
-
allowed: this.
|
|
346
|
+
allowed: this.pool(openOnly),
|
|
321
347
|
maxAxisSize: this.maxAxisSize
|
|
322
348
|
});
|
|
323
349
|
}
|
|
350
|
+
nearest(size, openOnly) {
|
|
351
|
+
return findNearest(size, this.resolved, this.pool(openOnly));
|
|
352
|
+
}
|
|
353
|
+
pool(openOnly) {
|
|
354
|
+
return openOnly ? this.allowedIds.filter((id) => (this.findById(id)?.size ?? 0) > 0) : this.allowedIds;
|
|
355
|
+
}
|
|
324
356
|
getAllowedRange() {
|
|
325
357
|
return this.rangeCache ?? (this.rangeCache = allowedRange(this.resolved, this.allowedIds));
|
|
326
358
|
}
|
|
@@ -362,22 +394,23 @@ const SIZE_WRITE_EPSILON = 0.5;
|
|
|
362
394
|
const OPACITY_WRITE_EPSILON = 5e-3;
|
|
363
395
|
const POINTER_EVENTS_OPACITY_THRESHOLD = 0.05;
|
|
364
396
|
const RANGE_DIVISION_EPSILON = 1e-4;
|
|
397
|
+
const seatOf = (deps) => {
|
|
398
|
+
const snap = deps.resolveActiveSnap();
|
|
399
|
+
const max = deps.getMaxAxisSize();
|
|
400
|
+
return {
|
|
401
|
+
target: snap ? Math.min(snap.size, max) : null,
|
|
402
|
+
max
|
|
403
|
+
};
|
|
404
|
+
};
|
|
405
|
+
const sameSeat = (before, after) => before.target !== null && after.target !== null && Math.abs(after.target - before.target) < SIZE_WRITE_EPSILON && Math.abs(after.max - before.max) < SIZE_WRITE_EPSILON;
|
|
365
406
|
function installResizeObserver(deps) {
|
|
366
407
|
if (typeof window === "undefined") {
|
|
367
408
|
return () => {
|
|
368
409
|
};
|
|
369
410
|
}
|
|
370
|
-
const seatFor = () => {
|
|
371
|
-
const snap = deps.resolveActiveSnap();
|
|
372
|
-
const max = deps.getMaxAxisSize();
|
|
373
|
-
return {
|
|
374
|
-
target: snap ? Math.min(snap.size, max) : null,
|
|
375
|
-
max
|
|
376
|
-
};
|
|
377
|
-
};
|
|
378
411
|
const onResize = () => {
|
|
379
412
|
if (deps.isDestroyed()) return;
|
|
380
|
-
const before =
|
|
413
|
+
const before = seatOf(deps);
|
|
381
414
|
deps.recomputeSnaps();
|
|
382
415
|
const mode = deps.getMode();
|
|
383
416
|
const isVerticalAxis = mode === "bottom" || mode === "top";
|
|
@@ -386,21 +419,8 @@ function installResizeObserver(deps) {
|
|
|
386
419
|
if (viewportSize2 > 0 && viewportSize2 < max) {
|
|
387
420
|
deps.setMaxAxisSize(viewportSize2);
|
|
388
421
|
}
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
return;
|
|
392
|
-
}
|
|
393
|
-
const wasAnimating = deps.isAnimating?.() ?? false;
|
|
394
|
-
deps.cancelInFlight();
|
|
395
|
-
deps.newCycle();
|
|
396
|
-
const current = deps.resolveActiveSnap();
|
|
397
|
-
const newMax = deps.getMaxAxisSize();
|
|
398
|
-
const targetSize = current ? Math.min(current.size, newMax) : Math.min(deps.getSize(), newMax);
|
|
399
|
-
deps.setSize(targetSize);
|
|
400
|
-
if (current && !deps.isDragging()) {
|
|
401
|
-
deps.applySize(targetSize);
|
|
402
|
-
if (wasAnimating) deps.resyncAfterCancel?.();
|
|
403
|
-
}
|
|
422
|
+
if (sameSeat(before, seatOf(deps))) return;
|
|
423
|
+
deps.reseat();
|
|
404
424
|
};
|
|
405
425
|
let resizeObserver = null;
|
|
406
426
|
if (typeof ResizeObserver !== "undefined") {
|
|
@@ -412,7 +432,7 @@ function installResizeObserver(deps) {
|
|
|
412
432
|
window.addEventListener("orientationchange", onResize);
|
|
413
433
|
const onVisibility = () => {
|
|
414
434
|
if (deps.isDestroyed()) return;
|
|
415
|
-
if (document.hidden) deps.
|
|
435
|
+
if (document.hidden) deps.landNow();
|
|
416
436
|
};
|
|
417
437
|
document.addEventListener("visibilitychange", onVisibility);
|
|
418
438
|
return () => {
|
|
@@ -435,6 +455,16 @@ const contentTargets = (scroller) => {
|
|
|
435
455
|
}
|
|
436
456
|
return out;
|
|
437
457
|
};
|
|
458
|
+
const chromeTargets = (deps) => {
|
|
459
|
+
const out = [];
|
|
460
|
+
for (const child of Array.from(deps.element.children)) {
|
|
461
|
+
if (child !== deps.scrollContainer) out.push(child);
|
|
462
|
+
}
|
|
463
|
+
if (deps.handle !== deps.element && !out.includes(deps.handle)) {
|
|
464
|
+
out.push(deps.handle);
|
|
465
|
+
}
|
|
466
|
+
return out;
|
|
467
|
+
};
|
|
438
468
|
function installFitObserver(deps) {
|
|
439
469
|
if (typeof ResizeObserver === "undefined") return () => {
|
|
440
470
|
};
|
|
@@ -450,7 +480,6 @@ function installFitObserver(deps) {
|
|
|
450
480
|
});
|
|
451
481
|
};
|
|
452
482
|
const ro = new ResizeObserver(schedule);
|
|
453
|
-
ro.observe(deps.handle);
|
|
454
483
|
const scroller = deps.scrollContainer;
|
|
455
484
|
const observed = /* @__PURE__ */ new Set();
|
|
456
485
|
const slots = /* @__PURE__ */ new Set();
|
|
@@ -467,7 +496,10 @@ function installFitObserver(deps) {
|
|
|
467
496
|
};
|
|
468
497
|
const resync = () => {
|
|
469
498
|
trackSlots();
|
|
470
|
-
const targets =
|
|
499
|
+
const targets = [
|
|
500
|
+
...chromeTargets(deps),
|
|
501
|
+
...contentTargets(scroller)
|
|
502
|
+
];
|
|
471
503
|
for (const el of observed) {
|
|
472
504
|
if (!targets.includes(el)) {
|
|
473
505
|
ro.unobserve(el);
|
|
@@ -482,15 +514,16 @@ function installFitObserver(deps) {
|
|
|
482
514
|
}
|
|
483
515
|
schedule();
|
|
484
516
|
};
|
|
517
|
+
resync();
|
|
485
518
|
let mo;
|
|
486
|
-
if (
|
|
487
|
-
resync
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
}
|
|
519
|
+
if (typeof MutationObserver !== "undefined") {
|
|
520
|
+
mo = new MutationObserver(resync);
|
|
521
|
+
mo.observe(deps.element, {
|
|
522
|
+
childList: true
|
|
523
|
+
});
|
|
524
|
+
if (scroller) mo.observe(scroller, {
|
|
525
|
+
childList: true
|
|
526
|
+
});
|
|
494
527
|
}
|
|
495
528
|
return () => {
|
|
496
529
|
ro.disconnect();
|
|
@@ -501,6 +534,77 @@ function installFitObserver(deps) {
|
|
|
501
534
|
if (raf) cancelAnimationFrame(raf);
|
|
502
535
|
};
|
|
503
536
|
}
|
|
537
|
+
const resolveTeleportTarget = (target) => {
|
|
538
|
+
if (!target) return null;
|
|
539
|
+
if (typeof target !== "string") return target;
|
|
540
|
+
if (typeof document === "undefined") return null;
|
|
541
|
+
if (target === "body") return document.body;
|
|
542
|
+
return document.querySelector(target);
|
|
543
|
+
};
|
|
544
|
+
const ROOT_CLASS = "bs-root";
|
|
545
|
+
const homes = /* @__PURE__ */ new WeakMap();
|
|
546
|
+
const homeRootOf = (root) => (root && homes.get(root)) ?? null;
|
|
547
|
+
const mirrorClasses = (home, wrapper) => {
|
|
548
|
+
wrapper.className = home?.className ?? "";
|
|
549
|
+
wrapper.classList.add(ROOT_CLASS);
|
|
550
|
+
};
|
|
551
|
+
const teleportElements = (els, target) => {
|
|
552
|
+
const nodes = els.filter((el) => !!el && el.parentNode !== target);
|
|
553
|
+
if (nodes.length === 0) return () => {
|
|
554
|
+
};
|
|
555
|
+
const home = nodes[0].closest(`.${ROOT_CLASS}`);
|
|
556
|
+
if (home && home.parentNode === target) return () => {
|
|
557
|
+
};
|
|
558
|
+
const wrapper = target.ownerDocument.createElement("div");
|
|
559
|
+
wrapper.setAttribute("data-bs-teleported", "");
|
|
560
|
+
mirrorClasses(home, wrapper);
|
|
561
|
+
let watcher = null;
|
|
562
|
+
if (home) {
|
|
563
|
+
for (const name of Array.from(home.style)) {
|
|
564
|
+
if (name.startsWith("--")) {
|
|
565
|
+
wrapper.style.setProperty(name, home.style.getPropertyValue(name));
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
homes.set(wrapper, home);
|
|
569
|
+
if (typeof MutationObserver !== "undefined") {
|
|
570
|
+
watcher = new MutationObserver(() => mirrorClasses(home, wrapper));
|
|
571
|
+
watcher.observe(home, {
|
|
572
|
+
attributes: true,
|
|
573
|
+
attributeFilter: [
|
|
574
|
+
"class"
|
|
575
|
+
]
|
|
576
|
+
});
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
target.appendChild(wrapper);
|
|
580
|
+
const records = [];
|
|
581
|
+
for (const el of nodes) {
|
|
582
|
+
records.push({
|
|
583
|
+
el,
|
|
584
|
+
parent: el.parentNode,
|
|
585
|
+
next: el.nextSibling
|
|
586
|
+
});
|
|
587
|
+
wrapper.appendChild(el);
|
|
588
|
+
}
|
|
589
|
+
return () => {
|
|
590
|
+
watcher?.disconnect();
|
|
591
|
+
for (let i = records.length - 1; i >= 0; i--) {
|
|
592
|
+
const { el, parent, next } = records[i];
|
|
593
|
+
if (parent && parent.isConnected) {
|
|
594
|
+
const before = next && next.parentNode === parent ? next : null;
|
|
595
|
+
try {
|
|
596
|
+
parent.insertBefore(el, before);
|
|
597
|
+
} catch {
|
|
598
|
+
el.parentNode?.removeChild(el);
|
|
599
|
+
}
|
|
600
|
+
} else {
|
|
601
|
+
el.parentNode?.removeChild(el);
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
homes.delete(wrapper);
|
|
605
|
+
wrapper.parentNode?.removeChild(wrapper);
|
|
606
|
+
};
|
|
607
|
+
};
|
|
504
608
|
function createMaxHeightController(deps) {
|
|
505
609
|
let maxHeightCap;
|
|
506
610
|
let maxHeightRaw;
|
|
@@ -589,8 +693,9 @@ function installSliderKeyboard(deps) {
|
|
|
589
693
|
const isVerticalAxis = deps.mode === "bottom" || deps.mode === "top";
|
|
590
694
|
const stepUp = isVerticalAxis ? "ArrowUp" : "ArrowRight";
|
|
591
695
|
const stepDown = isVerticalAxis ? "ArrowDown" : "ArrowLeft";
|
|
592
|
-
const
|
|
593
|
-
const
|
|
696
|
+
const flipped = deps.mode === "top" || deps.mode === "right";
|
|
697
|
+
const sheetExpandsKey = flipped ? stepDown : stepUp;
|
|
698
|
+
const sheetCollapsesKey = flipped ? stepUp : stepDown;
|
|
594
699
|
const onHandleKey = (e) => {
|
|
595
700
|
if (deps.isDestroyed()) return;
|
|
596
701
|
const allowed = deps.getAllowedIds();
|
|
@@ -672,18 +777,19 @@ class WriteSentinel {
|
|
|
672
777
|
}
|
|
673
778
|
function resolveSheetAnchoredStyle(mode, position, inset) {
|
|
674
779
|
const sizeOffset = `calc(var(--bs-size, 0px) + ${inset})`;
|
|
780
|
+
const lifted = `calc(var(--bs-size, 0px) + var(--bs-keyboard-inset, 0px) + ${inset})`;
|
|
675
781
|
switch (mode) {
|
|
676
782
|
case "bottom":
|
|
677
783
|
if (position === "sheet-top-left") return {
|
|
678
|
-
bottom:
|
|
784
|
+
bottom: lifted,
|
|
679
785
|
left: inset
|
|
680
786
|
};
|
|
681
787
|
if (position === "sheet-top-right") return {
|
|
682
|
-
bottom:
|
|
788
|
+
bottom: lifted,
|
|
683
789
|
right: inset
|
|
684
790
|
};
|
|
685
791
|
return {
|
|
686
|
-
bottom:
|
|
792
|
+
bottom: lifted,
|
|
687
793
|
left: "50%",
|
|
688
794
|
transform: "translateX(-50%)"
|
|
689
795
|
};
|
|
@@ -731,6 +837,13 @@ function resolveSheetAnchoredStyle(mode, position, inset) {
|
|
|
731
837
|
};
|
|
732
838
|
}
|
|
733
839
|
}
|
|
840
|
+
function resetOverlayPosition(ws) {
|
|
841
|
+
ws.top = "";
|
|
842
|
+
ws.bottom = "";
|
|
843
|
+
ws.left = "";
|
|
844
|
+
ws.right = "";
|
|
845
|
+
ws.transform = "";
|
|
846
|
+
}
|
|
734
847
|
function applyOverlayPosition(ws, mode, position, inset) {
|
|
735
848
|
switch (position) {
|
|
736
849
|
case "top-left":
|
|
@@ -789,6 +902,8 @@ function applyOverlayPosition(ws, mode, position, inset) {
|
|
|
789
902
|
}
|
|
790
903
|
const VALID_CSS_LENGTH = /^-?\d+(?:\.\d+)?(?:px|em|rem|%)$/;
|
|
791
904
|
const DEFAULT_SCRIM_DIM = "rgba(0, 0, 0, 0.4)";
|
|
905
|
+
const BACKDROP_COLOR_VAR = "--bs-backdrop-color";
|
|
906
|
+
const unit = (value) => Number.isFinite(value) ? Math.min(Math.max(value, 0), 1) : 1;
|
|
792
907
|
class ScrimController {
|
|
793
908
|
constructor(deps, opts) {
|
|
794
909
|
__publicField(this, "screenComponent");
|
|
@@ -803,11 +918,13 @@ class ScrimController {
|
|
|
803
918
|
__publicField(this, "scrimMode");
|
|
804
919
|
__publicField(this, "backdropRange");
|
|
805
920
|
__publicField(this, "screenRange");
|
|
921
|
+
__publicField(this, "backdropPeak");
|
|
806
922
|
__publicField(this, "scrimEnabled", true);
|
|
807
923
|
__publicField(this, "savedScrimRanges", null);
|
|
808
924
|
__publicField(this, "scrimTapToCloseEnabled", false);
|
|
809
925
|
__publicField(this, "detachScrimTap", null);
|
|
810
926
|
__publicField(this, "scrimOverlayTeardown", null);
|
|
927
|
+
__publicField(this, "placeOverlay", null);
|
|
811
928
|
__publicField(this, "backdropOpacitySentinel", new WriteSentinel());
|
|
812
929
|
__publicField(this, "screenOpacitySentinel", new WriteSentinel());
|
|
813
930
|
__publicField(this, "lastBackdropPointer", "");
|
|
@@ -830,6 +947,8 @@ class ScrimController {
|
|
|
830
947
|
0,
|
|
831
948
|
1
|
|
832
949
|
];
|
|
950
|
+
this.backdropPeak = unit(opts.backdropOpacity ?? 1);
|
|
951
|
+
this.setBackdropColor(opts.backdropColor);
|
|
833
952
|
this.scrimTapToCloseEnabled = opts.scrimTapToClose ?? false;
|
|
834
953
|
const explicitColor = opts.scrimColor ?? preset?.color;
|
|
835
954
|
const effectiveColor = explicitColor ?? (this.scrimMode !== "off" && !this.backdrop ? DEFAULT_SCRIM_DIM : void 0);
|
|
@@ -859,18 +978,19 @@ class ScrimController {
|
|
|
859
978
|
this.setScrimTapToClose(true);
|
|
860
979
|
}
|
|
861
980
|
}
|
|
862
|
-
applyOpacity(progress, progressChanged) {
|
|
863
|
-
if (!progressChanged && this.backdropOpacitySentinel.value !== -1 && this.screenOpacitySentinel.value !== -1) {
|
|
981
|
+
applyOpacity(progress, progressChanged, epsilon = OPACITY_WRITE_EPSILON) {
|
|
982
|
+
if (epsilon > 0 && !progressChanged && this.backdropOpacitySentinel.value !== -1 && this.screenOpacitySentinel.value !== -1) {
|
|
864
983
|
return;
|
|
865
984
|
}
|
|
866
985
|
if (this.backdrop) {
|
|
867
986
|
const [s, e] = this.backdropRange;
|
|
868
987
|
const range = Math.max(e - s, RANGE_DIVISION_EPSILON);
|
|
869
|
-
const
|
|
870
|
-
|
|
988
|
+
const along = Math.min(Math.max((progress - s) / range, 0), 1);
|
|
989
|
+
const backdropOpacity = along * this.backdropPeak;
|
|
990
|
+
if (this.backdropOpacitySentinel.shouldWrite(backdropOpacity, epsilon)) {
|
|
871
991
|
this.backdrop.style.opacity = String(backdropOpacity);
|
|
872
992
|
}
|
|
873
|
-
const nextPointer =
|
|
993
|
+
const nextPointer = along > POINTER_EVENTS_OPACITY_THRESHOLD ? "auto" : "none";
|
|
874
994
|
if (nextPointer !== this.lastBackdropPointer) {
|
|
875
995
|
this.backdrop.style.pointerEvents = nextPointer;
|
|
876
996
|
this.lastBackdropPointer = nextPointer;
|
|
@@ -879,7 +999,7 @@ class ScrimController {
|
|
|
879
999
|
if (this.screenComponent && this.scrimMode !== "off") {
|
|
880
1000
|
const [ss, se] = this.screenRange;
|
|
881
1001
|
const screenOpacity = Math.min(1, Math.max(0, (progress - ss) / Math.max(RANGE_DIVISION_EPSILON, se - ss)));
|
|
882
|
-
if (this.screenOpacitySentinel.shouldWrite(screenOpacity,
|
|
1002
|
+
if (this.screenOpacitySentinel.shouldWrite(screenOpacity, epsilon)) {
|
|
883
1003
|
this.screenComponent.style.opacity = String(screenOpacity);
|
|
884
1004
|
}
|
|
885
1005
|
const nextDisplay = screenOpacity > 0 ? "" : "none";
|
|
@@ -895,6 +1015,17 @@ class ScrimController {
|
|
|
895
1015
|
this.invalidateOpacityCache();
|
|
896
1016
|
applySize();
|
|
897
1017
|
}
|
|
1018
|
+
setBackdropColor(color) {
|
|
1019
|
+
if (this.isDestroyed() || !this.backdrop) return;
|
|
1020
|
+
if (color) this.backdrop.style.setProperty(BACKDROP_COLOR_VAR, color);
|
|
1021
|
+
else this.backdrop.style.removeProperty(BACKDROP_COLOR_VAR);
|
|
1022
|
+
}
|
|
1023
|
+
setBackdropOpacity(value, applySize) {
|
|
1024
|
+
if (this.isDestroyed()) return;
|
|
1025
|
+
this.backdropPeak = unit(value);
|
|
1026
|
+
this.invalidateOpacityCache();
|
|
1027
|
+
applySize();
|
|
1028
|
+
}
|
|
898
1029
|
setScreenRange(range, applySize) {
|
|
899
1030
|
if (this.isDestroyed()) return;
|
|
900
1031
|
this.screenRange = range;
|
|
@@ -923,6 +1054,8 @@ class ScrimController {
|
|
|
923
1054
|
if (this.isDestroyed() || mode === this.mode) return;
|
|
924
1055
|
this.mode = mode;
|
|
925
1056
|
this.invalidateOpacityCache();
|
|
1057
|
+
if (this.scrimMode === "above-sheet") this.applyAboveSheetInset();
|
|
1058
|
+
this.placeOverlay?.();
|
|
926
1059
|
}
|
|
927
1060
|
setScrimMode(mode, applySize) {
|
|
928
1061
|
if (this.isDestroyed()) return;
|
|
@@ -935,7 +1068,7 @@ class ScrimController {
|
|
|
935
1068
|
if (previous === "above-sheet" && mode !== "above-sheet") {
|
|
936
1069
|
s.position = "";
|
|
937
1070
|
s.inset = "";
|
|
938
|
-
|
|
1071
|
+
this.syncPointer();
|
|
939
1072
|
}
|
|
940
1073
|
if (mode === "above-sheet") {
|
|
941
1074
|
this.applyAboveSheetInset();
|
|
@@ -960,9 +1093,6 @@ class ScrimController {
|
|
|
960
1093
|
if (enabled === installed) return;
|
|
961
1094
|
if (enabled) {
|
|
962
1095
|
const target = this.screenComponent;
|
|
963
|
-
if (target.style.pointerEvents === "none") {
|
|
964
|
-
target.style.pointerEvents = "auto";
|
|
965
|
-
}
|
|
966
1096
|
const onClick = () => {
|
|
967
1097
|
if (this.isDestroyed()) return;
|
|
968
1098
|
if (this.closeFn) {
|
|
@@ -985,6 +1115,11 @@ class ScrimController {
|
|
|
985
1115
|
this.detachScrimTap = null;
|
|
986
1116
|
this.scrimTapToCloseEnabled = false;
|
|
987
1117
|
}
|
|
1118
|
+
this.syncPointer();
|
|
1119
|
+
}
|
|
1120
|
+
syncPointer() {
|
|
1121
|
+
if (!this.screenComponent) return;
|
|
1122
|
+
this.screenComponent.style.pointerEvents = this.scrimInteractive || this.detachScrimTap ? "auto" : "none";
|
|
988
1123
|
}
|
|
989
1124
|
setScrimEnabled(enabled, applySize) {
|
|
990
1125
|
if (this.isDestroyed()) return;
|
|
@@ -1079,8 +1214,12 @@ class ScrimController {
|
|
|
1079
1214
|
const wrapper = screen.ownerDocument.createElement("div");
|
|
1080
1215
|
wrapper.className = "bs-scrim-overlay";
|
|
1081
1216
|
const ws = wrapper.style;
|
|
1082
|
-
ws.position = "
|
|
1083
|
-
|
|
1217
|
+
ws.position = "fixed";
|
|
1218
|
+
const place = () => {
|
|
1219
|
+
resetOverlayPosition(ws);
|
|
1220
|
+
applyOverlayPosition(ws, this.mode, position, inset);
|
|
1221
|
+
};
|
|
1222
|
+
place();
|
|
1084
1223
|
if (interactive) {
|
|
1085
1224
|
ws.pointerEvents = "auto";
|
|
1086
1225
|
}
|
|
@@ -1092,8 +1231,10 @@ class ScrimController {
|
|
|
1092
1231
|
};
|
|
1093
1232
|
}
|
|
1094
1233
|
host.appendChild(wrapper);
|
|
1234
|
+
this.placeOverlay = place;
|
|
1095
1235
|
const teardown = () => {
|
|
1096
1236
|
if (wrapper.parentNode) wrapper.parentNode.removeChild(wrapper);
|
|
1237
|
+
if (this.placeOverlay === place) this.placeOverlay = null;
|
|
1097
1238
|
if (this.scrimOverlayTeardown === teardown) {
|
|
1098
1239
|
this.scrimOverlayTeardown = null;
|
|
1099
1240
|
}
|
|
@@ -1117,6 +1258,7 @@ class ScrimController {
|
|
|
1117
1258
|
if (this.backdrop) {
|
|
1118
1259
|
this.backdrop.style.opacity = "";
|
|
1119
1260
|
this.backdrop.style.pointerEvents = "";
|
|
1261
|
+
this.backdrop.style.removeProperty(BACKDROP_COLOR_VAR);
|
|
1120
1262
|
}
|
|
1121
1263
|
this.invalidateOpacityCache();
|
|
1122
1264
|
this.lastBackdropPointer = "";
|
|
@@ -1141,13 +1283,13 @@ class ScrimController {
|
|
|
1141
1283
|
}
|
|
1142
1284
|
if (opts.interactive !== void 0) {
|
|
1143
1285
|
this.scrimInteractive = opts.interactive;
|
|
1144
|
-
|
|
1286
|
+
this.syncPointer();
|
|
1145
1287
|
}
|
|
1146
1288
|
}
|
|
1147
1289
|
applyAboveSheetInset() {
|
|
1148
1290
|
if (!this.screenComponent) return;
|
|
1149
1291
|
const insetByMode = {
|
|
1150
|
-
bottom: "0 0 var(--bs-size) 0",
|
|
1292
|
+
bottom: "0 0 calc(var(--bs-size) + var(--bs-keyboard-inset, 0px)) 0",
|
|
1151
1293
|
top: "var(--bs-size) 0 0 0",
|
|
1152
1294
|
left: "0 0 0 var(--bs-size)",
|
|
1153
1295
|
right: "0 var(--bs-size) 0 0"
|
|
@@ -1155,7 +1297,7 @@ class ScrimController {
|
|
|
1155
1297
|
const s = this.screenComponent.style;
|
|
1156
1298
|
s.position = "fixed";
|
|
1157
1299
|
s.inset = insetByMode[this.mode];
|
|
1158
|
-
|
|
1300
|
+
this.syncPointer();
|
|
1159
1301
|
}
|
|
1160
1302
|
}
|
|
1161
1303
|
const easeOutBack = (t) => {
|
|
@@ -1166,14 +1308,21 @@ const easeOutBack = (t) => {
|
|
|
1166
1308
|
const easeOutCubic = (t) => 1 - Math.pow(1 - t, 3);
|
|
1167
1309
|
const easeLinear = (t) => t;
|
|
1168
1310
|
const easeOutQuint = (t) => 1 - Math.pow(1 - t, 5);
|
|
1311
|
+
const MIN_RETARGET_MS = 120;
|
|
1169
1312
|
const tween = ({ from, to, duration, easing = easeOutCubic, onUpdate }) => {
|
|
1170
1313
|
let cancelled = false;
|
|
1314
|
+
let finished = false;
|
|
1171
1315
|
let rafId = 0;
|
|
1172
1316
|
let resolveFn = null;
|
|
1173
|
-
|
|
1317
|
+
let start = performance.now();
|
|
1318
|
+
let origin = from;
|
|
1319
|
+
let target = to;
|
|
1320
|
+
let span = duration;
|
|
1321
|
+
let last = from;
|
|
1174
1322
|
const promise = new Promise((resolve) => {
|
|
1175
1323
|
resolveFn = resolve;
|
|
1176
1324
|
if (duration <= 0) {
|
|
1325
|
+
finished = true;
|
|
1177
1326
|
onUpdate(to);
|
|
1178
1327
|
resolve();
|
|
1179
1328
|
return;
|
|
@@ -1181,12 +1330,13 @@ const tween = ({ from, to, duration, easing = easeOutCubic, onUpdate }) => {
|
|
|
1181
1330
|
const step = (now) => {
|
|
1182
1331
|
if (cancelled) return;
|
|
1183
1332
|
const elapsed = Math.max(0, now - start);
|
|
1184
|
-
const t = Math.min(elapsed /
|
|
1185
|
-
|
|
1186
|
-
onUpdate(
|
|
1333
|
+
const t = Math.min(elapsed / span, 1);
|
|
1334
|
+
last = origin + (target - origin) * easing(t);
|
|
1335
|
+
onUpdate(last);
|
|
1187
1336
|
if (t < 1) {
|
|
1188
1337
|
rafId = requestAnimationFrame(step);
|
|
1189
1338
|
} else {
|
|
1339
|
+
finished = true;
|
|
1190
1340
|
resolve();
|
|
1191
1341
|
}
|
|
1192
1342
|
};
|
|
@@ -1198,6 +1348,15 @@ const tween = ({ from, to, duration, easing = easeOutCubic, onUpdate }) => {
|
|
|
1198
1348
|
cancelAnimationFrame(rafId);
|
|
1199
1349
|
resolveFn?.();
|
|
1200
1350
|
},
|
|
1351
|
+
retarget: (next) => {
|
|
1352
|
+
if (cancelled || finished || next === target) return;
|
|
1353
|
+
const now = performance.now();
|
|
1354
|
+
const remaining = span - Math.max(0, now - start);
|
|
1355
|
+
origin = last;
|
|
1356
|
+
target = next;
|
|
1357
|
+
start = now;
|
|
1358
|
+
span = Math.max(remaining, MIN_RETARGET_MS);
|
|
1359
|
+
},
|
|
1201
1360
|
promise
|
|
1202
1361
|
};
|
|
1203
1362
|
};
|
|
@@ -1213,19 +1372,28 @@ const DEFAULT_SPRING = {
|
|
|
1213
1372
|
restSpeed: 0.5
|
|
1214
1373
|
};
|
|
1215
1374
|
const MAX_DT = 1 / 30;
|
|
1216
|
-
const
|
|
1375
|
+
const MAX_DURATION_MS = 3e3;
|
|
1376
|
+
const runSpring = ({ from, to, velocity = 0, config, maxDuration = MAX_DURATION_MS, onUpdate }) => {
|
|
1217
1377
|
const cfg = {
|
|
1218
1378
|
...DEFAULT_SPRING,
|
|
1219
1379
|
...config
|
|
1220
1380
|
};
|
|
1221
1381
|
let cancelled = false;
|
|
1382
|
+
let finished = false;
|
|
1222
1383
|
let rafId = 0;
|
|
1223
1384
|
let resolveFn = null;
|
|
1224
1385
|
let x = from;
|
|
1225
1386
|
let v = velocity;
|
|
1387
|
+
let target = to;
|
|
1226
1388
|
let lastTime = performance.now();
|
|
1389
|
+
let deadline = lastTime + maxDuration;
|
|
1227
1390
|
const promise = new Promise((resolve) => {
|
|
1228
1391
|
resolveFn = resolve;
|
|
1392
|
+
const land = () => {
|
|
1393
|
+
finished = true;
|
|
1394
|
+
onUpdate(target, 0);
|
|
1395
|
+
resolve();
|
|
1396
|
+
};
|
|
1229
1397
|
const step = (now) => {
|
|
1230
1398
|
if (cancelled) return;
|
|
1231
1399
|
const dt = Math.min(Math.max(0, now - lastTime) / 1e3, MAX_DT);
|
|
@@ -1234,21 +1402,19 @@ const runSpring = ({ from, to, velocity = 0, config, onUpdate }) => {
|
|
|
1234
1402
|
const subDt = dt / steps;
|
|
1235
1403
|
let stepsLeft = steps;
|
|
1236
1404
|
while (stepsLeft-- > 0) {
|
|
1237
|
-
const force = -cfg.stiffness * (x -
|
|
1405
|
+
const force = -cfg.stiffness * (x - target) - cfg.damping * v;
|
|
1238
1406
|
const accel = force / cfg.mass;
|
|
1239
1407
|
v += accel * subDt;
|
|
1240
1408
|
x += v * subDt;
|
|
1241
1409
|
}
|
|
1242
|
-
if (!Number.isFinite(x) || !Number.isFinite(v)) {
|
|
1243
|
-
|
|
1244
|
-
resolve();
|
|
1410
|
+
if (!Number.isFinite(x) || !Number.isFinite(v) || lastTime >= deadline) {
|
|
1411
|
+
land();
|
|
1245
1412
|
return;
|
|
1246
1413
|
}
|
|
1247
1414
|
onUpdate(x, v);
|
|
1248
|
-
const isAtRest = Math.abs(
|
|
1415
|
+
const isAtRest = Math.abs(target - x) < (cfg.restDelta ?? 0.5) && Math.abs(v) < (cfg.restSpeed ?? 0.5);
|
|
1249
1416
|
if (isAtRest) {
|
|
1250
|
-
|
|
1251
|
-
resolve();
|
|
1417
|
+
land();
|
|
1252
1418
|
} else {
|
|
1253
1419
|
rafId = requestAnimationFrame(step);
|
|
1254
1420
|
}
|
|
@@ -1261,6 +1427,11 @@ const runSpring = ({ from, to, velocity = 0, config, onUpdate }) => {
|
|
|
1261
1427
|
cancelAnimationFrame(rafId);
|
|
1262
1428
|
resolveFn?.();
|
|
1263
1429
|
},
|
|
1430
|
+
retarget: (next) => {
|
|
1431
|
+
if (cancelled || finished) return;
|
|
1432
|
+
target = next;
|
|
1433
|
+
deadline = performance.now() + maxDuration;
|
|
1434
|
+
},
|
|
1264
1435
|
promise
|
|
1265
1436
|
};
|
|
1266
1437
|
};
|
|
@@ -1434,21 +1605,40 @@ class AnimationRunner {
|
|
|
1434
1605
|
this.applySizeFn(size);
|
|
1435
1606
|
}
|
|
1436
1607
|
cancel() {
|
|
1437
|
-
this.currentTween
|
|
1438
|
-
this.currentSpring
|
|
1608
|
+
const tween2 = this.currentTween;
|
|
1609
|
+
const spring = this.currentSpring;
|
|
1610
|
+
this.currentTween = null;
|
|
1611
|
+
this.currentSpring = null;
|
|
1612
|
+
tween2?.cancel();
|
|
1613
|
+
spring?.cancel();
|
|
1439
1614
|
this.cancelWaapi();
|
|
1440
1615
|
}
|
|
1616
|
+
retarget(target) {
|
|
1617
|
+
if (this.currentSpring) {
|
|
1618
|
+
this.currentSpring.retarget(target);
|
|
1619
|
+
return true;
|
|
1620
|
+
}
|
|
1621
|
+
if (this.currentTween) {
|
|
1622
|
+
this.currentTween.retarget(target);
|
|
1623
|
+
return true;
|
|
1624
|
+
}
|
|
1625
|
+
if (this.currentWaapi) {
|
|
1626
|
+
this.currentWaapi.retarget(target);
|
|
1627
|
+
return true;
|
|
1628
|
+
}
|
|
1629
|
+
return false;
|
|
1630
|
+
}
|
|
1441
1631
|
async animateTo(target, velocityPxPerMs) {
|
|
1442
1632
|
if (target === this.getSize()) {
|
|
1443
|
-
if (
|
|
1633
|
+
if (this.isAnimating) this.cancel();
|
|
1634
|
+
if (!this.isDragging()) {
|
|
1444
1635
|
this.applySizeFn(target);
|
|
1636
|
+
this.getRootEl()?.removeAttribute("data-animating");
|
|
1445
1637
|
this.element.style.willChange = "";
|
|
1446
1638
|
}
|
|
1447
1639
|
return;
|
|
1448
1640
|
}
|
|
1449
|
-
this.
|
|
1450
|
-
this.currentSpring?.cancel();
|
|
1451
|
-
this.cancelWaapi();
|
|
1641
|
+
this.cancel();
|
|
1452
1642
|
this.getRootEl()?.setAttribute("data-animating", "true");
|
|
1453
1643
|
this.element.style.willChange = "transform";
|
|
1454
1644
|
if (this.reducedMotion) {
|
|
@@ -1458,9 +1648,12 @@ class AnimationRunner {
|
|
|
1458
1648
|
return;
|
|
1459
1649
|
}
|
|
1460
1650
|
if (this.settleWaapi && this.applyAux && this.getTransformFor && typeof this.element.animate === "function") {
|
|
1461
|
-
const samples = this.
|
|
1651
|
+
const samples = this.sampleSettle(this.getSize(), target, velocityPxPerMs * VELOCITY_PX_PER_S);
|
|
1462
1652
|
if (samples.values.length >= 2 && samples.durationMs > 0) {
|
|
1463
|
-
await this.runWaapiSettle(
|
|
1653
|
+
await this.runWaapiSettle({
|
|
1654
|
+
samples,
|
|
1655
|
+
target
|
|
1656
|
+
});
|
|
1464
1657
|
if (!this.isAnimating) {
|
|
1465
1658
|
this.getRootEl()?.removeAttribute("data-animating");
|
|
1466
1659
|
if (!this.isDragging()) this.element.style.willChange = "";
|
|
@@ -1502,7 +1695,10 @@ class AnimationRunner {
|
|
|
1502
1695
|
}
|
|
1503
1696
|
}
|
|
1504
1697
|
}
|
|
1505
|
-
|
|
1698
|
+
sampleSettle(from, to, velocityPxPerS) {
|
|
1699
|
+
return this.animationKind === "spring" ? sampleSpringSettle(from, to, velocityPxPerS, this.springConfig) : sampleTweenSettle(from, to, this.duration, this.easing);
|
|
1700
|
+
}
|
|
1701
|
+
startWaapiLeg(samples, onRetarget) {
|
|
1506
1702
|
const applyAux = this.applyAux;
|
|
1507
1703
|
const getTransformFor = this.getTransformFor;
|
|
1508
1704
|
const frames = samples.values.map((v) => ({
|
|
@@ -1517,13 +1713,20 @@ class AnimationRunner {
|
|
|
1517
1713
|
let stopped = false;
|
|
1518
1714
|
let auxRaf = 0;
|
|
1519
1715
|
const lastIdx = samples.values.length - 1;
|
|
1716
|
+
const cursor = () => Math.min((Number(anim.currentTime) || 0) / samples.stepMs, lastIdx);
|
|
1520
1717
|
const sizeNow = () => {
|
|
1521
|
-
const at =
|
|
1718
|
+
const at = cursor();
|
|
1522
1719
|
const idx = Math.floor(at);
|
|
1523
1720
|
const from = samples.values[idx];
|
|
1524
1721
|
const to = samples.values[Math.min(idx + 1, lastIdx)];
|
|
1525
1722
|
return from + (to - from) * (at - idx);
|
|
1526
1723
|
};
|
|
1724
|
+
const velocityNow = () => {
|
|
1725
|
+
const idx = Math.min(Math.floor(cursor()), Math.max(lastIdx - 1, 0));
|
|
1726
|
+
const from = samples.values[idx];
|
|
1727
|
+
const to = samples.values[Math.min(idx + 1, lastIdx)];
|
|
1728
|
+
return (to - from) / samples.stepMs * 1e3;
|
|
1729
|
+
};
|
|
1527
1730
|
const auxTick = () => {
|
|
1528
1731
|
if (stopped) return;
|
|
1529
1732
|
applyAux(sizeNow());
|
|
@@ -1536,22 +1739,55 @@ class AnimationRunner {
|
|
|
1536
1739
|
stopped = true;
|
|
1537
1740
|
cancelAnimationFrame(auxRaf);
|
|
1538
1741
|
},
|
|
1539
|
-
sizeNow
|
|
1742
|
+
sizeNow,
|
|
1743
|
+
velocityNow,
|
|
1744
|
+
retarget: (to) => onRetarget(entry, to)
|
|
1540
1745
|
};
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1746
|
+
return entry;
|
|
1747
|
+
}
|
|
1748
|
+
async runWaapiSettle(first) {
|
|
1749
|
+
let leg = first;
|
|
1750
|
+
for (; ; ) {
|
|
1751
|
+
let queued = null;
|
|
1752
|
+
const entry = this.startWaapiLeg(leg.samples, (running, to) => {
|
|
1753
|
+
const size = this.readShownSize?.() ?? running.sizeNow();
|
|
1754
|
+
queued = {
|
|
1755
|
+
samples: this.sampleSettle(size, to, running.velocityNow()),
|
|
1756
|
+
target: to
|
|
1757
|
+
};
|
|
1758
|
+
running.stop();
|
|
1759
|
+
try {
|
|
1760
|
+
running.anim.cancel();
|
|
1761
|
+
} catch {
|
|
1762
|
+
}
|
|
1763
|
+
this.applySizeFn(size);
|
|
1764
|
+
});
|
|
1765
|
+
this.currentWaapi = entry;
|
|
1551
1766
|
try {
|
|
1552
|
-
anim.
|
|
1767
|
+
await entry.anim.finished;
|
|
1553
1768
|
} catch {
|
|
1554
1769
|
}
|
|
1770
|
+
entry.stop();
|
|
1771
|
+
const wasCurrent = this.currentWaapi === entry;
|
|
1772
|
+
const next = queued;
|
|
1773
|
+
if (wasCurrent && next) {
|
|
1774
|
+
if (next.samples.values.length >= 2 && next.samples.durationMs > 0) {
|
|
1775
|
+
leg = next;
|
|
1776
|
+
continue;
|
|
1777
|
+
}
|
|
1778
|
+
this.currentWaapi = null;
|
|
1779
|
+
this.applySizeFn(next.target);
|
|
1780
|
+
return;
|
|
1781
|
+
}
|
|
1782
|
+
if (wasCurrent) this.currentWaapi = null;
|
|
1783
|
+
if (wasCurrent && entry.anim.playState === "finished") {
|
|
1784
|
+
this.applySizeFn(leg.target);
|
|
1785
|
+
try {
|
|
1786
|
+
entry.anim.cancel();
|
|
1787
|
+
} catch {
|
|
1788
|
+
}
|
|
1789
|
+
}
|
|
1790
|
+
return;
|
|
1555
1791
|
}
|
|
1556
1792
|
}
|
|
1557
1793
|
destroy() {
|
|
@@ -1605,27 +1841,51 @@ const collectFocusables = (root) => {
|
|
|
1605
1841
|
return out;
|
|
1606
1842
|
};
|
|
1607
1843
|
const focusables = (root) => collectFocusables(root).filter((el) => !el.hasAttribute("inert") && isVisible(el) && !el.matches("[aria-hidden='true']"));
|
|
1844
|
+
const findDeep = (root, selector) => {
|
|
1845
|
+
const direct = root.querySelector(selector);
|
|
1846
|
+
if (direct || typeof HTMLSlotElement === "undefined") return direct;
|
|
1847
|
+
for (const slot of Array.from(root.querySelectorAll("slot"))) {
|
|
1848
|
+
for (const assigned of slot.assignedElements()) {
|
|
1849
|
+
if (assigned.matches(selector)) return assigned;
|
|
1850
|
+
const inner = findDeep(assigned, selector);
|
|
1851
|
+
if (inner) return inner;
|
|
1852
|
+
}
|
|
1853
|
+
}
|
|
1854
|
+
return null;
|
|
1855
|
+
};
|
|
1856
|
+
const FOCUS_ALLOW_ATTR = "data-bs-focus-allow";
|
|
1857
|
+
const composedParent = (node) => {
|
|
1858
|
+
const slot = node.assignedSlot;
|
|
1859
|
+
if (slot) return slot;
|
|
1860
|
+
const parent = node.parentNode;
|
|
1861
|
+
return parent instanceof ShadowRoot ? parent.host : parent;
|
|
1862
|
+
};
|
|
1608
1863
|
const containsComposed = (container, node) => {
|
|
1609
|
-
let n = node;
|
|
1610
|
-
while (n) {
|
|
1864
|
+
for (let n = node; n; n = composedParent(n)) {
|
|
1611
1865
|
if (n === container) return true;
|
|
1612
|
-
const slot = n.assignedSlot;
|
|
1613
|
-
if (slot) {
|
|
1614
|
-
n = slot;
|
|
1615
|
-
continue;
|
|
1616
|
-
}
|
|
1617
|
-
const parent = n.parentNode;
|
|
1618
|
-
n = parent instanceof ShadowRoot ? parent.host : parent;
|
|
1619
1866
|
}
|
|
1620
1867
|
return false;
|
|
1621
1868
|
};
|
|
1622
|
-
const
|
|
1869
|
+
const isAllowedOutside = (node) => {
|
|
1870
|
+
for (let n = node; n; n = composedParent(n)) {
|
|
1871
|
+
if (n instanceof Element && n.hasAttribute(FOCUS_ALLOW_ATTR)) return true;
|
|
1872
|
+
}
|
|
1873
|
+
return false;
|
|
1874
|
+
};
|
|
1875
|
+
const deepActiveElement = () => {
|
|
1876
|
+
let active = document.activeElement;
|
|
1877
|
+
while (active?.shadowRoot?.activeElement) {
|
|
1878
|
+
active = active.shadowRoot.activeElement;
|
|
1879
|
+
}
|
|
1880
|
+
return active;
|
|
1881
|
+
};
|
|
1882
|
+
const trapStack = shared("focusTraps", () => []);
|
|
1623
1883
|
const installFocusTrap = (container, options = {}) => {
|
|
1624
1884
|
if (typeof document === "undefined") return () => {
|
|
1625
1885
|
};
|
|
1626
1886
|
const token = {};
|
|
1627
|
-
trapStack.push(token);
|
|
1628
|
-
const isActive = () => trapStack[trapStack.length - 1] === token;
|
|
1887
|
+
if (!options.isActive) trapStack.push(token);
|
|
1888
|
+
const isActive = options.isActive ?? (() => trapStack[trapStack.length - 1] === token);
|
|
1629
1889
|
const previouslyFocused = document.activeElement;
|
|
1630
1890
|
const focusInitial = () => {
|
|
1631
1891
|
const spec = options.initialFocus;
|
|
@@ -1636,12 +1896,13 @@ const installFocusTrap = (container, options = {}) => {
|
|
|
1636
1896
|
});
|
|
1637
1897
|
return;
|
|
1638
1898
|
}
|
|
1639
|
-
const initial = typeof spec === "string" ? container
|
|
1899
|
+
const initial = typeof spec === "string" ? findDeep(container, spec) : spec ?? focusables(container)[0] ?? container;
|
|
1640
1900
|
initial?.focus({
|
|
1641
1901
|
preventScroll: true
|
|
1642
1902
|
});
|
|
1643
1903
|
};
|
|
1644
|
-
focusInitial();
|
|
1904
|
+
if (!containsComposed(container, deepActiveElement())) focusInitial();
|
|
1905
|
+
let backwards = false;
|
|
1645
1906
|
const handleKey = (e) => {
|
|
1646
1907
|
if (!isActive()) return;
|
|
1647
1908
|
if (e.key === "Escape" && options.onEscape) {
|
|
@@ -1649,6 +1910,10 @@ const installFocusTrap = (container, options = {}) => {
|
|
|
1649
1910
|
return;
|
|
1650
1911
|
}
|
|
1651
1912
|
if (e.key !== "Tab") return;
|
|
1913
|
+
backwards = e.shiftKey;
|
|
1914
|
+
setTimeout(() => {
|
|
1915
|
+
backwards = false;
|
|
1916
|
+
}, 0);
|
|
1652
1917
|
const list = focusables(container);
|
|
1653
1918
|
if (list.length === 0) {
|
|
1654
1919
|
e.preventDefault();
|
|
@@ -1656,7 +1921,7 @@ const installFocusTrap = (container, options = {}) => {
|
|
|
1656
1921
|
}
|
|
1657
1922
|
const first = list[0];
|
|
1658
1923
|
const last = list[list.length - 1];
|
|
1659
|
-
const active =
|
|
1924
|
+
const active = deepActiveElement();
|
|
1660
1925
|
if (e.shiftKey && active === first) {
|
|
1661
1926
|
e.preventDefault();
|
|
1662
1927
|
last.focus();
|
|
@@ -1667,13 +1932,13 @@ const installFocusTrap = (container, options = {}) => {
|
|
|
1667
1932
|
};
|
|
1668
1933
|
const handleFocusIn = (e) => {
|
|
1669
1934
|
if (!isActive()) return;
|
|
1670
|
-
const target = e.target;
|
|
1671
|
-
if (target
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
}
|
|
1935
|
+
const target = e.composedPath()[0] ?? e.target;
|
|
1936
|
+
if (!target || containsComposed(container, target)) return;
|
|
1937
|
+
if (isAllowedOutside(target)) return;
|
|
1938
|
+
const list = focusables(container);
|
|
1939
|
+
list[backwards ? list.length - 1 : 0]?.focus({
|
|
1940
|
+
preventScroll: true
|
|
1941
|
+
});
|
|
1677
1942
|
};
|
|
1678
1943
|
document.addEventListener("keydown", handleKey, true);
|
|
1679
1944
|
document.addEventListener("focusin", handleFocusIn, true);
|
|
@@ -1697,21 +1962,25 @@ const installFocusTrap = (container, options = {}) => {
|
|
|
1697
1962
|
}
|
|
1698
1963
|
};
|
|
1699
1964
|
};
|
|
1700
|
-
|
|
1701
|
-
|
|
1965
|
+
const state$1 = shared("scrollLock", () => ({
|
|
1966
|
+
count: 0,
|
|
1967
|
+
saved: null
|
|
1968
|
+
}));
|
|
1702
1969
|
const lockBodyScroll = () => {
|
|
1703
1970
|
if (typeof document === "undefined") return () => {
|
|
1704
1971
|
};
|
|
1705
|
-
if (
|
|
1972
|
+
if (state$1.count === 0) {
|
|
1706
1973
|
const body = document.body;
|
|
1707
|
-
|
|
1974
|
+
const saved = {
|
|
1708
1975
|
overflow: body.style.overflow,
|
|
1709
1976
|
position: body.style.position,
|
|
1710
1977
|
top: body.style.top,
|
|
1711
1978
|
width: body.style.width,
|
|
1712
1979
|
paddingRight: body.style.paddingRight,
|
|
1980
|
+
scrollX: window.scrollX,
|
|
1713
1981
|
scrollY: window.scrollY
|
|
1714
1982
|
};
|
|
1983
|
+
state$1.saved = saved;
|
|
1715
1984
|
const scrollbarGap = window.innerWidth - document.documentElement.clientWidth;
|
|
1716
1985
|
if (scrollbarGap > 0) {
|
|
1717
1986
|
const current = parseFloat(getComputedStyle(body).paddingRight) || 0;
|
|
@@ -1719,56 +1988,76 @@ const lockBodyScroll = () => {
|
|
|
1719
1988
|
}
|
|
1720
1989
|
body.style.overflow = "hidden";
|
|
1721
1990
|
body.style.position = "fixed";
|
|
1722
|
-
body.style.top = `-${
|
|
1991
|
+
body.style.top = `-${saved.scrollY}px`;
|
|
1723
1992
|
body.style.width = "100%";
|
|
1724
1993
|
}
|
|
1725
|
-
|
|
1994
|
+
state$1.count++;
|
|
1726
1995
|
let released = false;
|
|
1727
1996
|
return () => {
|
|
1728
1997
|
if (released) return;
|
|
1729
1998
|
released = true;
|
|
1730
|
-
|
|
1731
|
-
if (
|
|
1999
|
+
state$1.count = Math.max(0, state$1.count - 1);
|
|
2000
|
+
if (state$1.count === 0 && state$1.saved) {
|
|
1732
2001
|
const body = document.body;
|
|
1733
|
-
|
|
1734
|
-
body.style.
|
|
1735
|
-
body.style.
|
|
1736
|
-
body.style.
|
|
1737
|
-
body.style.
|
|
1738
|
-
|
|
1739
|
-
|
|
2002
|
+
const saved = state$1.saved;
|
|
2003
|
+
body.style.overflow = saved.overflow;
|
|
2004
|
+
body.style.position = saved.position;
|
|
2005
|
+
body.style.top = saved.top;
|
|
2006
|
+
body.style.width = saved.width;
|
|
2007
|
+
body.style.paddingRight = saved.paddingRight;
|
|
2008
|
+
window.scrollTo({
|
|
2009
|
+
top: saved.scrollY,
|
|
2010
|
+
left: saved.scrollX,
|
|
2011
|
+
behavior: "instant"
|
|
2012
|
+
});
|
|
2013
|
+
state$1.saved = null;
|
|
1740
2014
|
}
|
|
1741
2015
|
};
|
|
1742
2016
|
};
|
|
2017
|
+
const OWN_ROOTS = ".bs-root, .ovl-root";
|
|
2018
|
+
const LAYER_CLASSES = [
|
|
2019
|
+
"bs-backdrop",
|
|
2020
|
+
"bs-screen",
|
|
2021
|
+
"bs-root",
|
|
2022
|
+
"ovl-backdrop",
|
|
2023
|
+
"ovl-root"
|
|
2024
|
+
];
|
|
2025
|
+
const isLayer = (el) => LAYER_CLASSES.some((name) => el.classList.contains(name));
|
|
1743
2026
|
function createInertSiblings(rootProvider) {
|
|
1744
2027
|
const tracked = [];
|
|
2028
|
+
const remove = () => {
|
|
2029
|
+
for (const el of tracked) el.removeAttribute("inert");
|
|
2030
|
+
tracked.length = 0;
|
|
2031
|
+
};
|
|
1745
2032
|
return {
|
|
1746
2033
|
apply() {
|
|
1747
2034
|
if (typeof document === "undefined") return;
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
if (child.classList.contains("bs-backdrop") || child.classList.contains("bs-screen") || child.classList.contains("bs-root")) {
|
|
2035
|
+
remove();
|
|
2036
|
+
const surface = rootProvider();
|
|
2037
|
+
let node = surface.closest(OWN_ROOTS) ?? surface;
|
|
2038
|
+
while (node && node !== document.body) {
|
|
2039
|
+
const parent = node.parentElement;
|
|
2040
|
+
if (!parent) {
|
|
2041
|
+
const root = node.getRootNode();
|
|
2042
|
+
node = typeof ShadowRoot !== "undefined" && root instanceof ShadowRoot ? root.host : null;
|
|
1757
2043
|
continue;
|
|
1758
2044
|
}
|
|
1759
|
-
|
|
1760
|
-
|
|
2045
|
+
for (const sibling of Array.from(parent.children)) {
|
|
2046
|
+
if (sibling === node) continue;
|
|
2047
|
+
if (sibling.hasAttribute("inert") || isLayer(sibling)) continue;
|
|
2048
|
+
sibling.setAttribute("inert", "");
|
|
2049
|
+
tracked.push(sibling);
|
|
2050
|
+
}
|
|
2051
|
+
node = parent;
|
|
1761
2052
|
}
|
|
1762
2053
|
},
|
|
1763
|
-
remove
|
|
1764
|
-
for (const el of tracked) el.removeAttribute("inert");
|
|
1765
|
-
tracked.length = 0;
|
|
1766
|
-
}
|
|
2054
|
+
remove
|
|
1767
2055
|
};
|
|
1768
2056
|
}
|
|
1769
2057
|
class LifecycleController {
|
|
1770
2058
|
constructor(deps, opts) {
|
|
1771
2059
|
__publicField(this, "element");
|
|
2060
|
+
__publicField(this, "isTop");
|
|
1772
2061
|
__publicField(this, "focusTrapEnabled");
|
|
1773
2062
|
__publicField(this, "initialFocus");
|
|
1774
2063
|
__publicField(this, "closeOnEscape");
|
|
@@ -1782,6 +2071,7 @@ class LifecycleController {
|
|
|
1782
2071
|
__publicField(this, "installed", false);
|
|
1783
2072
|
__publicField(this, "destroyed", false);
|
|
1784
2073
|
this.element = deps.element;
|
|
2074
|
+
this.isTop = deps.isTop;
|
|
1785
2075
|
this.focusTrapEnabled = opts.focusTrap ?? false;
|
|
1786
2076
|
this.initialFocus = opts.initialFocus;
|
|
1787
2077
|
this.closeOnEscape = opts.closeOnEscape ?? true;
|
|
@@ -1802,12 +2092,17 @@ class LifecycleController {
|
|
|
1802
2092
|
}
|
|
1803
2093
|
if (this.focusTrapEnabled && !this.releaseFocusTrap) {
|
|
1804
2094
|
this.releaseFocusTrap = installFocusTrap(this.element, {
|
|
1805
|
-
initialFocus: this.initialFocus
|
|
2095
|
+
initialFocus: this.initialFocus,
|
|
2096
|
+
isActive: this.isTop
|
|
1806
2097
|
});
|
|
1807
2098
|
}
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
2099
|
+
this.syncTop();
|
|
2100
|
+
}
|
|
2101
|
+
syncTop() {
|
|
2102
|
+
if (!this.installed || !this.inertSiblingsEnabled) return;
|
|
2103
|
+
if (!this.shouldApplyInertSiblings()) return;
|
|
2104
|
+
if (this.isTop?.() ?? true) this.inertSiblings.apply();
|
|
2105
|
+
else this.inertSiblings.remove();
|
|
1811
2106
|
}
|
|
1812
2107
|
setReturnFocus(target) {
|
|
1813
2108
|
if (this.destroyed) return;
|
|
@@ -1978,10 +2273,14 @@ const installGestures = (handle, mode, callbacks, options = {}) => {
|
|
|
1978
2273
|
if (wasPending) return;
|
|
1979
2274
|
callbacks.onCancel?.();
|
|
1980
2275
|
};
|
|
2276
|
+
const onLostCapture = (e) => {
|
|
2277
|
+
if (e.target === handle) onPointerCancel(e);
|
|
2278
|
+
};
|
|
1981
2279
|
handle.addEventListener("pointerdown", onPointerDown);
|
|
1982
2280
|
handle.addEventListener("pointermove", onPointerMove);
|
|
1983
2281
|
handle.addEventListener("pointerup", finishGesture);
|
|
1984
2282
|
handle.addEventListener("pointercancel", onPointerCancel);
|
|
2283
|
+
handle.addEventListener("lostpointercapture", onLostCapture);
|
|
1985
2284
|
const managesTouchAction = options.manageTouchAction !== false;
|
|
1986
2285
|
const prevTouchAction = handle.style.touchAction;
|
|
1987
2286
|
if (managesTouchAction) {
|
|
@@ -1992,6 +2291,7 @@ const installGestures = (handle, mode, callbacks, options = {}) => {
|
|
|
1992
2291
|
handle.removeEventListener("pointermove", onPointerMove);
|
|
1993
2292
|
handle.removeEventListener("pointerup", finishGesture);
|
|
1994
2293
|
handle.removeEventListener("pointercancel", onPointerCancel);
|
|
2294
|
+
handle.removeEventListener("lostpointercapture", onLostCapture);
|
|
1995
2295
|
if (managesTouchAction) handle.style.touchAction = prevTouchAction;
|
|
1996
2296
|
if (activePointerId !== null && handle.hasPointerCapture(activePointerId)) {
|
|
1997
2297
|
handle.releasePointerCapture(activePointerId);
|
|
@@ -2107,8 +2407,7 @@ class GestureController {
|
|
|
2107
2407
|
size: this.deps.getDragContext().size,
|
|
2108
2408
|
velocity: 0
|
|
2109
2409
|
});
|
|
2110
|
-
|
|
2111
|
-
void this.deps.animateTo(restoreTo, 0);
|
|
2410
|
+
this.deps.restoreAfterDrag();
|
|
2112
2411
|
}
|
|
2113
2412
|
}, this.deps.gestureOptions);
|
|
2114
2413
|
return this.detach;
|
|
@@ -2167,6 +2466,11 @@ const readDirection = (el) => {
|
|
|
2167
2466
|
}
|
|
2168
2467
|
return "ltr";
|
|
2169
2468
|
};
|
|
2469
|
+
const guessPhysicalMode = (mode) => {
|
|
2470
|
+
if (mode === "start") return "left";
|
|
2471
|
+
if (mode === "end") return "right";
|
|
2472
|
+
return mode ?? "bottom";
|
|
2473
|
+
};
|
|
2170
2474
|
const resolveMode = (mode, el) => {
|
|
2171
2475
|
if (!isLogicalMode(mode)) return mode;
|
|
2172
2476
|
const rtl = readDirection(el) === "rtl";
|
|
@@ -2213,22 +2517,24 @@ function createScrollCache(deps) {
|
|
|
2213
2517
|
return size < max / 2;
|
|
2214
2518
|
};
|
|
2215
2519
|
return {
|
|
2216
|
-
|
|
2520
|
+
leave(fromId, prevSize, nextSize) {
|
|
2217
2521
|
const container = deps.scrollContainer;
|
|
2218
2522
|
if (!container) return;
|
|
2219
|
-
if (
|
|
2220
|
-
|
|
2523
|
+
if (nextSize <= 0) {
|
|
2524
|
+
positions.clear();
|
|
2525
|
+
return;
|
|
2526
|
+
}
|
|
2527
|
+
if (isSmallSize(prevSize) || !isSmallSize(nextSize)) return;
|
|
2221
2528
|
positions.set(fromId, container.scrollTop);
|
|
2222
2529
|
},
|
|
2223
|
-
|
|
2530
|
+
enter(toId, prevSize, nextSize) {
|
|
2224
2531
|
const container = deps.scrollContainer;
|
|
2225
2532
|
if (!container) return;
|
|
2226
|
-
if (isSmallSize(nextSize)) return;
|
|
2227
|
-
if (!isSmallSize(prevSize)) return;
|
|
2533
|
+
if (isSmallSize(nextSize) || !isSmallSize(prevSize)) return;
|
|
2228
2534
|
const cached = positions.get(toId);
|
|
2229
2535
|
if (cached === void 0) return;
|
|
2230
|
-
container.scrollTop = cached;
|
|
2231
2536
|
positions.delete(toId);
|
|
2537
|
+
container.scrollTop = cached;
|
|
2232
2538
|
},
|
|
2233
2539
|
clear() {
|
|
2234
2540
|
positions.clear();
|
|
@@ -2398,17 +2704,18 @@ function attachAnchor(deps, opts) {
|
|
|
2398
2704
|
const isDock = position === "dock-bottom" || position === "dock-top";
|
|
2399
2705
|
const ws = wrapper.style;
|
|
2400
2706
|
ws.position = "fixed";
|
|
2401
|
-
|
|
2707
|
+
const place = () => {
|
|
2708
|
+
resetOverlayPosition(ws);
|
|
2709
|
+
if (!isDock) {
|
|
2710
|
+
applyOverlayPosition(ws, deps.getMode(), position, opts.inset ?? "16px");
|
|
2711
|
+
return;
|
|
2712
|
+
}
|
|
2402
2713
|
ws.left = "0";
|
|
2403
2714
|
ws.right = "0";
|
|
2404
|
-
if (position === "dock-bottom")
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
}
|
|
2409
|
-
} else {
|
|
2410
|
-
applyOverlayPosition(ws, deps.mode, position, opts.inset ?? "16px");
|
|
2411
|
-
}
|
|
2715
|
+
if (position === "dock-bottom") ws.bottom = "0";
|
|
2716
|
+
else ws.top = "0";
|
|
2717
|
+
};
|
|
2718
|
+
place();
|
|
2412
2719
|
if (opts.fadeRange) {
|
|
2413
2720
|
const [r0, r1] = opts.fadeRange;
|
|
2414
2721
|
const span = Math.max(r1 - r0, 1e-4);
|
|
@@ -2418,10 +2725,10 @@ function attachAnchor(deps, opts) {
|
|
|
2418
2725
|
deps.host.appendChild(wrapper);
|
|
2419
2726
|
let visible = null;
|
|
2420
2727
|
let inFlight = null;
|
|
2421
|
-
const shouldShow = (
|
|
2422
|
-
if (typeof opts.showOn === "function") return opts.showOn(
|
|
2423
|
-
if (Array.isArray(opts.showOn)) return opts.showOn.includes(
|
|
2424
|
-
return isDock ||
|
|
2728
|
+
const shouldShow = (state2) => {
|
|
2729
|
+
if (typeof opts.showOn === "function") return opts.showOn(state2);
|
|
2730
|
+
if (Array.isArray(opts.showOn)) return opts.showOn.includes(state2.activeId);
|
|
2731
|
+
return isDock || state2.size > 0;
|
|
2425
2732
|
};
|
|
2426
2733
|
const applyVisibility = (next, animate) => {
|
|
2427
2734
|
if (next === visible) return;
|
|
@@ -2468,7 +2775,8 @@ function attachAnchor(deps, opts) {
|
|
|
2468
2775
|
},
|
|
2469
2776
|
syncZ: (z) => {
|
|
2470
2777
|
wrapper.style.zIndex = String(z);
|
|
2471
|
-
}
|
|
2778
|
+
},
|
|
2779
|
+
reposition: place
|
|
2472
2780
|
};
|
|
2473
2781
|
}
|
|
2474
2782
|
const matchesId = (def, activeId) => {
|
|
@@ -2482,12 +2790,11 @@ const matchesRange = (def, progress) => {
|
|
|
2482
2790
|
};
|
|
2483
2791
|
function installScrimStages(deps, opts) {
|
|
2484
2792
|
const doc = deps.host.ownerDocument;
|
|
2485
|
-
const
|
|
2793
|
+
const entries = opts.stages.map((def) => {
|
|
2486
2794
|
const wrapper = doc.createElement("div");
|
|
2487
2795
|
wrapper.className = "bs-scrim-stage";
|
|
2488
2796
|
const ws = wrapper.style;
|
|
2489
|
-
ws.position = "
|
|
2490
|
-
applyOverlayPosition(ws, deps.mode, def.position ?? opts.position ?? "center", def.inset ?? opts.inset ?? "16px");
|
|
2797
|
+
ws.position = "fixed";
|
|
2491
2798
|
ws.visibility = "hidden";
|
|
2492
2799
|
ws.pointerEvents = "none";
|
|
2493
2800
|
wrapper.appendChild(def.element);
|
|
@@ -2498,13 +2805,21 @@ function installScrimStages(deps, opts) {
|
|
|
2498
2805
|
inFlight: null
|
|
2499
2806
|
};
|
|
2500
2807
|
});
|
|
2808
|
+
const place = () => {
|
|
2809
|
+
for (const entry of entries) {
|
|
2810
|
+
const ws = entry.wrapper.style;
|
|
2811
|
+
resetOverlayPosition(ws);
|
|
2812
|
+
applyOverlayPosition(ws, deps.getMode(), entry.def.position ?? opts.position ?? "center", entry.def.inset ?? opts.inset ?? "16px");
|
|
2813
|
+
}
|
|
2814
|
+
};
|
|
2815
|
+
place();
|
|
2501
2816
|
let active = null;
|
|
2502
2817
|
const resolveActive = () => {
|
|
2503
|
-
const
|
|
2504
|
-
if (
|
|
2505
|
-
return
|
|
2818
|
+
const state2 = deps.getState();
|
|
2819
|
+
if (state2.size === 0) {
|
|
2820
|
+
return entries.find((e) => matchesId(e.def, state2.activeId)) ?? null;
|
|
2506
2821
|
}
|
|
2507
|
-
return
|
|
2822
|
+
return entries.find((e) => matchesId(e.def, state2.activeId)) ?? entries.find((e) => matchesRange(e.def, state2.progress)) ?? null;
|
|
2508
2823
|
};
|
|
2509
2824
|
const hide = (entry, animate) => {
|
|
2510
2825
|
entry.inFlight?.cancel();
|
|
@@ -2545,20 +2860,23 @@ function installScrimStages(deps, opts) {
|
|
|
2545
2860
|
deps.on("open", () => update(true)),
|
|
2546
2861
|
deps.on("close", () => update(true))
|
|
2547
2862
|
];
|
|
2548
|
-
if (
|
|
2863
|
+
if (entries.some((e) => e.def.forRange)) {
|
|
2549
2864
|
offs.push(deps.on("progress", () => update(true)));
|
|
2550
2865
|
}
|
|
2551
2866
|
update(false);
|
|
2552
|
-
return
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
entry
|
|
2557
|
-
|
|
2558
|
-
entry.
|
|
2867
|
+
return {
|
|
2868
|
+
reposition: place,
|
|
2869
|
+
detach: () => {
|
|
2870
|
+
offs.forEach((off) => off());
|
|
2871
|
+
for (const entry of entries) {
|
|
2872
|
+
entry.inFlight?.cancel();
|
|
2873
|
+
entry.inFlight = null;
|
|
2874
|
+
if (entry.wrapper.parentNode) {
|
|
2875
|
+
entry.wrapper.parentNode.removeChild(entry.wrapper);
|
|
2876
|
+
}
|
|
2559
2877
|
}
|
|
2878
|
+
active = null;
|
|
2560
2879
|
}
|
|
2561
|
-
active = null;
|
|
2562
2880
|
};
|
|
2563
2881
|
}
|
|
2564
2882
|
const DEFAULT_FLICK_VELOCITY = 0.65;
|
|
@@ -2592,11 +2910,13 @@ function resolveEngineOptions(opts, extras) {
|
|
|
2592
2910
|
scrimMode: opts.scrimMode,
|
|
2593
2911
|
scrimColor: opts.scrimColor,
|
|
2594
2912
|
scrimBlur: opts.scrimBlur,
|
|
2595
|
-
scrimInteractive: opts.scrimInteractive
|
|
2913
|
+
scrimInteractive: opts.scrimInteractive,
|
|
2596
2914
|
scrimTapToClose: opts.scrimTapToClose,
|
|
2597
2915
|
scrimPreset: opts.scrimPreset,
|
|
2598
2916
|
screenRange: opts.screenRange,
|
|
2599
|
-
backdropRange: opts.backdropRange
|
|
2917
|
+
backdropRange: opts.backdropRange,
|
|
2918
|
+
backdropOpacity: opts.backdropOpacity,
|
|
2919
|
+
backdropColor: opts.backdropColor
|
|
2600
2920
|
},
|
|
2601
2921
|
animation: {
|
|
2602
2922
|
animation: opts.animation,
|
|
@@ -2671,6 +2991,7 @@ class BottomSheetCore {
|
|
|
2671
2991
|
__publicField(this, "fitContentToSnap");
|
|
2672
2992
|
__publicField(this, "settleAnimation");
|
|
2673
2993
|
__publicField(this, "persistent");
|
|
2994
|
+
__publicField(this, "keyboardInset", 0);
|
|
2674
2995
|
__publicField(this, "disableCloseFlag");
|
|
2675
2996
|
__publicField(this, "disableDragFlag");
|
|
2676
2997
|
__publicField(this, "closeOnRouteChange");
|
|
@@ -2685,6 +3006,7 @@ class BottomSheetCore {
|
|
|
2685
3006
|
__publicField(this, "detachSheetGesture", null);
|
|
2686
3007
|
__publicField(this, "detachSliderKeyboard", null);
|
|
2687
3008
|
__publicField(this, "rootEl", null);
|
|
3009
|
+
__publicField(this, "homeRoot", null);
|
|
2688
3010
|
__publicField(this, "destroyed", false);
|
|
2689
3011
|
__publicField(this, "restClosed", false);
|
|
2690
3012
|
__publicField(this, "currentViewTransition", null);
|
|
@@ -2692,8 +3014,13 @@ class BottomSheetCore {
|
|
|
2692
3014
|
__publicField(this, "sizeWriteSentinel", new WriteSentinel());
|
|
2693
3015
|
__publicField(this, "progressWriteSentinel", new WriteSentinel());
|
|
2694
3016
|
__publicField(this, "isTopSheet", true);
|
|
2695
|
-
__publicField(this, "
|
|
2696
|
-
__publicField(this, "
|
|
3017
|
+
__publicField(this, "phase", "closed");
|
|
3018
|
+
__publicField(this, "transition", null);
|
|
3019
|
+
__publicField(this, "inFlight", null);
|
|
3020
|
+
__publicField(this, "outbox", []);
|
|
3021
|
+
__publicField(this, "flushing", false);
|
|
3022
|
+
__publicField(this, "openPending", false);
|
|
3023
|
+
__publicField(this, "settleHolds", []);
|
|
2697
3024
|
__publicField(this, "allowOvershoot", false);
|
|
2698
3025
|
__publicField(this, "progressPayload", {
|
|
2699
3026
|
value: 0,
|
|
@@ -2717,6 +3044,7 @@ class BottomSheetCore {
|
|
|
2717
3044
|
__publicField(this, "anchorHost", null);
|
|
2718
3045
|
__publicField(this, "stackZ", 100);
|
|
2719
3046
|
__publicField(this, "detachScrimStages", null);
|
|
3047
|
+
__publicField(this, "placeScrimStages", null);
|
|
2720
3048
|
__publicField(this, "stackEffectEnabled", false);
|
|
2721
3049
|
__publicField(this, "stackEffectPrimed", false);
|
|
2722
3050
|
__publicField(this, "fitDeps");
|
|
@@ -2724,11 +3052,7 @@ class BottomSheetCore {
|
|
|
2724
3052
|
__publicField(this, "featureList", []);
|
|
2725
3053
|
__publicField(this, "featureCtx");
|
|
2726
3054
|
const resolved = resolveEngineOptions(opts, {
|
|
2727
|
-
shouldApplyInertSiblings: () =>
|
|
2728
|
-
if (typeof document === "undefined") return false;
|
|
2729
|
-
const bodyChildren = Array.from(document.body.children);
|
|
2730
|
-
return bodyChildren.some((c) => c === opts.element || c.contains(opts.element));
|
|
2731
|
-
}
|
|
3055
|
+
shouldApplyInertSiblings: () => opts.element.isConnected
|
|
2732
3056
|
});
|
|
2733
3057
|
this.element = opts.element;
|
|
2734
3058
|
this.handle = opts.handle ?? opts.element;
|
|
@@ -2756,7 +3080,6 @@ class BottomSheetCore {
|
|
|
2756
3080
|
void this.snapTo(id);
|
|
2757
3081
|
},
|
|
2758
3082
|
close: () => {
|
|
2759
|
-
if (!this.canDismiss()) return;
|
|
2760
3083
|
void this.close("backdrop");
|
|
2761
3084
|
}
|
|
2762
3085
|
}, resolved.scrim);
|
|
@@ -2780,7 +3103,8 @@ class BottomSheetCore {
|
|
|
2780
3103
|
}, resolved.animation);
|
|
2781
3104
|
this.lifecycle = new LifecycleController({
|
|
2782
3105
|
element: this.element,
|
|
2783
|
-
close: () => this.close()
|
|
3106
|
+
close: () => this.close(),
|
|
3107
|
+
isTop: () => this.isTopSheet
|
|
2784
3108
|
}, resolved.lifecycle);
|
|
2785
3109
|
this.closeOnBack = resolved.closeOnBack;
|
|
2786
3110
|
this.routedTo = opts.routedTo;
|
|
@@ -2805,6 +3129,7 @@ class BottomSheetCore {
|
|
|
2805
3129
|
auditVhUsage(this.snapPointsRaw);
|
|
2806
3130
|
this.auditDuplicateSnapIds(this.snapPointsRaw);
|
|
2807
3131
|
this.rootEl = this.element.closest(".bs-root");
|
|
3132
|
+
this.homeRoot = homeRootOf(this.rootEl);
|
|
2808
3133
|
this.maxHeightDeps = {
|
|
2809
3134
|
element: this.element,
|
|
2810
3135
|
mode: this.mode,
|
|
@@ -2826,7 +3151,9 @@ class BottomSheetCore {
|
|
|
2826
3151
|
});
|
|
2827
3152
|
const initial = this.snaps.findById(this.activeId);
|
|
2828
3153
|
if (initial) this.size = initial.size;
|
|
3154
|
+
this.phase = this.size > 0 ? "open" : "closed";
|
|
2829
3155
|
this.applySize(this.size);
|
|
3156
|
+
this.restClosed = this.element.getAttribute("data-bs-rest") === "closed";
|
|
2830
3157
|
this.setRestClosed(this.size === 0);
|
|
2831
3158
|
this.updateAriaSlider();
|
|
2832
3159
|
this.scrollCache = createScrollCache({
|
|
@@ -2837,12 +3164,11 @@ class BottomSheetCore {
|
|
|
2837
3164
|
this.attach();
|
|
2838
3165
|
this.registerInStack();
|
|
2839
3166
|
if (this.snaps.getMaxAxisSize() === 0 && typeof IntersectionObserver !== "undefined" && typeof document !== "undefined") {
|
|
2840
|
-
const io = new IntersectionObserver((
|
|
2841
|
-
for (const entry of
|
|
3167
|
+
const io = new IntersectionObserver((entries) => {
|
|
3168
|
+
for (const entry of entries) {
|
|
2842
3169
|
if (!entry.isIntersecting) continue;
|
|
2843
3170
|
if (this.destroyed) return;
|
|
2844
|
-
this.
|
|
2845
|
-
this.healActiveSnap();
|
|
3171
|
+
this.recompute();
|
|
2846
3172
|
io.disconnect();
|
|
2847
3173
|
return;
|
|
2848
3174
|
}
|
|
@@ -2851,6 +3177,7 @@ class BottomSheetCore {
|
|
|
2851
3177
|
this.teardowns.add(() => io.disconnect());
|
|
2852
3178
|
}
|
|
2853
3179
|
this.teardowns.add(installFitObserver({
|
|
3180
|
+
element: this.element,
|
|
2854
3181
|
handle: this.handle,
|
|
2855
3182
|
scrollContainer: this.scrollContainer,
|
|
2856
3183
|
hasFitSnap: () => this.snapPointsRaw.some((p) => p.size === "fit" || p.size === "content"),
|
|
@@ -2863,9 +3190,7 @@ class BottomSheetCore {
|
|
|
2863
3190
|
this.applyAutoAriaLabelledBy();
|
|
2864
3191
|
this.runFeatureStage("post");
|
|
2865
3192
|
this.warnOrphanFeatureOptions();
|
|
2866
|
-
if (this.
|
|
2867
|
-
this.handleOpen();
|
|
2868
|
-
}
|
|
3193
|
+
if (this.phase === "open") this.installLifecycle(true);
|
|
2869
3194
|
}
|
|
2870
3195
|
buildFeatureContext(autoCollapseAfter) {
|
|
2871
3196
|
return {
|
|
@@ -2893,21 +3218,27 @@ class BottomSheetCore {
|
|
|
2893
3218
|
getMaxAxisSize: () => this.snaps.getMaxAxisSize(),
|
|
2894
3219
|
resolveSnap: (id) => this.snaps.findById(id),
|
|
2895
3220
|
resolveActiveSnap: () => this.snaps.findById(this.activeId),
|
|
3221
|
+
getPhase: () => this.phase,
|
|
2896
3222
|
setSize: (size) => {
|
|
2897
3223
|
this.size = size;
|
|
2898
3224
|
},
|
|
2899
3225
|
setMaxAxisSize: (size) => {
|
|
2900
3226
|
this.snaps.setMaxAxisSize(size);
|
|
2901
3227
|
},
|
|
3228
|
+
setKeyboardInset: (px) => this.setKeyboardInset(px),
|
|
2902
3229
|
applySize: (size) => this.applySize(size),
|
|
2903
3230
|
recomputeSnaps: () => this.recomputeSnaps(),
|
|
3231
|
+
reseat: () => this.reseat(),
|
|
3232
|
+
holdSettle: (settled) => {
|
|
3233
|
+
this.settleHolds.push(settled);
|
|
3234
|
+
},
|
|
2904
3235
|
newCycle: () => {
|
|
2905
3236
|
this.newCycle();
|
|
2906
3237
|
},
|
|
2907
|
-
cancelInFlight: () =>
|
|
2908
|
-
|
|
3238
|
+
cancelInFlight: () => this.suspend(),
|
|
3239
|
+
resyncAfterResize: () => {
|
|
3240
|
+
if (!this.destroyed) this.land(false);
|
|
2909
3241
|
},
|
|
2910
|
-
resyncAfterResize: () => this.resyncAfterResize(),
|
|
2911
3242
|
snapTo: (id) => {
|
|
2912
3243
|
void this.snapTo(id);
|
|
2913
3244
|
},
|
|
@@ -2940,8 +3271,9 @@ class BottomSheetCore {
|
|
|
2940
3271
|
return {
|
|
2941
3272
|
size: this.size,
|
|
2942
3273
|
activeId: this.activeId,
|
|
3274
|
+
phase: this.phase,
|
|
2943
3275
|
isDragging: this.isDraggingAny(),
|
|
2944
|
-
isAnimating: this.animation.isAnimating,
|
|
3276
|
+
isAnimating: this.animation.isAnimating || this.currentViewTransition !== null,
|
|
2945
3277
|
progress: this.computeProgress(this.size)
|
|
2946
3278
|
};
|
|
2947
3279
|
}
|
|
@@ -2993,53 +3325,49 @@ class BottomSheetCore {
|
|
|
2993
3325
|
getResolvedSnaps() {
|
|
2994
3326
|
return this.snaps.getResolvedSnaps();
|
|
2995
3327
|
}
|
|
2996
|
-
|
|
2997
|
-
if (this.destroyed) return;
|
|
3328
|
+
snapTo(id, velocityOrOpts = 0, _skipBeforeSnap = false) {
|
|
2998
3329
|
const opts = typeof velocityOrOpts === "object" ? velocityOrOpts : {
|
|
2999
3330
|
velocity: velocityOrOpts
|
|
3000
3331
|
};
|
|
3001
|
-
|
|
3002
|
-
|
|
3003
|
-
|
|
3332
|
+
return this.transitionTo(id, opts.velocity ?? 0, _skipBeforeSnap, opts.signal);
|
|
3333
|
+
}
|
|
3334
|
+
transitionTo(id, velocityPxPerMs = 0, skipBeforeSnap = false, externalSignal) {
|
|
3335
|
+
if (this.destroyed || externalSignal?.aborted) return Promise.resolve();
|
|
3004
3336
|
const target = this.snaps.findById(id);
|
|
3005
3337
|
if (!target) {
|
|
3006
3338
|
devWarn(`[BottomSheet] unknown snap id: ${id}`);
|
|
3007
|
-
return;
|
|
3339
|
+
return Promise.resolve();
|
|
3008
3340
|
}
|
|
3009
3341
|
if (!this.snaps.getAllowedIds().includes(id)) {
|
|
3010
3342
|
devWarn(`[BottomSheet] snap "${id}" is not in allowed list`);
|
|
3011
|
-
return;
|
|
3343
|
+
return Promise.resolve();
|
|
3012
3344
|
}
|
|
3013
|
-
if (
|
|
3014
|
-
|
|
3345
|
+
if (target.size === 0 && this.closingBlocked()) return Promise.resolve();
|
|
3346
|
+
if (!externalSignal && this.inFlight?.id === id) return this.inFlight.done;
|
|
3347
|
+
if (!skipBeforeSnap && this.emitBeforeSnap(target, this.activeId)) {
|
|
3348
|
+
return Promise.resolve();
|
|
3015
3349
|
}
|
|
3016
|
-
const
|
|
3017
|
-
|
|
3018
|
-
|
|
3019
|
-
this.animation.cancel();
|
|
3020
|
-
this.newCycle();
|
|
3350
|
+
const flight = {
|
|
3351
|
+
id,
|
|
3352
|
+
done: Promise.resolve()
|
|
3021
3353
|
};
|
|
3354
|
+
flight.done = this.runTransition(target, velocityPxPerMs, false, externalSignal, flight);
|
|
3355
|
+
return flight.done;
|
|
3356
|
+
}
|
|
3357
|
+
async runTransition(target, velocityPxPerMs, fromDrag, externalSignal, flight = null) {
|
|
3358
|
+
const signal = this.beginTransition(target);
|
|
3359
|
+
const transition = this.transition;
|
|
3360
|
+
if (signal.aborted || !transition) return;
|
|
3361
|
+
this.inFlight = flight;
|
|
3362
|
+
const onExternalAbort = () => this.suspend();
|
|
3022
3363
|
externalSignal?.addEventListener("abort", onExternalAbort, {
|
|
3023
3364
|
once: true
|
|
3024
3365
|
});
|
|
3025
|
-
|
|
3026
|
-
|
|
3027
|
-
const previousSize = this.size;
|
|
3028
|
-
this.scrollCache.cache(previousId, previousSize, target.size);
|
|
3029
|
-
this.activeId = id;
|
|
3030
|
-
const rawTargetSize = this.snapPointsRaw.find((p) => p.id === id)?.size;
|
|
3031
|
-
const opensToRest = id === "closed" || rawTargetSize === 0;
|
|
3032
|
-
if (wasClosed && !opensToRest) {
|
|
3033
|
-
this.opening = true;
|
|
3034
|
-
if (!resumingOpen) {
|
|
3035
|
-
this.promotedForOpen = true;
|
|
3036
|
-
sheetStack.promote(this.id);
|
|
3037
|
-
}
|
|
3038
|
-
}
|
|
3039
|
-
if (this.animation.viewTransitionsAvailable) {
|
|
3366
|
+
this.allowOvershoot = fromDrag;
|
|
3367
|
+
if (!fromDrag && this.animation.viewTransitionsAvailable) {
|
|
3040
3368
|
this.currentViewTransition?.skipTransition?.();
|
|
3041
3369
|
const vt = document.startViewTransition(() => {
|
|
3042
|
-
this.applySize(
|
|
3370
|
+
if (!signal.aborted) this.applySize(transition.size);
|
|
3043
3371
|
});
|
|
3044
3372
|
vt.ready?.catch(() => {
|
|
3045
3373
|
});
|
|
@@ -3050,25 +3378,208 @@ class BottomSheetCore {
|
|
|
3050
3378
|
}
|
|
3051
3379
|
if (this.currentViewTransition === vt) this.currentViewTransition = null;
|
|
3052
3380
|
} else {
|
|
3053
|
-
await this.animation.animateTo(
|
|
3381
|
+
await this.animation.animateTo(transition.size, velocityPxPerMs);
|
|
3054
3382
|
}
|
|
3055
3383
|
externalSignal?.removeEventListener("abort", onExternalAbort);
|
|
3056
3384
|
if (signal.aborted) return;
|
|
3057
|
-
this.
|
|
3058
|
-
this.
|
|
3385
|
+
if (this.size !== transition.size) this.applySize(transition.size);
|
|
3386
|
+
this.land(fromDrag);
|
|
3387
|
+
if (this.settleHolds.length > 0) {
|
|
3388
|
+
await Promise.allSettled(this.settleHolds.splice(0));
|
|
3389
|
+
}
|
|
3390
|
+
}
|
|
3391
|
+
beginTransition(target) {
|
|
3392
|
+
const signal = this.newCycle();
|
|
3393
|
+
const cap = this.snaps.getMaxAxisSize();
|
|
3394
|
+
const fromId = this.activeId;
|
|
3395
|
+
const fromSize = this.snaps.findById(fromId)?.size ?? this.size;
|
|
3396
|
+
this.scrollCache.leave(fromId, fromSize, target.size);
|
|
3397
|
+
this.scrollCache.enter(target.id, fromSize, target.size);
|
|
3398
|
+
this.activeId = target.id;
|
|
3399
|
+
this.transition = {
|
|
3400
|
+
size: cap > 0 ? Math.min(target.size, cap) : target.size,
|
|
3401
|
+
cap
|
|
3402
|
+
};
|
|
3403
|
+
if (target.size > 0) {
|
|
3404
|
+
this.enterOpening(target.id);
|
|
3405
|
+
} else {
|
|
3406
|
+
if (this.phase === "closed" && !this.isClosedSnap(target.id)) {
|
|
3407
|
+
sheetStack.promote(this.id);
|
|
3408
|
+
}
|
|
3409
|
+
this.enterClosing();
|
|
3410
|
+
}
|
|
3411
|
+
this.flush();
|
|
3412
|
+
return signal;
|
|
3413
|
+
}
|
|
3414
|
+
enterOpening(id) {
|
|
3415
|
+
if (this.phase === "open" || this.phase === "opening") return;
|
|
3416
|
+
const fresh = this.phase === "closed";
|
|
3417
|
+
this.phase = "opening";
|
|
3418
|
+
this.setRestClosed(false);
|
|
3419
|
+
if (!this.installLifecycle(fresh)) return;
|
|
3420
|
+
this.post("open", {
|
|
3421
|
+
id
|
|
3422
|
+
});
|
|
3423
|
+
this.outbox.push(() => {
|
|
3424
|
+
if (this.phase !== "closed") {
|
|
3425
|
+
notifyLinkedSheets(this.linkedSheets, this);
|
|
3426
|
+
}
|
|
3427
|
+
});
|
|
3059
3428
|
}
|
|
3060
|
-
|
|
3061
|
-
this.
|
|
3429
|
+
enterClosing() {
|
|
3430
|
+
if (this.phase === "closed" || this.phase === "closing") return;
|
|
3431
|
+
this.phase = "closing";
|
|
3432
|
+
this.post("close", void 0);
|
|
3433
|
+
}
|
|
3434
|
+
settlePhase(heal) {
|
|
3435
|
+
if (this.size > 0) {
|
|
3436
|
+
this.enterOpening(this.activeId);
|
|
3437
|
+
if (this.phase === "opening") {
|
|
3438
|
+
this.phase = "open";
|
|
3439
|
+
this.post("opened", {
|
|
3440
|
+
id: this.activeId
|
|
3441
|
+
});
|
|
3442
|
+
}
|
|
3443
|
+
return;
|
|
3444
|
+
}
|
|
3445
|
+
if (heal && this.phase !== "closing") return;
|
|
3446
|
+
this.enterClosing();
|
|
3447
|
+
if (this.phase === "closing") {
|
|
3448
|
+
this.phase = "closed";
|
|
3449
|
+
this.setRestClosed(true);
|
|
3450
|
+
this.lifecycle.release();
|
|
3451
|
+
sheetStack.update();
|
|
3452
|
+
this.post("closed", void 0);
|
|
3453
|
+
}
|
|
3454
|
+
}
|
|
3455
|
+
land(haptic) {
|
|
3456
|
+
this.transition = null;
|
|
3457
|
+
this.inFlight = null;
|
|
3458
|
+
this.allowOvershoot = false;
|
|
3459
|
+
this.openPending = this.size === 0 && !this.isClosedSnap(this.activeId);
|
|
3062
3460
|
this.updateAriaSlider();
|
|
3063
|
-
this.
|
|
3064
|
-
|
|
3461
|
+
this.setRestClosed(this.size === 0);
|
|
3462
|
+
this.post("snap", {
|
|
3463
|
+
id: this.activeId,
|
|
3065
3464
|
size: this.size,
|
|
3066
3465
|
progress: this.computeProgress(this.size)
|
|
3067
3466
|
});
|
|
3068
|
-
|
|
3069
|
-
this.
|
|
3070
|
-
|
|
3071
|
-
|
|
3467
|
+
this.settlePhase(false);
|
|
3468
|
+
if (haptic) this.haptic();
|
|
3469
|
+
this.flush();
|
|
3470
|
+
}
|
|
3471
|
+
suspend() {
|
|
3472
|
+
this.animation.cancel();
|
|
3473
|
+
this.currentViewTransition?.skipTransition?.();
|
|
3474
|
+
this.newCycle();
|
|
3475
|
+
}
|
|
3476
|
+
landNow() {
|
|
3477
|
+
const transition = this.transition;
|
|
3478
|
+
if (this.destroyed || !transition || this.isDraggingAny()) return;
|
|
3479
|
+
this.animation.cancel();
|
|
3480
|
+
this.currentViewTransition?.skipTransition?.();
|
|
3481
|
+
this.newCycle();
|
|
3482
|
+
this.applySize(transition.size);
|
|
3483
|
+
this.land(false);
|
|
3484
|
+
}
|
|
3485
|
+
reseat() {
|
|
3486
|
+
if (this.destroyed || this.isDraggingAny()) return;
|
|
3487
|
+
const active = this.snaps.findById(this.activeId);
|
|
3488
|
+
if (!active) return;
|
|
3489
|
+
const cap = this.snaps.getMaxAxisSize();
|
|
3490
|
+
const target = cap > 0 ? Math.min(active.size, cap) : active.size;
|
|
3491
|
+
this.updateAriaSlider();
|
|
3492
|
+
const transition = this.transition;
|
|
3493
|
+
if (transition) {
|
|
3494
|
+
if (Math.abs(target - transition.size) < SIZE_WRITE_EPSILON && Math.abs(cap - transition.cap) < SIZE_WRITE_EPSILON) {
|
|
3495
|
+
return;
|
|
3496
|
+
}
|
|
3497
|
+
transition.size = target;
|
|
3498
|
+
transition.cap = cap;
|
|
3499
|
+
this.applySize(this.animation.retarget(target) ? this.size : target);
|
|
3500
|
+
return;
|
|
3501
|
+
}
|
|
3502
|
+
if (this.openPending && target > 0 && this.phase === "closed") {
|
|
3503
|
+
void this.runTransition(active, 0, false);
|
|
3504
|
+
return;
|
|
3505
|
+
}
|
|
3506
|
+
this.applySize(target);
|
|
3507
|
+
this.settlePhase(true);
|
|
3508
|
+
this.flush();
|
|
3509
|
+
}
|
|
3510
|
+
installLifecycle(promote) {
|
|
3511
|
+
if (promote) sheetStack.promote(this.id);
|
|
3512
|
+
if (this.lifecycle.isInstalled) return true;
|
|
3513
|
+
try {
|
|
3514
|
+
this.lifecycle.install();
|
|
3515
|
+
return true;
|
|
3516
|
+
} catch (err) {
|
|
3517
|
+
this.abortOpen();
|
|
3518
|
+
queueMicrotask(() => {
|
|
3519
|
+
throw err;
|
|
3520
|
+
});
|
|
3521
|
+
return false;
|
|
3522
|
+
}
|
|
3523
|
+
}
|
|
3524
|
+
abortOpen() {
|
|
3525
|
+
this.animation.cancel();
|
|
3526
|
+
if (!this.destroyed) this.newCycle();
|
|
3527
|
+
this.phase = "closed";
|
|
3528
|
+
this.openPending = false;
|
|
3529
|
+
this.lifecycle.release();
|
|
3530
|
+
sheetStack.update();
|
|
3531
|
+
this.activeId = this.zeroSnapId() ?? this.activeId;
|
|
3532
|
+
this.applySize(0);
|
|
3533
|
+
this.setRestClosed(true);
|
|
3534
|
+
this.updateAriaSlider();
|
|
3535
|
+
}
|
|
3536
|
+
zeroSnapId() {
|
|
3537
|
+
return this.snapPointsRaw.find((p) => p.id === "closed")?.id ?? this.snaps.getAllowedIds().find((a) => this.snaps.findById(a)?.size === 0);
|
|
3538
|
+
}
|
|
3539
|
+
isClosedSnap(id) {
|
|
3540
|
+
if (id === "closed") return true;
|
|
3541
|
+
return this.snapPointsRaw.find((p) => p.id === id)?.size === 0;
|
|
3542
|
+
}
|
|
3543
|
+
closingBlocked() {
|
|
3544
|
+
return this.disableCloseFlag && this.phase !== "closed";
|
|
3545
|
+
}
|
|
3546
|
+
setKeyboardInset(px) {
|
|
3547
|
+
if (px === this.keyboardInset) return;
|
|
3548
|
+
this.keyboardInset = px;
|
|
3549
|
+
for (const host of this.varHosts()) {
|
|
3550
|
+
host.style.setProperty("--bs-keyboard-inset", `${px}px`);
|
|
3551
|
+
}
|
|
3552
|
+
}
|
|
3553
|
+
varHosts() {
|
|
3554
|
+
const hosts = /* @__PURE__ */ new Set([
|
|
3555
|
+
this.element
|
|
3556
|
+
]);
|
|
3557
|
+
for (const host of [
|
|
3558
|
+
this.anchorHost,
|
|
3559
|
+
this.scrimParent,
|
|
3560
|
+
this.rootEl,
|
|
3561
|
+
this.homeRoot
|
|
3562
|
+
]) {
|
|
3563
|
+
if (host) hosts.add(host);
|
|
3564
|
+
}
|
|
3565
|
+
return hosts;
|
|
3566
|
+
}
|
|
3567
|
+
dismissBlocked() {
|
|
3568
|
+
return (this.disableCloseFlag || this.persistent) && this.phase !== "closed";
|
|
3569
|
+
}
|
|
3570
|
+
post(event, payload) {
|
|
3571
|
+
this.outbox.push(() => this.bus.emit(event, payload));
|
|
3572
|
+
}
|
|
3573
|
+
flush() {
|
|
3574
|
+
if (this.flushing) return;
|
|
3575
|
+
this.flushing = true;
|
|
3576
|
+
try {
|
|
3577
|
+
for (let next = this.outbox.shift(); next; next = this.outbox.shift()) {
|
|
3578
|
+
next();
|
|
3579
|
+
}
|
|
3580
|
+
} finally {
|
|
3581
|
+
this.flushing = false;
|
|
3582
|
+
}
|
|
3072
3583
|
}
|
|
3073
3584
|
setRestClosed(closed) {
|
|
3074
3585
|
if (closed === this.restClosed) return;
|
|
@@ -3076,21 +3587,6 @@ class BottomSheetCore {
|
|
|
3076
3587
|
if (closed) this.element.setAttribute("data-bs-rest", "closed");
|
|
3077
3588
|
else this.element.removeAttribute("data-bs-rest");
|
|
3078
3589
|
}
|
|
3079
|
-
emitOpenSequence(id) {
|
|
3080
|
-
this.emit("open", {
|
|
3081
|
-
id
|
|
3082
|
-
});
|
|
3083
|
-
this.handleOpen();
|
|
3084
|
-
notifyLinkedSheets(this.linkedSheets, this);
|
|
3085
|
-
this.emit("opened", {
|
|
3086
|
-
id
|
|
3087
|
-
});
|
|
3088
|
-
}
|
|
3089
|
-
emitCloseSequence() {
|
|
3090
|
-
this.emit("close", void 0);
|
|
3091
|
-
this.handleClose();
|
|
3092
|
-
this.emit("closed", void 0);
|
|
3093
|
-
}
|
|
3094
3590
|
async dragTo(targetSize, velocityOrOpts = 0) {
|
|
3095
3591
|
if (this.destroyed) return;
|
|
3096
3592
|
const opts = typeof velocityOrOpts === "object" ? velocityOrOpts : {
|
|
@@ -3100,29 +3596,33 @@ class BottomSheetCore {
|
|
|
3100
3596
|
const externalSignal = opts.signal;
|
|
3101
3597
|
if (externalSignal?.aborted) return;
|
|
3102
3598
|
this.newCycle();
|
|
3103
|
-
const onExternalAbort = () =>
|
|
3104
|
-
this.animation.cancel();
|
|
3105
|
-
this.newCycle();
|
|
3106
|
-
};
|
|
3599
|
+
const onExternalAbort = () => this.suspend();
|
|
3107
3600
|
externalSignal?.addEventListener("abort", onExternalAbort, {
|
|
3108
3601
|
once: true
|
|
3109
3602
|
});
|
|
3110
3603
|
const clamped = Math.max(0, Math.min(targetSize, this.snaps.getMaxAxisSize()));
|
|
3111
3604
|
await this.animation.animateTo(clamped, velocityPxPerMs);
|
|
3112
3605
|
externalSignal?.removeEventListener("abort", onExternalAbort);
|
|
3113
|
-
if (this.destroyed || externalSignal?.aborted) return;
|
|
3114
3606
|
}
|
|
3115
3607
|
open(id) {
|
|
3116
3608
|
if (this.destroyed) return Promise.resolve();
|
|
3609
|
+
if (!id && (this.phase === "open" || this.phase === "opening")) {
|
|
3610
|
+
return Promise.resolve();
|
|
3611
|
+
}
|
|
3117
3612
|
const target = id ?? this.snaps.getAllowedIds().find((a) => (this.snaps.findById(a)?.size ?? 0) > 0) ?? this.activeId;
|
|
3118
3613
|
return this.snapTo(target);
|
|
3119
3614
|
}
|
|
3120
3615
|
close(reason = "programmatic") {
|
|
3121
|
-
if (this.destroyed) return Promise.resolve();
|
|
3122
|
-
if (this.
|
|
3123
|
-
|
|
3124
|
-
|
|
3125
|
-
|
|
3616
|
+
if (this.destroyed || this.disableCloseFlag) return Promise.resolve();
|
|
3617
|
+
if (this.persistent && reason !== "programmatic") return Promise.resolve();
|
|
3618
|
+
const target = this.zeroSnapId() ?? this.allowedIdsBySize()[0];
|
|
3619
|
+
if (target === void 0) return Promise.resolve();
|
|
3620
|
+
if (this.phase === "closed" && this.activeId === target) {
|
|
3621
|
+
return Promise.resolve();
|
|
3622
|
+
}
|
|
3623
|
+
const visible = this.phase === "open" || this.phase === "opening";
|
|
3624
|
+
if (visible && this.emitBeforeClose(reason)) return Promise.resolve();
|
|
3625
|
+
return this.snapTo(target);
|
|
3126
3626
|
}
|
|
3127
3627
|
canDismiss() {
|
|
3128
3628
|
return !this.persistent && !this.disableCloseFlag && !this.destroyed;
|
|
@@ -3145,10 +3645,12 @@ class BottomSheetCore {
|
|
|
3145
3645
|
}
|
|
3146
3646
|
setDragFrom(mode) {
|
|
3147
3647
|
if (this.destroyed || mode === this.dragFromMode) return;
|
|
3648
|
+
const wasDragging = this.gesture?.isDragging ?? false;
|
|
3148
3649
|
this.gesture?.forceClearDragState();
|
|
3149
3650
|
this.detachSheetGesture?.();
|
|
3150
3651
|
this.dragFromMode = mode;
|
|
3151
3652
|
this.mountSheetGesture();
|
|
3653
|
+
if (wasDragging) this.restoreAfterDrag();
|
|
3152
3654
|
}
|
|
3153
3655
|
getDragFrom() {
|
|
3154
3656
|
return this.dragFromMode;
|
|
@@ -3161,6 +3663,7 @@ class BottomSheetCore {
|
|
|
3161
3663
|
const nowVertical = next === "bottom" || next === "top";
|
|
3162
3664
|
this.gesture?.forceClearDragState();
|
|
3163
3665
|
this.detachSheetGesture?.();
|
|
3666
|
+
this.landNow();
|
|
3164
3667
|
this.animation.cancel();
|
|
3165
3668
|
this.element.style.removeProperty(layoutAxis(this.mode));
|
|
3166
3669
|
this.mode = next;
|
|
@@ -3176,9 +3679,12 @@ class BottomSheetCore {
|
|
|
3176
3679
|
this.detachSliderKeyboard?.();
|
|
3177
3680
|
this.mountSliderKeyboard();
|
|
3178
3681
|
this.mountSheetGesture();
|
|
3682
|
+
for (const anchor of this.anchors) anchor.reposition();
|
|
3683
|
+
this.placeScrimStages?.();
|
|
3684
|
+
if (this.stackEffectPrimed) {
|
|
3685
|
+
this.element.style.transformOrigin = nowVertical ? "50% 100%" : "0% 50%";
|
|
3686
|
+
}
|
|
3179
3687
|
this.recompute();
|
|
3180
|
-
const active = this.snaps.findById(this.activeId);
|
|
3181
|
-
if (active) this.applySize(active.size);
|
|
3182
3688
|
this.updateAriaSlider();
|
|
3183
3689
|
}
|
|
3184
3690
|
getMode() {
|
|
@@ -3220,13 +3726,22 @@ class BottomSheetCore {
|
|
|
3220
3726
|
if (snap && ids.includes(snap)) {
|
|
3221
3727
|
void this.snapTo(snap, 0, true);
|
|
3222
3728
|
} else if (!ids.includes(this.activeId)) {
|
|
3223
|
-
const fallback =
|
|
3224
|
-
if (fallback) void this.snapTo(fallback, 0, true);
|
|
3729
|
+
const fallback = this.fallbackSnap(this.snaps.findById(this.activeId)?.size ?? this.size);
|
|
3730
|
+
if (fallback) void this.snapTo(fallback.id, 0, true);
|
|
3225
3731
|
}
|
|
3226
3732
|
}
|
|
3733
|
+
fallbackSnap(from) {
|
|
3734
|
+
return (from > 0 ? this.snaps.nearest(from, true) : null) ?? this.snaps.nearest(from, false);
|
|
3735
|
+
}
|
|
3227
3736
|
setBackdropRange(range) {
|
|
3228
3737
|
this.scrim.setBackdropRange(range, () => this.applySize(this.size));
|
|
3229
3738
|
}
|
|
3739
|
+
setBackdropOpacity(value) {
|
|
3740
|
+
this.scrim.setBackdropOpacity(value, () => this.applySize(this.size));
|
|
3741
|
+
}
|
|
3742
|
+
setBackdropColor(color) {
|
|
3743
|
+
this.scrim.setBackdropColor(color);
|
|
3744
|
+
}
|
|
3230
3745
|
setScreenRange(range) {
|
|
3231
3746
|
this.scrim.setScreenRange(range, () => this.applySize(this.size));
|
|
3232
3747
|
}
|
|
@@ -3263,7 +3778,7 @@ class BottomSheetCore {
|
|
|
3263
3778
|
this.anchorHost.style.setProperty("--bs-progress", String(this.computeProgress(this.size)));
|
|
3264
3779
|
}
|
|
3265
3780
|
const handle = attachAnchor({
|
|
3266
|
-
|
|
3781
|
+
getMode: () => this.mode,
|
|
3267
3782
|
host: this.anchorHost,
|
|
3268
3783
|
getState: () => ({
|
|
3269
3784
|
activeId: this.activeId,
|
|
@@ -3297,8 +3812,8 @@ class BottomSheetCore {
|
|
|
3297
3812
|
return () => {
|
|
3298
3813
|
};
|
|
3299
3814
|
}
|
|
3300
|
-
const
|
|
3301
|
-
|
|
3815
|
+
const stages = installScrimStages({
|
|
3816
|
+
getMode: () => this.mode,
|
|
3302
3817
|
host,
|
|
3303
3818
|
getState: () => ({
|
|
3304
3819
|
activeId: this.activeId,
|
|
@@ -3309,10 +3824,14 @@ class BottomSheetCore {
|
|
|
3309
3824
|
isDestroyed: () => this.destroyed
|
|
3310
3825
|
}, opts);
|
|
3311
3826
|
const detach = () => {
|
|
3312
|
-
if (this.detachScrimStages === detach)
|
|
3313
|
-
|
|
3827
|
+
if (this.detachScrimStages === detach) {
|
|
3828
|
+
this.detachScrimStages = null;
|
|
3829
|
+
this.placeScrimStages = null;
|
|
3830
|
+
}
|
|
3831
|
+
stages.detach();
|
|
3314
3832
|
};
|
|
3315
3833
|
this.detachScrimStages = detach;
|
|
3834
|
+
this.placeScrimStages = stages.reposition;
|
|
3316
3835
|
this.teardowns.add(() => this.detachScrimStages?.());
|
|
3317
3836
|
return detach;
|
|
3318
3837
|
}
|
|
@@ -3324,32 +3843,29 @@ class BottomSheetCore {
|
|
|
3324
3843
|
}
|
|
3325
3844
|
setSnapPoints(points, allowed) {
|
|
3326
3845
|
if (this.destroyed) return;
|
|
3327
|
-
this.
|
|
3328
|
-
this.
|
|
3846
|
+
const from = this.snaps.findById(this.activeId)?.size ?? this.size;
|
|
3847
|
+
const restricted = this.snaps.getResolvedSnaps().some((s) => !this.snaps.getAllowedIds().includes(s.id));
|
|
3329
3848
|
this.snapPointsRaw = points;
|
|
3330
3849
|
auditVhUsage(points);
|
|
3331
3850
|
this.auditDuplicateSnapIds(points);
|
|
3851
|
+
this.maxHeight.resolveCap();
|
|
3332
3852
|
this.snaps.setRaw(points);
|
|
3853
|
+
this.maxHeight.clampTo();
|
|
3333
3854
|
if (allowed) {
|
|
3334
3855
|
this.snaps.setAllowedIds(allowed);
|
|
3335
3856
|
} else {
|
|
3336
3857
|
const present = this.snaps.getResolvedSnaps().map((s) => s.id);
|
|
3337
|
-
const kept = this.snaps.getAllowedIds().filter((id) => present.includes(id));
|
|
3858
|
+
const kept = restricted ? this.snaps.getAllowedIds().filter((id) => present.includes(id)) : [];
|
|
3338
3859
|
this.snaps.setAllowedIds(kept.length ? kept : present);
|
|
3339
3860
|
}
|
|
3340
3861
|
this.scrollCache.clear();
|
|
3341
3862
|
this.scrim.invalidateOpacityCache();
|
|
3342
|
-
|
|
3343
|
-
|
|
3344
|
-
|
|
3345
|
-
} else {
|
|
3346
|
-
const fallback = this.snaps.getResolvedSnaps().find((s) => this.snaps.getAllowedIds().includes(s.id)) ?? this.snaps.getResolvedSnaps()[0];
|
|
3347
|
-
if (fallback) {
|
|
3348
|
-
this.activeId = fallback.id;
|
|
3349
|
-
this.applySize(fallback.size);
|
|
3350
|
-
}
|
|
3863
|
+
if (this.snaps.findById(this.activeId)) {
|
|
3864
|
+
this.reseat();
|
|
3865
|
+
return;
|
|
3351
3866
|
}
|
|
3352
|
-
this.
|
|
3867
|
+
const fallback = this.fallbackSnap(from) ?? this.snaps.getResolvedSnaps()[0];
|
|
3868
|
+
if (fallback) void this.transitionTo(fallback.id, 0, true);
|
|
3353
3869
|
}
|
|
3354
3870
|
recompute() {
|
|
3355
3871
|
if (this.destroyed) return;
|
|
@@ -3357,22 +3873,11 @@ class BottomSheetCore {
|
|
|
3357
3873
|
this.snaps.recompute();
|
|
3358
3874
|
this.maxHeight.clampTo();
|
|
3359
3875
|
this.scrim.invalidateOpacityCache();
|
|
3360
|
-
|
|
3361
|
-
this.healActiveSnap();
|
|
3362
|
-
}
|
|
3363
|
-
healActiveSnap() {
|
|
3364
|
-
const current = this.snaps.findById(this.activeId);
|
|
3365
|
-
if (!current) return;
|
|
3366
|
-
this.size = current.size;
|
|
3367
|
-
this.applySize(this.size);
|
|
3368
|
-
this.updateAriaSlider();
|
|
3369
|
-
if (this.size > 0 && !this.lifecycle.isInstalled && !this.opening && !this.animation.isAnimating) {
|
|
3370
|
-
this.emitOpenSequence(this.activeId);
|
|
3371
|
-
}
|
|
3876
|
+
this.reseat();
|
|
3372
3877
|
}
|
|
3373
3878
|
destroy() {
|
|
3374
3879
|
this.destroyed = true;
|
|
3375
|
-
this.
|
|
3880
|
+
this.outbox.length = 0;
|
|
3376
3881
|
this.currentAbort.abort();
|
|
3377
3882
|
this.currentViewTransition?.skipTransition?.();
|
|
3378
3883
|
this.currentViewTransition = null;
|
|
@@ -3392,12 +3897,16 @@ class BottomSheetCore {
|
|
|
3392
3897
|
for (const host of [
|
|
3393
3898
|
this.anchorHost,
|
|
3394
3899
|
this.scrimParent,
|
|
3395
|
-
this.rootEl
|
|
3900
|
+
this.rootEl,
|
|
3901
|
+
this.homeRoot
|
|
3396
3902
|
]) {
|
|
3397
3903
|
if (!host) continue;
|
|
3398
3904
|
host.style.removeProperty("--bs-size");
|
|
3399
3905
|
host.style.removeProperty("--bs-progress");
|
|
3906
|
+
host.style.removeProperty("--bs-z-sheet");
|
|
3907
|
+
host.style.removeProperty("--bs-keyboard-inset");
|
|
3400
3908
|
}
|
|
3909
|
+
this.element.style.removeProperty("--bs-keyboard-inset");
|
|
3401
3910
|
if (this.screenComponent) {
|
|
3402
3911
|
this.screenComponent.style.opacity = "";
|
|
3403
3912
|
this.screenComponent.style.display = "";
|
|
@@ -3410,7 +3919,8 @@ class BottomSheetCore {
|
|
|
3410
3919
|
}
|
|
3411
3920
|
newCycle() {
|
|
3412
3921
|
this.allowOvershoot = false;
|
|
3413
|
-
this.
|
|
3922
|
+
this.transition = null;
|
|
3923
|
+
this.inFlight = null;
|
|
3414
3924
|
this.currentAbort.abort();
|
|
3415
3925
|
this.currentAbort = new AbortController();
|
|
3416
3926
|
return this.currentAbort.signal;
|
|
@@ -3431,8 +3941,9 @@ class BottomSheetCore {
|
|
|
3431
3941
|
if (typeof document === "undefined") return;
|
|
3432
3942
|
if (!this.lifecycle.focusTrapEnabled) return;
|
|
3433
3943
|
if (this.element.getAttribute("aria-labelledby")) return;
|
|
3434
|
-
const
|
|
3435
|
-
const
|
|
3944
|
+
const HEADING = "h1,h2,h3,h4,h5,h6";
|
|
3945
|
+
const header = this.element.querySelector(".bs-header");
|
|
3946
|
+
const titled = this.element.querySelector("[data-bs-title]") ?? this.handle.querySelector(HEADING) ?? header?.querySelector(HEADING);
|
|
3436
3947
|
if (!titled) return;
|
|
3437
3948
|
if (!titled.id) {
|
|
3438
3949
|
titled.id = nextInstanceId("bs-title");
|
|
@@ -3470,12 +3981,16 @@ class BottomSheetCore {
|
|
|
3470
3981
|
this.stackZ = z;
|
|
3471
3982
|
this.element.style.zIndex = String(z);
|
|
3472
3983
|
if (this.backdrop) this.backdrop.style.zIndex = String(z - 1);
|
|
3984
|
+
this.rootEl?.style.setProperty("--bs-z-sheet", String(z));
|
|
3985
|
+
this.homeRoot?.style.setProperty("--bs-z-sheet", String(z));
|
|
3473
3986
|
for (const anchor of this.anchors) anchor.syncZ(z + 1);
|
|
3474
3987
|
},
|
|
3475
3988
|
setIsTop: (isTop) => {
|
|
3989
|
+
if (isTop === this.isTopSheet) return;
|
|
3476
3990
|
this.isTopSheet = isTop;
|
|
3991
|
+
this.lifecycle.syncTop();
|
|
3477
3992
|
},
|
|
3478
|
-
isOpen: () => this.
|
|
3993
|
+
isOpen: () => this.phase !== "closed",
|
|
3479
3994
|
setDepth: (depth) => this.applyStackDepth(depth)
|
|
3480
3995
|
}));
|
|
3481
3996
|
}
|
|
@@ -3503,15 +4018,15 @@ class BottomSheetCore {
|
|
|
3503
4018
|
const r = this.getAllowedRange();
|
|
3504
4019
|
buf.size = this.size;
|
|
3505
4020
|
buf.maxAxisSize = this.snaps.getMaxAxisSize();
|
|
3506
|
-
buf.range.min = r.min;
|
|
4021
|
+
buf.range.min = this.dismissBlocked() ? Math.max(r.min, this.smallestOpenSize()) : r.min;
|
|
3507
4022
|
buf.range.max = r.max;
|
|
3508
4023
|
buf.rubberBandEnabled = this.rubberBandEnabled;
|
|
3509
4024
|
return buf;
|
|
3510
4025
|
},
|
|
3511
4026
|
getDisableDrag: () => this.disableDragFlag,
|
|
3512
|
-
cancelAnimation: () => this.
|
|
4027
|
+
cancelAnimation: () => this.suspend(),
|
|
3513
4028
|
applySize: (size) => this.applySize(size),
|
|
3514
|
-
|
|
4029
|
+
restoreAfterDrag: () => this.restoreAfterDrag(),
|
|
3515
4030
|
settleAfterDrag: (delta, velocity, kind) => this.settleAfterDrag(delta, velocity, kind),
|
|
3516
4031
|
emit: (event, payload) => this.emit(event, payload),
|
|
3517
4032
|
listenerCount: (event) => this.bus.listenerCount(event)
|
|
@@ -3628,32 +4143,19 @@ class BottomSheetCore {
|
|
|
3628
4143
|
element: this.element,
|
|
3629
4144
|
getMode: () => this.mode,
|
|
3630
4145
|
isDestroyed: () => this.destroyed,
|
|
3631
|
-
isDragging: () => this.isDraggingAny(),
|
|
3632
4146
|
resolveActiveSnap: () => this.snaps.findById(this.activeId),
|
|
3633
4147
|
getMaxAxisSize: () => this.snaps.getMaxAxisSize(),
|
|
3634
|
-
getSize: () => this.size,
|
|
3635
4148
|
setMaxAxisSize: (size) => {
|
|
3636
4149
|
this.snaps.setMaxAxisSize(size);
|
|
3637
4150
|
},
|
|
3638
|
-
setSize: (size) => {
|
|
3639
|
-
this.size = size;
|
|
3640
|
-
},
|
|
3641
4151
|
recomputeSnaps: () => this.recomputeSnaps(),
|
|
3642
|
-
|
|
3643
|
-
|
|
3644
|
-
this.animation.cancel();
|
|
3645
|
-
},
|
|
3646
|
-
newCycle: () => {
|
|
3647
|
-
this.newCycle();
|
|
3648
|
-
},
|
|
3649
|
-
isAnimating: () => this.animation.isAnimating,
|
|
3650
|
-
resyncAfterCancel: () => this.resyncAfterResize()
|
|
4152
|
+
reseat: () => this.reseat(),
|
|
4153
|
+
landNow: () => this.landNow()
|
|
3651
4154
|
}));
|
|
3652
4155
|
if (this.lifecycle.closeOnEscape) {
|
|
3653
4156
|
const onKey = (e) => {
|
|
3654
4157
|
if (this.destroyed || e.defaultPrevented) return;
|
|
3655
|
-
if (
|
|
3656
|
-
if (e.key === "Escape" && this.isTopSheet && this.size > 0) {
|
|
4158
|
+
if (e.key === "Escape" && this.isTopSheet) {
|
|
3657
4159
|
void this.close("escape");
|
|
3658
4160
|
}
|
|
3659
4161
|
};
|
|
@@ -3669,7 +4171,10 @@ class BottomSheetCore {
|
|
|
3669
4171
|
handle: this.handle,
|
|
3670
4172
|
mode: this.mode,
|
|
3671
4173
|
isDestroyed: () => this.destroyed,
|
|
3672
|
-
getAllowedIds: () =>
|
|
4174
|
+
getAllowedIds: () => {
|
|
4175
|
+
const ids = this.allowedIdsBySize();
|
|
4176
|
+
return this.dismissBlocked() ? ids.filter((id) => (this.snaps.findById(id)?.size ?? 0) > 0) : ids;
|
|
4177
|
+
},
|
|
3673
4178
|
getActiveId: () => this.activeId,
|
|
3674
4179
|
snapTo: (id) => {
|
|
3675
4180
|
void this.snapTo(id);
|
|
@@ -3682,7 +4187,6 @@ class BottomSheetCore {
|
|
|
3682
4187
|
});
|
|
3683
4188
|
}
|
|
3684
4189
|
settleAfterDrag(delta, velocity, kind = "touch") {
|
|
3685
|
-
const signal = this.newCycle();
|
|
3686
4190
|
const target = this.snaps.findDragSettleTarget({
|
|
3687
4191
|
delta,
|
|
3688
4192
|
velocity,
|
|
@@ -3691,41 +4195,35 @@ class BottomSheetCore {
|
|
|
3691
4195
|
activeId: this.activeId,
|
|
3692
4196
|
flickVelocity: this.flickVelocity,
|
|
3693
4197
|
dragThreshold: this.dragThreshold
|
|
3694
|
-
});
|
|
4198
|
+
}, this.dismissBlocked());
|
|
3695
4199
|
if (!target) {
|
|
3696
4200
|
this.clearWillChangeIfIdle();
|
|
3697
4201
|
return;
|
|
3698
4202
|
}
|
|
3699
4203
|
const previousId = this.activeId;
|
|
3700
4204
|
if (target.id === previousId && target.size === this.size) {
|
|
4205
|
+
if (this.phase === "opening" || this.phase === "closing") {
|
|
4206
|
+
this.land(false);
|
|
4207
|
+
}
|
|
3701
4208
|
this.clearWillChangeIfIdle();
|
|
3702
4209
|
return;
|
|
3703
4210
|
}
|
|
3704
4211
|
if (this.emitBeforeSnap(target, previousId)) {
|
|
3705
|
-
|
|
3706
|
-
if (restore2) {
|
|
3707
|
-
void this.animation.animateTo(restore2.size, 0);
|
|
3708
|
-
}
|
|
4212
|
+
this.restoreAfterDrag();
|
|
3709
4213
|
return;
|
|
3710
4214
|
}
|
|
3711
|
-
|
|
3712
|
-
|
|
3713
|
-
|
|
3714
|
-
|
|
3715
|
-
if (
|
|
3716
|
-
|
|
3717
|
-
|
|
3718
|
-
|
|
3719
|
-
|
|
3720
|
-
|
|
3721
|
-
|
|
3722
|
-
|
|
3723
|
-
void this.animation.animateTo(settleSize, velocity).then(() => {
|
|
3724
|
-
if (signal.aborted) return;
|
|
3725
|
-
this.allowOvershoot = false;
|
|
3726
|
-
this.opening = false;
|
|
3727
|
-
this.completeSnap(target.id, target.size, previousSize, previousSnapSize === 0, true);
|
|
3728
|
-
});
|
|
4215
|
+
void this.runTransition(target, velocity, true);
|
|
4216
|
+
}
|
|
4217
|
+
restoreAfterDrag() {
|
|
4218
|
+
const active = this.snaps.findById(this.activeId);
|
|
4219
|
+
if (active) void this.runTransition(active, 0, false);
|
|
4220
|
+
}
|
|
4221
|
+
smallestOpenSize() {
|
|
4222
|
+
const allowed = this.snaps.getAllowedIds();
|
|
4223
|
+
for (const snap of this.snaps.getResolvedSnaps()) {
|
|
4224
|
+
if (snap.size > 0 && allowed.includes(snap.id)) return snap.size;
|
|
4225
|
+
}
|
|
4226
|
+
return 0;
|
|
3729
4227
|
}
|
|
3730
4228
|
isDraggingAny() {
|
|
3731
4229
|
return (this.gesture?.isDragging ?? false) || (this.contentGesture?.isDragging ?? false);
|
|
@@ -3750,7 +4248,8 @@ class BottomSheetCore {
|
|
|
3750
4248
|
const offset = cap - clamped;
|
|
3751
4249
|
const style = this.element.style;
|
|
3752
4250
|
if (!skipTransform) style.transform = this.transformTemplate(offset);
|
|
3753
|
-
const
|
|
4251
|
+
const moving = this.animation.isAnimating || this.isDraggingAny();
|
|
4252
|
+
const sizeChanged = this.sizeWriteSentinel.shouldWrite(clamped, moving ? SIZE_WRITE_EPSILON : 0);
|
|
3754
4253
|
if (sizeChanged) {
|
|
3755
4254
|
style.setProperty("--bs-size", `${clamped}px`);
|
|
3756
4255
|
if (this.scrimParent) {
|
|
@@ -3762,9 +4261,11 @@ class BottomSheetCore {
|
|
|
3762
4261
|
if (this.rootEl && this.rootEl !== this.scrimParent && this.rootEl !== this.anchorHost) {
|
|
3763
4262
|
this.rootEl.style.setProperty("--bs-size", `${clamped}px`);
|
|
3764
4263
|
}
|
|
4264
|
+
this.homeRoot?.style.setProperty("--bs-size", `${clamped}px`);
|
|
3765
4265
|
}
|
|
3766
4266
|
const progress = this.computeProgress(clamped);
|
|
3767
|
-
const
|
|
4267
|
+
const opacityEpsilon = moving ? OPACITY_WRITE_EPSILON : 0;
|
|
4268
|
+
const progressChanged = this.progressWriteSentinel.shouldWrite(progress, opacityEpsilon);
|
|
3768
4269
|
if (progressChanged) {
|
|
3769
4270
|
style.setProperty("--bs-progress", String(progress));
|
|
3770
4271
|
if (this.anchorHost) {
|
|
@@ -3773,8 +4274,9 @@ class BottomSheetCore {
|
|
|
3773
4274
|
if (this.rootEl && this.rootEl !== this.anchorHost) {
|
|
3774
4275
|
this.rootEl.style.setProperty("--bs-progress", String(progress));
|
|
3775
4276
|
}
|
|
4277
|
+
this.homeRoot?.style.setProperty("--bs-progress", String(progress));
|
|
3776
4278
|
}
|
|
3777
|
-
this.scrim.applyOpacity(progress, progressChanged);
|
|
4279
|
+
this.scrim.applyOpacity(progress, progressChanged, opacityEpsilon);
|
|
3778
4280
|
if ((progressChanged || sizeChanged) && this.bus.listenerCount("progress") > 0) {
|
|
3779
4281
|
this.progressPayload.value = progress;
|
|
3780
4282
|
this.progressPayload.size = clamped;
|
|
@@ -3787,236 +4289,328 @@ class BottomSheetCore {
|
|
|
3787
4289
|
if (reachableMax <= min) return 0;
|
|
3788
4290
|
return Math.min(Math.max((size - min) / (reachableMax - min), 0), 1);
|
|
3789
4291
|
}
|
|
3790
|
-
handleOpen() {
|
|
3791
|
-
if (this.promotedForOpen) this.promotedForOpen = false;
|
|
3792
|
-
else sheetStack.promote(this.id);
|
|
3793
|
-
try {
|
|
3794
|
-
this.lifecycle.install();
|
|
3795
|
-
} catch (err) {
|
|
3796
|
-
if (!this.destroyed) this.newCycle();
|
|
3797
|
-
this.handleClose();
|
|
3798
|
-
this.applySize(0);
|
|
3799
|
-
this.activeId = this.snapPointsRaw.find((p) => p.id === "closed")?.id ?? this.activeId;
|
|
3800
|
-
this.size = 0;
|
|
3801
|
-
queueMicrotask(() => {
|
|
3802
|
-
throw err;
|
|
3803
|
-
});
|
|
3804
|
-
}
|
|
3805
|
-
}
|
|
3806
|
-
resyncAfterResize() {
|
|
3807
|
-
if (this.destroyed) return;
|
|
3808
|
-
const target = this.snaps.findById(this.activeId);
|
|
3809
|
-
if (!target) return;
|
|
3810
|
-
this.updateAriaSlider();
|
|
3811
|
-
this.emit("snap", {
|
|
3812
|
-
id: target.id,
|
|
3813
|
-
size: this.size,
|
|
3814
|
-
progress: this.computeProgress(this.size)
|
|
3815
|
-
});
|
|
3816
|
-
if (target.size > 0 && !this.lifecycle.isInstalled) {
|
|
3817
|
-
this.emitOpenSequence(target.id);
|
|
3818
|
-
} else if (target.size === 0 && this.lifecycle.isInstalled) {
|
|
3819
|
-
this.emitCloseSequence();
|
|
3820
|
-
}
|
|
3821
|
-
}
|
|
3822
|
-
handleClose() {
|
|
3823
|
-
this.opening = false;
|
|
3824
|
-
this.promotedForOpen = false;
|
|
3825
|
-
this.lifecycle.release();
|
|
3826
|
-
sheetStack.update();
|
|
3827
|
-
}
|
|
3828
4292
|
}
|
|
3829
|
-
const
|
|
3830
|
-
const
|
|
3831
|
-
|
|
3832
|
-
|
|
3833
|
-
const
|
|
3834
|
-
|
|
3835
|
-
|
|
3836
|
-
|
|
3837
|
-
|
|
3838
|
-
|
|
3839
|
-
|
|
3840
|
-
|
|
3841
|
-
|
|
3842
|
-
|
|
4293
|
+
const MARKER = "__bs";
|
|
4294
|
+
const STACK = "__bsStack";
|
|
4295
|
+
const DEPTH = "__bsDepth";
|
|
4296
|
+
const ROUTED = "__bsRouted";
|
|
4297
|
+
const SETTLE_TIMEOUT_MS = 1e3;
|
|
4298
|
+
const state = shared("history", () => ({
|
|
4299
|
+
live: [],
|
|
4300
|
+
stackId: void 0,
|
|
4301
|
+
baseUrl: void 0,
|
|
4302
|
+
position: null,
|
|
4303
|
+
knownLength: 0,
|
|
4304
|
+
href: "",
|
|
4305
|
+
subscribers: /* @__PURE__ */ new Set(),
|
|
4306
|
+
internal: 0,
|
|
4307
|
+
pending: [],
|
|
4308
|
+
deferred: [],
|
|
4309
|
+
waiters: [],
|
|
4310
|
+
timer: null,
|
|
4311
|
+
listening: false,
|
|
4312
|
+
leftovers: false,
|
|
4313
|
+
orphanChecked: false,
|
|
4314
|
+
push: null,
|
|
4315
|
+
replace: null
|
|
4316
|
+
}));
|
|
4317
|
+
const hasHistory = () => typeof window !== "undefined" && typeof history !== "undefined";
|
|
4318
|
+
const plain = (value) => value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
4319
|
+
const depthOf = (value) => {
|
|
4320
|
+
const depth = value?.[DEPTH];
|
|
4321
|
+
return typeof depth === "number" ? depth : null;
|
|
4322
|
+
};
|
|
4323
|
+
const isMarker = (value) => value?.[MARKER] === true;
|
|
4324
|
+
const inStack = (value) => value !== null && state.stackId !== void 0 && value[STACK] === state.stackId && depthOf(value) !== null;
|
|
4325
|
+
const foreignKeys = (value) => {
|
|
4326
|
+
const out = {};
|
|
4327
|
+
const source = plain(value);
|
|
4328
|
+
if (!source) return out;
|
|
4329
|
+
for (const key of Object.keys(source)) {
|
|
4330
|
+
if (!key.startsWith(MARKER)) out[key] = source[key];
|
|
4331
|
+
}
|
|
4332
|
+
return out;
|
|
4333
|
+
};
|
|
4334
|
+
const markerFor = (surface, depth) => ({
|
|
4335
|
+
...foreignKeys(history.state),
|
|
3843
4336
|
...surface.markerState(),
|
|
3844
|
-
|
|
4337
|
+
[MARKER]: true,
|
|
4338
|
+
[STACK]: state.stackId,
|
|
4339
|
+
[DEPTH]: depth
|
|
3845
4340
|
});
|
|
4341
|
+
const urlForDepth = (depth) => {
|
|
4342
|
+
for (let i = depth - 1; i >= 0; i -= 1) {
|
|
4343
|
+
const url = state.live[i]?.surface.url;
|
|
4344
|
+
if (url !== void 0) return url;
|
|
4345
|
+
}
|
|
4346
|
+
return state.baseUrl;
|
|
4347
|
+
};
|
|
3846
4348
|
const runInternal = (fn) => {
|
|
3847
|
-
|
|
4349
|
+
state.internal += 1;
|
|
3848
4350
|
try {
|
|
3849
4351
|
fn();
|
|
4352
|
+
return true;
|
|
4353
|
+
} catch {
|
|
4354
|
+
return false;
|
|
3850
4355
|
} finally {
|
|
3851
|
-
|
|
4356
|
+
state.internal -= 1;
|
|
3852
4357
|
}
|
|
3853
4358
|
};
|
|
3854
4359
|
const notifyRouteChange = () => {
|
|
3855
|
-
const
|
|
3856
|
-
|
|
4360
|
+
for (const fn of Array.from(state.subscribers)) {
|
|
4361
|
+
try {
|
|
4362
|
+
fn();
|
|
4363
|
+
} catch (err) {
|
|
4364
|
+
queueMicrotask(() => {
|
|
4365
|
+
throw err;
|
|
4366
|
+
});
|
|
4367
|
+
}
|
|
4368
|
+
}
|
|
3857
4369
|
};
|
|
3858
4370
|
const ensureListener = () => {
|
|
3859
|
-
if (
|
|
4371
|
+
if (state.listening || !hasHistory()) return;
|
|
3860
4372
|
window.addEventListener("popstate", onPopState);
|
|
3861
|
-
|
|
4373
|
+
state.listening = true;
|
|
3862
4374
|
};
|
|
3863
4375
|
const maybeTeardownListener = () => {
|
|
3864
|
-
if (!
|
|
3865
|
-
if (
|
|
3866
|
-
if (hasWindow()) window.removeEventListener("popstate", onPopState);
|
|
3867
|
-
listenerInstalled = false;
|
|
3868
|
-
};
|
|
3869
|
-
const installPatch = () => {
|
|
3870
|
-
if (patchInstalled || !hasHistory()) return;
|
|
3871
|
-
origPushState = history.pushState;
|
|
3872
|
-
origReplaceState = history.replaceState;
|
|
3873
|
-
patchedPushState = function(...args) {
|
|
3874
|
-
origPushState.apply(this, args);
|
|
3875
|
-
if (internalDepth === 0) notifyRouteChange();
|
|
3876
|
-
};
|
|
3877
|
-
patchedReplaceState = function(...args) {
|
|
3878
|
-
origReplaceState.apply(this, args);
|
|
3879
|
-
if (internalDepth === 0) notifyRouteChange();
|
|
3880
|
-
};
|
|
3881
|
-
history.pushState = patchedPushState;
|
|
3882
|
-
history.replaceState = patchedReplaceState;
|
|
3883
|
-
patchInstalled = true;
|
|
3884
|
-
};
|
|
3885
|
-
const removePatch = () => {
|
|
3886
|
-
if (!patchInstalled) return;
|
|
3887
|
-
if (history.pushState === patchedPushState && origPushState) {
|
|
3888
|
-
history.pushState = origPushState;
|
|
3889
|
-
}
|
|
3890
|
-
if (history.replaceState === patchedReplaceState && origReplaceState) {
|
|
3891
|
-
history.replaceState = origReplaceState;
|
|
3892
|
-
}
|
|
3893
|
-
origPushState = null;
|
|
3894
|
-
origReplaceState = null;
|
|
3895
|
-
patchedPushState = null;
|
|
3896
|
-
patchedReplaceState = null;
|
|
3897
|
-
patchInstalled = false;
|
|
3898
|
-
};
|
|
3899
|
-
function onPopState() {
|
|
3900
|
-
if (suppress > 0) {
|
|
3901
|
-
suppress -= 1;
|
|
3902
|
-
const rebrand = pendingRebrands.shift();
|
|
3903
|
-
if (rebrand) rebrand();
|
|
3904
|
-
maybeTeardownListener();
|
|
4376
|
+
if (!state.listening) return;
|
|
4377
|
+
if (state.live.length > 0 || state.subscribers.size > 0 || state.pending.length > 0 || state.leftovers) {
|
|
3905
4378
|
return;
|
|
3906
4379
|
}
|
|
3907
|
-
if (
|
|
3908
|
-
|
|
3909
|
-
|
|
3910
|
-
|
|
3911
|
-
|
|
3912
|
-
|
|
3913
|
-
|
|
3914
|
-
|
|
3915
|
-
|
|
3916
|
-
|
|
3917
|
-
|
|
3918
|
-
|
|
3919
|
-
|
|
3920
|
-
|
|
3921
|
-
|
|
3922
|
-
|
|
3923
|
-
|
|
3924
|
-
|
|
3925
|
-
|
|
3926
|
-
}
|
|
3927
|
-
if (!target) {
|
|
3928
|
-
const removed = entries.pop();
|
|
3929
|
-
if (removed) removed.live = false;
|
|
4380
|
+
if (hasHistory()) window.removeEventListener("popstate", onPopState);
|
|
4381
|
+
state.listening = false;
|
|
4382
|
+
};
|
|
4383
|
+
const settleIfIdle = () => {
|
|
4384
|
+
if (state.pending.length > 0) return;
|
|
4385
|
+
if (state.timer !== null) {
|
|
4386
|
+
clearTimeout(state.timer);
|
|
4387
|
+
state.timer = null;
|
|
4388
|
+
}
|
|
4389
|
+
for (const entry of state.deferred.splice(0)) pushEntry(entry);
|
|
4390
|
+
for (const resolve of state.waiters.splice(0)) resolve();
|
|
4391
|
+
};
|
|
4392
|
+
const traverse = (delta, kind) => {
|
|
4393
|
+
state.pending.push(kind);
|
|
4394
|
+
if (state.timer !== null) clearTimeout(state.timer);
|
|
4395
|
+
state.timer = setTimeout(() => {
|
|
4396
|
+
state.timer = null;
|
|
4397
|
+
state.pending.length = 0;
|
|
4398
|
+
settleIfIdle();
|
|
3930
4399
|
maybeTeardownListener();
|
|
3931
|
-
|
|
4400
|
+
}, SETTLE_TIMEOUT_MS);
|
|
4401
|
+
ensureListener();
|
|
4402
|
+
const moved = runInternal(() => {
|
|
4403
|
+
if (delta < 0) history.back();
|
|
4404
|
+
else history.forward();
|
|
4405
|
+
});
|
|
4406
|
+
if (!moved) {
|
|
4407
|
+
state.pending.pop();
|
|
4408
|
+
settleIfIdle();
|
|
3932
4409
|
}
|
|
3933
|
-
|
|
3934
|
-
|
|
3935
|
-
|
|
3936
|
-
|
|
3937
|
-
|
|
4410
|
+
};
|
|
4411
|
+
const newStackId = () => `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
|
|
4412
|
+
const startStack = () => {
|
|
4413
|
+
state.stackId = newStackId();
|
|
4414
|
+
const current = history.state;
|
|
4415
|
+
const leftover = plain(current);
|
|
4416
|
+
state.baseUrl = isMarker(leftover) && leftover?.[ROUTED] !== void 0 ? void 0 : location.href;
|
|
4417
|
+
if (isMarker(leftover)) return;
|
|
4418
|
+
if (current !== null && current !== void 0 && !leftover) return;
|
|
4419
|
+
runInternal(() => {
|
|
4420
|
+
history.replaceState({
|
|
4421
|
+
...foreignKeys(current),
|
|
4422
|
+
[STACK]: state.stackId,
|
|
4423
|
+
[DEPTH]: 0
|
|
4424
|
+
}, "", location.href);
|
|
3938
4425
|
});
|
|
3939
|
-
|
|
4426
|
+
};
|
|
4427
|
+
function pushEntry(entry) {
|
|
4428
|
+
const depth = state.live.length + 1;
|
|
4429
|
+
if (state.live.length === 0) startStack();
|
|
4430
|
+
const written = runInternal(() => {
|
|
4431
|
+
history.pushState(markerFor(entry.surface, depth), "", entry.surface.url);
|
|
4432
|
+
});
|
|
4433
|
+
if (!written) return false;
|
|
4434
|
+
state.live.push(entry);
|
|
4435
|
+
state.position = depth;
|
|
4436
|
+
state.knownLength = history.length;
|
|
4437
|
+
state.href = location.href;
|
|
4438
|
+
ensureListener();
|
|
4439
|
+
return true;
|
|
3940
4440
|
}
|
|
3941
|
-
|
|
3942
|
-
if (
|
|
3943
|
-
entry
|
|
4441
|
+
const enlist = (entry) => {
|
|
4442
|
+
if (state.pending.length > 0) state.deferred.push(entry);
|
|
4443
|
+
else pushEntry(entry);
|
|
4444
|
+
};
|
|
4445
|
+
const sameBrand = (a, b) => {
|
|
4446
|
+
for (const key of /* @__PURE__ */ new Set([
|
|
4447
|
+
...Object.keys(a),
|
|
4448
|
+
...Object.keys(b)
|
|
4449
|
+
])) {
|
|
4450
|
+
if (key.startsWith(MARKER) && a[key] !== b[key]) return false;
|
|
4451
|
+
}
|
|
4452
|
+
return true;
|
|
4453
|
+
};
|
|
4454
|
+
const rebrand = (depth) => {
|
|
4455
|
+
const entry = state.live[depth - 1];
|
|
4456
|
+
if (!entry) return;
|
|
4457
|
+
const url = urlForDepth(depth);
|
|
4458
|
+
const marker = markerFor(entry.surface, depth);
|
|
4459
|
+
const atUrl = url === void 0 || new URL(url, location.href).href === location.href;
|
|
4460
|
+
if (atUrl && sameBrand(plain(history.state) ?? {}, marker)) return;
|
|
3944
4461
|
runInternal(() => {
|
|
4462
|
+
history.replaceState(marker, "", url);
|
|
4463
|
+
});
|
|
4464
|
+
state.href = location.href;
|
|
4465
|
+
};
|
|
4466
|
+
const closeAbove = (depth) => {
|
|
4467
|
+
const dropped = state.live.splice(depth);
|
|
4468
|
+
for (let i = dropped.length - 1; i >= 0; i -= 1) {
|
|
4469
|
+
const entry = dropped[i];
|
|
4470
|
+
let closing = void 0;
|
|
3945
4471
|
try {
|
|
3946
|
-
|
|
3947
|
-
} catch {
|
|
3948
|
-
|
|
4472
|
+
closing = entry.surface.close();
|
|
4473
|
+
} catch (err) {
|
|
4474
|
+
queueMicrotask(() => {
|
|
4475
|
+
throw err;
|
|
4476
|
+
});
|
|
3949
4477
|
}
|
|
3950
|
-
|
|
3951
|
-
|
|
3952
|
-
|
|
3953
|
-
|
|
4478
|
+
void Promise.resolve(closing).then(() => {
|
|
4479
|
+
if (!entry.surface.isOpen()) return;
|
|
4480
|
+
const known = (e) => e.surface === entry.surface;
|
|
4481
|
+
if (state.live.some(known) || state.deferred.some(known)) return;
|
|
4482
|
+
enlist(entry);
|
|
4483
|
+
});
|
|
3954
4484
|
}
|
|
4485
|
+
};
|
|
4486
|
+
function onPopState(event) {
|
|
4487
|
+
const kind = state.pending.shift();
|
|
4488
|
+
const byUser = kind !== "pop";
|
|
4489
|
+
const landed = plain(event.state);
|
|
4490
|
+
const depth = depthOf(landed);
|
|
4491
|
+
const from = state.position;
|
|
4492
|
+
const liveBefore = state.live.length;
|
|
4493
|
+
const grew = history.length > state.knownLength;
|
|
4494
|
+
state.knownLength = history.length;
|
|
4495
|
+
const within = inStack(landed);
|
|
4496
|
+
if (within && depth !== null) {
|
|
4497
|
+
state.position = depth;
|
|
4498
|
+
if (depth > state.live.length) {
|
|
4499
|
+
const forward = from !== null && from < depth;
|
|
4500
|
+
traverse(forward ? 1 : -1, byUser ? "skip" : "pop");
|
|
4501
|
+
} else if (depth < state.live.length) {
|
|
4502
|
+
if (byUser) closeAbove(depth);
|
|
4503
|
+
} else {
|
|
4504
|
+
rebrand(depth);
|
|
4505
|
+
}
|
|
4506
|
+
} else if (isMarker(landed)) {
|
|
4507
|
+
state.position = null;
|
|
4508
|
+
traverse(-1, byUser ? "skip" : "pop");
|
|
4509
|
+
} else {
|
|
4510
|
+
state.position = null;
|
|
4511
|
+
if (byUser && !grew && state.live.length > 0) {
|
|
4512
|
+
closeAbove(state.live.length - 1);
|
|
4513
|
+
}
|
|
4514
|
+
}
|
|
4515
|
+
const moved = location.href !== state.href;
|
|
4516
|
+
state.href = location.href;
|
|
4517
|
+
if (kind === void 0 && moved && (liveBefore === 0 || !within)) {
|
|
4518
|
+
notifyRouteChange();
|
|
4519
|
+
}
|
|
4520
|
+
settleIfIdle();
|
|
4521
|
+
maybeTeardownListener();
|
|
3955
4522
|
}
|
|
3956
4523
|
function pushBackMarker(surface) {
|
|
3957
4524
|
const entry = {
|
|
3958
|
-
surface
|
|
3959
|
-
priorUrl: void 0,
|
|
3960
|
-
live: false
|
|
4525
|
+
surface
|
|
3961
4526
|
};
|
|
3962
|
-
if (
|
|
3963
|
-
return entry;
|
|
3964
|
-
}
|
|
3965
|
-
entry.priorUrl = surface.url !== void 0 ? location.href : void 0;
|
|
3966
|
-
runInternal(() => {
|
|
3967
|
-
try {
|
|
3968
|
-
history.pushState(brandMarker(surface), "", surface.url);
|
|
3969
|
-
entry.live = true;
|
|
3970
|
-
} catch {
|
|
3971
|
-
entry.live = false;
|
|
3972
|
-
}
|
|
3973
|
-
});
|
|
3974
|
-
if (entry.live) {
|
|
3975
|
-
entries.push(entry);
|
|
3976
|
-
ensureListener();
|
|
3977
|
-
}
|
|
4527
|
+
if (hasHistory()) enlist(entry);
|
|
3978
4528
|
return entry;
|
|
3979
4529
|
}
|
|
3980
4530
|
function popBackMarker(handle) {
|
|
3981
|
-
if (!
|
|
4531
|
+
if (!hasHistory()) return Promise.resolve();
|
|
3982
4532
|
const entry = handle;
|
|
3983
|
-
const
|
|
3984
|
-
if (
|
|
3985
|
-
const
|
|
3986
|
-
|
|
3987
|
-
|
|
3988
|
-
|
|
3989
|
-
|
|
3990
|
-
|
|
3991
|
-
|
|
3992
|
-
|
|
3993
|
-
|
|
3994
|
-
|
|
3995
|
-
|
|
3996
|
-
};
|
|
4533
|
+
const waiting = state.deferred.indexOf(entry);
|
|
4534
|
+
if (waiting !== -1) state.deferred.splice(waiting, 1);
|
|
4535
|
+
const idx = state.live.indexOf(entry);
|
|
4536
|
+
if (idx === -1) return historySettled();
|
|
4537
|
+
const top = state.live.length;
|
|
4538
|
+
state.live.splice(idx, 1);
|
|
4539
|
+
const current = plain(history.state);
|
|
4540
|
+
const onTop = isMarker(current) && inStack(current) && depthOf(current) === top;
|
|
4541
|
+
if (onTop && state.pending.length === 0) {
|
|
4542
|
+
traverse(-1, "pop");
|
|
4543
|
+
} else {
|
|
4544
|
+
state.leftovers = true;
|
|
4545
|
+
ensureListener();
|
|
3997
4546
|
}
|
|
3998
|
-
pendingRebrands.push(rebrand);
|
|
3999
|
-
suppress += 1;
|
|
4000
|
-
runInternal(() => {
|
|
4001
|
-
try {
|
|
4002
|
-
history.back();
|
|
4003
|
-
} catch {
|
|
4004
|
-
suppress = Math.max(0, suppress - 1);
|
|
4005
|
-
pendingRebrands.pop();
|
|
4006
|
-
}
|
|
4007
|
-
});
|
|
4008
4547
|
maybeTeardownListener();
|
|
4548
|
+
return historySettled();
|
|
4009
4549
|
}
|
|
4550
|
+
function releaseOrphanMarker() {
|
|
4551
|
+
if (!hasHistory() || state.orphanChecked) return;
|
|
4552
|
+
state.orphanChecked = true;
|
|
4553
|
+
if (state.live.length > 0 || state.pending.length > 0) return;
|
|
4554
|
+
if (!isMarker(plain(history.state))) return;
|
|
4555
|
+
state.leftovers = true;
|
|
4556
|
+
traverse(-1, "pop");
|
|
4557
|
+
}
|
|
4558
|
+
function historySettled() {
|
|
4559
|
+
if (state.pending.length === 0) return Promise.resolve();
|
|
4560
|
+
return new Promise((resolve) => {
|
|
4561
|
+
state.waiters.push(resolve);
|
|
4562
|
+
});
|
|
4563
|
+
}
|
|
4564
|
+
const wrap = (original) => function(...args) {
|
|
4565
|
+
original.apply(this, args);
|
|
4566
|
+
state.knownLength = history.length;
|
|
4567
|
+
if (location.href === state.href) return;
|
|
4568
|
+
state.href = location.href;
|
|
4569
|
+
if (state.internal === 0 && state.subscribers.size > 0) {
|
|
4570
|
+
notifyRouteChange();
|
|
4571
|
+
}
|
|
4572
|
+
};
|
|
4573
|
+
const installPatch = () => {
|
|
4574
|
+
if (!state.push || history.pushState === state.push.original) {
|
|
4575
|
+
const original = history.pushState;
|
|
4576
|
+
const wrapper = wrap(original);
|
|
4577
|
+
history.pushState = wrapper;
|
|
4578
|
+
state.push = {
|
|
4579
|
+
wrapper,
|
|
4580
|
+
original
|
|
4581
|
+
};
|
|
4582
|
+
}
|
|
4583
|
+
if (!state.replace || history.replaceState === state.replace.original) {
|
|
4584
|
+
const original = history.replaceState;
|
|
4585
|
+
const wrapper = wrap(original);
|
|
4586
|
+
history.replaceState = wrapper;
|
|
4587
|
+
state.replace = {
|
|
4588
|
+
wrapper,
|
|
4589
|
+
original
|
|
4590
|
+
};
|
|
4591
|
+
}
|
|
4592
|
+
};
|
|
4593
|
+
const removePatch = () => {
|
|
4594
|
+
if (state.push && history.pushState === state.push.wrapper) {
|
|
4595
|
+
history.pushState = state.push.original;
|
|
4596
|
+
state.push = null;
|
|
4597
|
+
}
|
|
4598
|
+
if (state.replace && history.replaceState === state.replace.wrapper) {
|
|
4599
|
+
history.replaceState = state.replace.original;
|
|
4600
|
+
state.replace = null;
|
|
4601
|
+
}
|
|
4602
|
+
};
|
|
4010
4603
|
function subscribeRouteChange(fn) {
|
|
4011
|
-
if (!
|
|
4604
|
+
if (!hasHistory()) return () => {
|
|
4012
4605
|
};
|
|
4013
|
-
subscribers.add(fn);
|
|
4014
|
-
|
|
4606
|
+
state.subscribers.add(fn);
|
|
4607
|
+
state.href = location.href;
|
|
4608
|
+
state.knownLength = history.length;
|
|
4609
|
+
installPatch();
|
|
4015
4610
|
ensureListener();
|
|
4016
4611
|
return () => {
|
|
4017
|
-
if (!subscribers.
|
|
4018
|
-
subscribers.
|
|
4019
|
-
if (subscribers.size === 0) removePatch();
|
|
4612
|
+
if (!state.subscribers.delete(fn)) return;
|
|
4613
|
+
if (state.subscribers.size === 0) removePatch();
|
|
4020
4614
|
maybeTeardownListener();
|
|
4021
4615
|
};
|
|
4022
4616
|
}
|
|
@@ -4030,40 +4624,41 @@ function installRoute(deps) {
|
|
|
4030
4624
|
};
|
|
4031
4625
|
}
|
|
4032
4626
|
let handle = null;
|
|
4627
|
+
const surface = {
|
|
4628
|
+
isOpen: () => !deps.isDestroyed() && deps.isOpen(),
|
|
4629
|
+
close: deps.close,
|
|
4630
|
+
markerState: () => deps.routedTo !== void 0 ? {
|
|
4631
|
+
__bsRouted: deps.sheetId
|
|
4632
|
+
} : {
|
|
4633
|
+
__bsSheet: deps.sheetId
|
|
4634
|
+
},
|
|
4635
|
+
url: deps.routedTo
|
|
4636
|
+
};
|
|
4033
4637
|
const onOpen = () => {
|
|
4034
4638
|
if (handle) return;
|
|
4035
|
-
handle = pushBackMarker(
|
|
4036
|
-
isOpen: () => !deps.isDestroyed() && deps.getSize() > 0,
|
|
4037
|
-
isTop: deps.isTopSheet,
|
|
4038
|
-
close: deps.close,
|
|
4039
|
-
markerState: () => deps.routedTo !== void 0 ? {
|
|
4040
|
-
__bsRouted: deps.sheetId
|
|
4041
|
-
} : {
|
|
4042
|
-
__bsSheet: deps.sheetId
|
|
4043
|
-
},
|
|
4044
|
-
url: deps.routedTo
|
|
4045
|
-
});
|
|
4639
|
+
handle = pushBackMarker(surface);
|
|
4046
4640
|
};
|
|
4047
4641
|
const onClose = () => {
|
|
4048
4642
|
if (!handle) return;
|
|
4049
|
-
popBackMarker(handle);
|
|
4643
|
+
deps.hold(popBackMarker(handle));
|
|
4050
4644
|
handle = null;
|
|
4051
4645
|
};
|
|
4052
4646
|
const unsubscribeOpen = deps.on("open", onOpen);
|
|
4053
4647
|
const unsubscribeClose = deps.on("close", onClose);
|
|
4054
|
-
if (deps.
|
|
4648
|
+
if (deps.isOpen()) onOpen();
|
|
4649
|
+
else releaseOrphanMarker();
|
|
4055
4650
|
return () => {
|
|
4056
4651
|
unsubscribeOpen();
|
|
4057
4652
|
unsubscribeClose();
|
|
4058
4653
|
if (handle) {
|
|
4059
|
-
popBackMarker(handle);
|
|
4654
|
+
void popBackMarker(handle);
|
|
4060
4655
|
handle = null;
|
|
4061
4656
|
}
|
|
4062
4657
|
};
|
|
4063
4658
|
}
|
|
4064
4659
|
function installRouteChange(deps) {
|
|
4065
4660
|
return subscribeRouteChange(() => {
|
|
4066
|
-
if (deps.isDestroyed() || deps.
|
|
4661
|
+
if (deps.isDestroyed() || !deps.isOpen()) return;
|
|
4067
4662
|
void deps.close();
|
|
4068
4663
|
});
|
|
4069
4664
|
}
|
|
@@ -4093,7 +4688,10 @@ function installAutoCollapse(deps) {
|
|
|
4093
4688
|
};
|
|
4094
4689
|
const fire = () => {
|
|
4095
4690
|
if (deps.isDestroyed()) return;
|
|
4096
|
-
if (deps.isDragging())
|
|
4691
|
+
if (deps.isDragging() || deps.isAnimating()) {
|
|
4692
|
+
reset();
|
|
4693
|
+
return;
|
|
4694
|
+
}
|
|
4097
4695
|
const active = deps.resolveSnap(deps.getActiveId());
|
|
4098
4696
|
if (!active || active.size === 0) return;
|
|
4099
4697
|
let target = null;
|
|
@@ -4126,6 +4724,9 @@ function installContentDrag(deps) {
|
|
|
4126
4724
|
return () => detach?.();
|
|
4127
4725
|
}
|
|
4128
4726
|
const SAFETY_PAD = 8;
|
|
4727
|
+
const KEYBOARD_MIN_INSET = 1;
|
|
4728
|
+
const KEYBOARD_MIN_COVER = 60;
|
|
4729
|
+
const ZOOMED = 1.01;
|
|
4129
4730
|
function installVisualViewport(deps) {
|
|
4130
4731
|
if (typeof window === "undefined" || !window.visualViewport) {
|
|
4131
4732
|
return () => {
|
|
@@ -4133,36 +4734,36 @@ function installVisualViewport(deps) {
|
|
|
4133
4734
|
}
|
|
4134
4735
|
const vv = window.visualViewport;
|
|
4135
4736
|
let rafId = null;
|
|
4737
|
+
const coveredByKeyboard = () => {
|
|
4738
|
+
if ((vv.scale || 1) > ZOOMED) return 0;
|
|
4739
|
+
const covered = window.innerHeight - ((vv.offsetTop || 0) + vv.height);
|
|
4740
|
+
return covered >= KEYBOARD_MIN_COVER ? Math.round(covered) : 0;
|
|
4741
|
+
};
|
|
4136
4742
|
const onResize = () => {
|
|
4137
4743
|
if (rafId !== null) return;
|
|
4138
4744
|
rafId = requestAnimationFrame(() => {
|
|
4139
4745
|
rafId = null;
|
|
4140
4746
|
if (deps.isDestroyed()) return;
|
|
4747
|
+
deps.setKeyboardInset(coveredByKeyboard());
|
|
4748
|
+
const before = seatOf(deps);
|
|
4141
4749
|
deps.recomputeSnaps();
|
|
4142
4750
|
const isVertical = deps.isVerticalAxis();
|
|
4143
4751
|
const visible = isVertical ? vv.height : vv.width;
|
|
4144
4752
|
const windowSize = isVertical ? window.innerHeight : window.innerWidth;
|
|
4145
|
-
const
|
|
4753
|
+
const pad = windowSize - visible > KEYBOARD_MIN_INSET ? SAFETY_PAD : 0;
|
|
4754
|
+
const clamp = Math.max(0, Math.min(windowSize, visible) - pad);
|
|
4146
4755
|
if (clamp < deps.getMaxAxisSize()) {
|
|
4147
4756
|
deps.setMaxAxisSize(clamp);
|
|
4148
4757
|
}
|
|
4149
|
-
|
|
4150
|
-
deps.
|
|
4151
|
-
deps.newCycle();
|
|
4152
|
-
const max = deps.getMaxAxisSize();
|
|
4153
|
-
deps.setSize(Math.min(deps.getSize(), max));
|
|
4154
|
-
const current = deps.resolveActiveSnap();
|
|
4155
|
-
if (current && !deps.isDragging()) {
|
|
4156
|
-
deps.applySize(Math.min(current.size, max));
|
|
4157
|
-
if (wasAnimating) deps.resyncAfterCancel?.();
|
|
4158
|
-
} else {
|
|
4159
|
-
deps.applySize(deps.getSize());
|
|
4160
|
-
}
|
|
4758
|
+
if (sameSeat(before, seatOf(deps))) return;
|
|
4759
|
+
deps.reseat();
|
|
4161
4760
|
});
|
|
4162
4761
|
};
|
|
4163
4762
|
vv.addEventListener("resize", onResize);
|
|
4763
|
+
vv.addEventListener("scroll", onResize);
|
|
4164
4764
|
return () => {
|
|
4165
4765
|
vv.removeEventListener("resize", onResize);
|
|
4766
|
+
vv.removeEventListener("scroll", onResize);
|
|
4166
4767
|
if (rafId !== null) cancelAnimationFrame(rafId);
|
|
4167
4768
|
};
|
|
4168
4769
|
}
|
|
@@ -4174,17 +4775,17 @@ function routeFeature() {
|
|
|
4174
4775
|
if (ctx.options.closeOnRouteChange) {
|
|
4175
4776
|
teardowns.add(installRouteChange({
|
|
4176
4777
|
isDestroyed: ctx.isDestroyed,
|
|
4177
|
-
|
|
4778
|
+
isOpen: () => ctx.getPhase() !== "closed",
|
|
4178
4779
|
close: () => ctx.close()
|
|
4179
4780
|
}));
|
|
4180
4781
|
}
|
|
4181
4782
|
teardowns.add(installRoute({
|
|
4182
4783
|
routedTo: ctx.options.routedTo,
|
|
4183
4784
|
closeOnBack: ctx.options.closeOnBack,
|
|
4184
|
-
|
|
4185
|
-
getSize: ctx.getSize,
|
|
4785
|
+
isOpen: () => ctx.getPhase() !== "closed",
|
|
4186
4786
|
isDestroyed: ctx.isDestroyed,
|
|
4187
4787
|
close: () => ctx.close("back"),
|
|
4788
|
+
hold: ctx.holdSettle,
|
|
4188
4789
|
on: ctx.on,
|
|
4189
4790
|
sheetId: ctx.sheetId
|
|
4190
4791
|
}));
|
|
@@ -4211,6 +4812,7 @@ function autoCollapseFeature() {
|
|
|
4211
4812
|
ms: ctx.options.autoCollapseAfter,
|
|
4212
4813
|
isDestroyed: ctx.isDestroyed,
|
|
4213
4814
|
isDragging: ctx.isDragging,
|
|
4815
|
+
isAnimating: ctx.isAnimating,
|
|
4214
4816
|
getAllowedIds: ctx.getAllowedIds,
|
|
4215
4817
|
getActiveId: ctx.getActiveId,
|
|
4216
4818
|
resolveSnap: ctx.resolveSnap,
|
|
@@ -4238,21 +4840,14 @@ function visualViewportFeature() {
|
|
|
4238
4840
|
install: (ctx) => {
|
|
4239
4841
|
if (typeof window === "undefined") return;
|
|
4240
4842
|
return installVisualViewport({
|
|
4241
|
-
element: ctx.element,
|
|
4242
4843
|
isVerticalAxis: ctx.isVerticalAxis,
|
|
4243
4844
|
isDestroyed: ctx.isDestroyed,
|
|
4244
|
-
isDragging: ctx.isDragging,
|
|
4245
4845
|
recomputeSnaps: ctx.recomputeSnaps,
|
|
4246
4846
|
resolveActiveSnap: ctx.resolveActiveSnap,
|
|
4247
4847
|
getMaxAxisSize: ctx.getMaxAxisSize,
|
|
4248
|
-
getSize: ctx.getSize,
|
|
4249
4848
|
setMaxAxisSize: ctx.setMaxAxisSize,
|
|
4250
|
-
|
|
4251
|
-
|
|
4252
|
-
cancelInFlight: ctx.cancelInFlight,
|
|
4253
|
-
newCycle: ctx.newCycle,
|
|
4254
|
-
isAnimating: ctx.isAnimating,
|
|
4255
|
-
resyncAfterCancel: ctx.resyncAfterResize
|
|
4849
|
+
setKeyboardInset: ctx.setKeyboardInset,
|
|
4850
|
+
reseat: ctx.reseat
|
|
4256
4851
|
});
|
|
4257
4852
|
}
|
|
4258
4853
|
};
|
|
@@ -4407,39 +5002,68 @@ class BottomSheetEngine extends BottomSheetCore {
|
|
|
4407
5002
|
});
|
|
4408
5003
|
}
|
|
4409
5004
|
}
|
|
4410
|
-
const
|
|
4411
|
-
|
|
4412
|
-
|
|
4413
|
-
|
|
4414
|
-
|
|
4415
|
-
|
|
4416
|
-
|
|
4417
|
-
|
|
4418
|
-
|
|
4419
|
-
|
|
4420
|
-
|
|
4421
|
-
|
|
4422
|
-
|
|
4423
|
-
|
|
4424
|
-
|
|
4425
|
-
|
|
4426
|
-
|
|
5005
|
+
const ENGINE_OPTION_KEYS = [
|
|
5006
|
+
"mode",
|
|
5007
|
+
"snapPoints",
|
|
5008
|
+
"allowed",
|
|
5009
|
+
"initial",
|
|
5010
|
+
"duration",
|
|
5011
|
+
"easing",
|
|
5012
|
+
"respectReducedMotion",
|
|
5013
|
+
"flickVelocity",
|
|
5014
|
+
"dragThreshold",
|
|
5015
|
+
"dragFrom",
|
|
5016
|
+
"dragFromContent",
|
|
5017
|
+
"fitContentToSnap",
|
|
5018
|
+
"rubberBand",
|
|
5019
|
+
"backdropRange",
|
|
5020
|
+
"backdropOpacity",
|
|
5021
|
+
"backdropColor",
|
|
5022
|
+
"screenRange",
|
|
5023
|
+
"scrimMode",
|
|
5024
|
+
"scrimColor",
|
|
5025
|
+
"scrimBlur",
|
|
5026
|
+
"scrimInteractive",
|
|
5027
|
+
"scrimTapToClose",
|
|
5028
|
+
"scrimPreset",
|
|
5029
|
+
"animation",
|
|
5030
|
+
"settleAnimation",
|
|
5031
|
+
"spring",
|
|
5032
|
+
"focusTrap",
|
|
5033
|
+
"initialFocus",
|
|
5034
|
+
"closeOnEscape",
|
|
5035
|
+
"lockBodyScroll",
|
|
5036
|
+
"closeOnBack",
|
|
5037
|
+
"routedTo",
|
|
5038
|
+
"inertSiblings",
|
|
5039
|
+
"persistent",
|
|
5040
|
+
"disableClose",
|
|
5041
|
+
"disableDrag",
|
|
5042
|
+
"closeOnRouteChange",
|
|
5043
|
+
"radius",
|
|
5044
|
+
"maxHeight",
|
|
5045
|
+
"returnFocusTo",
|
|
5046
|
+
"persistKey",
|
|
5047
|
+
"autoCollapseAfter",
|
|
5048
|
+
"linkedSheets",
|
|
5049
|
+
"viewTransitions",
|
|
5050
|
+
"stackEffect",
|
|
5051
|
+
"features"
|
|
5052
|
+
];
|
|
5053
|
+
const pickEngineOptions = (source) => {
|
|
5054
|
+
const out = {};
|
|
5055
|
+
for (const key of ENGINE_OPTION_KEYS) {
|
|
5056
|
+
const value = source[key];
|
|
5057
|
+
if (value !== void 0) out[key] = value;
|
|
4427
5058
|
}
|
|
4428
|
-
return
|
|
4429
|
-
|
|
4430
|
-
|
|
4431
|
-
|
|
4432
|
-
|
|
4433
|
-
|
|
4434
|
-
|
|
4435
|
-
|
|
4436
|
-
el.parentNode?.removeChild(el);
|
|
4437
|
-
}
|
|
4438
|
-
} else {
|
|
4439
|
-
el.parentNode?.removeChild(el);
|
|
4440
|
-
}
|
|
4441
|
-
}
|
|
4442
|
-
};
|
|
5059
|
+
return out;
|
|
5060
|
+
};
|
|
5061
|
+
const closedSnapId = (points) => points.find((point) => point.size === 0)?.id;
|
|
5062
|
+
const syncOpen = (engine, open) => {
|
|
5063
|
+
const phase = engine.state.phase;
|
|
5064
|
+
const showing = phase === "open" || phase === "opening";
|
|
5065
|
+
if (open && !showing) void engine.open();
|
|
5066
|
+
else if (!open && showing) void engine.close();
|
|
4443
5067
|
};
|
|
4444
5068
|
const BottomSheet = component$((props) => {
|
|
4445
5069
|
const sheetRef = useSignal();
|
|
@@ -4451,10 +5075,12 @@ const BottomSheet = component$((props) => {
|
|
|
4451
5075
|
const footerRef = useSignal();
|
|
4452
5076
|
const leftBtnRef = useSignal();
|
|
4453
5077
|
const rightBtnRef = useSignal();
|
|
4454
|
-
const
|
|
4455
|
-
const
|
|
5078
|
+
const initialSnap = props.initial ?? (props.open === false ? closedSnapId(props.snapPoints) : void 0) ?? props.snap;
|
|
5079
|
+
const initialActive = initialSnap ?? props.snapPoints[0]?.id ?? "default";
|
|
5080
|
+
const state2 = useStore({
|
|
4456
5081
|
size: 0,
|
|
4457
5082
|
activeId: initialActive,
|
|
5083
|
+
phase: "closed",
|
|
4458
5084
|
isDragging: false,
|
|
4459
5085
|
isAnimating: false,
|
|
4460
5086
|
progress: 0
|
|
@@ -4462,12 +5088,15 @@ const BottomSheet = component$((props) => {
|
|
|
4462
5088
|
const engineStore = useStore({
|
|
4463
5089
|
engine: void 0
|
|
4464
5090
|
});
|
|
5091
|
+
const physicalMode = useSignal(guessPhysicalMode(props.mode));
|
|
5092
|
+
const restAtStart = startsClosed(props.snapPoints, initialSnap, props.allowed) ? "closed" : void 0;
|
|
4465
5093
|
useVisibleTask$(({ cleanup }) => {
|
|
4466
5094
|
if (!sheetRef.value) return;
|
|
4467
5095
|
if (!handleRef.value || !contentRef.value) {
|
|
4468
5096
|
devWarn("[BottomSheet/qwik] handle or content ref missing at attach time — drag and content-swipe disabled. Make sure ref bindings render before useVisibleTask$ fires.");
|
|
4469
5097
|
}
|
|
4470
5098
|
const engineOpts = {
|
|
5099
|
+
...pickEngineOptions(props),
|
|
4471
5100
|
element: sheetRef.value,
|
|
4472
5101
|
handle: handleRef.value,
|
|
4473
5102
|
scrollContainer: contentRef.value,
|
|
@@ -4475,43 +5104,25 @@ const BottomSheet = component$((props) => {
|
|
|
4475
5104
|
scrim: scrimRef.value,
|
|
4476
5105
|
mode: props.mode ?? "bottom",
|
|
4477
5106
|
snapPoints: props.snapPoints,
|
|
4478
|
-
|
|
4479
|
-
initial: props.initial,
|
|
4480
|
-
animation: props.animation,
|
|
4481
|
-
spring: props.spring,
|
|
4482
|
-
focusTrap: props.focusTrap,
|
|
4483
|
-
closeOnEscape: props.closeOnEscape,
|
|
4484
|
-
closeOnBack: props.closeOnBack,
|
|
4485
|
-
lockBodyScroll: props.lockBodyScroll,
|
|
4486
|
-
rubberBand: props.rubberBand,
|
|
4487
|
-
backdropRange: props.backdropRange,
|
|
4488
|
-
persistent: props.persistent,
|
|
4489
|
-
disableClose: props.disableClose,
|
|
4490
|
-
disableDrag: props.disableDrag,
|
|
4491
|
-
dragFrom: props.dragFrom,
|
|
4492
|
-
dragFromContent: props.dragFromContent,
|
|
4493
|
-
fitContentToSnap: props.fitContentToSnap,
|
|
4494
|
-
closeOnRouteChange: props.closeOnRouteChange,
|
|
4495
|
-
stackEffect: props.stackEffect,
|
|
4496
|
-
returnFocusTo: props.returnFocusTo,
|
|
4497
|
-
radius: props.radius,
|
|
4498
|
-
maxHeight: props.maxHeight
|
|
5107
|
+
initial: initialSnap
|
|
4499
5108
|
};
|
|
4500
|
-
const engine = new BottomSheetEngine(engineOpts);
|
|
4501
|
-
engineStore.engine = noSerialize(engine);
|
|
4502
5109
|
const teleportTarget = props.teleport === false ? null : resolveTeleportTarget(props.teleportTo);
|
|
4503
5110
|
const restoreTeleport = teleportTarget ? teleportElements([
|
|
4504
5111
|
backdropRef.value,
|
|
4505
5112
|
scrimRef.value,
|
|
4506
5113
|
sheetRef.value
|
|
4507
5114
|
], teleportTarget) : null;
|
|
5115
|
+
const engine = new BottomSheetEngine(engineOpts);
|
|
5116
|
+
engineStore.engine = noSerialize(engine);
|
|
5117
|
+
physicalMode.value = engine.getMode();
|
|
4508
5118
|
const sync = () => {
|
|
4509
5119
|
const s = engine.state;
|
|
4510
|
-
|
|
4511
|
-
|
|
4512
|
-
|
|
4513
|
-
|
|
4514
|
-
|
|
5120
|
+
state2.size = s.size;
|
|
5121
|
+
state2.activeId = s.activeId;
|
|
5122
|
+
state2.isDragging = s.isDragging;
|
|
5123
|
+
state2.phase = s.phase;
|
|
5124
|
+
state2.isAnimating = s.isAnimating;
|
|
5125
|
+
state2.progress = s.progress;
|
|
4515
5126
|
props.onChange$?.(s);
|
|
4516
5127
|
};
|
|
4517
5128
|
sync();
|
|
@@ -4519,8 +5130,15 @@ const BottomSheet = component$((props) => {
|
|
|
4519
5130
|
sync();
|
|
4520
5131
|
props.onSnap$?.(payload.id);
|
|
4521
5132
|
});
|
|
4522
|
-
engine.on("open", (payload) =>
|
|
4523
|
-
|
|
5133
|
+
engine.on("open", (payload) => {
|
|
5134
|
+
props.onOpen$?.(payload.id);
|
|
5135
|
+
props.onOpenChange$?.(true);
|
|
5136
|
+
});
|
|
5137
|
+
engine.on("close", () => {
|
|
5138
|
+
props.onClose$?.();
|
|
5139
|
+
props.onOpenChange$?.(false);
|
|
5140
|
+
});
|
|
5141
|
+
if (props.open !== void 0) syncOpen(engine, props.open);
|
|
4524
5142
|
engine.on("opened", (payload) => props.onOpened$?.(payload.id));
|
|
4525
5143
|
engine.on("closed", () => props.onClosed$?.());
|
|
4526
5144
|
engine.on("dragstart", (payload) => {
|
|
@@ -4578,6 +5196,8 @@ const BottomSheet = component$((props) => {
|
|
|
4578
5196
|
engineStore.engine = void 0;
|
|
4579
5197
|
engine.destroy();
|
|
4580
5198
|
});
|
|
5199
|
+
}, {
|
|
5200
|
+
strategy: "document-ready"
|
|
4581
5201
|
});
|
|
4582
5202
|
useTask$(({ track }) => {
|
|
4583
5203
|
const points = track(() => props.snapPoints);
|
|
@@ -4610,14 +5230,25 @@ const BottomSheet = component$((props) => {
|
|
|
4610
5230
|
}
|
|
4611
5231
|
if (dragFrom !== void 0) engine.setDragFrom(dragFrom);
|
|
4612
5232
|
});
|
|
5233
|
+
useTask$(({ track }) => {
|
|
5234
|
+
const open = track(() => props.open);
|
|
5235
|
+
const engine = engineStore.engine;
|
|
5236
|
+
if (engine && open !== void 0) syncOpen(engine, open);
|
|
5237
|
+
});
|
|
5238
|
+
useTask$(({ track }) => {
|
|
5239
|
+
const snap = track(() => props.snap);
|
|
5240
|
+
const engine = engineStore.engine;
|
|
5241
|
+
if (!engine || snap === void 0 || snap === engine.state.activeId) return;
|
|
5242
|
+
void engine.snapTo(snap);
|
|
5243
|
+
});
|
|
4613
5244
|
useTask$(({ track }) => {
|
|
4614
5245
|
const nextMode = track(() => props.mode);
|
|
4615
5246
|
const engine = engineStore.engine;
|
|
4616
5247
|
if (!engine || nextMode === void 0) return;
|
|
4617
5248
|
engine.setMode(nextMode);
|
|
5249
|
+
physicalMode.value = engine.getMode();
|
|
4618
5250
|
});
|
|
4619
5251
|
const showBackdrop = props.backdrop !== false;
|
|
4620
|
-
const mode = props.mode ?? "bottom";
|
|
4621
5252
|
const ariaLabel = props.ariaLabel ?? "Bottom sheet";
|
|
4622
5253
|
return /* @__PURE__ */ jsxs("div", {
|
|
4623
5254
|
class: "bs-root",
|
|
@@ -4636,8 +5267,9 @@ const BottomSheet = component$((props) => {
|
|
|
4636
5267
|
/* @__PURE__ */ jsxs("section", {
|
|
4637
5268
|
class: "bs-sheet",
|
|
4638
5269
|
ref: sheetRef,
|
|
4639
|
-
"data-mode":
|
|
4640
|
-
"data-
|
|
5270
|
+
"data-mode": physicalMode.value,
|
|
5271
|
+
"data-bs-rest": restAtStart,
|
|
5272
|
+
"data-active": state2.activeId,
|
|
4641
5273
|
role: "dialog",
|
|
4642
5274
|
"aria-modal": props.focusTrap ? "true" : void 0,
|
|
4643
5275
|
"aria-label": ariaLabel,
|
|
@@ -4647,7 +5279,7 @@ const BottomSheet = component$((props) => {
|
|
|
4647
5279
|
ref: handleRef,
|
|
4648
5280
|
role: "slider",
|
|
4649
5281
|
tabIndex: 0,
|
|
4650
|
-
"aria-label": "Resize sheet"
|
|
5282
|
+
"aria-label": props.handleLabel ?? "Resize sheet"
|
|
4651
5283
|
}),
|
|
4652
5284
|
/* @__PURE__ */ jsx("div", {
|
|
4653
5285
|
class: "bs-header",
|
|
@@ -4659,6 +5291,9 @@ const BottomSheet = component$((props) => {
|
|
|
4659
5291
|
/* @__PURE__ */ jsx("div", {
|
|
4660
5292
|
class: "bs-content",
|
|
4661
5293
|
ref: contentRef,
|
|
5294
|
+
tabIndex: 0,
|
|
5295
|
+
role: "region",
|
|
5296
|
+
"aria-label": props.contentLabel ?? "Sheet content",
|
|
4662
5297
|
children: /* @__PURE__ */ jsx(Slot, {})
|
|
4663
5298
|
}),
|
|
4664
5299
|
/* @__PURE__ */ jsx("div", {
|
|
@@ -4672,14 +5307,14 @@ const BottomSheet = component$((props) => {
|
|
|
4672
5307
|
class: "bs-sr-only",
|
|
4673
5308
|
role: "status",
|
|
4674
5309
|
"aria-live": "polite",
|
|
4675
|
-
children:
|
|
5310
|
+
children: state2.activeId
|
|
4676
5311
|
})
|
|
4677
5312
|
]
|
|
4678
5313
|
}),
|
|
4679
5314
|
/* @__PURE__ */ jsx("div", {
|
|
4680
5315
|
class: "bs-button-slot",
|
|
4681
5316
|
"data-side": "left",
|
|
4682
|
-
"data-mode":
|
|
5317
|
+
"data-mode": physicalMode.value,
|
|
4683
5318
|
ref: leftBtnRef,
|
|
4684
5319
|
children: /* @__PURE__ */ jsx(Slot, {
|
|
4685
5320
|
name: "leftButton"
|
|
@@ -4688,7 +5323,7 @@ const BottomSheet = component$((props) => {
|
|
|
4688
5323
|
/* @__PURE__ */ jsx("div", {
|
|
4689
5324
|
class: "bs-button-slot",
|
|
4690
5325
|
"data-side": "right",
|
|
4691
|
-
"data-mode":
|
|
5326
|
+
"data-mode": physicalMode.value,
|
|
4692
5327
|
ref: rightBtnRef,
|
|
4693
5328
|
children: /* @__PURE__ */ jsx(Slot, {
|
|
4694
5329
|
name: "rightButton"
|