@surdeddd/bottom-sheet 0.12.0 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/README.md +85 -1
  2. package/dist/{BottomSheetEngine-D6oiEfZ4.d.cts → BottomSheetCore-Cp7dWy91.d.cts} +85 -5
  3. package/dist/{BottomSheetEngine-Dhy0X_e9.d.ts → BottomSheetCore-DJYwWOqt.d.ts} +85 -5
  4. package/dist/BottomSheetEngine-Lm7HIVHv.d.cts +7 -0
  5. package/dist/BottomSheetEngine-O5PbeKfe.d.ts +7 -0
  6. package/dist/core.cjs +3680 -0
  7. package/dist/core.d.cts +3 -0
  8. package/dist/core.d.ts +3 -0
  9. package/dist/core.js +3678 -0
  10. package/dist/debug.cjs +109 -0
  11. package/dist/debug.d.cts +22 -0
  12. package/dist/debug.d.ts +22 -0
  13. package/dist/debug.js +107 -0
  14. package/dist/element.cjs +1314 -829
  15. package/dist/element.d.cts +2 -1
  16. package/dist/element.d.ts +2 -1
  17. package/dist/element.iife.global.js +2 -2
  18. package/dist/element.js +1314 -829
  19. package/dist/features.cjs +489 -0
  20. package/dist/features.d.cts +13 -0
  21. package/dist/features.d.ts +13 -0
  22. package/dist/features.js +482 -0
  23. package/dist/index-Bb5TRmyg.d.cts +9 -0
  24. package/dist/index-Cl9ebCP2.d.ts +9 -0
  25. package/dist/index.cjs +979 -1293
  26. package/dist/index.d.cts +43 -55
  27. package/dist/index.d.ts +43 -55
  28. package/dist/index.js +973 -1280
  29. package/dist/integrations/formik.d.cts +1 -1
  30. package/dist/integrations/formik.d.ts +1 -1
  31. package/dist/integrations/react-hook-form.d.cts +1 -1
  32. package/dist/integrations/react-hook-form.d.ts +1 -1
  33. package/dist/overlay.cjs +11 -10
  34. package/dist/overlay.js +11 -10
  35. package/dist/preact.d.cts +2 -1
  36. package/dist/preact.d.ts +2 -1
  37. package/dist/qwik-lib/index.qwik.cjs +4418 -0
  38. package/dist/qwik-lib/index.qwik.mjs +4418 -0
  39. package/dist/qwik.cjs +1089 -657
  40. package/dist/qwik.d.cts +4 -2
  41. package/dist/qwik.d.ts +4 -2
  42. package/dist/qwik.js +1089 -657
  43. package/dist/react.cjs +1100 -665
  44. package/dist/react.d.cts +3 -3
  45. package/dist/react.d.ts +3 -3
  46. package/dist/react.js +1100 -665
  47. package/dist/solid.cjs +1095 -657
  48. package/dist/solid.d.cts +90 -4
  49. package/dist/solid.d.ts +90 -4
  50. package/dist/solid.js +1095 -657
  51. package/dist/solid.source.jsx +1103 -657
  52. package/dist/styles.css +24 -4
  53. package/dist/svelte-core.cjs +981 -557
  54. package/dist/svelte-core.js +981 -557
  55. package/dist/svelte-src/BottomSheet.svelte +14 -0
  56. package/dist/svelte.cjs +1091 -657
  57. package/dist/svelte.d.cts +2 -0
  58. package/dist/svelte.d.ts +2 -0
  59. package/dist/svelte.js +1091 -657
  60. package/dist/vue.cjs +1105 -657
  61. package/dist/vue.js +1105 -657
  62. package/dist/webgl.cjs +583 -0
  63. package/dist/webgl.d.cts +17 -0
  64. package/dist/webgl.d.ts +17 -0
  65. package/dist/webgl.js +581 -0
  66. package/package.json +88 -12
@@ -0,0 +1,4418 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
3
+ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
4
+ import { jsxs, jsx } from "@builder.io/qwik/jsx-runtime";
5
+ import { component$, useSignal, useStore, useVisibleTask$, noSerialize, useTask$, Slot } from "@builder.io/qwik";
6
+ const BASE_Z = 100;
7
+ const STEP = 10;
8
+ class SheetStack {
9
+ constructor() {
10
+ __publicField(this, "entries", []);
11
+ }
12
+ push(entry) {
13
+ this.entries.push(entry);
14
+ this.recompute();
15
+ return () => this.remove(entry.id);
16
+ }
17
+ remove(id) {
18
+ const idx = this.entries.findIndex((e) => e.id === id);
19
+ if (idx === -1) return;
20
+ this.entries.splice(idx, 1);
21
+ this.recompute();
22
+ }
23
+ promote(id) {
24
+ const idx = this.entries.findIndex((e) => e.id === id);
25
+ if (idx === -1) return;
26
+ if (idx !== this.entries.length - 1) {
27
+ const [entry] = this.entries.splice(idx, 1);
28
+ this.entries.push(entry);
29
+ }
30
+ this.recompute();
31
+ }
32
+ update() {
33
+ this.recompute();
34
+ }
35
+ size() {
36
+ return this.entries.length;
37
+ }
38
+ depthOf(id) {
39
+ const open = [];
40
+ for (const entry of this.entries) {
41
+ if (!entry.isOpen || entry.isOpen()) open.push(entry);
42
+ }
43
+ const idx = open.findIndex((e) => e.id === id);
44
+ if (idx === -1) return 0;
45
+ return open.length - 1 - idx;
46
+ }
47
+ clear() {
48
+ this.entries = [];
49
+ }
50
+ recompute() {
51
+ let top = null;
52
+ const open = [];
53
+ for (const entry of this.entries) {
54
+ if (!entry.isOpen || entry.isOpen()) {
55
+ top = entry;
56
+ open.push(entry);
57
+ }
58
+ }
59
+ if (!top && this.entries.length > 0) {
60
+ top = this.entries[this.entries.length - 1];
61
+ }
62
+ this.entries.forEach((entry, i) => {
63
+ entry.setZIndex(BASE_Z + i * STEP);
64
+ entry.setIsTop(entry === top);
65
+ if (entry.setDepth) {
66
+ const openIdx = open.indexOf(entry);
67
+ entry.setDepth(openIdx === -1 ? 0 : open.length - 1 - openIdx);
68
+ }
69
+ });
70
+ }
71
+ }
72
+ const sheetStack = new SheetStack();
73
+ function buildTransformTemplate(axis) {
74
+ switch (axis) {
75
+ case "bottom":
76
+ return (offset) => `translate3d(0, ${offset}px, 0)`;
77
+ case "top":
78
+ return (offset) => `translate3d(0, ${-offset}px, 0)`;
79
+ case "left":
80
+ return (offset) => `translate3d(${-offset}px, 0, 0)`;
81
+ case "right":
82
+ return (offset) => `translate3d(${offset}px, 0, 0)`;
83
+ }
84
+ }
85
+ const layoutAxis = (axis) => axis === "left" || axis === "right" ? "width" : "height";
86
+ const counters = {};
87
+ function nextInstanceId(prefix) {
88
+ if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
89
+ return `${prefix}-${crypto.randomUUID()}`;
90
+ }
91
+ counters[prefix] = (counters[prefix] ?? 0) + 1;
92
+ return `${prefix}-${counters[prefix]}`;
93
+ }
94
+ function createEventBus() {
95
+ const listeners = /* @__PURE__ */ new Map();
96
+ return {
97
+ on(event, fn) {
98
+ let set = listeners.get(event);
99
+ if (!set) {
100
+ set = /* @__PURE__ */ new Set();
101
+ listeners.set(event, set);
102
+ }
103
+ set.add(fn);
104
+ return () => {
105
+ set.delete(fn);
106
+ };
107
+ },
108
+ emit(event, payload) {
109
+ const set = listeners.get(event);
110
+ if (!set) return;
111
+ for (const fn of [
112
+ ...set
113
+ ]) {
114
+ fn(payload);
115
+ }
116
+ },
117
+ listenerCount(event) {
118
+ return listeners.get(event)?.size ?? 0;
119
+ },
120
+ clear() {
121
+ listeners.clear();
122
+ }
123
+ };
124
+ }
125
+ let probe = null;
126
+ const PROBE_ATTR = "data-bs-probe";
127
+ const ensureProbe = (axis) => {
128
+ if (typeof document === "undefined") return null;
129
+ if (!probe || !probe.isConnected) {
130
+ const existing = document.querySelector(`[${PROBE_ATTR}]`);
131
+ if (existing) {
132
+ probe = existing;
133
+ } else {
134
+ probe = document.createElement("div");
135
+ probe.setAttribute("aria-hidden", "true");
136
+ probe.setAttribute(PROBE_ATTR, "");
137
+ probe.style.cssText = "position:absolute;visibility:hidden;pointer-events:none;contain:strict;left:0;top:0;width:0;height:0;";
138
+ document.body.appendChild(probe);
139
+ }
140
+ }
141
+ if (axis === "vertical") {
142
+ probe.style.width = "0";
143
+ probe.style.height = "";
144
+ } else {
145
+ probe.style.height = "0";
146
+ probe.style.width = "";
147
+ }
148
+ return probe;
149
+ };
150
+ const isViewportAxisVertical = (mode) => mode === "bottom" || mode === "top";
151
+ const viewportSize = (mode) => {
152
+ if (typeof window === "undefined") return 0;
153
+ return isViewportAxisVertical(mode) ? window.innerHeight : window.innerWidth;
154
+ };
155
+ const PERCENT = /^(-?\d+(?:\.\d+)?)%$/;
156
+ const resolveSnap = (point, mode, measureFit) => {
157
+ if (typeof point === "number") return Math.max(0, point);
158
+ if (point === "full") return viewportSize(mode);
159
+ if (point === "fit" || point === "content") {
160
+ return Math.max(0, measureFit?.() ?? 0);
161
+ }
162
+ const pctMatch = point.match(PERCENT);
163
+ if (pctMatch) {
164
+ const pct = parseFloat(pctMatch[1]) / 100;
165
+ return Math.max(0, viewportSize(mode) * pct);
166
+ }
167
+ const axis = isViewportAxisVertical(mode) ? "vertical" : "horizontal";
168
+ const el = ensureProbe(axis);
169
+ if (!el) return 0;
170
+ if (axis === "vertical") {
171
+ el.style.height = point;
172
+ return Math.max(0, el.getBoundingClientRect().height);
173
+ }
174
+ el.style.width = point;
175
+ return Math.max(0, el.getBoundingClientRect().width);
176
+ };
177
+ function devWarn(...args) {
178
+ if (typeof process !== "undefined" && process.env && process.env.NODE_ENV !== "production" && typeof console !== "undefined") {
179
+ console.warn(...args);
180
+ }
181
+ }
182
+ const resolveSnapList = (points, mode, measureFit) => points.map((p) => {
183
+ const raw = resolveSnap(p.size, mode, measureFit);
184
+ if (!Number.isFinite(raw) || raw < 0) {
185
+ devWarn(`[BottomSheet] snap "${p.id}" resolved to invalid size (${String(p.size)} → ${raw}); clamped to 0.`);
186
+ return {
187
+ id: p.id,
188
+ size: 0
189
+ };
190
+ }
191
+ return {
192
+ id: p.id,
193
+ size: raw
194
+ };
195
+ }).sort((a, b) => a.size - b.size);
196
+ const VH_NOT_DVH = /(^|[^a-z])vh\b/i;
197
+ const auditVhUsage = (points) => {
198
+ for (const p of points) {
199
+ if (typeof p.size !== "string") continue;
200
+ if (!VH_NOT_DVH.test(p.size)) continue;
201
+ devWarn(`[BottomSheet] snap "${p.id}" uses "vh" — prefer "dvh" on mobile (iOS Safari URL bar makes vh unstable). See https://web.dev/blog/viewport-units`);
202
+ }
203
+ };
204
+ const findNearest = (size, resolved, allowed, direction = 0, bias = 0) => {
205
+ const pool = resolved.filter((p) => allowed.includes(p.id));
206
+ if (pool.length === 0) return null;
207
+ const target = size + direction * bias;
208
+ let best = pool[0];
209
+ let bestDist = Math.abs(target - best.size);
210
+ for (let i = 1; i < pool.length; i++) {
211
+ const cand = pool[i];
212
+ const dist = Math.abs(target - cand.size);
213
+ if (dist < bestDist) {
214
+ best = cand;
215
+ bestDist = dist;
216
+ }
217
+ }
218
+ return best;
219
+ };
220
+ const allowedRange = (resolved, allowed) => {
221
+ const pool = resolved.filter((p) => allowed.includes(p.id));
222
+ if (pool.length === 0) return {
223
+ min: 0,
224
+ max: 0
225
+ };
226
+ return {
227
+ min: pool[0].size,
228
+ max: pool[pool.length - 1].size
229
+ };
230
+ };
231
+ const findById = (id, resolved) => resolved.find((p) => p.id === id) ?? null;
232
+ const MOUSE_FLICK_VELOCITY = 0.4;
233
+ const MOUSE_DRAG_THRESHOLD = 12;
234
+ const DIRECTIONAL_INTENT_VELOCITY = 0.15;
235
+ const findDragSettleTarget = (input) => {
236
+ const speed = Math.abs(input.velocity);
237
+ const flickThreshold = input.pointerKind === "mouse" ? MOUSE_FLICK_VELOCITY : input.flickVelocity;
238
+ const dragThresh = input.pointerKind === "mouse" ? MOUSE_DRAG_THRESHOLD : input.dragThreshold;
239
+ let direction = 0;
240
+ if (speed > flickThreshold || speed > DIRECTIONAL_INTENT_VELOCITY) {
241
+ direction = input.velocity > 0 ? 1 : -1;
242
+ }
243
+ if (Math.abs(input.delta) < dragThresh && direction === 0) {
244
+ return findById(input.activeId, input.resolved);
245
+ }
246
+ const speedBias = speed > flickThreshold ? Math.min(speed * 180, input.maxAxisSize) : 0;
247
+ return findNearest(input.size, input.resolved, input.allowed, direction, speedBias);
248
+ };
249
+ function emitCancelable(emit, base, warnLabel) {
250
+ let cancelled = false;
251
+ let frozen = false;
252
+ emit({
253
+ ...base,
254
+ cancel: () => {
255
+ if (frozen) {
256
+ devWarn(`[BottomSheet] ${warnLabel}.cancel() called asynchronously — ignored. cancel() must be invoked synchronously inside the listener.`);
257
+ return;
258
+ }
259
+ cancelled = true;
260
+ }
261
+ });
262
+ frozen = true;
263
+ return cancelled;
264
+ }
265
+ class SnapResolver {
266
+ constructor(raw, allowed, mode, measureFit, onMaxAxisSizeChange) {
267
+ __publicField(this, "mode");
268
+ __publicField(this, "measureFit");
269
+ __publicField(this, "onMaxAxisSizeChange");
270
+ __publicField(this, "raw");
271
+ __publicField(this, "allowedIds");
272
+ __publicField(this, "resolved");
273
+ __publicField(this, "maxAxisSize");
274
+ __publicField(this, "rangeCache");
275
+ __publicField(this, "notified");
276
+ this.mode = mode;
277
+ this.measureFit = measureFit;
278
+ this.onMaxAxisSizeChange = onMaxAxisSizeChange;
279
+ this.resolved = [];
280
+ this.maxAxisSize = 0;
281
+ this.rangeCache = null;
282
+ this.notified = false;
283
+ this.raw = raw;
284
+ this.allowedIds = allowed ?? raw.map((p) => p.id);
285
+ this.recompute();
286
+ }
287
+ recompute() {
288
+ const measure = this.measureFit;
289
+ let measured;
290
+ const measureOnce = measure ? () => measured ?? (measured = measure()) : void 0;
291
+ this.resolved = resolveSnapList(this.raw, this.mode, measureOnce);
292
+ const nextMax = this.resolved.reduce((m, s) => s.size > m ? s.size : m, 0);
293
+ this.rangeCache = null;
294
+ const changed = nextMax !== this.maxAxisSize || !this.notified;
295
+ this.maxAxisSize = nextMax;
296
+ this.notified = true;
297
+ if (changed) this.onMaxAxisSizeChange?.(this.maxAxisSize);
298
+ }
299
+ setRaw(raw) {
300
+ this.raw = raw;
301
+ this.recompute();
302
+ }
303
+ setAllowedIds(ids) {
304
+ this.allowedIds = ids;
305
+ this.rangeCache = null;
306
+ }
307
+ findById(id) {
308
+ return findById(id, this.resolved);
309
+ }
310
+ findDragSettleTarget(args) {
311
+ return findDragSettleTarget({
312
+ ...args,
313
+ resolved: this.resolved,
314
+ allowed: this.allowedIds,
315
+ maxAxisSize: this.maxAxisSize
316
+ });
317
+ }
318
+ getAllowedRange() {
319
+ return this.rangeCache ?? (this.rangeCache = allowedRange(this.resolved, this.allowedIds));
320
+ }
321
+ getResolvedSnaps() {
322
+ return this.resolved;
323
+ }
324
+ getAllowedIds() {
325
+ return this.allowedIds;
326
+ }
327
+ getMaxAxisSize() {
328
+ return this.maxAxisSize;
329
+ }
330
+ setMaxAxisSize(n) {
331
+ this.maxAxisSize = n;
332
+ this.onMaxAxisSizeChange?.(n);
333
+ }
334
+ }
335
+ function notifyLinkedSheets(sheets, self) {
336
+ if (sheets.length === 0) return;
337
+ for (const linked of sheets) {
338
+ if (linked === self) continue;
339
+ try {
340
+ if (linked.state.size === 0) continue;
341
+ const ids = linked.getAllowedIds();
342
+ const resolved = linked.getResolvedSnaps();
343
+ const sizeOf = (id) => resolved.find((s) => s.id === id)?.size ?? 0;
344
+ const target = ids.find((id) => sizeOf(id) > 0);
345
+ if (target && target !== linked.state.activeId) {
346
+ void linked.snapTo(target);
347
+ }
348
+ } catch (err) {
349
+ queueMicrotask(() => {
350
+ throw err;
351
+ });
352
+ }
353
+ }
354
+ }
355
+ function installResizeObserver(deps) {
356
+ if (typeof window === "undefined") {
357
+ return () => {
358
+ };
359
+ }
360
+ const onResize = () => {
361
+ if (deps.isDestroyed()) return;
362
+ deps.recomputeSnaps();
363
+ const mode = deps.getMode();
364
+ const isVerticalAxis = mode === "bottom" || mode === "top";
365
+ const viewportSize2 = isVerticalAxis ? window.innerHeight : window.innerWidth;
366
+ const max = deps.getMaxAxisSize();
367
+ if (viewportSize2 > 0 && viewportSize2 < max) {
368
+ deps.setMaxAxisSize(viewportSize2);
369
+ }
370
+ const wasAnimating = deps.isAnimating?.() ?? false;
371
+ deps.cancelInFlight();
372
+ deps.newCycle();
373
+ const current = deps.resolveActiveSnap();
374
+ const newMax = deps.getMaxAxisSize();
375
+ const targetSize = current ? Math.min(current.size, newMax) : Math.min(deps.getSize(), newMax);
376
+ deps.setSize(targetSize);
377
+ if (current && !deps.isDragging()) {
378
+ deps.applySize(targetSize);
379
+ if (wasAnimating) deps.resyncAfterCancel?.();
380
+ }
381
+ };
382
+ let resizeObserver = null;
383
+ if (typeof ResizeObserver !== "undefined") {
384
+ resizeObserver = new ResizeObserver(onResize);
385
+ resizeObserver.observe(document.documentElement);
386
+ } else {
387
+ window.addEventListener("resize", onResize);
388
+ }
389
+ window.addEventListener("orientationchange", onResize);
390
+ const onVisibility = () => {
391
+ if (deps.isDestroyed()) return;
392
+ if (document.hidden) deps.cancelInFlight();
393
+ };
394
+ document.addEventListener("visibilitychange", onVisibility);
395
+ return () => {
396
+ resizeObserver?.disconnect();
397
+ window.removeEventListener("resize", onResize);
398
+ window.removeEventListener("orientationchange", onResize);
399
+ document.removeEventListener("visibilitychange", onVisibility);
400
+ };
401
+ }
402
+ const contentTargets = (scroller) => {
403
+ if (!scroller) return [];
404
+ const hasSlot = typeof HTMLSlotElement !== "undefined";
405
+ const out = [];
406
+ for (const child of Array.from(scroller.children)) {
407
+ if (hasSlot && child instanceof HTMLSlotElement) {
408
+ for (const el of child.assignedElements()) out.push(el);
409
+ } else {
410
+ out.push(child);
411
+ }
412
+ }
413
+ return out;
414
+ };
415
+ function installFitObserver(deps) {
416
+ if (typeof ResizeObserver === "undefined") return () => {
417
+ };
418
+ if (!deps.hasFitSnap()) return () => {
419
+ };
420
+ let raf = 0;
421
+ const schedule = () => {
422
+ if (raf) return;
423
+ raf = requestAnimationFrame(() => {
424
+ raf = 0;
425
+ if (deps.isDestroyed() || deps.isDragging()) return;
426
+ deps.recompute();
427
+ });
428
+ };
429
+ const ro = new ResizeObserver(schedule);
430
+ ro.observe(deps.handle);
431
+ const scroller = deps.scrollContainer;
432
+ const observed = /* @__PURE__ */ new Set();
433
+ const slots = /* @__PURE__ */ new Set();
434
+ const hasSlot = typeof HTMLSlotElement !== "undefined";
435
+ const onSlotChange = () => resync();
436
+ const trackSlots = () => {
437
+ if (!scroller || !hasSlot) return;
438
+ for (const child of Array.from(scroller.children)) {
439
+ if (child instanceof HTMLSlotElement && !slots.has(child)) {
440
+ child.addEventListener("slotchange", onSlotChange);
441
+ slots.add(child);
442
+ }
443
+ }
444
+ };
445
+ const resync = () => {
446
+ trackSlots();
447
+ const targets = contentTargets(scroller);
448
+ for (const el of observed) {
449
+ if (!targets.includes(el)) {
450
+ ro.unobserve(el);
451
+ observed.delete(el);
452
+ }
453
+ }
454
+ for (const el of targets) {
455
+ if (!observed.has(el)) {
456
+ ro.observe(el);
457
+ observed.add(el);
458
+ }
459
+ }
460
+ schedule();
461
+ };
462
+ let mo;
463
+ if (scroller) {
464
+ resync();
465
+ if (typeof MutationObserver !== "undefined") {
466
+ mo = new MutationObserver(resync);
467
+ mo.observe(scroller, {
468
+ childList: true
469
+ });
470
+ }
471
+ }
472
+ return () => {
473
+ ro.disconnect();
474
+ mo?.disconnect();
475
+ for (const slot of slots) {
476
+ slot.removeEventListener("slotchange", onSlotChange);
477
+ }
478
+ if (raf) cancelAnimationFrame(raf);
479
+ };
480
+ }
481
+ function createMaxHeightController(deps) {
482
+ let maxHeightCap;
483
+ let maxHeightRaw;
484
+ const resolveCap = () => {
485
+ const raw = maxHeightRaw;
486
+ if (raw === void 0) {
487
+ maxHeightCap = void 0;
488
+ return;
489
+ }
490
+ if (typeof raw === "number") {
491
+ maxHeightCap = raw;
492
+ return;
493
+ }
494
+ if (typeof window === "undefined") return;
495
+ const measured = resolveSnap(raw, deps.mode);
496
+ maxHeightCap = measured > 0 ? measured : void 0;
497
+ };
498
+ const clampTo = () => {
499
+ if (maxHeightCap === void 0) return;
500
+ if (deps.getMaxAxisSize() > maxHeightCap) {
501
+ deps.setMaxAxisSize(maxHeightCap);
502
+ }
503
+ };
504
+ return {
505
+ setRadius(r) {
506
+ deps.element.style.setProperty("--bs-radius", typeof r === "number" ? `${r}px` : r);
507
+ },
508
+ setMaxHeight(h) {
509
+ const value = typeof h === "number" ? `${h}px` : h;
510
+ const axis = layoutAxis(deps.mode);
511
+ deps.element.style.setProperty(axis === "height" ? "max-height" : "max-width", value);
512
+ maxHeightRaw = h;
513
+ resolveCap();
514
+ deps.recompute();
515
+ },
516
+ resolveCap,
517
+ clampTo,
518
+ getCap: () => maxHeightCap
519
+ };
520
+ }
521
+ const measureSheetNatural = (element, content, vertical) => {
522
+ const axis = vertical ? "height" : "width";
523
+ const sheetStyle = element.style;
524
+ if (!content) {
525
+ const prev = sheetStyle[axis];
526
+ sheetStyle[axis] = "auto";
527
+ const poked2 = vertical ? element.offsetHeight : element.offsetWidth;
528
+ sheetStyle[axis] = prev;
529
+ return poked2;
530
+ }
531
+ const cs = content.style;
532
+ const prevSheet = sheetStyle[axis];
533
+ const prevFlex = cs.flex;
534
+ const prevContent = vertical ? cs.height : cs.width;
535
+ sheetStyle[axis] = "auto";
536
+ cs.flex = "none";
537
+ if (vertical) cs.height = "auto";
538
+ else cs.width = "auto";
539
+ const poked = vertical ? element.offsetHeight : element.offsetWidth;
540
+ const contentBox = vertical ? content.clientHeight : content.clientWidth;
541
+ const contentScroll = vertical ? content.scrollHeight : content.scrollWidth;
542
+ const natural = Math.max(poked, poked - contentBox + contentScroll);
543
+ sheetStyle[axis] = prevSheet;
544
+ cs.flex = prevFlex;
545
+ if (vertical) cs.height = prevContent;
546
+ else cs.width = prevContent;
547
+ return natural;
548
+ };
549
+ const containingExtent = (element, vertical) => {
550
+ const op = element.offsetParent;
551
+ if (op && op !== document.body && op !== document.documentElement) {
552
+ return vertical ? op.clientHeight : op.clientWidth;
553
+ }
554
+ return vertical ? window.innerHeight : window.innerWidth;
555
+ };
556
+ const measureFitSize = (deps) => {
557
+ const vertical = layoutAxis(deps.mode) === "height";
558
+ const natural = measureSheetNatural(deps.element, deps.scrollContainer, vertical);
559
+ const cap = deps.getMaxHeightCap();
560
+ const capped = cap !== void 0 ? Math.min(natural, cap) : natural;
561
+ if (typeof window === "undefined") return capped;
562
+ const viewport = containingExtent(deps.element, vertical);
563
+ return viewport > 0 ? Math.min(capped, viewport) : capped;
564
+ };
565
+ function installSliderKeyboard(deps) {
566
+ const isVerticalAxis = deps.mode === "bottom" || deps.mode === "top";
567
+ const stepUp = isVerticalAxis ? "ArrowUp" : "ArrowRight";
568
+ const stepDown = isVerticalAxis ? "ArrowDown" : "ArrowLeft";
569
+ const sheetExpandsKey = deps.mode === "top" ? stepDown : stepUp;
570
+ const sheetCollapsesKey = deps.mode === "top" ? stepUp : stepDown;
571
+ const onHandleKey = (e) => {
572
+ if (deps.isDestroyed()) return;
573
+ const allowed = deps.getAllowedIds();
574
+ const idx = allowed.indexOf(deps.getActiveId());
575
+ if (idx === -1) return;
576
+ if (e.key === sheetExpandsKey && idx < allowed.length - 1) {
577
+ e.preventDefault();
578
+ deps.snapTo(allowed[idx + 1]);
579
+ } else if (e.key === sheetCollapsesKey && idx > 0) {
580
+ e.preventDefault();
581
+ deps.snapTo(allowed[idx - 1]);
582
+ } else if (e.key === "Home") {
583
+ e.preventDefault();
584
+ deps.snapTo(allowed[0]);
585
+ } else if (e.key === "End") {
586
+ e.preventDefault();
587
+ deps.snapTo(allowed[allowed.length - 1]);
588
+ }
589
+ };
590
+ deps.handle.addEventListener("keydown", onHandleKey);
591
+ return () => deps.handle.removeEventListener("keydown", onHandleKey);
592
+ }
593
+ const SCRIM_PRESETS = Object.freeze({
594
+ subtle: {
595
+ color: "rgba(0,0,0,0.2)",
596
+ blur: void 0,
597
+ range: [
598
+ 0.3,
599
+ 1
600
+ ],
601
+ interactive: false
602
+ },
603
+ standard: {
604
+ color: "rgba(0,0,0,0.4)",
605
+ blur: void 0,
606
+ range: [
607
+ 0,
608
+ 1
609
+ ],
610
+ interactive: false
611
+ },
612
+ monitoring: {
613
+ color: "rgba(15,15,20,0.55)",
614
+ blur: "4px",
615
+ range: [
616
+ 0,
617
+ 1
618
+ ],
619
+ interactive: false
620
+ },
621
+ cinematic: {
622
+ color: "rgba(0,0,0,0.7)",
623
+ blur: "12px",
624
+ range: [
625
+ 0,
626
+ 1
627
+ ],
628
+ interactive: false
629
+ }
630
+ });
631
+ const SIZE_WRITE_EPSILON = 0.5;
632
+ const OPACITY_WRITE_EPSILON = 5e-3;
633
+ const POINTER_EVENTS_OPACITY_THRESHOLD = 0.05;
634
+ const RANGE_DIVISION_EPSILON = 1e-4;
635
+ class WriteSentinel {
636
+ constructor() {
637
+ __publicField(this, "last", -1);
638
+ }
639
+ shouldWrite(next, epsilon) {
640
+ if (Math.abs(next - this.last) <= epsilon) return false;
641
+ this.last = next;
642
+ return true;
643
+ }
644
+ invalidate() {
645
+ this.last = -1;
646
+ }
647
+ setLastWritten(value) {
648
+ this.last = value;
649
+ }
650
+ get value() {
651
+ return this.last;
652
+ }
653
+ }
654
+ function resolveSheetAnchoredStyle(mode, position, inset) {
655
+ const sizeOffset = `calc(var(--bs-size, 0px) + ${inset})`;
656
+ switch (mode) {
657
+ case "bottom":
658
+ if (position === "sheet-top-left") return {
659
+ bottom: sizeOffset,
660
+ left: inset
661
+ };
662
+ if (position === "sheet-top-right") return {
663
+ bottom: sizeOffset,
664
+ right: inset
665
+ };
666
+ return {
667
+ bottom: sizeOffset,
668
+ left: "50%",
669
+ transform: "translateX(-50%)"
670
+ };
671
+ case "top":
672
+ if (position === "sheet-top-left") return {
673
+ top: sizeOffset,
674
+ left: inset
675
+ };
676
+ if (position === "sheet-top-right") return {
677
+ top: sizeOffset,
678
+ right: inset
679
+ };
680
+ return {
681
+ top: sizeOffset,
682
+ left: "50%",
683
+ transform: "translateX(-50%)"
684
+ };
685
+ case "left":
686
+ if (position === "sheet-top-left") return {
687
+ left: sizeOffset,
688
+ top: inset
689
+ };
690
+ if (position === "sheet-top-right") return {
691
+ left: sizeOffset,
692
+ bottom: inset
693
+ };
694
+ return {
695
+ left: sizeOffset,
696
+ top: "50%",
697
+ transform: "translateY(-50%)"
698
+ };
699
+ case "right":
700
+ if (position === "sheet-top-left") return {
701
+ right: sizeOffset,
702
+ top: inset
703
+ };
704
+ if (position === "sheet-top-right") return {
705
+ right: sizeOffset,
706
+ bottom: inset
707
+ };
708
+ return {
709
+ right: sizeOffset,
710
+ top: "50%",
711
+ transform: "translateY(-50%)"
712
+ };
713
+ }
714
+ }
715
+ function applyOverlayPosition(ws, mode, position, inset) {
716
+ switch (position) {
717
+ case "top-left":
718
+ ws.top = inset;
719
+ ws.left = inset;
720
+ break;
721
+ case "top-center":
722
+ ws.top = inset;
723
+ ws.left = "50%";
724
+ ws.transform = "translateX(-50%)";
725
+ break;
726
+ case "top-right":
727
+ ws.top = inset;
728
+ ws.right = inset;
729
+ break;
730
+ case "center-left":
731
+ ws.top = "50%";
732
+ ws.left = inset;
733
+ ws.transform = "translateY(-50%)";
734
+ break;
735
+ case "center":
736
+ ws.top = "50%";
737
+ ws.left = "50%";
738
+ ws.transform = "translate(-50%, -50%)";
739
+ break;
740
+ case "center-right":
741
+ ws.top = "50%";
742
+ ws.right = inset;
743
+ ws.transform = "translateY(-50%)";
744
+ break;
745
+ case "bottom-left":
746
+ ws.bottom = inset;
747
+ ws.left = inset;
748
+ break;
749
+ case "bottom-center":
750
+ ws.bottom = inset;
751
+ ws.left = "50%";
752
+ ws.transform = "translateX(-50%)";
753
+ break;
754
+ case "bottom-right":
755
+ ws.bottom = inset;
756
+ ws.right = inset;
757
+ break;
758
+ case "sheet-top-left":
759
+ case "sheet-top-center":
760
+ case "sheet-top-right": {
761
+ const style = resolveSheetAnchoredStyle(mode, position, inset);
762
+ if (style.top !== void 0) ws.top = style.top;
763
+ if (style.bottom !== void 0) ws.bottom = style.bottom;
764
+ if (style.left !== void 0) ws.left = style.left;
765
+ if (style.right !== void 0) ws.right = style.right;
766
+ if (style.transform !== void 0) ws.transform = style.transform;
767
+ break;
768
+ }
769
+ }
770
+ }
771
+ const VALID_CSS_LENGTH = /^-?\d+(?:\.\d+)?(?:px|em|rem|%)$/;
772
+ const DEFAULT_SCRIM_DIM = "rgba(0, 0, 0, 0.4)";
773
+ class ScrimController {
774
+ constructor(deps, opts) {
775
+ __publicField(this, "screenComponent");
776
+ __publicField(this, "backdrop");
777
+ __publicField(this, "mode");
778
+ __publicField(this, "isDestroyed");
779
+ __publicField(this, "isTopSheet");
780
+ __publicField(this, "getAllowedIds");
781
+ __publicField(this, "getResolvedSnaps");
782
+ __publicField(this, "snapToFn");
783
+ __publicField(this, "closeFn");
784
+ __publicField(this, "scrimInteractive", false);
785
+ __publicField(this, "scrimMode");
786
+ __publicField(this, "backdropRange");
787
+ __publicField(this, "screenRange");
788
+ __publicField(this, "scrimEnabled", true);
789
+ __publicField(this, "savedScrimRanges", null);
790
+ __publicField(this, "scrimTapToCloseEnabled", false);
791
+ __publicField(this, "detachScrimTap", null);
792
+ __publicField(this, "scrimOverlayTeardown", null);
793
+ __publicField(this, "backdropOpacitySentinel", new WriteSentinel());
794
+ __publicField(this, "screenOpacitySentinel", new WriteSentinel());
795
+ __publicField(this, "lastBackdropPointer", "");
796
+ __publicField(this, "lastScreenDisplay", "");
797
+ this.screenComponent = deps.screenComponent;
798
+ this.backdrop = deps.backdrop;
799
+ this.mode = deps.mode;
800
+ this.isDestroyed = deps.isDestroyed;
801
+ this.isTopSheet = deps.isTopSheet;
802
+ this.getAllowedIds = deps.getAllowedIds;
803
+ this.getResolvedSnaps = deps.getResolvedSnaps;
804
+ this.snapToFn = deps.snapTo;
805
+ this.closeFn = deps.close;
806
+ this.scrimMode = opts.scrimMode ?? "full";
807
+ const preset = opts.scrimPreset ? SCRIM_PRESETS[opts.scrimPreset] : null;
808
+ this.backdropRange = opts.backdropRange ?? preset?.range ?? [
809
+ 0,
810
+ 1
811
+ ];
812
+ this.screenRange = opts.screenRange ?? preset?.range ?? [
813
+ 0,
814
+ 1
815
+ ];
816
+ this.scrimTapToCloseEnabled = opts.scrimTapToClose ?? false;
817
+ const explicitColor = opts.scrimColor ?? preset?.color;
818
+ const effectiveColor = explicitColor ?? (this.scrimMode !== "off" && !this.backdrop ? DEFAULT_SCRIM_DIM : void 0);
819
+ if (this.screenComponent) {
820
+ this.applyScrimStyles({
821
+ color: effectiveColor,
822
+ blur: opts.scrimBlur ?? preset?.blur,
823
+ interactive: opts.scrimInteractive ?? preset?.interactive ?? false
824
+ });
825
+ if (this.scrimMode === "above-sheet") {
826
+ this.applyAboveSheetInset();
827
+ } else if (this.scrimMode === "off") {
828
+ const s = this.screenComponent.style;
829
+ s.opacity = "0";
830
+ s.display = "none";
831
+ this.screenOpacitySentinel.setLastWritten(0);
832
+ this.lastScreenDisplay = "none";
833
+ }
834
+ }
835
+ }
836
+ invalidateOpacityCache() {
837
+ this.backdropOpacitySentinel.invalidate();
838
+ this.screenOpacitySentinel.invalidate();
839
+ }
840
+ attach() {
841
+ if (this.scrimTapToCloseEnabled) {
842
+ this.setScrimTapToClose(true);
843
+ }
844
+ }
845
+ applyOpacity(progress, progressChanged) {
846
+ if (!progressChanged && this.backdropOpacitySentinel.value !== -1 && this.screenOpacitySentinel.value !== -1) {
847
+ return;
848
+ }
849
+ if (this.backdrop && this.isTopSheet()) {
850
+ const [s, e] = this.backdropRange;
851
+ const range = Math.max(e - s, RANGE_DIVISION_EPSILON);
852
+ const backdropOpacity = Math.min(Math.max((progress - s) / range, 0), 1);
853
+ if (this.backdropOpacitySentinel.shouldWrite(backdropOpacity, OPACITY_WRITE_EPSILON)) {
854
+ this.backdrop.style.opacity = String(backdropOpacity);
855
+ }
856
+ const nextPointer = backdropOpacity > POINTER_EVENTS_OPACITY_THRESHOLD ? "auto" : "none";
857
+ if (nextPointer !== this.lastBackdropPointer) {
858
+ this.backdrop.style.pointerEvents = nextPointer;
859
+ this.lastBackdropPointer = nextPointer;
860
+ }
861
+ }
862
+ if (this.screenComponent && this.scrimMode !== "off") {
863
+ const [ss, se] = this.screenRange;
864
+ const screenOpacity = Math.min(1, Math.max(0, (progress - ss) / Math.max(RANGE_DIVISION_EPSILON, se - ss)));
865
+ if (this.screenOpacitySentinel.shouldWrite(screenOpacity, OPACITY_WRITE_EPSILON)) {
866
+ this.screenComponent.style.opacity = String(screenOpacity);
867
+ }
868
+ const nextDisplay = screenOpacity > 0 ? "" : "none";
869
+ if (nextDisplay !== this.lastScreenDisplay) {
870
+ this.screenComponent.style.display = nextDisplay;
871
+ this.lastScreenDisplay = nextDisplay;
872
+ }
873
+ }
874
+ }
875
+ setBackdropRange(range, applySize) {
876
+ if (this.isDestroyed()) return;
877
+ this.backdropRange = range;
878
+ this.invalidateOpacityCache();
879
+ applySize();
880
+ }
881
+ setScreenRange(range, applySize) {
882
+ if (this.isDestroyed()) return;
883
+ this.screenRange = range;
884
+ this.invalidateOpacityCache();
885
+ applySize();
886
+ }
887
+ setScrimColor(color) {
888
+ if (this.isDestroyed()) return;
889
+ this.applyScrimStyles({
890
+ color: color === void 0 ? null : color
891
+ });
892
+ }
893
+ setScrimBlur(blur) {
894
+ if (this.isDestroyed()) return;
895
+ this.applyScrimStyles({
896
+ blur: blur === void 0 ? null : blur
897
+ });
898
+ }
899
+ setScrimInteractive(interactive) {
900
+ if (this.isDestroyed()) return;
901
+ this.applyScrimStyles({
902
+ interactive
903
+ });
904
+ }
905
+ setScrimMode(mode, applySize) {
906
+ if (this.isDestroyed()) return;
907
+ if (mode === this.scrimMode) return;
908
+ this.invalidateOpacityCache();
909
+ const previous = this.scrimMode;
910
+ this.scrimMode = mode;
911
+ if (this.screenComponent) {
912
+ const s = this.screenComponent.style;
913
+ if (previous === "above-sheet" && mode !== "above-sheet") {
914
+ s.position = "";
915
+ s.inset = "";
916
+ s.pointerEvents = "";
917
+ }
918
+ if (mode === "above-sheet") {
919
+ this.applyAboveSheetInset();
920
+ } else if (mode === "off") {
921
+ s.opacity = "0";
922
+ s.display = "none";
923
+ this.screenOpacitySentinel.setLastWritten(0);
924
+ this.lastScreenDisplay = "none";
925
+ } else {
926
+ if (this.lastScreenDisplay === "none") {
927
+ s.display = "";
928
+ this.lastScreenDisplay = "";
929
+ }
930
+ }
931
+ }
932
+ applySize();
933
+ }
934
+ setScrimTapToClose(enabled) {
935
+ if (this.isDestroyed()) return;
936
+ if (!this.screenComponent) return;
937
+ const installed = this.detachScrimTap !== null;
938
+ if (enabled === installed) return;
939
+ if (enabled) {
940
+ const target = this.screenComponent;
941
+ if (target.style.pointerEvents === "none") {
942
+ target.style.pointerEvents = "auto";
943
+ }
944
+ const onClick = () => {
945
+ if (this.isDestroyed()) return;
946
+ if (this.closeFn) {
947
+ this.closeFn();
948
+ return;
949
+ }
950
+ const allowed = this.getAllowedIds();
951
+ const snaps = this.getResolvedSnaps();
952
+ const fallback = allowed.find((id) => {
953
+ const snap = findById(id, snaps);
954
+ return snap !== null && snap.size === 0;
955
+ });
956
+ if (fallback) this.snapToFn(fallback);
957
+ };
958
+ target.addEventListener("click", onClick);
959
+ this.detachScrimTap = () => target.removeEventListener("click", onClick);
960
+ this.scrimTapToCloseEnabled = true;
961
+ } else {
962
+ this.detachScrimTap?.();
963
+ this.detachScrimTap = null;
964
+ this.scrimTapToCloseEnabled = false;
965
+ }
966
+ }
967
+ setScrimEnabled(enabled, applySize) {
968
+ if (this.isDestroyed()) return;
969
+ if (enabled === this.scrimEnabled) return;
970
+ this.scrimEnabled = enabled;
971
+ this.invalidateOpacityCache();
972
+ if (!enabled) {
973
+ this.savedScrimRanges = {
974
+ screen: this.screenRange,
975
+ backdrop: this.backdropRange
976
+ };
977
+ this.screenRange = [
978
+ 1,
979
+ 1
980
+ ];
981
+ this.backdropRange = [
982
+ 1,
983
+ 1
984
+ ];
985
+ } else {
986
+ this.screenRange = this.savedScrimRanges?.screen ?? [
987
+ 0,
988
+ 1
989
+ ];
990
+ this.backdropRange = this.savedScrimRanges?.backdrop ?? [
991
+ 0,
992
+ 1
993
+ ];
994
+ this.savedScrimRanges = null;
995
+ }
996
+ applySize();
997
+ }
998
+ setScrim(opts, applySize) {
999
+ if (this.isDestroyed()) return;
1000
+ if (opts.preset) {
1001
+ const cfg = SCRIM_PRESETS[opts.preset];
1002
+ this.applyScrimStyles({
1003
+ color: cfg.color,
1004
+ blur: cfg.blur ?? null,
1005
+ interactive: cfg.interactive
1006
+ });
1007
+ if (!this.scrimEnabled && this.savedScrimRanges) {
1008
+ this.savedScrimRanges = {
1009
+ screen: cfg.range,
1010
+ backdrop: cfg.range
1011
+ };
1012
+ } else {
1013
+ this.screenRange = cfg.range;
1014
+ this.backdropRange = cfg.range;
1015
+ }
1016
+ }
1017
+ const noop = () => {
1018
+ };
1019
+ if (opts.mode !== void 0) this.setScrimMode(opts.mode, noop);
1020
+ if (opts.enabled !== void 0) this.setScrimEnabled(opts.enabled, noop);
1021
+ if (opts.tapToClose !== void 0) this.setScrimTapToClose(opts.tapToClose);
1022
+ this.applyScrimStyles({
1023
+ color: opts.color,
1024
+ blur: opts.blur,
1025
+ interactive: opts.interactive
1026
+ });
1027
+ if (opts.range !== void 0) {
1028
+ if (!this.scrimEnabled && this.savedScrimRanges) {
1029
+ this.savedScrimRanges = {
1030
+ screen: opts.range,
1031
+ backdrop: this.savedScrimRanges.backdrop
1032
+ };
1033
+ } else {
1034
+ this.screenRange = opts.range;
1035
+ }
1036
+ }
1037
+ this.invalidateOpacityCache();
1038
+ applySize();
1039
+ }
1040
+ setScrimOverlay(opts) {
1041
+ if (this.isDestroyed()) return () => {
1042
+ };
1043
+ if (!this.screenComponent) return () => {
1044
+ };
1045
+ this.scrimOverlayTeardown?.();
1046
+ const screen = this.screenComponent;
1047
+ const insetRaw = opts.inset ?? "16px";
1048
+ const inset = VALID_CSS_LENGTH.test(insetRaw) ? insetRaw : "16px";
1049
+ if (inset !== insetRaw) {
1050
+ devWarn(`[bottom-sheet] setScrimOverlay: invalid inset ${JSON.stringify(insetRaw)}; expected a CSS length like "16px" / "1rem" / "5%". Falling back to "16px".`);
1051
+ }
1052
+ if (opts.children.ownerDocument && opts.children.ownerDocument !== screen.ownerDocument) {
1053
+ devWarn("[bottom-sheet] setScrimOverlay: children belong to a different document than the scrim. Cross-document adoption can drop event listeners; construct children via the same document as the sheet root.");
1054
+ }
1055
+ const position = opts.position ?? "top-right";
1056
+ const interactive = opts.interactive ?? true;
1057
+ const wrapper = screen.ownerDocument.createElement("div");
1058
+ wrapper.className = "bs-scrim-overlay";
1059
+ const ws = wrapper.style;
1060
+ ws.position = "absolute";
1061
+ applyOverlayPosition(ws, this.mode, position, inset);
1062
+ if (interactive) {
1063
+ ws.pointerEvents = "auto";
1064
+ }
1065
+ wrapper.appendChild(opts.children);
1066
+ const host = screen.parentElement;
1067
+ if (!host) {
1068
+ devWarn("[bottom-sheet] setScrimOverlay: scrim element has no parent, skipping overlay injection. Mount the sheet root before calling setScrimOverlay.");
1069
+ return () => {
1070
+ };
1071
+ }
1072
+ host.appendChild(wrapper);
1073
+ const teardown = () => {
1074
+ if (wrapper.parentNode) wrapper.parentNode.removeChild(wrapper);
1075
+ if (this.scrimOverlayTeardown === teardown) {
1076
+ this.scrimOverlayTeardown = null;
1077
+ }
1078
+ };
1079
+ this.scrimOverlayTeardown = teardown;
1080
+ return teardown;
1081
+ }
1082
+ destroy() {
1083
+ this.detachScrimTap?.();
1084
+ this.detachScrimTap = null;
1085
+ this.scrimOverlayTeardown?.();
1086
+ this.scrimOverlayTeardown = null;
1087
+ if (this.screenComponent) {
1088
+ const s = this.screenComponent.style;
1089
+ s.opacity = "";
1090
+ s.display = "";
1091
+ s.position = "";
1092
+ s.inset = "";
1093
+ s.pointerEvents = "";
1094
+ }
1095
+ if (this.backdrop) {
1096
+ this.backdrop.style.opacity = "";
1097
+ this.backdrop.style.pointerEvents = "";
1098
+ }
1099
+ this.invalidateOpacityCache();
1100
+ this.lastBackdropPointer = "";
1101
+ this.lastScreenDisplay = "";
1102
+ }
1103
+ applyScrimStyles(opts) {
1104
+ if (!this.screenComponent) return;
1105
+ const s = this.screenComponent.style;
1106
+ if (opts.color !== void 0) {
1107
+ s.background = opts.color === null ? "" : opts.color;
1108
+ }
1109
+ if (opts.blur !== void 0) {
1110
+ const w = s;
1111
+ if (opts.blur === null) {
1112
+ s.backdropFilter = "";
1113
+ w.webkitBackdropFilter = "";
1114
+ } else {
1115
+ const filter = `blur(${opts.blur})`;
1116
+ s.backdropFilter = filter;
1117
+ w.webkitBackdropFilter = filter;
1118
+ }
1119
+ }
1120
+ if (opts.interactive !== void 0) {
1121
+ this.scrimInteractive = opts.interactive;
1122
+ s.pointerEvents = opts.interactive ? "auto" : "none";
1123
+ }
1124
+ }
1125
+ applyAboveSheetInset() {
1126
+ if (!this.screenComponent) return;
1127
+ const insetByMode = {
1128
+ bottom: "0 0 var(--bs-size) 0",
1129
+ top: "var(--bs-size) 0 0 0",
1130
+ left: "0 0 0 var(--bs-size)",
1131
+ right: "0 var(--bs-size) 0 0"
1132
+ };
1133
+ const s = this.screenComponent.style;
1134
+ s.position = "fixed";
1135
+ s.inset = insetByMode[this.mode];
1136
+ s.pointerEvents = this.scrimInteractive || this.scrimTapToCloseEnabled ? "auto" : "none";
1137
+ }
1138
+ }
1139
+ const easeOutBack = (t) => {
1140
+ const c1 = 1.40158;
1141
+ const c3 = c1 + 1;
1142
+ return 1 + c3 * Math.pow(t - 1, 3) + c1 * Math.pow(t - 1, 2);
1143
+ };
1144
+ const easeOutCubic = (t) => 1 - Math.pow(1 - t, 3);
1145
+ const easeLinear = (t) => t;
1146
+ const easeOutQuint = (t) => 1 - Math.pow(1 - t, 5);
1147
+ const tween = ({ from, to, duration, easing = easeOutCubic, onUpdate }) => {
1148
+ let cancelled = false;
1149
+ let rafId = 0;
1150
+ let resolveFn = null;
1151
+ const start = performance.now();
1152
+ const promise = new Promise((resolve) => {
1153
+ resolveFn = resolve;
1154
+ if (duration <= 0) {
1155
+ onUpdate(to);
1156
+ resolve();
1157
+ return;
1158
+ }
1159
+ const step = (now) => {
1160
+ if (cancelled) return;
1161
+ const elapsed = now - start;
1162
+ const t = Math.min(elapsed / duration, 1);
1163
+ const value = from + (to - from) * easing(t);
1164
+ onUpdate(value);
1165
+ if (t < 1) {
1166
+ rafId = requestAnimationFrame(step);
1167
+ } else {
1168
+ resolve();
1169
+ }
1170
+ };
1171
+ rafId = requestAnimationFrame(step);
1172
+ });
1173
+ return {
1174
+ cancel: () => {
1175
+ cancelled = true;
1176
+ cancelAnimationFrame(rafId);
1177
+ resolveFn?.();
1178
+ },
1179
+ promise
1180
+ };
1181
+ };
1182
+ const prefersReducedMotion = () => {
1183
+ if (typeof window === "undefined" || !window.matchMedia) return false;
1184
+ return window.matchMedia("(prefers-reduced-motion: reduce)").matches;
1185
+ };
1186
+ const DEFAULT_SPRING = {
1187
+ stiffness: 220,
1188
+ damping: 26,
1189
+ mass: 1,
1190
+ restDelta: 0.5,
1191
+ restSpeed: 0.5
1192
+ };
1193
+ const MAX_DT = 1 / 30;
1194
+ const runSpring = ({ from, to, velocity = 0, config, onUpdate }) => {
1195
+ const cfg = {
1196
+ ...DEFAULT_SPRING,
1197
+ ...config
1198
+ };
1199
+ let cancelled = false;
1200
+ let rafId = 0;
1201
+ let resolveFn = null;
1202
+ let x = from;
1203
+ let v = velocity;
1204
+ let lastTime = performance.now();
1205
+ const promise = new Promise((resolve) => {
1206
+ resolveFn = resolve;
1207
+ const step = (now) => {
1208
+ if (cancelled) return;
1209
+ const dt = Math.min((now - lastTime) / 1e3, MAX_DT);
1210
+ lastTime = now;
1211
+ const steps = Math.max(1, Math.ceil(dt * 240));
1212
+ const subDt = dt / steps;
1213
+ let stepsLeft = steps;
1214
+ while (stepsLeft-- > 0) {
1215
+ const force = -cfg.stiffness * (x - to) - cfg.damping * v;
1216
+ const accel = force / cfg.mass;
1217
+ v += accel * subDt;
1218
+ x += v * subDt;
1219
+ }
1220
+ if (!Number.isFinite(x) || !Number.isFinite(v)) {
1221
+ onUpdate(to, 0);
1222
+ resolve();
1223
+ return;
1224
+ }
1225
+ onUpdate(x, v);
1226
+ const isAtRest = Math.abs(to - x) < (cfg.restDelta ?? 0.5) && Math.abs(v) < (cfg.restSpeed ?? 0.5);
1227
+ if (isAtRest) {
1228
+ onUpdate(to, 0);
1229
+ resolve();
1230
+ } else {
1231
+ rafId = requestAnimationFrame(step);
1232
+ }
1233
+ };
1234
+ rafId = requestAnimationFrame(step);
1235
+ });
1236
+ return {
1237
+ cancel: () => {
1238
+ cancelled = true;
1239
+ cancelAnimationFrame(rafId);
1240
+ resolveFn?.();
1241
+ },
1242
+ promise
1243
+ };
1244
+ };
1245
+ const DEFAULT_DURATION$2 = 220;
1246
+ function resolveAnimationPreset(animation) {
1247
+ switch (animation) {
1248
+ case "ios-spring":
1249
+ return {
1250
+ kind: "spring",
1251
+ spring: {
1252
+ stiffness: 300,
1253
+ damping: 30,
1254
+ mass: 1
1255
+ }
1256
+ };
1257
+ case "material-bounce":
1258
+ return {
1259
+ kind: "spring",
1260
+ spring: {
1261
+ stiffness: 200,
1262
+ damping: 22,
1263
+ mass: 1
1264
+ }
1265
+ };
1266
+ case "linear":
1267
+ return {
1268
+ kind: "tween",
1269
+ duration: DEFAULT_DURATION$2,
1270
+ easing: easeLinear
1271
+ };
1272
+ case "snappy":
1273
+ return {
1274
+ kind: "tween",
1275
+ duration: 180,
1276
+ easing: easeOutQuint
1277
+ };
1278
+ case "tween":
1279
+ return {
1280
+ kind: "tween"
1281
+ };
1282
+ case "spring":
1283
+ case void 0:
1284
+ default:
1285
+ return {
1286
+ kind: "spring"
1287
+ };
1288
+ }
1289
+ }
1290
+ const SIM_HZ = 240;
1291
+ const SAMPLE_STRIDE = 4;
1292
+ const MAX_SIM_MS = 3e3;
1293
+ function sampleSpringSettle(from, to, velocityPxPerS, config) {
1294
+ const cfg = {
1295
+ ...DEFAULT_SPRING,
1296
+ ...config
1297
+ };
1298
+ const subDt = 1 / SIM_HZ;
1299
+ const stepMs = SAMPLE_STRIDE / SIM_HZ * 1e3;
1300
+ const maxSteps = Math.ceil(MAX_SIM_MS / 1e3 * SIM_HZ);
1301
+ const values = [
1302
+ from
1303
+ ];
1304
+ let x = from;
1305
+ let v = velocityPxPerS;
1306
+ for (let i = 1; i <= maxSteps; i += 1) {
1307
+ const force = -cfg.stiffness * (x - to) - cfg.damping * v;
1308
+ v += force / cfg.mass * subDt;
1309
+ x += v * subDt;
1310
+ if (!Number.isFinite(x) || !Number.isFinite(v)) {
1311
+ values.push(to);
1312
+ break;
1313
+ }
1314
+ if (i % SAMPLE_STRIDE === 0) values.push(x);
1315
+ const atRest = Math.abs(to - x) < (cfg.restDelta ?? 0.5) && Math.abs(v) < (cfg.restSpeed ?? 0.5);
1316
+ if (atRest) break;
1317
+ }
1318
+ if (values[values.length - 1] !== to) values.push(to);
1319
+ return {
1320
+ values,
1321
+ stepMs,
1322
+ durationMs: (values.length - 1) * stepMs
1323
+ };
1324
+ }
1325
+ function sampleTweenSettle(from, to, durationMs, easing) {
1326
+ const count = Math.max(2, Math.min(60, Math.round(durationMs / 16)));
1327
+ const values = [];
1328
+ for (let i = 0; i <= count; i += 1) {
1329
+ const t = i / count;
1330
+ values.push(from + (to - from) * easing(t));
1331
+ }
1332
+ values[values.length - 1] = to;
1333
+ return {
1334
+ values,
1335
+ stepMs: durationMs / count,
1336
+ durationMs
1337
+ };
1338
+ }
1339
+ const DEFAULT_DURATION$1 = 220;
1340
+ const VELOCITY_PX_PER_S = 1e3;
1341
+ class AnimationRunner {
1342
+ constructor(deps, opts) {
1343
+ __publicField(this, "element");
1344
+ __publicField(this, "getRootEl");
1345
+ __publicField(this, "applySizeFn");
1346
+ __publicField(this, "getSize");
1347
+ __publicField(this, "isDragging");
1348
+ __publicField(this, "applyAux");
1349
+ __publicField(this, "getTransformFor");
1350
+ __publicField(this, "settleWaapi");
1351
+ __publicField(this, "currentWaapi", null);
1352
+ __publicField(this, "animationKind");
1353
+ __publicField(this, "springConfig");
1354
+ __publicField(this, "duration");
1355
+ __publicField(this, "easing");
1356
+ __publicField(this, "respectReducedMotion");
1357
+ __publicField(this, "reducedMotion", false);
1358
+ __publicField(this, "detachReducedMotion", null);
1359
+ __publicField(this, "viewTransitionsAvailable");
1360
+ __publicField(this, "currentTween", null);
1361
+ __publicField(this, "currentSpring", null);
1362
+ this.element = deps.element;
1363
+ this.getRootEl = deps.getRootEl;
1364
+ this.applySizeFn = deps.applySize;
1365
+ this.getSize = deps.getSize;
1366
+ this.isDragging = deps.isDragging;
1367
+ this.applyAux = deps.applyAux;
1368
+ this.getTransformFor = deps.getTransformFor;
1369
+ this.settleWaapi = opts.settleAnimation === "waapi";
1370
+ const preset = resolveAnimationPreset(opts.animation);
1371
+ this.animationKind = preset.kind;
1372
+ this.duration = opts.duration ?? preset.duration ?? DEFAULT_DURATION$1;
1373
+ this.easing = opts.easing ?? preset.easing ?? easeOutBack;
1374
+ this.springConfig = {
1375
+ ...preset.spring ?? {},
1376
+ ...opts.spring ?? {}
1377
+ };
1378
+ this.respectReducedMotion = opts.respectReducedMotion ?? true;
1379
+ const viewTransitionsEnabled = opts.viewTransitions ?? false;
1380
+ this.viewTransitionsAvailable = viewTransitionsEnabled && typeof document !== "undefined" && typeof document.startViewTransition === "function";
1381
+ if (this.respectReducedMotion && typeof window !== "undefined" && typeof window.matchMedia === "function") {
1382
+ const mq = window.matchMedia("(prefers-reduced-motion: reduce)");
1383
+ this.reducedMotion = mq.matches;
1384
+ const onMqChange = (e) => {
1385
+ this.reducedMotion = e.matches;
1386
+ };
1387
+ if (typeof mq.addEventListener === "function") {
1388
+ mq.addEventListener("change", onMqChange);
1389
+ this.detachReducedMotion = () => mq.removeEventListener("change", onMqChange);
1390
+ } else if (typeof mq.addListener === "function") {
1391
+ mq.addListener(onMqChange);
1392
+ this.detachReducedMotion = () => mq.removeListener(onMqChange);
1393
+ }
1394
+ }
1395
+ }
1396
+ get isAnimating() {
1397
+ return this.currentTween !== null || this.currentSpring !== null || this.currentWaapi !== null;
1398
+ }
1399
+ cancel() {
1400
+ this.currentTween?.cancel();
1401
+ this.currentSpring?.cancel();
1402
+ if (this.currentWaapi) {
1403
+ const entry = this.currentWaapi;
1404
+ this.currentWaapi = null;
1405
+ entry.stop();
1406
+ try {
1407
+ entry.anim.cancel();
1408
+ } catch {
1409
+ }
1410
+ }
1411
+ }
1412
+ async animateTo(target, velocityPxPerMs) {
1413
+ if (target === this.getSize()) {
1414
+ if (!this.isAnimating && !this.isDragging()) {
1415
+ this.element.style.willChange = "";
1416
+ }
1417
+ return;
1418
+ }
1419
+ this.currentTween?.cancel();
1420
+ this.currentSpring?.cancel();
1421
+ this.getRootEl()?.setAttribute("data-animating", "true");
1422
+ this.element.style.willChange = "transform";
1423
+ if (this.reducedMotion) {
1424
+ this.applySizeFn(target);
1425
+ this.getRootEl()?.removeAttribute("data-animating");
1426
+ if (!this.isDragging()) this.element.style.willChange = "";
1427
+ return;
1428
+ }
1429
+ if (this.settleWaapi && this.applyAux && this.getTransformFor && typeof this.element.animate === "function") {
1430
+ const samples = this.animationKind === "spring" ? sampleSpringSettle(this.getSize(), target, velocityPxPerMs * VELOCITY_PX_PER_S, this.springConfig) : sampleTweenSettle(this.getSize(), target, this.duration, this.easing);
1431
+ if (samples.values.length >= 2 && samples.durationMs > 0) {
1432
+ await this.runWaapiSettle(samples, target);
1433
+ if (!this.isAnimating) {
1434
+ this.getRootEl()?.removeAttribute("data-animating");
1435
+ if (!this.isDragging()) this.element.style.willChange = "";
1436
+ }
1437
+ return;
1438
+ }
1439
+ }
1440
+ if (this.animationKind === "spring") {
1441
+ const spring = runSpring({
1442
+ from: this.getSize(),
1443
+ to: target,
1444
+ velocity: velocityPxPerMs * VELOCITY_PX_PER_S,
1445
+ config: this.springConfig,
1446
+ onUpdate: (v) => this.applySizeFn(v)
1447
+ });
1448
+ this.currentSpring = spring;
1449
+ await spring.promise;
1450
+ if (this.currentSpring === spring) this.currentSpring = null;
1451
+ } else {
1452
+ const tw = tween({
1453
+ from: this.getSize(),
1454
+ to: target,
1455
+ duration: this.duration,
1456
+ easing: this.easing,
1457
+ onUpdate: (v) => this.applySizeFn(v)
1458
+ });
1459
+ this.currentTween = tw;
1460
+ await tw.promise;
1461
+ if (this.currentTween === tw) this.currentTween = null;
1462
+ }
1463
+ if (!this.isAnimating) {
1464
+ this.getRootEl()?.removeAttribute("data-animating");
1465
+ if (!this.isDragging()) this.element.style.willChange = "";
1466
+ }
1467
+ }
1468
+ async runWaapiSettle(samples, target) {
1469
+ const applyAux = this.applyAux;
1470
+ const getTransformFor = this.getTransformFor;
1471
+ const frames = samples.values.map((v) => ({
1472
+ transform: getTransformFor(v)
1473
+ }));
1474
+ const anim = this.element.animate(frames, {
1475
+ duration: samples.durationMs,
1476
+ easing: "linear",
1477
+ fill: "forwards"
1478
+ });
1479
+ let stopped = false;
1480
+ let auxRaf = 0;
1481
+ const start = performance.now();
1482
+ const lastIdx = samples.values.length - 1;
1483
+ const auxTick = () => {
1484
+ if (stopped) return;
1485
+ const elapsed = performance.now() - start;
1486
+ const idx = Math.min(Math.floor(elapsed / samples.stepMs), lastIdx);
1487
+ applyAux(samples.values[idx]);
1488
+ auxRaf = requestAnimationFrame(auxTick);
1489
+ };
1490
+ auxRaf = requestAnimationFrame(auxTick);
1491
+ const entry = {
1492
+ anim,
1493
+ stop: () => {
1494
+ stopped = true;
1495
+ cancelAnimationFrame(auxRaf);
1496
+ }
1497
+ };
1498
+ this.currentWaapi = entry;
1499
+ try {
1500
+ await anim.finished;
1501
+ } catch {
1502
+ }
1503
+ entry.stop();
1504
+ const wasCurrent = this.currentWaapi === entry;
1505
+ if (wasCurrent) this.currentWaapi = null;
1506
+ if (wasCurrent && anim.playState === "finished") {
1507
+ this.applySizeFn(target);
1508
+ try {
1509
+ anim.cancel();
1510
+ } catch {
1511
+ }
1512
+ }
1513
+ }
1514
+ destroy() {
1515
+ this.detachReducedMotion?.();
1516
+ this.detachReducedMotion = null;
1517
+ this.currentTween = null;
1518
+ this.currentSpring = null;
1519
+ if (this.currentWaapi) {
1520
+ const entry = this.currentWaapi;
1521
+ this.currentWaapi = null;
1522
+ entry.stop();
1523
+ try {
1524
+ entry.anim.cancel();
1525
+ } catch {
1526
+ }
1527
+ }
1528
+ }
1529
+ }
1530
+ const FOCUSABLE = [
1531
+ "a[href]",
1532
+ "area[href]",
1533
+ "button:not([disabled])",
1534
+ "input:not([disabled]):not([type='hidden'])",
1535
+ "select:not([disabled])",
1536
+ "textarea:not([disabled])",
1537
+ "iframe",
1538
+ "object",
1539
+ "embed",
1540
+ "[contenteditable='true']",
1541
+ "[tabindex]:not([tabindex='-1'])"
1542
+ ].join(",");
1543
+ const isVisible = (el) => {
1544
+ if (typeof el.checkVisibility === "function") {
1545
+ return el.checkVisibility({
1546
+ checkOpacity: false,
1547
+ checkVisibilityCSS: true
1548
+ });
1549
+ }
1550
+ return el.offsetParent !== null;
1551
+ };
1552
+ const collectFocusables = (root) => {
1553
+ const out = Array.from(root.querySelectorAll(FOCUSABLE));
1554
+ if (typeof HTMLSlotElement !== "undefined") {
1555
+ for (const slot of Array.from(root.querySelectorAll("slot"))) {
1556
+ for (const assigned of slot.assignedElements()) {
1557
+ if (assigned.matches(FOCUSABLE)) out.push(assigned);
1558
+ out.push(...collectFocusables(assigned));
1559
+ }
1560
+ }
1561
+ }
1562
+ return out;
1563
+ };
1564
+ const focusables = (root) => collectFocusables(root).filter((el) => !el.hasAttribute("inert") && isVisible(el) && !el.matches("[aria-hidden='true']"));
1565
+ const containsComposed = (container, node) => {
1566
+ let n = node;
1567
+ while (n) {
1568
+ if (n === container) return true;
1569
+ const slot = n.assignedSlot;
1570
+ if (slot) {
1571
+ n = slot;
1572
+ continue;
1573
+ }
1574
+ const parent = n.parentNode;
1575
+ n = parent instanceof ShadowRoot ? parent.host : parent;
1576
+ }
1577
+ return false;
1578
+ };
1579
+ const trapStack = [];
1580
+ const installFocusTrap = (container, options = {}) => {
1581
+ if (typeof document === "undefined") return () => {
1582
+ };
1583
+ const token = {};
1584
+ trapStack.push(token);
1585
+ const isActive = () => trapStack[trapStack.length - 1] === token;
1586
+ const previouslyFocused = document.activeElement;
1587
+ const focusInitial = () => {
1588
+ const spec = options.initialFocus;
1589
+ if (spec === false) {
1590
+ if (!container.hasAttribute("tabindex")) container.tabIndex = -1;
1591
+ container.focus({
1592
+ preventScroll: true
1593
+ });
1594
+ return;
1595
+ }
1596
+ const initial = typeof spec === "string" ? container.querySelector(spec) : spec ?? focusables(container)[0] ?? container;
1597
+ initial?.focus({
1598
+ preventScroll: true
1599
+ });
1600
+ };
1601
+ focusInitial();
1602
+ const handleKey = (e) => {
1603
+ if (!isActive()) return;
1604
+ if (e.key === "Escape" && options.onEscape) {
1605
+ options.onEscape();
1606
+ return;
1607
+ }
1608
+ if (e.key !== "Tab") return;
1609
+ const list = focusables(container);
1610
+ if (list.length === 0) {
1611
+ e.preventDefault();
1612
+ return;
1613
+ }
1614
+ const first = list[0];
1615
+ const last = list[list.length - 1];
1616
+ const active = document.activeElement;
1617
+ if (e.shiftKey && active === first) {
1618
+ e.preventDefault();
1619
+ last.focus();
1620
+ } else if (!e.shiftKey && active === last) {
1621
+ e.preventDefault();
1622
+ first.focus();
1623
+ }
1624
+ };
1625
+ const handleFocusIn = (e) => {
1626
+ if (!isActive()) return;
1627
+ const target = e.target;
1628
+ if (target && !containsComposed(container, target)) {
1629
+ const list = focusables(container);
1630
+ list[0]?.focus({
1631
+ preventScroll: true
1632
+ });
1633
+ }
1634
+ };
1635
+ document.addEventListener("keydown", handleKey, true);
1636
+ document.addEventListener("focusin", handleFocusIn, true);
1637
+ let released = false;
1638
+ return () => {
1639
+ if (released) return;
1640
+ released = true;
1641
+ const idx = trapStack.indexOf(token);
1642
+ if (idx !== -1) trapStack.splice(idx, 1);
1643
+ document.removeEventListener("keydown", handleKey, true);
1644
+ document.removeEventListener("focusin", handleFocusIn, true);
1645
+ if (previouslyFocused && document.contains(previouslyFocused)) {
1646
+ previouslyFocused.focus({
1647
+ preventScroll: true
1648
+ });
1649
+ } else {
1650
+ const fallback = document.querySelector("[data-bs-restore-fallback]");
1651
+ fallback?.focus({
1652
+ preventScroll: true
1653
+ });
1654
+ }
1655
+ };
1656
+ };
1657
+ let lockCount = 0;
1658
+ let savedStyles = null;
1659
+ const lockBodyScroll = () => {
1660
+ if (typeof document === "undefined") return () => {
1661
+ };
1662
+ if (lockCount === 0) {
1663
+ const body = document.body;
1664
+ savedStyles = {
1665
+ overflow: body.style.overflow,
1666
+ position: body.style.position,
1667
+ top: body.style.top,
1668
+ width: body.style.width,
1669
+ paddingRight: body.style.paddingRight,
1670
+ scrollY: window.scrollY
1671
+ };
1672
+ const scrollbarGap = window.innerWidth - document.documentElement.clientWidth;
1673
+ if (scrollbarGap > 0) {
1674
+ const current = parseFloat(getComputedStyle(body).paddingRight) || 0;
1675
+ body.style.paddingRight = `${current + scrollbarGap}px`;
1676
+ }
1677
+ body.style.overflow = "hidden";
1678
+ body.style.position = "fixed";
1679
+ body.style.top = `-${savedStyles.scrollY}px`;
1680
+ body.style.width = "100%";
1681
+ }
1682
+ lockCount++;
1683
+ let released = false;
1684
+ return () => {
1685
+ if (released) return;
1686
+ released = true;
1687
+ lockCount = Math.max(0, lockCount - 1);
1688
+ if (lockCount === 0 && savedStyles) {
1689
+ const body = document.body;
1690
+ body.style.overflow = savedStyles.overflow;
1691
+ body.style.position = savedStyles.position;
1692
+ body.style.top = savedStyles.top;
1693
+ body.style.width = savedStyles.width;
1694
+ body.style.paddingRight = savedStyles.paddingRight;
1695
+ window.scrollTo(0, savedStyles.scrollY);
1696
+ savedStyles = null;
1697
+ }
1698
+ };
1699
+ };
1700
+ function createInertSiblings(rootProvider) {
1701
+ const tracked = [];
1702
+ return {
1703
+ apply() {
1704
+ if (typeof document === "undefined") return;
1705
+ let top = rootProvider();
1706
+ while (top.parentElement && top.parentElement !== document.body) {
1707
+ top = top.parentElement;
1708
+ }
1709
+ const parent = top.parentElement ?? document.body;
1710
+ for (const child of Array.from(parent.children)) {
1711
+ if (child === top) continue;
1712
+ if (child.hasAttribute("inert")) continue;
1713
+ if (child.classList.contains("bs-backdrop") || child.classList.contains("bs-screen") || child.classList.contains("bs-root")) {
1714
+ continue;
1715
+ }
1716
+ child.setAttribute("inert", "");
1717
+ tracked.push(child);
1718
+ }
1719
+ },
1720
+ remove() {
1721
+ for (const el of tracked) el.removeAttribute("inert");
1722
+ tracked.length = 0;
1723
+ }
1724
+ };
1725
+ }
1726
+ class LifecycleController {
1727
+ constructor(deps, opts) {
1728
+ __publicField(this, "element");
1729
+ __publicField(this, "focusTrapEnabled");
1730
+ __publicField(this, "initialFocus");
1731
+ __publicField(this, "closeOnEscape");
1732
+ __publicField(this, "bodyScrollLockEnabled");
1733
+ __publicField(this, "inertSiblingsEnabled");
1734
+ __publicField(this, "inertSiblings");
1735
+ __publicField(this, "shouldApplyInertSiblings");
1736
+ __publicField(this, "returnFocus");
1737
+ __publicField(this, "releaseFocusTrap", null);
1738
+ __publicField(this, "releaseScrollLock", null);
1739
+ __publicField(this, "installed", false);
1740
+ __publicField(this, "destroyed", false);
1741
+ this.element = deps.element;
1742
+ this.focusTrapEnabled = opts.focusTrap ?? false;
1743
+ this.initialFocus = opts.initialFocus;
1744
+ this.closeOnEscape = opts.closeOnEscape ?? true;
1745
+ this.bodyScrollLockEnabled = opts.lockBodyScroll ?? true;
1746
+ this.inertSiblingsEnabled = opts.inertSiblings ?? false;
1747
+ this.shouldApplyInertSiblings = opts.shouldApplyInertSiblings ?? (() => true);
1748
+ this.returnFocus = opts.returnFocus;
1749
+ this.inertSiblings = createInertSiblings(() => this.element);
1750
+ }
1751
+ get isInstalled() {
1752
+ return this.installed;
1753
+ }
1754
+ install() {
1755
+ if (this.destroyed) return;
1756
+ this.installed = true;
1757
+ if (this.bodyScrollLockEnabled && !this.releaseScrollLock) {
1758
+ this.releaseScrollLock = lockBodyScroll();
1759
+ }
1760
+ if (this.focusTrapEnabled && !this.releaseFocusTrap) {
1761
+ this.releaseFocusTrap = installFocusTrap(this.element, {
1762
+ initialFocus: this.initialFocus
1763
+ });
1764
+ }
1765
+ if (this.inertSiblingsEnabled && this.shouldApplyInertSiblings()) {
1766
+ this.inertSiblings.apply();
1767
+ }
1768
+ }
1769
+ setReturnFocus(target) {
1770
+ if (this.destroyed) return;
1771
+ this.returnFocus = target;
1772
+ }
1773
+ release() {
1774
+ if (!this.installed) return;
1775
+ this.installed = false;
1776
+ this.releaseFocusTrap?.();
1777
+ this.releaseFocusTrap = null;
1778
+ this.releaseScrollLock?.();
1779
+ this.releaseScrollLock = null;
1780
+ this.inertSiblings.remove();
1781
+ if (this.returnFocus) {
1782
+ const target = typeof this.returnFocus === "function" ? this.returnFocus() : typeof this.returnFocus === "string" ? typeof document !== "undefined" ? document.querySelector(this.returnFocus) : null : this.returnFocus;
1783
+ target?.focus?.();
1784
+ }
1785
+ }
1786
+ destroy() {
1787
+ this.release();
1788
+ this.destroyed = true;
1789
+ }
1790
+ }
1791
+ const isAxisVertical = (mode) => mode === "bottom" || mode === "top";
1792
+ class SampleRing {
1793
+ constructor(capacity) {
1794
+ __publicField(this, "slots");
1795
+ __publicField(this, "capacity");
1796
+ __publicField(this, "head", 0);
1797
+ __publicField(this, "size", 0);
1798
+ this.capacity = capacity;
1799
+ this.slots = new Array(capacity);
1800
+ for (let i = 0; i < capacity; i++) this.slots[i] = {
1801
+ t: 0,
1802
+ v: 0
1803
+ };
1804
+ }
1805
+ push(t, v) {
1806
+ const slot = this.slots[this.head];
1807
+ slot.t = t;
1808
+ slot.v = v;
1809
+ this.head = (this.head + 1) % this.capacity;
1810
+ if (this.size < this.capacity) this.size++;
1811
+ }
1812
+ pruneBefore(cutoff, minKeep) {
1813
+ while (this.size > minKeep) {
1814
+ const oldestIdx = (this.head - this.size + this.capacity) % this.capacity;
1815
+ if (this.slots[oldestIdx].t < cutoff) this.size--;
1816
+ else break;
1817
+ }
1818
+ }
1819
+ get length() {
1820
+ return this.size;
1821
+ }
1822
+ oldest() {
1823
+ if (this.size === 0) return void 0;
1824
+ return this.slots[(this.head - this.size + this.capacity) % this.capacity];
1825
+ }
1826
+ newest() {
1827
+ if (this.size === 0) return void 0;
1828
+ return this.slots[(this.head - 1 + this.capacity) % this.capacity];
1829
+ }
1830
+ reset() {
1831
+ this.head = 0;
1832
+ this.size = 0;
1833
+ }
1834
+ }
1835
+ const installGestures = (handle, mode, callbacks, options = {}) => {
1836
+ let activePointerId = null;
1837
+ let activePointerType = "touch";
1838
+ let startCoord = 0;
1839
+ let lastCoord = 0;
1840
+ let pendingStart = false;
1841
+ const VELOCITY_WINDOW_MS = (kind) => kind === "mouse" ? 160 : 120;
1842
+ const MAX_SAMPLES = 32;
1843
+ const samples = new SampleRing(MAX_SAMPLES);
1844
+ const axis = isAxisVertical(mode) ? "Y" : "X";
1845
+ const sign = mode === "bottom" || mode === "right" ? -1 : 1;
1846
+ const coordOf = (e) => axis === "Y" ? e.clientY : e.clientX;
1847
+ const capture = (pointerId) => {
1848
+ try {
1849
+ handle.setPointerCapture(pointerId);
1850
+ } catch {
1851
+ }
1852
+ };
1853
+ const releaseTracking = () => {
1854
+ activePointerId = null;
1855
+ pendingStart = false;
1856
+ samples.reset();
1857
+ };
1858
+ const onPointerDown = (e) => {
1859
+ if (activePointerId !== null) return;
1860
+ if (e.button !== void 0 && e.button !== 0) return;
1861
+ if (options.shouldStart && !options.shouldStart(e)) return;
1862
+ activePointerId = e.pointerId;
1863
+ activePointerType = e.pointerType || "touch";
1864
+ startCoord = coordOf(e);
1865
+ lastCoord = startCoord;
1866
+ samples.reset();
1867
+ samples.push(e.timeStamp, startCoord);
1868
+ if (options.deferStart) {
1869
+ pendingStart = true;
1870
+ return;
1871
+ }
1872
+ capture(e.pointerId);
1873
+ callbacks.onStart(startCoord, activePointerType);
1874
+ };
1875
+ const onPointerMove = (e) => {
1876
+ if (e.pointerId !== activePointerId) return;
1877
+ const coord = coordOf(e);
1878
+ const rawDelta = coord - startCoord;
1879
+ const delta = rawDelta * sign;
1880
+ if (pendingStart) {
1881
+ const verdict = options.deferStart(e, delta);
1882
+ if (verdict === false) {
1883
+ releaseTracking();
1884
+ return;
1885
+ }
1886
+ if (verdict !== true) return;
1887
+ pendingStart = false;
1888
+ startCoord = coord;
1889
+ lastCoord = coord;
1890
+ samples.reset();
1891
+ samples.push(e.timeStamp, coord);
1892
+ capture(e.pointerId);
1893
+ callbacks.onStart(startCoord, activePointerType);
1894
+ return;
1895
+ }
1896
+ lastCoord = coord;
1897
+ samples.push(e.timeStamp, coord);
1898
+ const cutoff = e.timeStamp - VELOCITY_WINDOW_MS(activePointerType);
1899
+ samples.pruneBefore(cutoff, 2);
1900
+ callbacks.onMove(delta);
1901
+ };
1902
+ const finishGesture = (e) => {
1903
+ if (e.pointerId !== activePointerId) return;
1904
+ if (pendingStart) {
1905
+ releaseTracking();
1906
+ return;
1907
+ }
1908
+ const rawDelta = lastCoord - startCoord;
1909
+ const delta = rawDelta * sign;
1910
+ let velocity = 0;
1911
+ if (samples.length >= 3) {
1912
+ const first = samples.oldest();
1913
+ const last = samples.newest();
1914
+ const dt = last.t - first.t;
1915
+ if (dt > 0) velocity = (last.v - first.v) / dt * sign;
1916
+ }
1917
+ const finishedKind = activePointerType;
1918
+ activePointerId = null;
1919
+ samples.reset();
1920
+ try {
1921
+ if (handle.hasPointerCapture(e.pointerId)) {
1922
+ handle.releasePointerCapture(e.pointerId);
1923
+ }
1924
+ } catch {
1925
+ }
1926
+ callbacks.onEnd(delta, velocity, finishedKind);
1927
+ };
1928
+ const onPointerCancel = (e) => {
1929
+ if (e.pointerId !== activePointerId) return;
1930
+ const wasPending = pendingStart;
1931
+ releaseTracking();
1932
+ if (wasPending) return;
1933
+ callbacks.onCancel?.();
1934
+ };
1935
+ handle.addEventListener("pointerdown", onPointerDown);
1936
+ handle.addEventListener("pointermove", onPointerMove);
1937
+ handle.addEventListener("pointerup", finishGesture);
1938
+ handle.addEventListener("pointercancel", onPointerCancel);
1939
+ const managesTouchAction = options.manageTouchAction !== false;
1940
+ const prevTouchAction = handle.style.touchAction;
1941
+ if (managesTouchAction) {
1942
+ handle.style.touchAction = isAxisVertical(mode) ? "pan-x" : "pan-y";
1943
+ }
1944
+ return () => {
1945
+ handle.removeEventListener("pointerdown", onPointerDown);
1946
+ handle.removeEventListener("pointermove", onPointerMove);
1947
+ handle.removeEventListener("pointerup", finishGesture);
1948
+ handle.removeEventListener("pointercancel", onPointerCancel);
1949
+ if (managesTouchAction) handle.style.touchAction = prevTouchAction;
1950
+ if (activePointerId !== null && handle.hasPointerCapture(activePointerId)) {
1951
+ handle.releasePointerCapture(activePointerId);
1952
+ }
1953
+ };
1954
+ };
1955
+ function rubberBand(overshoot, maxAxisSize) {
1956
+ const cap = Math.min(maxAxisSize * 0.15, 80);
1957
+ if (cap <= 0) return 0;
1958
+ const abs = Math.abs(overshoot);
1959
+ return Math.sign(overshoot) * (cap * abs / (abs + cap));
1960
+ }
1961
+ function dismissSoftKeyboardIfFocused(root) {
1962
+ if (typeof document === "undefined") return false;
1963
+ const active = document.activeElement;
1964
+ if (!active) return false;
1965
+ if (!root.contains(active)) return false;
1966
+ const tag = active.tagName;
1967
+ const isEditable = tag === "INPUT" || tag === "TEXTAREA" || active.isContentEditable === true;
1968
+ if (!isEditable) return false;
1969
+ try {
1970
+ active.blur();
1971
+ return true;
1972
+ } catch {
1973
+ return false;
1974
+ }
1975
+ }
1976
+ class GestureController {
1977
+ constructor(deps) {
1978
+ __publicField(this, "deps");
1979
+ __publicField(this, "detach", null);
1980
+ __publicField(this, "isDragging_", false);
1981
+ __publicField(this, "dragStartSize", 0);
1982
+ __publicField(this, "currentPointerKind", "touch");
1983
+ __publicField(this, "keyboardDismissed", false);
1984
+ __publicField(this, "dragSuppressed", false);
1985
+ __publicField(this, "dragPayload", {
1986
+ size: 0,
1987
+ delta: 0
1988
+ });
1989
+ this.deps = deps;
1990
+ }
1991
+ get isDragging() {
1992
+ return this.isDragging_;
1993
+ }
1994
+ install() {
1995
+ if (this.detach) return this.detach;
1996
+ this.detach = installGestures(this.deps.handle, this.deps.mode, {
1997
+ onStart: (_coord, kind) => {
1998
+ if (this.deps.getDisableDrag?.()) {
1999
+ this.dragSuppressed = true;
2000
+ return;
2001
+ }
2002
+ this.dragSuppressed = false;
2003
+ this.deps.cancelAnimation();
2004
+ this.isDragging_ = true;
2005
+ this.dragStartSize = this.deps.getDragContext().size;
2006
+ this.currentPointerKind = kind;
2007
+ this.keyboardDismissed = false;
2008
+ this.deps.getRoot()?.setAttribute("data-dragging", "true");
2009
+ this.deps.element.style.willChange = "transform";
2010
+ this.deps.emit("dragstart", {
2011
+ size: this.dragStartSize
2012
+ });
2013
+ },
2014
+ onMove: (delta) => {
2015
+ if (this.dragSuppressed) return;
2016
+ const ctx = this.deps.getDragContext();
2017
+ const { min, max } = ctx.range;
2018
+ const maxAxis = ctx.maxAxisSize;
2019
+ const rubberOn = ctx.rubberBandEnabled;
2020
+ let next = this.dragStartSize + delta;
2021
+ if (next > max) {
2022
+ next = rubberOn ? max + rubberBand(next - max, maxAxis) : max;
2023
+ } else if (next < min) {
2024
+ next = rubberOn ? min - rubberBand(min - next, maxAxis) : min;
2025
+ }
2026
+ this.deps.applySize(next);
2027
+ if (!this.keyboardDismissed && this.currentPointerKind === "touch" && this.deps.mode === "bottom" && next < this.dragStartSize) {
2028
+ if (dismissSoftKeyboardIfFocused(this.deps.element)) {
2029
+ this.keyboardDismissed = true;
2030
+ }
2031
+ }
2032
+ if (this.deps.listenerCount("drag") > 0) {
2033
+ this.dragPayload.size = next;
2034
+ this.dragPayload.delta = delta;
2035
+ this.deps.emit("drag", this.dragPayload);
2036
+ }
2037
+ },
2038
+ onEnd: (delta, velocity, kind) => {
2039
+ if (this.dragSuppressed) {
2040
+ this.dragSuppressed = false;
2041
+ return;
2042
+ }
2043
+ this.isDragging_ = false;
2044
+ this.keyboardDismissed = false;
2045
+ this.deps.getRoot()?.removeAttribute("data-dragging");
2046
+ this.deps.emit("dragend", {
2047
+ size: this.deps.getDragContext().size,
2048
+ velocity
2049
+ });
2050
+ this.deps.settleAfterDrag(delta, velocity, kind);
2051
+ },
2052
+ onCancel: () => {
2053
+ if (this.dragSuppressed) {
2054
+ this.dragSuppressed = false;
2055
+ return;
2056
+ }
2057
+ this.isDragging_ = false;
2058
+ this.keyboardDismissed = false;
2059
+ this.deps.getRoot()?.removeAttribute("data-dragging");
2060
+ this.deps.emit("dragend", {
2061
+ size: this.deps.getDragContext().size,
2062
+ velocity: 0
2063
+ });
2064
+ const restoreTo = this.dragStartSize;
2065
+ void this.deps.animateTo(restoreTo, 0);
2066
+ }
2067
+ }, this.deps.gestureOptions);
2068
+ return this.detach;
2069
+ }
2070
+ forceClearDragState() {
2071
+ if (!this.isDragging_) return;
2072
+ this.isDragging_ = false;
2073
+ this.keyboardDismissed = false;
2074
+ this.deps.getRoot()?.removeAttribute("data-dragging");
2075
+ this.deps.element.style.willChange = "";
2076
+ }
2077
+ }
2078
+ const DRAG_ZONE_SELECTOR = "[data-bs-drag]";
2079
+ const NO_DRAG_SELECTOR = "[data-bs-no-drag]";
2080
+ const asElement = (target) => {
2081
+ const candidate = target;
2082
+ return candidate && typeof candidate.closest === "function" ? candidate : null;
2083
+ };
2084
+ function isDragAllowedFrom(target, mode) {
2085
+ const el = asElement(target);
2086
+ if (!el) return mode !== "zones";
2087
+ if (el.closest(NO_DRAG_SELECTOR)) return false;
2088
+ if (mode === "zones") return el.closest(DRAG_ZONE_SELECTOR) !== null;
2089
+ return true;
2090
+ }
2091
+ const CONTENT_DRAG_SLOP = 6;
2092
+ function decideContentGesture(input) {
2093
+ const slop = input.slop ?? CONTENT_DRAG_SLOP;
2094
+ if (Math.abs(input.delta) < slop) return "pending";
2095
+ if (input.scrollTop > 0) return "scroll";
2096
+ if (input.delta > 0 && input.atMaxSnap) return "scroll";
2097
+ return "drag";
2098
+ }
2099
+ function installTouchScrollGuard(surface, isDragging) {
2100
+ const onTouchMove = (e) => {
2101
+ if (!isDragging()) return;
2102
+ if (e.cancelable) e.preventDefault();
2103
+ };
2104
+ surface.addEventListener("touchmove", onTouchMove, {
2105
+ passive: false
2106
+ });
2107
+ return () => surface.removeEventListener("touchmove", onTouchMove);
2108
+ }
2109
+ class AriaSliderWriter {
2110
+ constructor(handle, mode) {
2111
+ __publicField(this, "handle");
2112
+ this.handle = handle;
2113
+ handle.setAttribute("aria-orientation", mode === "bottom" || mode === "top" ? "vertical" : "horizontal");
2114
+ }
2115
+ setValue(allowedIds, activeId) {
2116
+ if (allowedIds.length === 0) {
2117
+ this.handle.removeAttribute("aria-valuemin");
2118
+ this.handle.removeAttribute("aria-valuemax");
2119
+ this.handle.removeAttribute("aria-valuenow");
2120
+ this.handle.removeAttribute("aria-valuetext");
2121
+ return;
2122
+ }
2123
+ const max = Math.max(allowedIds.length - 1, 0);
2124
+ const idx = Math.max(allowedIds.indexOf(activeId), 0);
2125
+ this.handle.setAttribute("aria-valuemin", "0");
2126
+ this.handle.setAttribute("aria-valuemax", String(max));
2127
+ this.handle.setAttribute("aria-valuenow", String(idx));
2128
+ this.handle.setAttribute("aria-valuetext", activeId);
2129
+ }
2130
+ }
2131
+ function createScrollCache(deps) {
2132
+ const positions = /* @__PURE__ */ new Map();
2133
+ const isSmallSize = (size) => {
2134
+ const max = deps.getMaxAxisSize();
2135
+ if (max <= 0) return false;
2136
+ return size < max / 2;
2137
+ };
2138
+ return {
2139
+ cache(fromId, prevSize, nextSize) {
2140
+ const container = deps.scrollContainer;
2141
+ if (!container) return;
2142
+ if (isSmallSize(prevSize)) return;
2143
+ if (!isSmallSize(nextSize)) return;
2144
+ positions.set(fromId, container.scrollTop);
2145
+ },
2146
+ restore(toId, prevSize, nextSize) {
2147
+ const container = deps.scrollContainer;
2148
+ if (!container) return;
2149
+ if (isSmallSize(nextSize)) return;
2150
+ if (!isSmallSize(prevSize)) return;
2151
+ const cached = positions.get(toId);
2152
+ if (cached === void 0) return;
2153
+ container.scrollTop = cached;
2154
+ positions.delete(toId);
2155
+ },
2156
+ clear() {
2157
+ positions.clear();
2158
+ }
2159
+ };
2160
+ }
2161
+ const DEFAULT_DURATION = 200;
2162
+ const DEFAULT_EASING = "cubic-bezier(0.22, 1, 0.36, 1)";
2163
+ const PRESET_FRAMES = {
2164
+ fade: {
2165
+ enter: [
2166
+ {
2167
+ opacity: 0
2168
+ },
2169
+ {
2170
+ opacity: 1
2171
+ }
2172
+ ],
2173
+ exit: [
2174
+ {
2175
+ opacity: 1
2176
+ },
2177
+ {
2178
+ opacity: 0
2179
+ }
2180
+ ]
2181
+ },
2182
+ scale: {
2183
+ enter: [
2184
+ {
2185
+ opacity: 0,
2186
+ transform: "scale(0.85)"
2187
+ },
2188
+ {
2189
+ opacity: 1,
2190
+ transform: "scale(1)"
2191
+ }
2192
+ ],
2193
+ exit: [
2194
+ {
2195
+ opacity: 1,
2196
+ transform: "scale(1)"
2197
+ },
2198
+ {
2199
+ opacity: 0,
2200
+ transform: "scale(0.85)"
2201
+ }
2202
+ ]
2203
+ },
2204
+ slide: {
2205
+ enter: [
2206
+ {
2207
+ opacity: 0,
2208
+ transform: "translateY(10px)"
2209
+ },
2210
+ {
2211
+ opacity: 1,
2212
+ transform: "translateY(0)"
2213
+ }
2214
+ ],
2215
+ exit: [
2216
+ {
2217
+ opacity: 1,
2218
+ transform: "translateY(0)"
2219
+ },
2220
+ {
2221
+ opacity: 0,
2222
+ transform: "translateY(10px)"
2223
+ }
2224
+ ]
2225
+ },
2226
+ pop: {
2227
+ enter: [
2228
+ {
2229
+ opacity: 0,
2230
+ transform: "scale(0.6)",
2231
+ offset: 0
2232
+ },
2233
+ {
2234
+ opacity: 1,
2235
+ transform: "scale(1.06)",
2236
+ offset: 0.7
2237
+ },
2238
+ {
2239
+ opacity: 1,
2240
+ transform: "scale(1)",
2241
+ offset: 1
2242
+ }
2243
+ ],
2244
+ exit: [
2245
+ {
2246
+ opacity: 1,
2247
+ transform: "scale(1)"
2248
+ },
2249
+ {
2250
+ opacity: 0,
2251
+ transform: "scale(0.6)"
2252
+ }
2253
+ ]
2254
+ }
2255
+ };
2256
+ const INSTANT = {
2257
+ finished: Promise.resolve(),
2258
+ cancel: () => {
2259
+ }
2260
+ };
2261
+ function resolveTransitionFrames(spec, phase) {
2262
+ const fallback = {
2263
+ frames: null,
2264
+ duration: 0,
2265
+ easing: DEFAULT_EASING
2266
+ };
2267
+ if (spec === "none") return fallback;
2268
+ if (spec === void 0 || typeof spec === "string") {
2269
+ const preset2 = PRESET_FRAMES[spec ?? "fade"];
2270
+ return {
2271
+ frames: preset2[phase],
2272
+ duration: DEFAULT_DURATION,
2273
+ easing: DEFAULT_EASING
2274
+ };
2275
+ }
2276
+ const custom = phase === "enter" ? spec.enter : spec.exit;
2277
+ if (custom) {
2278
+ return {
2279
+ frames: custom,
2280
+ duration: spec.duration ?? DEFAULT_DURATION,
2281
+ easing: spec.easing ?? DEFAULT_EASING
2282
+ };
2283
+ }
2284
+ if (spec.preset === "none") return fallback;
2285
+ const preset = PRESET_FRAMES[spec.preset ?? "fade"];
2286
+ return {
2287
+ frames: preset[phase],
2288
+ duration: spec.duration ?? DEFAULT_DURATION,
2289
+ easing: spec.easing ?? DEFAULT_EASING
2290
+ };
2291
+ }
2292
+ function runAnchorTransition(el, spec, phase) {
2293
+ const respectReduced = typeof spec === "object" ? spec.respectReducedMotion !== false : true;
2294
+ if (respectReduced && prefersReducedMotion()) return INSTANT;
2295
+ const { frames, duration, easing } = resolveTransitionFrames(spec, phase);
2296
+ if (!frames || duration <= 0) return INSTANT;
2297
+ if (typeof el.animate !== "function") return INSTANT;
2298
+ const animation = el.animate(frames, {
2299
+ duration,
2300
+ easing,
2301
+ fill: "both"
2302
+ });
2303
+ const finished = animation.finished.then(() => void 0).catch(() => void 0);
2304
+ return {
2305
+ finished,
2306
+ cancel: () => {
2307
+ try {
2308
+ animation.cancel();
2309
+ } catch {
2310
+ return;
2311
+ }
2312
+ }
2313
+ };
2314
+ }
2315
+ function attachAnchor(deps, opts) {
2316
+ const doc = deps.host.ownerDocument;
2317
+ const wrapper = doc.createElement("div");
2318
+ wrapper.className = "bs-anchor";
2319
+ const interactive = opts.interactive ?? true;
2320
+ const position = opts.position ?? "sheet-top-right";
2321
+ const isDock = position === "dock-bottom" || position === "dock-top";
2322
+ const ws = wrapper.style;
2323
+ ws.position = "fixed";
2324
+ if (isDock) {
2325
+ ws.left = "0";
2326
+ ws.right = "0";
2327
+ if (position === "dock-bottom") {
2328
+ ws.bottom = "0";
2329
+ } else {
2330
+ ws.top = "0";
2331
+ }
2332
+ } else {
2333
+ applyOverlayPosition(ws, deps.mode, position, opts.inset ?? "16px");
2334
+ }
2335
+ if (opts.fadeRange) {
2336
+ const [r0, r1] = opts.fadeRange;
2337
+ const span = Math.max(r1 - r0, 1e-4);
2338
+ ws.opacity = `clamp(0, calc((var(--bs-progress, 0) - ${r0}) / ${span}), 1)`;
2339
+ }
2340
+ wrapper.appendChild(opts.element);
2341
+ deps.host.appendChild(wrapper);
2342
+ let visible = null;
2343
+ let inFlight = null;
2344
+ const shouldShow = (state) => {
2345
+ if (typeof opts.showOn === "function") return opts.showOn(state);
2346
+ if (Array.isArray(opts.showOn)) return opts.showOn.includes(state.activeId);
2347
+ return isDock || state.size > 0;
2348
+ };
2349
+ const applyVisibility = (next, animate) => {
2350
+ if (next === visible) return;
2351
+ visible = next;
2352
+ inFlight?.cancel();
2353
+ inFlight = null;
2354
+ wrapper.style.pointerEvents = next && interactive ? "auto" : "none";
2355
+ if (next) {
2356
+ wrapper.style.visibility = "";
2357
+ if (animate) {
2358
+ inFlight = runAnchorTransition(opts.element, opts.animation, "enter");
2359
+ }
2360
+ return;
2361
+ }
2362
+ if (!animate) {
2363
+ wrapper.style.visibility = "hidden";
2364
+ return;
2365
+ }
2366
+ const handle = runAnchorTransition(opts.element, opts.animation, "exit");
2367
+ inFlight = handle;
2368
+ void handle.finished.then(() => {
2369
+ if (inFlight === handle && visible === false) {
2370
+ wrapper.style.visibility = "hidden";
2371
+ }
2372
+ });
2373
+ };
2374
+ const evaluate = (animate) => {
2375
+ if (deps.isDestroyed()) return;
2376
+ applyVisibility(shouldShow(deps.getState()), animate);
2377
+ };
2378
+ const offs = [
2379
+ deps.on("snap", () => evaluate(true)),
2380
+ deps.on("open", () => evaluate(true)),
2381
+ deps.on("close", () => evaluate(true))
2382
+ ];
2383
+ evaluate(false);
2384
+ return {
2385
+ wrapper,
2386
+ detach: () => {
2387
+ offs.forEach((off) => off());
2388
+ inFlight?.cancel();
2389
+ inFlight = null;
2390
+ if (wrapper.parentNode) wrapper.parentNode.removeChild(wrapper);
2391
+ },
2392
+ syncZ: (z) => {
2393
+ wrapper.style.zIndex = String(z);
2394
+ }
2395
+ };
2396
+ }
2397
+ const matchesId = (def, activeId) => {
2398
+ if (def.for === void 0) return false;
2399
+ return Array.isArray(def.for) ? def.for.includes(activeId) : def.for === activeId;
2400
+ };
2401
+ const matchesRange = (def, progress) => {
2402
+ if (!def.forRange) return false;
2403
+ const [from, to] = def.forRange;
2404
+ return progress >= from && progress <= to;
2405
+ };
2406
+ function installScrimStages(deps, opts) {
2407
+ const doc = deps.host.ownerDocument;
2408
+ const entries2 = opts.stages.map((def) => {
2409
+ const wrapper = doc.createElement("div");
2410
+ wrapper.className = "bs-scrim-stage";
2411
+ const ws = wrapper.style;
2412
+ ws.position = "absolute";
2413
+ applyOverlayPosition(ws, deps.mode, def.position ?? opts.position ?? "center", def.inset ?? opts.inset ?? "16px");
2414
+ ws.visibility = "hidden";
2415
+ ws.pointerEvents = "none";
2416
+ wrapper.appendChild(def.element);
2417
+ deps.host.appendChild(wrapper);
2418
+ return {
2419
+ def,
2420
+ wrapper,
2421
+ inFlight: null
2422
+ };
2423
+ });
2424
+ let active = null;
2425
+ const resolveActive = () => {
2426
+ const state = deps.getState();
2427
+ if (state.size === 0) {
2428
+ return entries2.find((e) => matchesId(e.def, state.activeId)) ?? null;
2429
+ }
2430
+ return entries2.find((e) => matchesId(e.def, state.activeId)) ?? entries2.find((e) => matchesRange(e.def, state.progress)) ?? null;
2431
+ };
2432
+ const hide = (entry, animate) => {
2433
+ entry.inFlight?.cancel();
2434
+ entry.wrapper.style.pointerEvents = "none";
2435
+ if (!animate) {
2436
+ entry.inFlight = null;
2437
+ entry.wrapper.style.visibility = "hidden";
2438
+ return;
2439
+ }
2440
+ const handle = runAnchorTransition(entry.def.element, entry.def.animation ?? opts.animation, "exit");
2441
+ entry.inFlight = handle;
2442
+ void handle.finished.then(() => {
2443
+ if (entry.inFlight === handle && active !== entry) {
2444
+ entry.wrapper.style.visibility = "hidden";
2445
+ }
2446
+ });
2447
+ };
2448
+ const show = (entry, animate) => {
2449
+ entry.inFlight?.cancel();
2450
+ entry.inFlight = null;
2451
+ entry.wrapper.style.visibility = "";
2452
+ entry.wrapper.style.pointerEvents = entry.def.interactive ?? opts.interactive ?? false ? "auto" : "none";
2453
+ if (animate) {
2454
+ entry.inFlight = runAnchorTransition(entry.def.element, entry.def.animation ?? opts.animation, "enter");
2455
+ }
2456
+ };
2457
+ const update = (animate) => {
2458
+ if (deps.isDestroyed()) return;
2459
+ const next = resolveActive();
2460
+ if (next === active) return;
2461
+ const prev = active;
2462
+ active = next;
2463
+ if (prev) hide(prev, animate);
2464
+ if (next) show(next, animate);
2465
+ };
2466
+ const offs = [
2467
+ deps.on("snap", () => update(true)),
2468
+ deps.on("open", () => update(true)),
2469
+ deps.on("close", () => update(true))
2470
+ ];
2471
+ if (entries2.some((e) => e.def.forRange)) {
2472
+ offs.push(deps.on("progress", () => update(true)));
2473
+ }
2474
+ update(false);
2475
+ return () => {
2476
+ offs.forEach((off) => off());
2477
+ for (const entry of entries2) {
2478
+ entry.inFlight?.cancel();
2479
+ entry.inFlight = null;
2480
+ if (entry.wrapper.parentNode) {
2481
+ entry.wrapper.parentNode.removeChild(entry.wrapper);
2482
+ }
2483
+ }
2484
+ active = null;
2485
+ };
2486
+ }
2487
+ const DEFAULT_FLICK_VELOCITY = 0.65;
2488
+ const DEFAULT_DRAG_THRESHOLD = 18;
2489
+ function resolveEngineOptions(opts, extras) {
2490
+ const initialAllowed = opts.allowed ?? opts.snapPoints.map((p) => p.id);
2491
+ let initialId = opts.initial ?? initialAllowed[0] ?? opts.snapPoints[0]?.id ?? "default";
2492
+ if (opts.persistKey) {
2493
+ const restored = readPersistedIdSafe(opts.persistKey);
2494
+ if (restored && initialAllowed.includes(restored)) {
2495
+ initialId = restored;
2496
+ }
2497
+ }
2498
+ return {
2499
+ mode: opts.mode ?? "bottom",
2500
+ flickVelocity: opts.flickVelocity ?? DEFAULT_FLICK_VELOCITY,
2501
+ dragThreshold: opts.dragThreshold ?? DEFAULT_DRAG_THRESHOLD,
2502
+ dragFrom: opts.dragFrom ?? (opts.handle ? "handle" : "sheet"),
2503
+ dragFromContent: opts.dragFromContent ?? true,
2504
+ rubberBandEnabled: opts.rubberBand ?? true,
2505
+ closeOnBack: opts.closeOnBack ?? false,
2506
+ persistent: opts.persistent ?? false,
2507
+ disableClose: opts.disableClose ?? false,
2508
+ disableDrag: opts.disableDrag ?? false,
2509
+ closeOnRouteChange: opts.closeOnRouteChange ?? false,
2510
+ radius: opts.radius,
2511
+ maxHeight: opts.maxHeight,
2512
+ initialAllowed,
2513
+ initialId,
2514
+ scrim: {
2515
+ scrimMode: opts.scrimMode,
2516
+ scrimColor: opts.scrimColor,
2517
+ scrimBlur: opts.scrimBlur,
2518
+ scrimInteractive: opts.scrimInteractive ?? (opts.scrimTapToClose ? true : void 0),
2519
+ scrimTapToClose: opts.scrimTapToClose,
2520
+ scrimPreset: opts.scrimPreset,
2521
+ screenRange: opts.screenRange,
2522
+ backdropRange: opts.backdropRange
2523
+ },
2524
+ animation: {
2525
+ animation: opts.animation,
2526
+ settleAnimation: opts.settleAnimation,
2527
+ duration: opts.duration,
2528
+ easing: opts.easing,
2529
+ spring: opts.spring,
2530
+ respectReducedMotion: opts.respectReducedMotion,
2531
+ viewTransitions: opts.viewTransitions
2532
+ },
2533
+ lifecycle: {
2534
+ focusTrap: opts.focusTrap,
2535
+ initialFocus: opts.initialFocus,
2536
+ closeOnEscape: opts.closeOnEscape,
2537
+ lockBodyScroll: opts.lockBodyScroll,
2538
+ inertSiblings: opts.inertSiblings,
2539
+ shouldApplyInertSiblings: extras?.shouldApplyInertSiblings,
2540
+ returnFocus: opts.returnFocusTo
2541
+ }
2542
+ };
2543
+ }
2544
+ const readPersistedIdSafe = (key) => {
2545
+ if (typeof window === "undefined") return null;
2546
+ if (typeof localStorage === "undefined") return null;
2547
+ try {
2548
+ return localStorage.getItem(key);
2549
+ } catch {
2550
+ return null;
2551
+ }
2552
+ };
2553
+ class TeardownStack {
2554
+ constructor() {
2555
+ __publicField(this, "fns", []);
2556
+ }
2557
+ add(fn) {
2558
+ if (fn) this.fns.push(fn);
2559
+ }
2560
+ drain() {
2561
+ while (this.fns.length) {
2562
+ try {
2563
+ this.fns.pop()();
2564
+ } catch (err) {
2565
+ queueMicrotask(() => {
2566
+ throw err;
2567
+ });
2568
+ }
2569
+ }
2570
+ }
2571
+ }
2572
+ const HAPTIC_DURATION_MS = 8;
2573
+ class BottomSheetCore {
2574
+ constructor(opts) {
2575
+ __publicField(this, "id", nextInstanceId("bs"));
2576
+ __publicField(this, "element");
2577
+ __publicField(this, "handle");
2578
+ __publicField(this, "scrollContainer");
2579
+ __publicField(this, "backdrop");
2580
+ __publicField(this, "screenComponent");
2581
+ __publicField(this, "scrimParent", null);
2582
+ __publicField(this, "mode");
2583
+ __publicField(this, "flickVelocity");
2584
+ __publicField(this, "dragThreshold");
2585
+ __publicField(this, "dragFromMode");
2586
+ __publicField(this, "dragFromContentDefault");
2587
+ __publicField(this, "rubberBandEnabled");
2588
+ __publicField(this, "scrim");
2589
+ __publicField(this, "aria");
2590
+ __publicField(this, "animation");
2591
+ __publicField(this, "lifecycle");
2592
+ __publicField(this, "closeOnBack");
2593
+ __publicField(this, "routedTo");
2594
+ __publicField(this, "persistent");
2595
+ __publicField(this, "disableCloseFlag");
2596
+ __publicField(this, "disableDragFlag");
2597
+ __publicField(this, "closeOnRouteChange");
2598
+ __publicField(this, "maxHeight");
2599
+ __publicField(this, "snapPointsRaw");
2600
+ __publicField(this, "snaps");
2601
+ __publicField(this, "activeId");
2602
+ __publicField(this, "size", 0);
2603
+ __publicField(this, "gesture");
2604
+ __publicField(this, "contentGesture");
2605
+ __publicField(this, "detachSheetGesture", null);
2606
+ __publicField(this, "rootEl", null);
2607
+ __publicField(this, "destroyed", false);
2608
+ __publicField(this, "restClosed", false);
2609
+ __publicField(this, "currentViewTransition", null);
2610
+ __publicField(this, "currentAbort", new AbortController());
2611
+ __publicField(this, "sizeWriteSentinel", new WriteSentinel());
2612
+ __publicField(this, "progressWriteSentinel", new WriteSentinel());
2613
+ __publicField(this, "isTopSheet", true);
2614
+ __publicField(this, "opening", false);
2615
+ __publicField(this, "allowOvershoot", false);
2616
+ __publicField(this, "progressPayload", {
2617
+ value: 0,
2618
+ size: 0
2619
+ });
2620
+ __publicField(this, "dragContextBuf", {
2621
+ size: 0,
2622
+ maxAxisSize: 0,
2623
+ range: {
2624
+ min: 0,
2625
+ max: 0
2626
+ },
2627
+ rubberBandEnabled: false
2628
+ });
2629
+ __publicField(this, "bus", createEventBus());
2630
+ __publicField(this, "persistKey");
2631
+ __publicField(this, "linkedSheets", []);
2632
+ __publicField(this, "scrollCache");
2633
+ __publicField(this, "teardowns", new TeardownStack());
2634
+ __publicField(this, "anchors", []);
2635
+ __publicField(this, "anchorHost", null);
2636
+ __publicField(this, "stackZ", 100);
2637
+ __publicField(this, "detachScrimStages", null);
2638
+ __publicField(this, "stackEffectEnabled", false);
2639
+ __publicField(this, "stackEffectPrimed", false);
2640
+ __publicField(this, "fitDeps");
2641
+ __publicField(this, "transformTemplate");
2642
+ __publicField(this, "featureList", []);
2643
+ __publicField(this, "featureCtx");
2644
+ const resolved = resolveEngineOptions(opts, {
2645
+ shouldApplyInertSiblings: () => {
2646
+ if (typeof document === "undefined") return false;
2647
+ const bodyChildren = Array.from(document.body.children);
2648
+ return bodyChildren.some((c) => c === opts.element || c.contains(opts.element));
2649
+ }
2650
+ });
2651
+ this.element = opts.element;
2652
+ this.handle = opts.handle ?? opts.element;
2653
+ this.scrollContainer = opts.scrollContainer;
2654
+ this.backdrop = opts.backdrop;
2655
+ this.screenComponent = opts.scrim ?? opts.screenComponent;
2656
+ this.scrimParent = this.screenComponent?.parentElement ?? null;
2657
+ this.mode = resolved.mode;
2658
+ this.transformTemplate = buildTransformTemplate(this.mode);
2659
+ this.aria = new AriaSliderWriter(this.handle, this.mode);
2660
+ this.flickVelocity = resolved.flickVelocity;
2661
+ this.dragThreshold = resolved.dragThreshold;
2662
+ this.dragFromMode = resolved.dragFrom;
2663
+ this.dragFromContentDefault = resolved.dragFromContent;
2664
+ this.rubberBandEnabled = resolved.rubberBandEnabled;
2665
+ this.scrim = new ScrimController({
2666
+ mode: this.mode,
2667
+ screenComponent: this.screenComponent,
2668
+ backdrop: this.backdrop,
2669
+ isDestroyed: () => this.destroyed,
2670
+ isTopSheet: () => this.isTopSheet,
2671
+ getAllowedIds: () => this.snaps.getAllowedIds().slice(),
2672
+ getResolvedSnaps: () => this.snaps.getResolvedSnaps().slice(),
2673
+ snapTo: (id) => {
2674
+ void this.snapTo(id);
2675
+ },
2676
+ close: () => {
2677
+ if (!this.canDismiss()) return;
2678
+ void this.close("backdrop");
2679
+ }
2680
+ }, resolved.scrim);
2681
+ this.animation = new AnimationRunner({
2682
+ element: this.element,
2683
+ getRootEl: () => this.rootEl,
2684
+ applySize: (size) => this.applySize(size),
2685
+ getSize: () => this.size,
2686
+ isDragging: () => this.isDraggingAny(),
2687
+ applyAux: (size) => this.applySize(size, true),
2688
+ getTransformFor: (size) => this.transformTemplate(this.snaps.getMaxAxisSize() - size)
2689
+ }, resolved.animation);
2690
+ this.lifecycle = new LifecycleController({
2691
+ element: this.element,
2692
+ close: () => this.close()
2693
+ }, resolved.lifecycle);
2694
+ this.closeOnBack = resolved.closeOnBack;
2695
+ this.routedTo = opts.routedTo;
2696
+ this.persistent = resolved.persistent;
2697
+ this.disableCloseFlag = resolved.disableClose;
2698
+ this.disableDragFlag = resolved.disableDrag;
2699
+ this.closeOnRouteChange = resolved.closeOnRouteChange;
2700
+ this.stackEffectEnabled = opts.stackEffect ?? false;
2701
+ this.snapPointsRaw = opts.snapPoints;
2702
+ this.persistKey = opts.persistKey;
2703
+ if (opts.linkedSheets) {
2704
+ this.linkedSheets = opts.linkedSheets;
2705
+ }
2706
+ const byName = /* @__PURE__ */ new Map();
2707
+ for (const feature of opts.features ?? []) {
2708
+ byName.set(feature.name, feature);
2709
+ }
2710
+ this.featureList = Array.from(byName.values());
2711
+ this.activeId = resolved.initialId;
2712
+ auditVhUsage(this.snapPointsRaw);
2713
+ this.auditDuplicateSnapIds(this.snapPointsRaw);
2714
+ this.rootEl = this.element.closest(".bs-root");
2715
+ this.maxHeight = createMaxHeightController({
2716
+ element: this.element,
2717
+ mode: this.mode,
2718
+ getMaxAxisSize: () => this.snaps.getMaxAxisSize(),
2719
+ setMaxAxisSize: (size) => {
2720
+ this.snaps.setMaxAxisSize(size);
2721
+ },
2722
+ recompute: () => this.recompute()
2723
+ });
2724
+ this.fitDeps = {
2725
+ element: this.element,
2726
+ scrollContainer: this.scrollContainer,
2727
+ mode: this.mode,
2728
+ getMaxHeightCap: () => this.maxHeight.getCap()
2729
+ };
2730
+ this.snaps = new SnapResolver(this.snapPointsRaw, resolved.initialAllowed, this.mode, () => measureFitSize(this.fitDeps), (maxAxisSize) => {
2731
+ this.element.style[layoutAxis(this.mode)] = `${maxAxisSize}px`;
2732
+ });
2733
+ const initial = this.snaps.findById(this.activeId);
2734
+ if (initial) this.size = initial.size;
2735
+ this.applySize(this.size);
2736
+ this.setRestClosed(this.size === 0);
2737
+ this.updateAriaSlider();
2738
+ this.scrollCache = createScrollCache({
2739
+ scrollContainer: this.scrollContainer,
2740
+ getMaxAxisSize: () => this.snaps.getMaxAxisSize()
2741
+ });
2742
+ this.featureCtx = this.buildFeatureContext(opts.autoCollapseAfter);
2743
+ this.attach();
2744
+ this.registerInStack();
2745
+ if (this.snaps.getMaxAxisSize() === 0 && typeof IntersectionObserver !== "undefined" && typeof document !== "undefined") {
2746
+ const io = new IntersectionObserver((entries2) => {
2747
+ for (const entry of entries2) {
2748
+ if (!entry.isIntersecting) continue;
2749
+ if (this.destroyed) return;
2750
+ this.snaps.recompute();
2751
+ this.healActiveSnap();
2752
+ io.disconnect();
2753
+ return;
2754
+ }
2755
+ });
2756
+ io.observe(this.element);
2757
+ this.teardowns.add(() => io.disconnect());
2758
+ }
2759
+ this.teardowns.add(installFitObserver({
2760
+ handle: this.handle,
2761
+ scrollContainer: this.scrollContainer,
2762
+ hasFitSnap: () => this.snapPointsRaw.some((p) => p.size === "fit" || p.size === "content"),
2763
+ isDestroyed: () => this.destroyed,
2764
+ isDragging: () => this.isDraggingAny(),
2765
+ recompute: () => this.recompute()
2766
+ }));
2767
+ if (resolved.radius !== void 0) this.setRadius(resolved.radius);
2768
+ if (resolved.maxHeight !== void 0) this.setMaxHeight(resolved.maxHeight);
2769
+ this.applyAutoAriaLabelledBy();
2770
+ this.runFeatureStage("post");
2771
+ this.warnOrphanFeatureOptions();
2772
+ if (this.size > 0) {
2773
+ this.handleOpen();
2774
+ }
2775
+ }
2776
+ buildFeatureContext(autoCollapseAfter) {
2777
+ return {
2778
+ element: this.element,
2779
+ scrollContainer: this.scrollContainer,
2780
+ sheetId: this.id,
2781
+ options: {
2782
+ routedTo: this.routedTo,
2783
+ closeOnBack: this.closeOnBack,
2784
+ closeOnRouteChange: this.closeOnRouteChange,
2785
+ persistKey: this.persistKey,
2786
+ autoCollapseAfter
2787
+ },
2788
+ isDestroyed: () => this.destroyed,
2789
+ isDragging: () => this.isDraggingAny(),
2790
+ isAnimating: () => this.animation.isAnimating,
2791
+ isTopSheet: () => this.isTopSheet,
2792
+ isVerticalAxis: () => this.mode === "bottom" || this.mode === "top",
2793
+ getSize: () => this.size,
2794
+ getActiveId: () => this.activeId,
2795
+ getAllowedIds: () => this.snaps.getAllowedIds().slice(),
2796
+ allowedIdsBySize: () => this.allowedIdsBySize(),
2797
+ getMaxAxisSize: () => this.snaps.getMaxAxisSize(),
2798
+ resolveSnap: (id) => this.snaps.findById(id),
2799
+ resolveActiveSnap: () => this.snaps.findById(this.activeId),
2800
+ setSize: (size) => {
2801
+ this.size = size;
2802
+ },
2803
+ setMaxAxisSize: (size) => {
2804
+ this.snaps.setMaxAxisSize(size);
2805
+ },
2806
+ applySize: (size) => this.applySize(size),
2807
+ recomputeSnaps: () => this.recomputeSnaps(),
2808
+ newCycle: () => {
2809
+ this.newCycle();
2810
+ },
2811
+ cancelInFlight: () => {
2812
+ this.animation.cancel();
2813
+ },
2814
+ resyncAfterResize: () => this.resyncAfterResize(),
2815
+ snapTo: (id) => {
2816
+ void this.snapTo(id);
2817
+ },
2818
+ close: (reason) => this.close(reason),
2819
+ attachDragSurface: (surface, kind) => this.attachDragSurface(surface, kind),
2820
+ on: (event, fn) => this.on(event, fn),
2821
+ addTeardown: (fn) => this.teardowns.add(fn)
2822
+ };
2823
+ }
2824
+ runFeatureStage(stage) {
2825
+ for (const feature of this.featureList) {
2826
+ if ((feature.stage ?? "post") !== stage) continue;
2827
+ const teardown = feature.install(this.featureCtx);
2828
+ if (teardown) this.teardowns.add(teardown);
2829
+ }
2830
+ }
2831
+ warnOrphanFeatureOptions() {
2832
+ const names = new Set(this.featureList.map((f) => f.name));
2833
+ if ((this.closeOnBack || this.routedTo !== void 0 || this.closeOnRouteChange) && !names.has("route")) {
2834
+ devWarn("[BottomSheet] closeOnBack/routedTo/closeOnRouteChange need routeFeature() — option ignored");
2835
+ }
2836
+ if (this.persistKey && !names.has("persist")) {
2837
+ devWarn("[BottomSheet] persistKey needs persistFeature() — snaps will not be saved");
2838
+ }
2839
+ }
2840
+ get state() {
2841
+ return {
2842
+ size: this.size,
2843
+ activeId: this.activeId,
2844
+ isDragging: this.isDraggingAny(),
2845
+ isAnimating: this.animation.isAnimating,
2846
+ progress: this.computeProgress(this.size)
2847
+ };
2848
+ }
2849
+ on(event, fn) {
2850
+ return this.bus.on(event, fn);
2851
+ }
2852
+ use(plugin) {
2853
+ if (this.destroyed) {
2854
+ devWarn(`[BottomSheet] use("${plugin.name}") called on destroyed engine — ignored.`);
2855
+ return this;
2856
+ }
2857
+ const scoped = [];
2858
+ const scope = {
2859
+ add: (fn) => {
2860
+ if (typeof fn === "function") scoped.push(fn);
2861
+ }
2862
+ };
2863
+ let teardown;
2864
+ try {
2865
+ teardown = plugin.install(this, scope);
2866
+ } catch (err) {
2867
+ for (let i = scoped.length - 1; i >= 0; i--) {
2868
+ try {
2869
+ scoped[i]();
2870
+ } catch (cleanupErr) {
2871
+ queueMicrotask(() => {
2872
+ throw cleanupErr;
2873
+ });
2874
+ }
2875
+ }
2876
+ queueMicrotask(() => {
2877
+ throw err;
2878
+ });
2879
+ return this;
2880
+ }
2881
+ for (const fn of scoped) this.teardowns.add(fn);
2882
+ if (typeof teardown === "function") {
2883
+ this.teardowns.add(teardown);
2884
+ }
2885
+ return this;
2886
+ }
2887
+ setLinkedSheets(sheets) {
2888
+ if (this.destroyed) return;
2889
+ this.linkedSheets = sheets;
2890
+ }
2891
+ getAllowedIds() {
2892
+ return this.snaps.getAllowedIds().slice();
2893
+ }
2894
+ getResolvedSnaps() {
2895
+ return this.snaps.getResolvedSnaps();
2896
+ }
2897
+ async snapTo(id, velocityOrOpts = 0, _skipBeforeSnap = false) {
2898
+ if (this.destroyed) return;
2899
+ const opts = typeof velocityOrOpts === "object" ? velocityOrOpts : {
2900
+ velocity: velocityOrOpts
2901
+ };
2902
+ const velocityPxPerMs = opts.velocity ?? 0;
2903
+ const externalSignal = opts.signal;
2904
+ if (externalSignal?.aborted) return;
2905
+ const target = this.snaps.findById(id);
2906
+ if (!target) {
2907
+ devWarn(`[BottomSheet] unknown snap id: ${id}`);
2908
+ return;
2909
+ }
2910
+ if (!this.snaps.getAllowedIds().includes(id)) {
2911
+ devWarn(`[BottomSheet] snap "${id}" is not in allowed list`);
2912
+ return;
2913
+ }
2914
+ if (!_skipBeforeSnap && this.emitBeforeSnap(target, this.activeId)) {
2915
+ return;
2916
+ }
2917
+ const signal = this.newCycle();
2918
+ const onExternalAbort = () => {
2919
+ this.animation.cancel();
2920
+ this.newCycle();
2921
+ };
2922
+ externalSignal?.addEventListener("abort", onExternalAbort, {
2923
+ once: true
2924
+ });
2925
+ const wasClosed = this.size === 0;
2926
+ const previousId = this.activeId;
2927
+ const previousSize = this.size;
2928
+ this.scrollCache.cache(previousId, previousSize, target.size);
2929
+ this.activeId = id;
2930
+ const rawTargetSize = this.snapPointsRaw.find((p) => p.id === id)?.size;
2931
+ const opensToRest = id === "closed" || rawTargetSize === 0;
2932
+ if (wasClosed && !opensToRest) {
2933
+ this.opening = true;
2934
+ sheetStack.promote(this.id);
2935
+ }
2936
+ if (this.animation.viewTransitionsAvailable) {
2937
+ this.currentViewTransition?.skipTransition?.();
2938
+ const vt = document.startViewTransition(() => {
2939
+ this.applySize(target.size);
2940
+ });
2941
+ vt.ready?.catch(() => {
2942
+ });
2943
+ this.currentViewTransition = vt;
2944
+ try {
2945
+ await vt.finished;
2946
+ } catch {
2947
+ }
2948
+ if (this.currentViewTransition === vt) this.currentViewTransition = null;
2949
+ } else {
2950
+ await this.animation.animateTo(target.size, velocityPxPerMs);
2951
+ }
2952
+ externalSignal?.removeEventListener("abort", onExternalAbort);
2953
+ if (signal.aborted) return;
2954
+ this.opening = false;
2955
+ this.completeSnap(id, target.size, previousSize, wasClosed, false);
2956
+ }
2957
+ completeSnap(id, targetSize, previousSize, wasClosed, withHaptic) {
2958
+ this.scrollCache.restore(id, previousSize, targetSize);
2959
+ this.updateAriaSlider();
2960
+ this.emit("snap", {
2961
+ id,
2962
+ size: this.size,
2963
+ progress: this.computeProgress(this.size)
2964
+ });
2965
+ if (withHaptic) this.haptic();
2966
+ this.setRestClosed(targetSize === 0);
2967
+ if (wasClosed && targetSize > 0) this.emitOpenSequence(id);
2968
+ if (targetSize === 0) this.emitCloseSequence();
2969
+ }
2970
+ setRestClosed(closed) {
2971
+ if (closed === this.restClosed) return;
2972
+ this.restClosed = closed;
2973
+ if (closed) this.element.setAttribute("data-bs-rest", "closed");
2974
+ else this.element.removeAttribute("data-bs-rest");
2975
+ }
2976
+ emitOpenSequence(id) {
2977
+ this.emit("open", {
2978
+ id
2979
+ });
2980
+ this.handleOpen();
2981
+ notifyLinkedSheets(this.linkedSheets, this);
2982
+ this.emit("opened", {
2983
+ id
2984
+ });
2985
+ }
2986
+ emitCloseSequence() {
2987
+ this.emit("close", void 0);
2988
+ this.handleClose();
2989
+ this.emit("closed", void 0);
2990
+ }
2991
+ async dragTo(targetSize, velocityOrOpts = 0) {
2992
+ if (this.destroyed) return;
2993
+ const opts = typeof velocityOrOpts === "object" ? velocityOrOpts : {
2994
+ velocity: velocityOrOpts
2995
+ };
2996
+ const velocityPxPerMs = opts.velocity ?? 0;
2997
+ const externalSignal = opts.signal;
2998
+ if (externalSignal?.aborted) return;
2999
+ this.newCycle();
3000
+ const onExternalAbort = () => {
3001
+ this.animation.cancel();
3002
+ this.newCycle();
3003
+ };
3004
+ externalSignal?.addEventListener("abort", onExternalAbort, {
3005
+ once: true
3006
+ });
3007
+ const clamped = Math.max(0, Math.min(targetSize, this.snaps.getMaxAxisSize()));
3008
+ await this.animation.animateTo(clamped, velocityPxPerMs);
3009
+ externalSignal?.removeEventListener("abort", onExternalAbort);
3010
+ if (this.destroyed || externalSignal?.aborted) return;
3011
+ }
3012
+ open(id) {
3013
+ if (this.destroyed) return Promise.resolve();
3014
+ const target = id ?? this.snaps.getAllowedIds().find((a) => (this.snaps.findById(a)?.size ?? 0) > 0) ?? this.activeId;
3015
+ return this.snapTo(target);
3016
+ }
3017
+ close(reason = "programmatic") {
3018
+ if (this.destroyed) return Promise.resolve();
3019
+ if (this.disableCloseFlag) return Promise.resolve();
3020
+ if (this.size > 0 && this.emitBeforeClose(reason)) return Promise.resolve();
3021
+ const closedId = this.snapPointsRaw.find((p) => p.id === "closed")?.id ?? this.snaps.getAllowedIds().find((a) => this.snaps.findById(a)?.size === 0) ?? this.snaps.getAllowedIds()[0];
3022
+ return this.snapTo(closedId ?? this.activeId);
3023
+ }
3024
+ canDismiss() {
3025
+ return !this.persistent && !this.disableCloseFlag && !this.destroyed;
3026
+ }
3027
+ setPersistent(value) {
3028
+ if (this.destroyed) return;
3029
+ this.persistent = value;
3030
+ }
3031
+ setDisableClose(value) {
3032
+ if (this.destroyed) return;
3033
+ this.disableCloseFlag = value;
3034
+ }
3035
+ setDisableDrag(value) {
3036
+ if (this.destroyed) return;
3037
+ this.disableDragFlag = value;
3038
+ }
3039
+ setDragFromContent(value) {
3040
+ if (this.destroyed) return;
3041
+ this.dragFromContentDefault = value;
3042
+ }
3043
+ setDragFrom(mode) {
3044
+ if (this.destroyed || mode === this.dragFromMode) return;
3045
+ this.gesture?.forceClearDragState();
3046
+ this.detachSheetGesture?.();
3047
+ this.dragFromMode = mode;
3048
+ this.mountSheetGesture();
3049
+ }
3050
+ getDragFrom() {
3051
+ return this.dragFromMode;
3052
+ }
3053
+ isTop() {
3054
+ return this.isTopSheet;
3055
+ }
3056
+ depth() {
3057
+ return sheetStack.depthOf(this.id);
3058
+ }
3059
+ expand() {
3060
+ if (this.destroyed) return Promise.resolve();
3061
+ const ids = this.allowedIdsBySize();
3062
+ const target = ids[ids.length - 1];
3063
+ if (!target) return Promise.resolve();
3064
+ return this.snapTo(target);
3065
+ }
3066
+ collapse() {
3067
+ if (this.destroyed) return Promise.resolve();
3068
+ const ids = this.allowedIdsBySize();
3069
+ const nonZero = ids.find((id) => (this.snaps.findById(id)?.size ?? 0) > 0);
3070
+ const target = nonZero ?? ids[0];
3071
+ if (!target) return Promise.resolve();
3072
+ return this.snapTo(target);
3073
+ }
3074
+ setRadius(r) {
3075
+ if (this.destroyed) return;
3076
+ this.maxHeight.setRadius(r);
3077
+ }
3078
+ setMaxHeight(h) {
3079
+ if (this.destroyed) return;
3080
+ this.maxHeight.setMaxHeight(h);
3081
+ }
3082
+ setAllowed(ids, snap) {
3083
+ if (this.destroyed) return;
3084
+ this.snaps.setAllowedIds(ids);
3085
+ this.updateAriaSlider();
3086
+ if (snap && ids.includes(snap)) {
3087
+ void this.snapTo(snap, 0, true);
3088
+ } else if (!ids.includes(this.activeId)) {
3089
+ const fallback = ids[0];
3090
+ if (fallback) void this.snapTo(fallback, 0, true);
3091
+ }
3092
+ }
3093
+ setBackdropRange(range) {
3094
+ this.scrim.setBackdropRange(range, () => this.applySize(this.size));
3095
+ }
3096
+ setScreenRange(range) {
3097
+ this.scrim.setScreenRange(range, () => this.applySize(this.size));
3098
+ }
3099
+ setScrimColor(color) {
3100
+ this.scrim.setScrimColor(color);
3101
+ }
3102
+ setScrimBlur(blur) {
3103
+ this.scrim.setScrimBlur(blur);
3104
+ }
3105
+ setScrimInteractive(interactive) {
3106
+ this.scrim.setScrimInteractive(interactive);
3107
+ }
3108
+ setScrim(opts) {
3109
+ this.scrim.setScrim(opts, () => this.applySize(this.size));
3110
+ }
3111
+ setScrimMode(mode) {
3112
+ this.scrim.setScrimMode(mode, () => this.applySize(this.size));
3113
+ }
3114
+ setScrimTapToClose(enabled) {
3115
+ this.scrim.setScrimTapToClose(enabled);
3116
+ }
3117
+ setScrimEnabled(enabled) {
3118
+ this.scrim.setScrimEnabled(enabled, () => this.applySize(this.size));
3119
+ }
3120
+ setScrimOverlay(opts) {
3121
+ return this.scrim.setScrimOverlay(opts);
3122
+ }
3123
+ addAnchor(opts) {
3124
+ if (this.destroyed || typeof document === "undefined") return () => {
3125
+ };
3126
+ if (!this.anchorHost) {
3127
+ this.anchorHost = this.rootEl ?? this.element.parentElement ?? document.body;
3128
+ this.anchorHost.style.setProperty("--bs-size", `${this.size}px`);
3129
+ this.anchorHost.style.setProperty("--bs-progress", String(this.computeProgress(this.size)));
3130
+ }
3131
+ const handle = attachAnchor({
3132
+ mode: this.mode,
3133
+ host: this.anchorHost,
3134
+ getState: () => ({
3135
+ activeId: this.activeId,
3136
+ size: this.size,
3137
+ progress: this.computeProgress(this.size)
3138
+ }),
3139
+ on: (event, fn) => this.on(event, fn),
3140
+ isDestroyed: () => this.destroyed
3141
+ }, opts);
3142
+ handle.syncZ(this.stackZ + 1);
3143
+ this.anchors.push(handle);
3144
+ const detach = () => {
3145
+ const idx = this.anchors.indexOf(handle);
3146
+ if (idx === -1) return;
3147
+ this.anchors.splice(idx, 1);
3148
+ handle.detach();
3149
+ };
3150
+ this.teardowns.add(detach);
3151
+ return detach;
3152
+ }
3153
+ setScrimStages(opts) {
3154
+ if (this.destroyed || typeof document === "undefined") return () => {
3155
+ };
3156
+ this.detachScrimStages?.();
3157
+ this.detachScrimStages = null;
3158
+ if (!opts) return () => {
3159
+ };
3160
+ const host = this.screenComponent?.parentElement;
3161
+ if (!host) {
3162
+ devWarn("[BottomSheet] setScrimStages: no scrim element mounted — pass `scrim` to the engine first.");
3163
+ return () => {
3164
+ };
3165
+ }
3166
+ const detachInstalled = installScrimStages({
3167
+ mode: this.mode,
3168
+ host,
3169
+ getState: () => ({
3170
+ activeId: this.activeId,
3171
+ size: this.size,
3172
+ progress: this.computeProgress(this.size)
3173
+ }),
3174
+ on: (event, fn) => this.on(event, fn),
3175
+ isDestroyed: () => this.destroyed
3176
+ }, opts);
3177
+ const detach = () => {
3178
+ if (this.detachScrimStages === detach) this.detachScrimStages = null;
3179
+ detachInstalled();
3180
+ };
3181
+ this.detachScrimStages = detach;
3182
+ this.teardowns.add(() => this.detachScrimStages?.());
3183
+ return detach;
3184
+ }
3185
+ getScrimState() {
3186
+ return {
3187
+ mode: this.scrim.scrimMode,
3188
+ enabled: this.scrim.scrimEnabled
3189
+ };
3190
+ }
3191
+ setSnapPoints(points, allowed) {
3192
+ if (this.destroyed) return;
3193
+ this.animation.cancel();
3194
+ this.newCycle();
3195
+ this.snapPointsRaw = points;
3196
+ auditVhUsage(points);
3197
+ this.auditDuplicateSnapIds(points);
3198
+ this.snaps.setRaw(points);
3199
+ if (allowed) {
3200
+ this.snaps.setAllowedIds(allowed);
3201
+ } else {
3202
+ const present = this.snaps.getResolvedSnaps().map((s) => s.id);
3203
+ const kept = this.snaps.getAllowedIds().filter((id) => present.includes(id));
3204
+ this.snaps.setAllowedIds(kept.length ? kept : present);
3205
+ }
3206
+ this.scrollCache.clear();
3207
+ this.scrim.invalidateOpacityCache();
3208
+ const current = this.snaps.findById(this.activeId);
3209
+ if (current) {
3210
+ this.applySize(current.size);
3211
+ } else {
3212
+ const fallback = this.snaps.getResolvedSnaps().find((s) => this.snaps.getAllowedIds().includes(s.id)) ?? this.snaps.getResolvedSnaps()[0];
3213
+ if (fallback) {
3214
+ this.activeId = fallback.id;
3215
+ this.applySize(fallback.size);
3216
+ }
3217
+ }
3218
+ this.updateAriaSlider();
3219
+ }
3220
+ recompute() {
3221
+ if (this.destroyed) return;
3222
+ this.maxHeight.resolveCap();
3223
+ this.snaps.recompute();
3224
+ this.maxHeight.clampTo();
3225
+ this.scrim.invalidateOpacityCache();
3226
+ if (this.isDraggingAny()) return;
3227
+ this.healActiveSnap();
3228
+ }
3229
+ healActiveSnap() {
3230
+ const current = this.snaps.findById(this.activeId);
3231
+ if (!current) return;
3232
+ this.size = current.size;
3233
+ this.applySize(this.size);
3234
+ this.updateAriaSlider();
3235
+ if (this.size > 0 && !this.lifecycle.isInstalled && !this.opening && !this.animation.isAnimating) {
3236
+ this.emitOpenSequence(this.activeId);
3237
+ }
3238
+ }
3239
+ destroy() {
3240
+ this.destroyed = true;
3241
+ this.opening = false;
3242
+ this.currentAbort.abort();
3243
+ this.currentViewTransition?.skipTransition?.();
3244
+ this.currentViewTransition = null;
3245
+ this.bus.clear();
3246
+ this.animation.cancel();
3247
+ this.gesture?.forceClearDragState();
3248
+ this.contentGesture?.forceClearDragState();
3249
+ this.setRestClosed(false);
3250
+ this.teardowns.drain();
3251
+ this.scrim.destroy();
3252
+ this.animation.destroy();
3253
+ this.lifecycle.destroy();
3254
+ if (this.backdrop) {
3255
+ this.backdrop.style.opacity = "";
3256
+ this.backdrop.style.pointerEvents = "";
3257
+ }
3258
+ for (const host of [
3259
+ this.anchorHost,
3260
+ this.scrimParent,
3261
+ this.rootEl
3262
+ ]) {
3263
+ if (!host) continue;
3264
+ host.style.removeProperty("--bs-size");
3265
+ host.style.removeProperty("--bs-progress");
3266
+ }
3267
+ if (this.screenComponent) {
3268
+ this.screenComponent.style.opacity = "";
3269
+ this.screenComponent.style.display = "";
3270
+ }
3271
+ this.sizeWriteSentinel.invalidate();
3272
+ this.progressWriteSentinel.invalidate();
3273
+ }
3274
+ emit(event, payload) {
3275
+ this.bus.emit(event, payload);
3276
+ }
3277
+ newCycle() {
3278
+ this.allowOvershoot = false;
3279
+ this.opening = false;
3280
+ this.currentAbort.abort();
3281
+ this.currentAbort = new AbortController();
3282
+ return this.currentAbort.signal;
3283
+ }
3284
+ emitBeforeSnap(target, previousId) {
3285
+ return emitCancelable((payload) => this.emit("before-snap", payload), {
3286
+ id: target.id,
3287
+ size: target.size,
3288
+ previousId
3289
+ }, "before-snap");
3290
+ }
3291
+ emitBeforeClose(reason) {
3292
+ return emitCancelable((payload) => this.emit("before-close", payload), {
3293
+ reason
3294
+ }, "before-close");
3295
+ }
3296
+ applyAutoAriaLabelledBy() {
3297
+ if (typeof document === "undefined") return;
3298
+ if (!this.lifecycle.focusTrapEnabled) return;
3299
+ if (this.element.getAttribute("aria-labelledby")) return;
3300
+ const scope = this.handle ?? this.element;
3301
+ const titled = scope.querySelector("[data-bs-title]") ?? scope.querySelector("h1,h2,h3,h4,h5,h6");
3302
+ if (!titled) return;
3303
+ if (!titled.id) {
3304
+ titled.id = nextInstanceId("bs-title");
3305
+ }
3306
+ this.element.setAttribute("aria-labelledby", titled.id);
3307
+ }
3308
+ recomputeSnaps() {
3309
+ this.maxHeight.resolveCap();
3310
+ this.snaps.setRaw(this.snapPointsRaw);
3311
+ this.maxHeight.clampTo();
3312
+ this.scrim.invalidateOpacityCache();
3313
+ }
3314
+ auditDuplicateSnapIds(points) {
3315
+ const seen = /* @__PURE__ */ new Set();
3316
+ for (const p of points) {
3317
+ if (seen.has(p.id)) {
3318
+ devWarn(`[BottomSheet] duplicate snap id "${p.id}" — only the first occurrence is reachable.`);
3319
+ }
3320
+ seen.add(p.id);
3321
+ }
3322
+ }
3323
+ getAllowedRange() {
3324
+ return this.snaps.getAllowedRange();
3325
+ }
3326
+ allowedIdsBySize() {
3327
+ return this.snaps.getAllowedIds().slice().sort((a, b) => (this.snaps.findById(a)?.size ?? 0) - (this.snaps.findById(b)?.size ?? 0));
3328
+ }
3329
+ updateAriaSlider() {
3330
+ this.aria.setValue(this.allowedIdsBySize(), this.activeId);
3331
+ }
3332
+ registerInStack() {
3333
+ this.teardowns.add(sheetStack.push({
3334
+ id: this.id,
3335
+ setZIndex: (z) => {
3336
+ this.stackZ = z;
3337
+ this.element.style.zIndex = String(z);
3338
+ if (this.backdrop) this.backdrop.style.zIndex = String(z - 1);
3339
+ for (const anchor of this.anchors) anchor.syncZ(z + 1);
3340
+ },
3341
+ setIsTop: (isTop) => {
3342
+ this.isTopSheet = isTop;
3343
+ },
3344
+ isOpen: () => this.size > 0 || this.opening,
3345
+ setDepth: (depth) => this.applyStackDepth(depth)
3346
+ }));
3347
+ }
3348
+ applyStackDepth(depth) {
3349
+ if (!this.stackEffectEnabled) return;
3350
+ this.element.setAttribute("data-stack-depth", String(depth));
3351
+ if (!this.stackEffectPrimed) {
3352
+ this.stackEffectPrimed = true;
3353
+ const isVertical = this.mode === "bottom" || this.mode === "top";
3354
+ this.element.style.transformOrigin = isVertical ? "50% 100%" : "0% 50%";
3355
+ this.element.style.transition = "scale 320ms cubic-bezier(0.29, 1.04, 0.84, 0.99)";
3356
+ }
3357
+ const scale = Math.max(1 - depth * 0.04, 0.86);
3358
+ this.element.style.scale = scale === 1 ? "" : String(scale);
3359
+ }
3360
+ buildGestureDeps(surface, gestureOptions) {
3361
+ return {
3362
+ handle: surface,
3363
+ element: this.element,
3364
+ mode: this.mode,
3365
+ gestureOptions,
3366
+ getRoot: () => this.rootEl,
3367
+ getDragContext: () => {
3368
+ const buf = this.dragContextBuf;
3369
+ const r = this.getAllowedRange();
3370
+ buf.size = this.size;
3371
+ buf.maxAxisSize = this.snaps.getMaxAxisSize();
3372
+ buf.range.min = r.min;
3373
+ buf.range.max = r.max;
3374
+ buf.rubberBandEnabled = this.rubberBandEnabled;
3375
+ return buf;
3376
+ },
3377
+ getDisableDrag: () => this.disableDragFlag,
3378
+ cancelAnimation: () => this.animation.cancel(),
3379
+ applySize: (size) => this.applySize(size),
3380
+ animateTo: (size, velocity) => this.animation.animateTo(size, velocity),
3381
+ settleAfterDrag: (delta, velocity, kind) => this.settleAfterDrag(delta, velocity, kind),
3382
+ emit: (event, payload) => this.emit(event, payload),
3383
+ listenerCount: (event) => this.bus.listenerCount(event)
3384
+ };
3385
+ }
3386
+ isContentDragAllowed() {
3387
+ if (this.disableDragFlag) return false;
3388
+ const point = this.snapPointsRaw.find((p) => p.id === this.activeId);
3389
+ return point?.dragFromContent ?? this.dragFromContentDefault;
3390
+ }
3391
+ decideContentDrag(surface, e, delta) {
3392
+ if (this.destroyed) return false;
3393
+ if (e.pointerType === "mouse") return false;
3394
+ if (this.gesture?.isDragging) return false;
3395
+ if (!this.isContentDragAllowed()) return false;
3396
+ if (!isDragAllowedFrom(e.target, "sheet")) return false;
3397
+ const decision = decideContentGesture({
3398
+ delta,
3399
+ scrollTop: surface.scrollTop,
3400
+ atMaxSnap: this.size >= this.getAllowedRange().max - 0.5
3401
+ });
3402
+ if (decision === "pending") return null;
3403
+ return decision === "drag";
3404
+ }
3405
+ attachDragSurface(surface, kind) {
3406
+ if (this.destroyed || kind !== "content") return;
3407
+ if (this.contentGesture) return;
3408
+ const controller = new GestureController(this.buildGestureDeps(surface, {
3409
+ manageTouchAction: false,
3410
+ deferStart: (e, delta) => this.decideContentDrag(surface, e, delta)
3411
+ }));
3412
+ this.contentGesture = controller;
3413
+ const detachGestures = controller.install();
3414
+ const detachGuard = installTouchScrollGuard(surface, () => controller.isDragging);
3415
+ return () => {
3416
+ detachGuard();
3417
+ detachGestures();
3418
+ if (this.contentGesture === controller) this.contentGesture = void 0;
3419
+ };
3420
+ }
3421
+ sheetDragSurface() {
3422
+ return this.dragFromMode === "handle" ? this.handle : this.element;
3423
+ }
3424
+ canStartSheetDrag(e) {
3425
+ if (this.contentGesture?.isDragging) return false;
3426
+ const target = e.target;
3427
+ const scoped = typeof target?.closest === "function";
3428
+ if (scoped && target.closest(NO_DRAG_SELECTOR)) return false;
3429
+ if (this.handle !== this.element && scoped && this.handle.contains(target)) {
3430
+ return true;
3431
+ }
3432
+ if (!isDragAllowedFrom(e.target, this.dragFromMode)) return false;
3433
+ if (this.dragFromMode === "handle") return true;
3434
+ if (this.scrollContainer && scoped && this.scrollContainer.contains(target) && !target.closest(DRAG_ZONE_SELECTOR)) {
3435
+ return false;
3436
+ }
3437
+ return true;
3438
+ }
3439
+ mountSheetGesture() {
3440
+ const surface = this.sheetDragSurface();
3441
+ const controller = new GestureController(this.buildGestureDeps(surface, {
3442
+ shouldStart: (e) => this.canStartSheetDrag(e),
3443
+ manageTouchAction: this.dragFromMode === "handle"
3444
+ }));
3445
+ this.gesture = controller;
3446
+ const detachGestures = controller.install();
3447
+ const detachGuard = this.dragFromMode === "handle" ? null : installTouchScrollGuard(surface, () => controller.isDragging);
3448
+ this.detachSheetGesture = () => {
3449
+ detachGuard?.();
3450
+ detachGestures();
3451
+ };
3452
+ }
3453
+ attach() {
3454
+ this.mountSheetGesture();
3455
+ this.teardowns.add(() => {
3456
+ this.detachSheetGesture?.();
3457
+ this.detachSheetGesture = null;
3458
+ });
3459
+ try {
3460
+ this.attachFeatures();
3461
+ } catch (err) {
3462
+ this.teardowns.drain();
3463
+ try {
3464
+ this.scrim.destroy();
3465
+ } catch (destroyErr) {
3466
+ queueMicrotask(() => {
3467
+ throw destroyErr;
3468
+ });
3469
+ }
3470
+ try {
3471
+ this.animation.destroy();
3472
+ } catch (destroyErr) {
3473
+ queueMicrotask(() => {
3474
+ throw destroyErr;
3475
+ });
3476
+ }
3477
+ try {
3478
+ this.lifecycle.destroy();
3479
+ } catch (destroyErr) {
3480
+ queueMicrotask(() => {
3481
+ throw destroyErr;
3482
+ });
3483
+ }
3484
+ this.destroyed = true;
3485
+ throw err;
3486
+ }
3487
+ }
3488
+ attachFeatures() {
3489
+ this.runFeatureStage("attach");
3490
+ if (typeof window !== "undefined") {
3491
+ this.teardowns.add(installResizeObserver({
3492
+ element: this.element,
3493
+ getMode: () => this.mode,
3494
+ isDestroyed: () => this.destroyed,
3495
+ isDragging: () => this.isDraggingAny(),
3496
+ resolveActiveSnap: () => this.snaps.findById(this.activeId),
3497
+ getMaxAxisSize: () => this.snaps.getMaxAxisSize(),
3498
+ getSize: () => this.size,
3499
+ setMaxAxisSize: (size) => {
3500
+ this.snaps.setMaxAxisSize(size);
3501
+ },
3502
+ setSize: (size) => {
3503
+ this.size = size;
3504
+ },
3505
+ recomputeSnaps: () => this.recomputeSnaps(),
3506
+ applySize: (size) => this.applySize(size),
3507
+ cancelInFlight: () => {
3508
+ this.animation.cancel();
3509
+ },
3510
+ newCycle: () => {
3511
+ this.newCycle();
3512
+ },
3513
+ isAnimating: () => this.animation.isAnimating,
3514
+ resyncAfterCancel: () => this.resyncAfterResize()
3515
+ }));
3516
+ if (this.lifecycle.closeOnEscape) {
3517
+ const onKey = (e) => {
3518
+ if (this.destroyed || e.defaultPrevented) return;
3519
+ if (!this.canDismiss()) return;
3520
+ if (e.key === "Escape" && this.isTopSheet && this.size > 0) {
3521
+ void this.close("escape");
3522
+ }
3523
+ };
3524
+ document.addEventListener("keydown", onKey);
3525
+ this.teardowns.add(() => document.removeEventListener("keydown", onKey));
3526
+ }
3527
+ }
3528
+ this.teardowns.add(installSliderKeyboard({
3529
+ handle: this.handle,
3530
+ mode: this.mode,
3531
+ isDestroyed: () => this.destroyed,
3532
+ getAllowedIds: () => this.allowedIdsBySize(),
3533
+ getActiveId: () => this.activeId,
3534
+ snapTo: (id) => {
3535
+ void this.snapTo(id);
3536
+ }
3537
+ }));
3538
+ this.scrim.attach();
3539
+ }
3540
+ settleAfterDrag(delta, velocity, kind = "touch") {
3541
+ const signal = this.newCycle();
3542
+ const target = this.snaps.findDragSettleTarget({
3543
+ delta,
3544
+ velocity,
3545
+ pointerKind: kind,
3546
+ size: this.size,
3547
+ activeId: this.activeId,
3548
+ flickVelocity: this.flickVelocity,
3549
+ dragThreshold: this.dragThreshold
3550
+ });
3551
+ if (!target) {
3552
+ this.clearWillChangeIfIdle();
3553
+ return;
3554
+ }
3555
+ const previousId = this.activeId;
3556
+ if (target.id === previousId && target.size === this.size) {
3557
+ this.clearWillChangeIfIdle();
3558
+ return;
3559
+ }
3560
+ if (this.emitBeforeSnap(target, previousId)) {
3561
+ const restore2 = this.snaps.findById(previousId);
3562
+ if (restore2) {
3563
+ void this.animation.animateTo(restore2.size, 0);
3564
+ }
3565
+ return;
3566
+ }
3567
+ const previousSize = this.size;
3568
+ const previousSnapSize = this.snaps.findById(previousId)?.size;
3569
+ this.scrollCache.cache(previousId, previousSize, target.size);
3570
+ this.activeId = target.id;
3571
+ if (previousSnapSize === 0 && target.size > 0) {
3572
+ this.opening = true;
3573
+ sheetStack.promote(this.id);
3574
+ }
3575
+ const settleCap = this.snaps.getMaxAxisSize();
3576
+ const settleSize = settleCap > 0 ? Math.min(target.size, settleCap) : target.size;
3577
+ this.allowOvershoot = true;
3578
+ void this.animation.animateTo(settleSize, velocity).then(() => {
3579
+ if (signal.aborted) return;
3580
+ this.allowOvershoot = false;
3581
+ this.opening = false;
3582
+ this.completeSnap(target.id, target.size, previousSize, previousSnapSize === 0, true);
3583
+ });
3584
+ }
3585
+ isDraggingAny() {
3586
+ return (this.gesture?.isDragging ?? false) || (this.contentGesture?.isDragging ?? false);
3587
+ }
3588
+ clearWillChangeIfIdle() {
3589
+ if (this.animation.isAnimating || this.isDraggingAny()) return;
3590
+ this.element.style.willChange = "";
3591
+ }
3592
+ haptic() {
3593
+ if (typeof navigator !== "undefined" && "vibrate" in navigator) {
3594
+ try {
3595
+ navigator.vibrate(HAPTIC_DURATION_MS);
3596
+ } catch {
3597
+ }
3598
+ }
3599
+ }
3600
+ applySize(size, skipTransform = false) {
3601
+ const cap = this.snaps.getMaxAxisSize();
3602
+ const clamped = cap > 0 && size > cap && !this.isDraggingAny() && !this.allowOvershoot ? cap : size;
3603
+ this.size = clamped;
3604
+ if (clamped > 0) this.setRestClosed(false);
3605
+ const offset = cap - clamped;
3606
+ const style = this.element.style;
3607
+ if (!skipTransform) style.transform = this.transformTemplate(offset);
3608
+ if (this.sizeWriteSentinel.shouldWrite(clamped, SIZE_WRITE_EPSILON)) {
3609
+ style.setProperty("--bs-size", `${clamped}px`);
3610
+ if (this.scrimParent) {
3611
+ this.scrimParent.style.setProperty("--bs-size", `${clamped}px`);
3612
+ }
3613
+ if (this.anchorHost && this.anchorHost !== this.scrimParent) {
3614
+ this.anchorHost.style.setProperty("--bs-size", `${clamped}px`);
3615
+ }
3616
+ if (this.rootEl && this.rootEl !== this.scrimParent && this.rootEl !== this.anchorHost) {
3617
+ this.rootEl.style.setProperty("--bs-size", `${clamped}px`);
3618
+ }
3619
+ }
3620
+ const progress = this.computeProgress(clamped);
3621
+ const progressChanged = this.progressWriteSentinel.shouldWrite(progress, OPACITY_WRITE_EPSILON);
3622
+ if (progressChanged) {
3623
+ style.setProperty("--bs-progress", String(progress));
3624
+ if (this.anchorHost) {
3625
+ this.anchorHost.style.setProperty("--bs-progress", String(progress));
3626
+ }
3627
+ if (this.rootEl && this.rootEl !== this.anchorHost) {
3628
+ this.rootEl.style.setProperty("--bs-progress", String(progress));
3629
+ }
3630
+ }
3631
+ this.scrim.applyOpacity(progress, progressChanged);
3632
+ if (progressChanged && this.bus.listenerCount("progress") > 0) {
3633
+ this.progressPayload.value = progress;
3634
+ this.progressPayload.size = clamped;
3635
+ this.emit("progress", this.progressPayload);
3636
+ }
3637
+ }
3638
+ computeProgress(size) {
3639
+ const { min, max } = this.getAllowedRange();
3640
+ const reachableMax = Math.min(max, this.snaps.getMaxAxisSize());
3641
+ if (reachableMax <= min) return 0;
3642
+ return Math.min(Math.max((size - min) / (reachableMax - min), 0), 1);
3643
+ }
3644
+ handleOpen() {
3645
+ sheetStack.promote(this.id);
3646
+ try {
3647
+ this.lifecycle.install();
3648
+ } catch (err) {
3649
+ if (!this.destroyed) this.newCycle();
3650
+ this.handleClose();
3651
+ this.applySize(0);
3652
+ this.activeId = this.snapPointsRaw.find((p) => p.id === "closed")?.id ?? this.activeId;
3653
+ this.size = 0;
3654
+ queueMicrotask(() => {
3655
+ throw err;
3656
+ });
3657
+ }
3658
+ }
3659
+ resyncAfterResize() {
3660
+ if (this.destroyed) return;
3661
+ const target = this.snaps.findById(this.activeId);
3662
+ if (!target) return;
3663
+ this.updateAriaSlider();
3664
+ this.emit("snap", {
3665
+ id: target.id,
3666
+ size: this.size,
3667
+ progress: this.computeProgress(this.size)
3668
+ });
3669
+ if (target.size > 0 && !this.lifecycle.isInstalled) {
3670
+ this.emitOpenSequence(target.id);
3671
+ } else if (target.size === 0 && this.lifecycle.isInstalled) {
3672
+ this.emitCloseSequence();
3673
+ }
3674
+ }
3675
+ handleClose() {
3676
+ this.opening = false;
3677
+ this.lifecycle.release();
3678
+ sheetStack.update();
3679
+ }
3680
+ }
3681
+ const entries = [];
3682
+ const subscribers = /* @__PURE__ */ new Set();
3683
+ let suppress = 0;
3684
+ let internalDepth = 0;
3685
+ const pendingRebrands = [];
3686
+ let listenerInstalled = false;
3687
+ let patchInstalled = false;
3688
+ let origPushState = null;
3689
+ let origReplaceState = null;
3690
+ let patchedPushState = null;
3691
+ let patchedReplaceState = null;
3692
+ const hasWindow = () => typeof window !== "undefined";
3693
+ const hasHistory = () => typeof history !== "undefined";
3694
+ const brandMarker = (surface) => ({
3695
+ ...surface.markerState(),
3696
+ __bs: true
3697
+ });
3698
+ const runInternal = (fn) => {
3699
+ internalDepth += 1;
3700
+ try {
3701
+ fn();
3702
+ } finally {
3703
+ internalDepth -= 1;
3704
+ }
3705
+ };
3706
+ const notifyRouteChange = () => {
3707
+ const snapshot = Array.from(subscribers);
3708
+ for (const fn of snapshot) fn();
3709
+ };
3710
+ const ensureListener = () => {
3711
+ if (listenerInstalled || !hasWindow()) return;
3712
+ window.addEventListener("popstate", onPopState);
3713
+ listenerInstalled = true;
3714
+ };
3715
+ const maybeTeardownListener = () => {
3716
+ if (!listenerInstalled) return;
3717
+ if (entries.length > 0 || subscribers.size > 0 || suppress > 0) return;
3718
+ if (hasWindow()) window.removeEventListener("popstate", onPopState);
3719
+ listenerInstalled = false;
3720
+ };
3721
+ const installPatch = () => {
3722
+ if (patchInstalled || !hasHistory()) return;
3723
+ origPushState = history.pushState;
3724
+ origReplaceState = history.replaceState;
3725
+ patchedPushState = function(...args) {
3726
+ origPushState.apply(this, args);
3727
+ if (internalDepth === 0) notifyRouteChange();
3728
+ };
3729
+ patchedReplaceState = function(...args) {
3730
+ origReplaceState.apply(this, args);
3731
+ if (internalDepth === 0) notifyRouteChange();
3732
+ };
3733
+ history.pushState = patchedPushState;
3734
+ history.replaceState = patchedReplaceState;
3735
+ patchInstalled = true;
3736
+ };
3737
+ const removePatch = () => {
3738
+ if (!patchInstalled) return;
3739
+ if (history.pushState === patchedPushState && origPushState) {
3740
+ history.pushState = origPushState;
3741
+ }
3742
+ if (history.replaceState === patchedReplaceState && origReplaceState) {
3743
+ history.replaceState = origReplaceState;
3744
+ }
3745
+ origPushState = null;
3746
+ origReplaceState = null;
3747
+ patchedPushState = null;
3748
+ patchedReplaceState = null;
3749
+ patchInstalled = false;
3750
+ };
3751
+ function onPopState() {
3752
+ if (suppress > 0) {
3753
+ suppress -= 1;
3754
+ const rebrand = pendingRebrands.shift();
3755
+ if (rebrand) rebrand();
3756
+ maybeTeardownListener();
3757
+ return;
3758
+ }
3759
+ if (entries.length === 0) {
3760
+ notifyRouteChange();
3761
+ maybeTeardownListener();
3762
+ return;
3763
+ }
3764
+ let target = null;
3765
+ let targetIdx = -1;
3766
+ for (let i = entries.length - 1; i >= 0; i -= 1) {
3767
+ const entry = entries[i];
3768
+ if (!entry.surface.isOpen()) continue;
3769
+ if (!target) {
3770
+ target = entry;
3771
+ targetIdx = i;
3772
+ }
3773
+ if (entry.surface.isTop()) {
3774
+ target = entry;
3775
+ targetIdx = i;
3776
+ break;
3777
+ }
3778
+ }
3779
+ if (!target) {
3780
+ const removed = entries.pop();
3781
+ if (removed) removed.live = false;
3782
+ maybeTeardownListener();
3783
+ return;
3784
+ }
3785
+ target.live = false;
3786
+ entries.splice(targetIdx, 1);
3787
+ const settled = target;
3788
+ void Promise.resolve(settled.surface.close()).then(() => {
3789
+ if (settled.surface.isOpen()) restore(settled);
3790
+ });
3791
+ maybeTeardownListener();
3792
+ }
3793
+ function restore(entry) {
3794
+ if (!hasWindow() || !hasHistory()) return;
3795
+ entry.live = true;
3796
+ runInternal(() => {
3797
+ try {
3798
+ history.pushState(brandMarker(entry.surface), "", entry.surface.url);
3799
+ } catch {
3800
+ entry.live = false;
3801
+ }
3802
+ });
3803
+ if (entry.live) {
3804
+ entries.push(entry);
3805
+ ensureListener();
3806
+ }
3807
+ }
3808
+ function pushBackMarker(surface) {
3809
+ const entry = {
3810
+ surface,
3811
+ priorUrl: void 0,
3812
+ live: false
3813
+ };
3814
+ if (!hasWindow() || !hasHistory()) {
3815
+ return entry;
3816
+ }
3817
+ entry.priorUrl = surface.url !== void 0 ? location.href : void 0;
3818
+ runInternal(() => {
3819
+ try {
3820
+ history.pushState(brandMarker(surface), "", surface.url);
3821
+ entry.live = true;
3822
+ } catch {
3823
+ entry.live = false;
3824
+ }
3825
+ });
3826
+ if (entry.live) {
3827
+ entries.push(entry);
3828
+ ensureListener();
3829
+ }
3830
+ return entry;
3831
+ }
3832
+ function popBackMarker(handle) {
3833
+ if (!hasWindow() || !hasHistory()) return;
3834
+ const entry = handle;
3835
+ const idx = entries.indexOf(entry);
3836
+ if (idx === -1 || !entry.live) return;
3837
+ const last = entries.length - 1;
3838
+ entry.live = false;
3839
+ entries.splice(idx, 1);
3840
+ let rebrand = null;
3841
+ if (idx !== last) {
3842
+ const rebrandSurface = entries[entries.length - 1].surface;
3843
+ const rebrandUrl = entry.priorUrl;
3844
+ rebrand = () => {
3845
+ runInternal(() => {
3846
+ history.replaceState(brandMarker(rebrandSurface), "", rebrandUrl !== void 0 ? rebrandUrl : location.href);
3847
+ });
3848
+ };
3849
+ }
3850
+ pendingRebrands.push(rebrand);
3851
+ suppress += 1;
3852
+ runInternal(() => {
3853
+ try {
3854
+ history.back();
3855
+ } catch {
3856
+ suppress = Math.max(0, suppress - 1);
3857
+ pendingRebrands.pop();
3858
+ }
3859
+ });
3860
+ maybeTeardownListener();
3861
+ }
3862
+ function subscribeRouteChange(fn) {
3863
+ if (!hasWindow() || !hasHistory()) return () => {
3864
+ };
3865
+ subscribers.add(fn);
3866
+ if (subscribers.size === 1) installPatch();
3867
+ ensureListener();
3868
+ return () => {
3869
+ if (!subscribers.has(fn)) return;
3870
+ subscribers.delete(fn);
3871
+ if (subscribers.size === 0) removePatch();
3872
+ maybeTeardownListener();
3873
+ };
3874
+ }
3875
+ function installRoute(deps) {
3876
+ if (typeof window === "undefined") {
3877
+ return () => {
3878
+ };
3879
+ }
3880
+ if (!deps.routedTo && !deps.closeOnBack) {
3881
+ return () => {
3882
+ };
3883
+ }
3884
+ let handle = null;
3885
+ const onOpen = () => {
3886
+ if (handle) return;
3887
+ handle = pushBackMarker({
3888
+ isOpen: () => !deps.isDestroyed() && deps.getSize() > 0,
3889
+ isTop: deps.isTopSheet,
3890
+ close: deps.close,
3891
+ markerState: () => deps.routedTo !== void 0 ? {
3892
+ __bsRouted: deps.sheetId
3893
+ } : {
3894
+ __bsSheet: deps.sheetId
3895
+ },
3896
+ url: deps.routedTo
3897
+ });
3898
+ };
3899
+ const onClose = () => {
3900
+ if (!handle) return;
3901
+ popBackMarker(handle);
3902
+ handle = null;
3903
+ };
3904
+ const unsubscribeOpen = deps.on("open", onOpen);
3905
+ const unsubscribeClose = deps.on("close", onClose);
3906
+ if (deps.getSize() > 0) onOpen();
3907
+ return () => {
3908
+ unsubscribeOpen();
3909
+ unsubscribeClose();
3910
+ if (handle) {
3911
+ popBackMarker(handle);
3912
+ handle = null;
3913
+ }
3914
+ };
3915
+ }
3916
+ function installRouteChange(deps) {
3917
+ return subscribeRouteChange(() => {
3918
+ if (deps.isDestroyed() || deps.getSize() <= 0) return;
3919
+ void deps.close();
3920
+ });
3921
+ }
3922
+ function installPersist(engine, key) {
3923
+ const unsubscribe = engine.on("snap", ({ id }) => {
3924
+ if (typeof window === "undefined") return;
3925
+ if (typeof localStorage === "undefined") return;
3926
+ try {
3927
+ localStorage.setItem(key, id);
3928
+ } catch {
3929
+ }
3930
+ });
3931
+ return unsubscribe;
3932
+ }
3933
+ function installAutoCollapse(deps) {
3934
+ const { ms } = deps;
3935
+ if (ms === void 0 || ms <= 0) {
3936
+ return () => {
3937
+ };
3938
+ }
3939
+ let timer = null;
3940
+ const clearTimer = () => {
3941
+ if (timer !== null) {
3942
+ clearTimeout(timer);
3943
+ timer = null;
3944
+ }
3945
+ };
3946
+ const fire = () => {
3947
+ if (deps.isDestroyed()) return;
3948
+ if (deps.isDragging()) return;
3949
+ const active = deps.resolveSnap(deps.getActiveId());
3950
+ if (!active || active.size === 0) return;
3951
+ let target = null;
3952
+ for (const id of deps.getAllowedIds()) {
3953
+ const snap = deps.resolveSnap(id);
3954
+ if (!snap || snap.size <= 0) continue;
3955
+ if (!target || snap.size < target.size) target = snap;
3956
+ }
3957
+ if (!target) return;
3958
+ if (target.id === active.id || target.size >= active.size) return;
3959
+ deps.snapTo(target.id);
3960
+ };
3961
+ const reset = () => {
3962
+ clearTimer();
3963
+ if (deps.isDestroyed()) return;
3964
+ timer = setTimeout(() => {
3965
+ timer = null;
3966
+ fire();
3967
+ }, ms);
3968
+ };
3969
+ const unsubscribe = deps.on("snap", () => reset());
3970
+ if ((deps.resolveSnap(deps.getActiveId())?.size ?? 0) > 0) reset();
3971
+ return () => {
3972
+ clearTimer();
3973
+ unsubscribe();
3974
+ };
3975
+ }
3976
+ function installContentDrag(deps) {
3977
+ const detach = deps.attachDragSurface(deps.container, "content");
3978
+ return () => detach?.();
3979
+ }
3980
+ const SAFETY_PAD = 8;
3981
+ function installVisualViewport(deps) {
3982
+ if (typeof window === "undefined" || !window.visualViewport) {
3983
+ return () => {
3984
+ };
3985
+ }
3986
+ const vv = window.visualViewport;
3987
+ let rafId = null;
3988
+ const onResize = () => {
3989
+ if (rafId !== null) return;
3990
+ rafId = requestAnimationFrame(() => {
3991
+ rafId = null;
3992
+ if (deps.isDestroyed()) return;
3993
+ deps.recomputeSnaps();
3994
+ const isVertical = deps.isVerticalAxis();
3995
+ const visible = isVertical ? vv.height : vv.width;
3996
+ const windowSize = isVertical ? window.innerHeight : window.innerWidth;
3997
+ const clamp = Math.max(0, Math.min(windowSize, visible) - SAFETY_PAD);
3998
+ if (clamp < deps.getMaxAxisSize()) {
3999
+ deps.setMaxAxisSize(clamp);
4000
+ }
4001
+ const wasAnimating = deps.isAnimating?.() ?? false;
4002
+ deps.cancelInFlight();
4003
+ deps.newCycle();
4004
+ const max = deps.getMaxAxisSize();
4005
+ deps.setSize(Math.min(deps.getSize(), max));
4006
+ const current = deps.resolveActiveSnap();
4007
+ if (current && !deps.isDragging()) {
4008
+ deps.applySize(Math.min(current.size, max));
4009
+ if (wasAnimating) deps.resyncAfterCancel?.();
4010
+ } else {
4011
+ deps.applySize(deps.getSize());
4012
+ }
4013
+ });
4014
+ };
4015
+ vv.addEventListener("resize", onResize);
4016
+ return () => {
4017
+ vv.removeEventListener("resize", onResize);
4018
+ if (rafId !== null) cancelAnimationFrame(rafId);
4019
+ };
4020
+ }
4021
+ function routeFeature() {
4022
+ return {
4023
+ name: "route",
4024
+ install: (ctx) => {
4025
+ const teardowns = new TeardownStack();
4026
+ if (ctx.options.closeOnRouteChange) {
4027
+ teardowns.add(installRouteChange({
4028
+ isDestroyed: ctx.isDestroyed,
4029
+ getSize: ctx.getSize,
4030
+ close: () => ctx.close()
4031
+ }));
4032
+ }
4033
+ teardowns.add(installRoute({
4034
+ routedTo: ctx.options.routedTo,
4035
+ closeOnBack: ctx.options.closeOnBack,
4036
+ isTopSheet: ctx.isTopSheet,
4037
+ getSize: ctx.getSize,
4038
+ isDestroyed: ctx.isDestroyed,
4039
+ close: () => ctx.close("back"),
4040
+ on: ctx.on,
4041
+ sheetId: ctx.sheetId
4042
+ }));
4043
+ return () => teardowns.drain();
4044
+ }
4045
+ };
4046
+ }
4047
+ function persistFeature() {
4048
+ return {
4049
+ name: "persist",
4050
+ install: (ctx) => {
4051
+ const key = ctx.options.persistKey;
4052
+ if (!key) return;
4053
+ return installPersist({
4054
+ on: ctx.on
4055
+ }, key);
4056
+ }
4057
+ };
4058
+ }
4059
+ function autoCollapseFeature() {
4060
+ return {
4061
+ name: "auto-collapse",
4062
+ install: (ctx) => installAutoCollapse({
4063
+ ms: ctx.options.autoCollapseAfter,
4064
+ isDestroyed: ctx.isDestroyed,
4065
+ isDragging: ctx.isDragging,
4066
+ getAllowedIds: ctx.getAllowedIds,
4067
+ getActiveId: ctx.getActiveId,
4068
+ resolveSnap: ctx.resolveSnap,
4069
+ snapTo: ctx.snapTo,
4070
+ on: ctx.on
4071
+ })
4072
+ };
4073
+ }
4074
+ function contentSwipeFeature() {
4075
+ return {
4076
+ name: "content-swipe",
4077
+ stage: "attach",
4078
+ install: (ctx) => {
4079
+ if (!ctx.scrollContainer) return;
4080
+ return installContentDrag({
4081
+ container: ctx.scrollContainer,
4082
+ attachDragSurface: ctx.attachDragSurface
4083
+ });
4084
+ }
4085
+ };
4086
+ }
4087
+ function visualViewportFeature() {
4088
+ return {
4089
+ name: "visual-viewport",
4090
+ install: (ctx) => {
4091
+ if (typeof window === "undefined") return;
4092
+ return installVisualViewport({
4093
+ element: ctx.element,
4094
+ isVerticalAxis: ctx.isVerticalAxis,
4095
+ isDestroyed: ctx.isDestroyed,
4096
+ isDragging: ctx.isDragging,
4097
+ recomputeSnaps: ctx.recomputeSnaps,
4098
+ resolveActiveSnap: ctx.resolveActiveSnap,
4099
+ getMaxAxisSize: ctx.getMaxAxisSize,
4100
+ getSize: ctx.getSize,
4101
+ setMaxAxisSize: ctx.setMaxAxisSize,
4102
+ setSize: ctx.setSize,
4103
+ applySize: ctx.applySize,
4104
+ cancelInFlight: ctx.cancelInFlight,
4105
+ newCycle: ctx.newCycle,
4106
+ isAnimating: ctx.isAnimating,
4107
+ resyncAfterCancel: ctx.resyncAfterResize
4108
+ });
4109
+ }
4110
+ };
4111
+ }
4112
+ function defaultEngineFeatures() {
4113
+ return [
4114
+ contentSwipeFeature(),
4115
+ visualViewportFeature(),
4116
+ persistFeature(),
4117
+ autoCollapseFeature(),
4118
+ routeFeature()
4119
+ ];
4120
+ }
4121
+ class BottomSheetEngine extends BottomSheetCore {
4122
+ constructor(opts) {
4123
+ super({
4124
+ ...opts,
4125
+ features: [
4126
+ ...defaultEngineFeatures(),
4127
+ ...opts.features ?? []
4128
+ ]
4129
+ });
4130
+ }
4131
+ }
4132
+ const resolveTeleportTarget = (target) => {
4133
+ if (!target) return null;
4134
+ if (typeof target !== "string") return target;
4135
+ if (typeof document === "undefined") return null;
4136
+ if (target === "body") return document.body;
4137
+ return document.querySelector(target);
4138
+ };
4139
+ const teleportElements = (els, target) => {
4140
+ const records = [];
4141
+ for (const el of els) {
4142
+ if (!el || el.parentNode === target) continue;
4143
+ records.push({
4144
+ el,
4145
+ parent: el.parentNode,
4146
+ next: el.nextSibling
4147
+ });
4148
+ target.appendChild(el);
4149
+ }
4150
+ return () => {
4151
+ for (let i = records.length - 1; i >= 0; i--) {
4152
+ const { el, parent, next } = records[i];
4153
+ if (parent && parent.isConnected) {
4154
+ const before = next && next.parentNode === parent ? next : null;
4155
+ try {
4156
+ parent.insertBefore(el, before);
4157
+ } catch {
4158
+ el.parentNode?.removeChild(el);
4159
+ }
4160
+ } else {
4161
+ el.parentNode?.removeChild(el);
4162
+ }
4163
+ }
4164
+ };
4165
+ };
4166
+ const BottomSheet = component$((props) => {
4167
+ const sheetRef = useSignal();
4168
+ const handleRef = useSignal();
4169
+ const contentRef = useSignal();
4170
+ const backdropRef = useSignal();
4171
+ const scrimRef = useSignal();
4172
+ const headerRef = useSignal();
4173
+ const footerRef = useSignal();
4174
+ const leftBtnRef = useSignal();
4175
+ const rightBtnRef = useSignal();
4176
+ const initialActive = props.initial ?? props.snapPoints[0]?.id ?? "default";
4177
+ const state = useStore({
4178
+ size: 0,
4179
+ activeId: initialActive,
4180
+ isDragging: false,
4181
+ isAnimating: false,
4182
+ progress: 0
4183
+ });
4184
+ const engineStore = useStore({
4185
+ engine: void 0
4186
+ });
4187
+ useVisibleTask$(({ cleanup }) => {
4188
+ if (!sheetRef.value) return;
4189
+ if (!handleRef.value || !contentRef.value) {
4190
+ devWarn("[BottomSheet/qwik] handle or content ref missing at attach time — drag and content-swipe disabled. Make sure ref bindings render before useVisibleTask$ fires.");
4191
+ }
4192
+ const engineOpts = {
4193
+ element: sheetRef.value,
4194
+ handle: handleRef.value,
4195
+ scrollContainer: contentRef.value,
4196
+ backdrop: props.backdrop !== false ? backdropRef.value : void 0,
4197
+ scrim: scrimRef.value,
4198
+ mode: props.mode ?? "bottom",
4199
+ snapPoints: props.snapPoints,
4200
+ allowed: props.allowed,
4201
+ initial: props.initial,
4202
+ animation: props.animation,
4203
+ spring: props.spring,
4204
+ focusTrap: props.focusTrap,
4205
+ closeOnEscape: props.closeOnEscape,
4206
+ closeOnBack: props.closeOnBack,
4207
+ lockBodyScroll: props.lockBodyScroll,
4208
+ rubberBand: props.rubberBand,
4209
+ backdropRange: props.backdropRange,
4210
+ persistent: props.persistent,
4211
+ disableClose: props.disableClose,
4212
+ disableDrag: props.disableDrag,
4213
+ dragFrom: props.dragFrom,
4214
+ dragFromContent: props.dragFromContent,
4215
+ closeOnRouteChange: props.closeOnRouteChange,
4216
+ stackEffect: props.stackEffect,
4217
+ returnFocusTo: props.returnFocusTo,
4218
+ radius: props.radius,
4219
+ maxHeight: props.maxHeight
4220
+ };
4221
+ const engine = new BottomSheetEngine(engineOpts);
4222
+ engineStore.engine = noSerialize(engine);
4223
+ const teleportTarget = props.teleport === false ? null : resolveTeleportTarget(props.teleportTo);
4224
+ const restoreTeleport = teleportTarget ? teleportElements([
4225
+ backdropRef.value,
4226
+ scrimRef.value,
4227
+ sheetRef.value
4228
+ ], teleportTarget) : null;
4229
+ const sync = () => {
4230
+ const s = engine.state;
4231
+ state.size = s.size;
4232
+ state.activeId = s.activeId;
4233
+ state.isDragging = s.isDragging;
4234
+ state.isAnimating = s.isAnimating;
4235
+ state.progress = s.progress;
4236
+ props.onChange$?.(s);
4237
+ };
4238
+ sync();
4239
+ engine.on("snap", (payload) => {
4240
+ sync();
4241
+ props.onSnap$?.(payload.id);
4242
+ });
4243
+ engine.on("open", (payload) => props.onOpen$?.(payload.id));
4244
+ engine.on("close", () => props.onClose$?.());
4245
+ engine.on("opened", (payload) => props.onOpened$?.(payload.id));
4246
+ engine.on("closed", () => props.onClosed$?.());
4247
+ engine.on("dragstart", (payload) => {
4248
+ sync();
4249
+ props.onDragStart$?.(payload);
4250
+ });
4251
+ engine.on("dragend", (payload) => {
4252
+ sync();
4253
+ props.onDragEnd$?.(payload);
4254
+ });
4255
+ let onBackdropClick;
4256
+ if (props.backdrop !== false && props.closeOnBackdrop !== false && backdropRef.value) {
4257
+ onBackdropClick = () => {
4258
+ if (engine.canDismiss()) void engine.close("backdrop");
4259
+ };
4260
+ backdropRef.value.addEventListener("click", onBackdropClick);
4261
+ }
4262
+ const collapseEmpty = (el) => {
4263
+ if (!el) return;
4264
+ const empty = el.children.length === 0 && el.textContent?.trim() === "";
4265
+ if (empty) el.setAttribute("hidden", "");
4266
+ else el.removeAttribute("hidden");
4267
+ };
4268
+ const collapseBoth = () => {
4269
+ collapseEmpty(headerRef.value);
4270
+ collapseEmpty(footerRef.value);
4271
+ collapseEmpty(leftBtnRef.value);
4272
+ collapseEmpty(rightBtnRef.value);
4273
+ };
4274
+ collapseBoth();
4275
+ let slotObserver;
4276
+ if (typeof MutationObserver !== "undefined") {
4277
+ slotObserver = new MutationObserver(collapseBoth);
4278
+ for (const el of [
4279
+ headerRef.value,
4280
+ footerRef.value,
4281
+ leftBtnRef.value,
4282
+ rightBtnRef.value
4283
+ ]) {
4284
+ if (el) {
4285
+ slotObserver.observe(el, {
4286
+ childList: true,
4287
+ subtree: true,
4288
+ characterData: true
4289
+ });
4290
+ }
4291
+ }
4292
+ }
4293
+ cleanup(() => {
4294
+ slotObserver?.disconnect();
4295
+ if (onBackdropClick && backdropRef.value) {
4296
+ backdropRef.value.removeEventListener("click", onBackdropClick);
4297
+ }
4298
+ restoreTeleport?.();
4299
+ engineStore.engine = void 0;
4300
+ engine.destroy();
4301
+ });
4302
+ });
4303
+ useTask$(({ track }) => {
4304
+ const points = track(() => props.snapPoints);
4305
+ const allowed = track(() => props.allowed);
4306
+ const engine = engineStore.engine;
4307
+ if (!engine) return;
4308
+ engine.setSnapPoints(points, allowed);
4309
+ });
4310
+ useTask$(({ track }) => {
4311
+ const radius = track(() => props.radius);
4312
+ const maxHeight = track(() => props.maxHeight);
4313
+ const engine = engineStore.engine;
4314
+ if (!engine) return;
4315
+ if (radius !== void 0) engine.setRadius(radius);
4316
+ if (maxHeight !== void 0) engine.setMaxHeight(maxHeight);
4317
+ });
4318
+ useTask$(({ track }) => {
4319
+ const persistent = track(() => props.persistent);
4320
+ const disableClose = track(() => props.disableClose);
4321
+ const disableDrag = track(() => props.disableDrag);
4322
+ const dragFromContent = track(() => props.dragFromContent);
4323
+ const dragFrom = track(() => props.dragFrom);
4324
+ const engine = engineStore.engine;
4325
+ if (!engine) return;
4326
+ if (persistent !== void 0) engine.setPersistent(persistent);
4327
+ if (disableClose !== void 0) engine.setDisableClose(disableClose);
4328
+ if (disableDrag !== void 0) engine.setDisableDrag(disableDrag);
4329
+ if (dragFromContent !== void 0) {
4330
+ engine.setDragFromContent(dragFromContent);
4331
+ }
4332
+ if (dragFrom !== void 0) engine.setDragFrom(dragFrom);
4333
+ });
4334
+ const showBackdrop = props.backdrop !== false;
4335
+ const mode = props.mode ?? "bottom";
4336
+ const ariaLabel = props.ariaLabel ?? "Bottom sheet";
4337
+ return /* @__PURE__ */ jsxs("div", {
4338
+ class: "bs-root",
4339
+ children: [
4340
+ showBackdrop ? /* @__PURE__ */ jsx("div", {
4341
+ class: "bs-backdrop",
4342
+ ref: backdropRef
4343
+ }) : null,
4344
+ /* @__PURE__ */ jsx("div", {
4345
+ class: "bs-screen",
4346
+ ref: scrimRef,
4347
+ children: /* @__PURE__ */ jsx(Slot, {
4348
+ name: "screen"
4349
+ })
4350
+ }),
4351
+ /* @__PURE__ */ jsxs("section", {
4352
+ class: "bs-sheet",
4353
+ ref: sheetRef,
4354
+ "data-mode": mode,
4355
+ "data-active": state.activeId,
4356
+ role: "dialog",
4357
+ "aria-modal": props.focusTrap ? "true" : void 0,
4358
+ "aria-label": ariaLabel,
4359
+ children: [
4360
+ /* @__PURE__ */ jsx("div", {
4361
+ class: "bs-handle",
4362
+ ref: handleRef,
4363
+ role: "slider",
4364
+ tabIndex: 0,
4365
+ "aria-label": "Resize sheet"
4366
+ }),
4367
+ /* @__PURE__ */ jsx("div", {
4368
+ class: "bs-header",
4369
+ ref: headerRef,
4370
+ children: /* @__PURE__ */ jsx(Slot, {
4371
+ name: "header"
4372
+ })
4373
+ }),
4374
+ /* @__PURE__ */ jsx("div", {
4375
+ class: "bs-content",
4376
+ ref: contentRef,
4377
+ children: /* @__PURE__ */ jsx(Slot, {})
4378
+ }),
4379
+ /* @__PURE__ */ jsx("div", {
4380
+ class: "bs-footer",
4381
+ ref: footerRef,
4382
+ children: /* @__PURE__ */ jsx(Slot, {
4383
+ name: "footer"
4384
+ })
4385
+ }),
4386
+ /* @__PURE__ */ jsx("span", {
4387
+ class: "bs-sr-only",
4388
+ role: "status",
4389
+ "aria-live": "polite",
4390
+ children: state.activeId
4391
+ })
4392
+ ]
4393
+ }),
4394
+ /* @__PURE__ */ jsx("div", {
4395
+ class: "bs-button-slot",
4396
+ "data-side": "left",
4397
+ "data-mode": mode,
4398
+ ref: leftBtnRef,
4399
+ children: /* @__PURE__ */ jsx(Slot, {
4400
+ name: "leftButton"
4401
+ })
4402
+ }),
4403
+ /* @__PURE__ */ jsx("div", {
4404
+ class: "bs-button-slot",
4405
+ "data-side": "right",
4406
+ "data-mode": mode,
4407
+ ref: rightBtnRef,
4408
+ children: /* @__PURE__ */ jsx(Slot, {
4409
+ name: "rightButton"
4410
+ })
4411
+ })
4412
+ ]
4413
+ });
4414
+ });
4415
+ export {
4416
+ BottomSheet
4417
+ };
4418
+ //# sourceMappingURL=index.qwik.mjs.map