@surdeddd/bottom-sheet 0.11.0 → 0.13.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 (58) hide show
  1. package/README.md +68 -1
  2. package/dist/{BottomSheetEngine-DzsIwD28.d.cts → BottomSheetCore-CsIjyq7C.d.cts} +61 -5
  3. package/dist/{BottomSheetEngine-DRjCU73e.d.ts → BottomSheetCore-D16lsa6g.d.ts} +61 -5
  4. package/dist/BottomSheetEngine-BYjAVrmd.d.cts +7 -0
  5. package/dist/BottomSheetEngine-YuCNugZn.d.ts +7 -0
  6. package/dist/core.cjs +3476 -0
  7. package/dist/core.d.cts +3 -0
  8. package/dist/core.d.ts +3 -0
  9. package/dist/core.js +3474 -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 +994 -697
  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 +994 -697
  19. package/dist/features.cjs +533 -0
  20. package/dist/features.d.cts +13 -0
  21. package/dist/features.d.ts +13 -0
  22. package/dist/features.js +526 -0
  23. package/dist/index-DdYLOIIT.d.ts +15 -0
  24. package/dist/index-dfrzw-lI.d.cts +15 -0
  25. package/dist/index.cjs +823 -542
  26. package/dist/index.d.cts +6 -15
  27. package/dist/index.d.ts +6 -15
  28. package/dist/index.js +817 -543
  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 +13 -8
  34. package/dist/overlay.js +13 -8
  35. package/dist/preact.d.cts +2 -1
  36. package/dist/preact.d.ts +2 -1
  37. package/dist/qwik-lib/index.qwik.cjs +4267 -0
  38. package/dist/qwik-lib/index.qwik.mjs +4267 -0
  39. package/dist/qwik.cjs +816 -542
  40. package/dist/qwik.d.cts +2 -2
  41. package/dist/qwik.d.ts +2 -2
  42. package/dist/qwik.js +816 -542
  43. package/dist/react.cjs +816 -542
  44. package/dist/react.d.cts +3 -2
  45. package/dist/react.d.ts +3 -2
  46. package/dist/react.js +816 -542
  47. package/dist/solid.cjs +816 -542
  48. package/dist/solid.d.cts +64 -4
  49. package/dist/solid.d.ts +64 -4
  50. package/dist/solid.js +816 -542
  51. package/dist/solid.source.jsx +816 -542
  52. package/dist/svelte-core.cjs +816 -542
  53. package/dist/svelte-core.js +816 -542
  54. package/dist/svelte.cjs +816 -542
  55. package/dist/svelte.js +816 -542
  56. package/dist/vue.cjs +927 -645
  57. package/dist/vue.js +927 -645
  58. package/package.json +63 -11
package/dist/core.cjs ADDED
@@ -0,0 +1,3476 @@
1
+ 'use strict';
2
+
3
+ var __defProp = Object.defineProperty;
4
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
5
+ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
6
+
7
+ // src/core/lifecycle/sheetStack.ts
8
+ var BASE_Z = 100;
9
+ var STEP = 10;
10
+ var SheetStack = class {
11
+ constructor() {
12
+ __publicField(this, "entries", []);
13
+ }
14
+ push(entry) {
15
+ this.entries.push(entry);
16
+ this.recompute();
17
+ return () => this.remove(entry.id);
18
+ }
19
+ remove(id) {
20
+ const idx = this.entries.findIndex((e) => e.id === id);
21
+ if (idx === -1) return;
22
+ this.entries.splice(idx, 1);
23
+ this.recompute();
24
+ }
25
+ promote(id) {
26
+ const idx = this.entries.findIndex((e) => e.id === id);
27
+ if (idx === -1) return;
28
+ if (idx !== this.entries.length - 1) {
29
+ const [entry] = this.entries.splice(idx, 1);
30
+ this.entries.push(entry);
31
+ }
32
+ this.recompute();
33
+ }
34
+ update() {
35
+ this.recompute();
36
+ }
37
+ size() {
38
+ return this.entries.length;
39
+ }
40
+ depthOf(id) {
41
+ const open = [];
42
+ for (const entry of this.entries) {
43
+ if (!entry.isOpen || entry.isOpen()) open.push(entry);
44
+ }
45
+ const idx = open.findIndex((e) => e.id === id);
46
+ if (idx === -1) return 0;
47
+ return open.length - 1 - idx;
48
+ }
49
+ clear() {
50
+ this.entries = [];
51
+ }
52
+ recompute() {
53
+ let top = null;
54
+ const open = [];
55
+ for (const entry of this.entries) {
56
+ if (!entry.isOpen || entry.isOpen()) {
57
+ top = entry;
58
+ open.push(entry);
59
+ }
60
+ }
61
+ if (!top && this.entries.length > 0) {
62
+ top = this.entries[this.entries.length - 1];
63
+ }
64
+ this.entries.forEach((entry, i) => {
65
+ entry.setZIndex(BASE_Z + i * STEP);
66
+ entry.setIsTop(entry === top);
67
+ if (entry.setDepth) {
68
+ const openIdx = open.indexOf(entry);
69
+ entry.setDepth(openIdx === -1 ? 0 : open.length - 1 - openIdx);
70
+ }
71
+ });
72
+ }
73
+ };
74
+ var sheetStack = new SheetStack();
75
+
76
+ // src/core/primitives/transform.ts
77
+ function buildTransformTemplate(axis) {
78
+ switch (axis) {
79
+ case "bottom":
80
+ return (offset) => `translate3d(0, ${offset}px, 0)`;
81
+ case "top":
82
+ return (offset) => `translate3d(0, ${-offset}px, 0)`;
83
+ case "left":
84
+ return (offset) => `translate3d(${-offset}px, 0, 0)`;
85
+ case "right":
86
+ return (offset) => `translate3d(${offset}px, 0, 0)`;
87
+ }
88
+ }
89
+ var layoutAxis = (axis) => axis === "left" || axis === "right" ? "width" : "height";
90
+
91
+ // src/core/primitives/instance-id.ts
92
+ var counters = {};
93
+ function nextInstanceId(prefix) {
94
+ if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
95
+ return `${prefix}-${crypto.randomUUID()}`;
96
+ }
97
+ counters[prefix] = (counters[prefix] ?? 0) + 1;
98
+ return `${prefix}-${counters[prefix]}`;
99
+ }
100
+
101
+ // src/core/primitives/event-bus.ts
102
+ function createEventBus() {
103
+ const listeners = /* @__PURE__ */ new Map();
104
+ return {
105
+ on(event, fn) {
106
+ let set = listeners.get(event);
107
+ if (!set) {
108
+ set = /* @__PURE__ */ new Set();
109
+ listeners.set(event, set);
110
+ }
111
+ set.add(fn);
112
+ return () => {
113
+ set.delete(fn);
114
+ };
115
+ },
116
+ emit(event, payload) {
117
+ const set = listeners.get(event);
118
+ if (!set) return;
119
+ for (const fn of [...set]) {
120
+ fn(payload);
121
+ }
122
+ },
123
+ listenerCount(event) {
124
+ return listeners.get(event)?.size ?? 0;
125
+ },
126
+ clear() {
127
+ listeners.clear();
128
+ }
129
+ };
130
+ }
131
+
132
+ // src/core/primitives/cssLength.ts
133
+ var probe = null;
134
+ var PROBE_ATTR = "data-bs-probe";
135
+ var ensureProbe = (axis) => {
136
+ if (typeof document === "undefined") return null;
137
+ if (!probe || !probe.isConnected) {
138
+ const existing = document.querySelector(`[${PROBE_ATTR}]`);
139
+ if (existing) {
140
+ probe = existing;
141
+ } else {
142
+ probe = document.createElement("div");
143
+ probe.setAttribute("aria-hidden", "true");
144
+ probe.setAttribute(PROBE_ATTR, "");
145
+ probe.style.cssText = "position:absolute;visibility:hidden;pointer-events:none;contain:strict;left:0;top:0;width:0;height:0;";
146
+ document.body.appendChild(probe);
147
+ }
148
+ }
149
+ if (axis === "vertical") {
150
+ probe.style.width = "0";
151
+ probe.style.height = "";
152
+ } else {
153
+ probe.style.height = "0";
154
+ probe.style.width = "";
155
+ }
156
+ return probe;
157
+ };
158
+ var isViewportAxisVertical = (mode) => mode === "bottom" || mode === "top";
159
+ var viewportSize = (mode) => {
160
+ if (typeof window === "undefined") return 0;
161
+ return isViewportAxisVertical(mode) ? window.innerHeight : window.innerWidth;
162
+ };
163
+ var PERCENT = /^(-?\d+(?:\.\d+)?)%$/;
164
+ var resolveSnap = (point, mode, measureFit) => {
165
+ if (typeof point === "number") return Math.max(0, point);
166
+ if (point === "full") return viewportSize(mode);
167
+ if (point === "fit" || point === "content") {
168
+ return Math.max(0, measureFit?.() ?? 0);
169
+ }
170
+ const pctMatch = point.match(PERCENT);
171
+ if (pctMatch) {
172
+ const pct = parseFloat(pctMatch[1]) / 100;
173
+ return Math.max(0, viewportSize(mode) * pct);
174
+ }
175
+ const axis = isViewportAxisVertical(mode) ? "vertical" : "horizontal";
176
+ const el = ensureProbe(axis);
177
+ if (!el) return 0;
178
+ if (axis === "vertical") {
179
+ el.style.height = point;
180
+ return Math.max(0, el.getBoundingClientRect().height);
181
+ }
182
+ el.style.width = point;
183
+ return Math.max(0, el.getBoundingClientRect().width);
184
+ };
185
+
186
+ // src/core/primitives/devWarn.ts
187
+ function devWarn(...args) {
188
+ if (typeof process !== "undefined" && process.env && process.env.NODE_ENV !== "production" && typeof console !== "undefined") {
189
+ console.warn(...args);
190
+ }
191
+ }
192
+
193
+ // src/core/primitives/snap-points.ts
194
+ var resolveSnapList = (points, mode, measureFit) => points.map((p) => {
195
+ const raw = resolveSnap(p.size, mode, measureFit);
196
+ if (!Number.isFinite(raw) || raw < 0) {
197
+ devWarn(
198
+ `[BottomSheet] snap "${p.id}" resolved to invalid size (${String(p.size)} \u2192 ${raw}); clamped to 0.`
199
+ );
200
+ return { id: p.id, size: 0 };
201
+ }
202
+ return { id: p.id, size: raw };
203
+ }).sort((a, b) => a.size - b.size);
204
+ var VH_NOT_DVH = /(^|[^a-z])vh\b/i;
205
+ var auditVhUsage = (points) => {
206
+ for (const p of points) {
207
+ if (typeof p.size !== "string") continue;
208
+ if (!VH_NOT_DVH.test(p.size)) continue;
209
+ devWarn(
210
+ `[BottomSheet] snap "${p.id}" uses "vh" \u2014 prefer "dvh" on mobile (iOS Safari URL bar makes vh unstable). See https://web.dev/blog/viewport-units`
211
+ );
212
+ }
213
+ };
214
+ var findNearest = (size, resolved, allowed, direction = 0, bias = 0) => {
215
+ const pool = resolved.filter((p) => allowed.includes(p.id));
216
+ if (pool.length === 0) return null;
217
+ const target = size + direction * bias;
218
+ let best = pool[0];
219
+ let bestDist = Math.abs(target - best.size);
220
+ for (let i = 1; i < pool.length; i++) {
221
+ const cand = pool[i];
222
+ const dist = Math.abs(target - cand.size);
223
+ if (dist < bestDist) {
224
+ best = cand;
225
+ bestDist = dist;
226
+ }
227
+ }
228
+ return best;
229
+ };
230
+ var allowedRange = (resolved, allowed) => {
231
+ const pool = resolved.filter((p) => allowed.includes(p.id));
232
+ if (pool.length === 0) return { min: 0, max: 0 };
233
+ return {
234
+ min: pool[0].size,
235
+ max: pool[pool.length - 1].size
236
+ };
237
+ };
238
+ var findById = (id, resolved) => resolved.find((p) => p.id === id) ?? null;
239
+ var MOUSE_FLICK_VELOCITY = 0.4;
240
+ var MOUSE_DRAG_THRESHOLD = 12;
241
+ var DIRECTIONAL_INTENT_VELOCITY = 0.15;
242
+ var findDragSettleTarget = (input) => {
243
+ const speed = Math.abs(input.velocity);
244
+ const flickThreshold = input.pointerKind === "mouse" ? MOUSE_FLICK_VELOCITY : input.flickVelocity;
245
+ const dragThresh = input.pointerKind === "mouse" ? MOUSE_DRAG_THRESHOLD : input.dragThreshold;
246
+ let direction = 0;
247
+ if (speed > flickThreshold || speed > DIRECTIONAL_INTENT_VELOCITY) {
248
+ direction = input.velocity > 0 ? 1 : -1;
249
+ }
250
+ if (Math.abs(input.delta) < dragThresh && direction === 0) {
251
+ return findById(input.activeId, input.resolved);
252
+ }
253
+ const speedBias = speed > flickThreshold ? Math.min(speed * 180, input.maxAxisSize) : 0;
254
+ return findNearest(
255
+ input.size,
256
+ input.resolved,
257
+ input.allowed,
258
+ direction,
259
+ speedBias
260
+ );
261
+ };
262
+
263
+ // src/core/primitives/cancelable-emit.ts
264
+ function emitCancelable(emit, base, warnLabel) {
265
+ let cancelled = false;
266
+ let frozen = false;
267
+ emit({
268
+ ...base,
269
+ cancel: () => {
270
+ if (frozen) {
271
+ devWarn(
272
+ `[BottomSheet] ${warnLabel}.cancel() called asynchronously \u2014 ignored. cancel() must be invoked synchronously inside the listener.`
273
+ );
274
+ return;
275
+ }
276
+ cancelled = true;
277
+ }
278
+ });
279
+ frozen = true;
280
+ return cancelled;
281
+ }
282
+
283
+ // src/core/primitives/snap-resolver.ts
284
+ var SnapResolver = class {
285
+ constructor(raw, allowed, mode, measureFit, onMaxAxisSizeChange) {
286
+ __publicField(this, "mode", mode);
287
+ __publicField(this, "measureFit", measureFit);
288
+ __publicField(this, "onMaxAxisSizeChange", onMaxAxisSizeChange);
289
+ __publicField(this, "raw");
290
+ __publicField(this, "allowedIds");
291
+ __publicField(this, "resolved", []);
292
+ __publicField(this, "maxAxisSize", 0);
293
+ __publicField(this, "rangeCache", null);
294
+ __publicField(this, "notified", false);
295
+ this.raw = raw;
296
+ this.allowedIds = allowed ?? raw.map((p) => p.id);
297
+ this.recompute();
298
+ }
299
+ recompute() {
300
+ const measure = this.measureFit;
301
+ let measured;
302
+ const measureOnce = measure ? () => measured ?? (measured = measure()) : void 0;
303
+ this.resolved = resolveSnapList(this.raw, this.mode, measureOnce);
304
+ const nextMax = this.resolved.reduce(
305
+ (m, s) => s.size > m ? s.size : m,
306
+ 0
307
+ );
308
+ this.rangeCache = null;
309
+ const changed = nextMax !== this.maxAxisSize || !this.notified;
310
+ this.maxAxisSize = nextMax;
311
+ this.notified = true;
312
+ if (changed) this.onMaxAxisSizeChange?.(this.maxAxisSize);
313
+ }
314
+ setRaw(raw) {
315
+ this.raw = raw;
316
+ this.recompute();
317
+ }
318
+ setAllowedIds(ids) {
319
+ this.allowedIds = ids;
320
+ this.rangeCache = null;
321
+ }
322
+ findById(id) {
323
+ return findById(id, this.resolved);
324
+ }
325
+ findDragSettleTarget(args) {
326
+ return findDragSettleTarget({
327
+ ...args,
328
+ resolved: this.resolved,
329
+ allowed: this.allowedIds,
330
+ maxAxisSize: this.maxAxisSize
331
+ });
332
+ }
333
+ getAllowedRange() {
334
+ return this.rangeCache ?? (this.rangeCache = allowedRange(this.resolved, this.allowedIds));
335
+ }
336
+ getResolvedSnaps() {
337
+ return this.resolved;
338
+ }
339
+ getAllowedIds() {
340
+ return this.allowedIds;
341
+ }
342
+ getMaxAxisSize() {
343
+ return this.maxAxisSize;
344
+ }
345
+ setMaxAxisSize(n) {
346
+ this.maxAxisSize = n;
347
+ this.onMaxAxisSizeChange?.(n);
348
+ }
349
+ };
350
+
351
+ // src/core/features/linked-sheets.ts
352
+ function notifyLinkedSheets(sheets, self) {
353
+ if (sheets.length === 0) return;
354
+ for (const linked of sheets) {
355
+ if (linked === self) continue;
356
+ try {
357
+ if (linked.state.size === 0) continue;
358
+ const ids = linked.getAllowedIds();
359
+ const resolved = linked.getResolvedSnaps();
360
+ const sizeOf = (id) => resolved.find((s) => s.id === id)?.size ?? 0;
361
+ const target = ids.find((id) => sizeOf(id) > 0);
362
+ if (target && target !== linked.state.activeId) {
363
+ void linked.snapTo(target);
364
+ }
365
+ } catch (err) {
366
+ queueMicrotask(() => {
367
+ throw err;
368
+ });
369
+ }
370
+ }
371
+ }
372
+
373
+ // src/core/features/resize-observer.ts
374
+ function installResizeObserver(deps) {
375
+ if (typeof window === "undefined") {
376
+ return () => {
377
+ };
378
+ }
379
+ const onResize = () => {
380
+ if (deps.isDestroyed()) return;
381
+ deps.recomputeSnaps();
382
+ const mode = deps.getMode();
383
+ const isVerticalAxis = mode === "bottom" || mode === "top";
384
+ const viewportSize2 = isVerticalAxis ? window.innerHeight : window.innerWidth;
385
+ const max = deps.getMaxAxisSize();
386
+ if (viewportSize2 > 0 && viewportSize2 < max) {
387
+ deps.setMaxAxisSize(viewportSize2);
388
+ }
389
+ const wasAnimating = deps.isAnimating?.() ?? false;
390
+ deps.cancelInFlight();
391
+ deps.newCycle();
392
+ const current = deps.resolveActiveSnap();
393
+ const newMax = deps.getMaxAxisSize();
394
+ const targetSize = current ? Math.min(current.size, newMax) : Math.min(deps.getSize(), newMax);
395
+ deps.setSize(targetSize);
396
+ if (current && !deps.isDragging()) {
397
+ deps.applySize(targetSize);
398
+ if (wasAnimating) deps.resyncAfterCancel?.();
399
+ }
400
+ };
401
+ let resizeObserver = null;
402
+ if (typeof ResizeObserver !== "undefined") {
403
+ resizeObserver = new ResizeObserver(onResize);
404
+ resizeObserver.observe(document.documentElement);
405
+ } else {
406
+ window.addEventListener("resize", onResize);
407
+ }
408
+ window.addEventListener("orientationchange", onResize);
409
+ const onVisibility = () => {
410
+ if (deps.isDestroyed()) return;
411
+ if (document.hidden) deps.cancelInFlight();
412
+ };
413
+ document.addEventListener("visibilitychange", onVisibility);
414
+ return () => {
415
+ resizeObserver?.disconnect();
416
+ window.removeEventListener("resize", onResize);
417
+ window.removeEventListener("orientationchange", onResize);
418
+ document.removeEventListener("visibilitychange", onVisibility);
419
+ };
420
+ }
421
+
422
+ // src/core/features/fit-observer.ts
423
+ var contentTargets = (scroller) => {
424
+ if (!scroller) return [];
425
+ const hasSlot = typeof HTMLSlotElement !== "undefined";
426
+ const out = [];
427
+ for (const child of Array.from(scroller.children)) {
428
+ if (hasSlot && child instanceof HTMLSlotElement) {
429
+ for (const el of child.assignedElements()) out.push(el);
430
+ } else {
431
+ out.push(child);
432
+ }
433
+ }
434
+ return out;
435
+ };
436
+ function installFitObserver(deps) {
437
+ if (typeof ResizeObserver === "undefined") return () => {
438
+ };
439
+ if (!deps.hasFitSnap()) return () => {
440
+ };
441
+ let raf = 0;
442
+ const schedule = () => {
443
+ if (raf) return;
444
+ raf = requestAnimationFrame(() => {
445
+ raf = 0;
446
+ if (deps.isDestroyed() || deps.isDragging()) return;
447
+ deps.recompute();
448
+ });
449
+ };
450
+ const ro = new ResizeObserver(schedule);
451
+ ro.observe(deps.handle);
452
+ const scroller = deps.scrollContainer;
453
+ const observed = /* @__PURE__ */ new Set();
454
+ const slots = /* @__PURE__ */ new Set();
455
+ const hasSlot = typeof HTMLSlotElement !== "undefined";
456
+ const onSlotChange = () => resync();
457
+ const trackSlots = () => {
458
+ if (!scroller || !hasSlot) return;
459
+ for (const child of Array.from(scroller.children)) {
460
+ if (child instanceof HTMLSlotElement && !slots.has(child)) {
461
+ child.addEventListener("slotchange", onSlotChange);
462
+ slots.add(child);
463
+ }
464
+ }
465
+ };
466
+ const resync = () => {
467
+ trackSlots();
468
+ const targets = contentTargets(scroller);
469
+ for (const el of observed) {
470
+ if (!targets.includes(el)) {
471
+ ro.unobserve(el);
472
+ observed.delete(el);
473
+ }
474
+ }
475
+ for (const el of targets) {
476
+ if (!observed.has(el)) {
477
+ ro.observe(el);
478
+ observed.add(el);
479
+ }
480
+ }
481
+ schedule();
482
+ };
483
+ let mo;
484
+ if (scroller) {
485
+ resync();
486
+ if (typeof MutationObserver !== "undefined") {
487
+ mo = new MutationObserver(resync);
488
+ mo.observe(scroller, { childList: true });
489
+ }
490
+ }
491
+ return () => {
492
+ ro.disconnect();
493
+ mo?.disconnect();
494
+ for (const slot of slots) {
495
+ slot.removeEventListener("slotchange", onSlotChange);
496
+ }
497
+ if (raf) cancelAnimationFrame(raf);
498
+ };
499
+ }
500
+
501
+ // src/core/features/max-height-controller.ts
502
+ function createMaxHeightController(deps) {
503
+ let maxHeightCap;
504
+ let maxHeightRaw;
505
+ const resolveCap = () => {
506
+ const raw = maxHeightRaw;
507
+ if (raw === void 0) {
508
+ maxHeightCap = void 0;
509
+ return;
510
+ }
511
+ if (typeof raw === "number") {
512
+ maxHeightCap = raw;
513
+ return;
514
+ }
515
+ if (typeof window === "undefined") return;
516
+ const measured = resolveSnap(raw, deps.mode);
517
+ maxHeightCap = measured > 0 ? measured : void 0;
518
+ };
519
+ const clampTo = () => {
520
+ if (maxHeightCap === void 0) return;
521
+ if (deps.getMaxAxisSize() > maxHeightCap) {
522
+ deps.setMaxAxisSize(maxHeightCap);
523
+ }
524
+ };
525
+ return {
526
+ setRadius(r) {
527
+ deps.element.style.setProperty(
528
+ "--bs-radius",
529
+ typeof r === "number" ? `${r}px` : r
530
+ );
531
+ },
532
+ setMaxHeight(h) {
533
+ const value = typeof h === "number" ? `${h}px` : h;
534
+ const axis = layoutAxis(deps.mode);
535
+ deps.element.style.setProperty(
536
+ axis === "height" ? "max-height" : "max-width",
537
+ value
538
+ );
539
+ maxHeightRaw = h;
540
+ resolveCap();
541
+ deps.recompute();
542
+ },
543
+ resolveCap,
544
+ clampTo,
545
+ getCap: () => maxHeightCap
546
+ };
547
+ }
548
+
549
+ // src/core/features/fit-measurement.ts
550
+ var measureSheetNatural = (element, content, vertical) => {
551
+ const axis = vertical ? "height" : "width";
552
+ const sheetStyle = element.style;
553
+ if (!content) {
554
+ const prev = sheetStyle[axis];
555
+ sheetStyle[axis] = "auto";
556
+ const poked2 = vertical ? element.offsetHeight : element.offsetWidth;
557
+ sheetStyle[axis] = prev;
558
+ return poked2;
559
+ }
560
+ const cs = content.style;
561
+ const prevSheet = sheetStyle[axis];
562
+ const prevFlex = cs.flex;
563
+ const prevContent = vertical ? cs.height : cs.width;
564
+ sheetStyle[axis] = "auto";
565
+ cs.flex = "none";
566
+ if (vertical) cs.height = "auto";
567
+ else cs.width = "auto";
568
+ const poked = vertical ? element.offsetHeight : element.offsetWidth;
569
+ const contentBox = vertical ? content.clientHeight : content.clientWidth;
570
+ const contentScroll = vertical ? content.scrollHeight : content.scrollWidth;
571
+ const natural = Math.max(poked, poked - contentBox + contentScroll);
572
+ sheetStyle[axis] = prevSheet;
573
+ cs.flex = prevFlex;
574
+ if (vertical) cs.height = prevContent;
575
+ else cs.width = prevContent;
576
+ return natural;
577
+ };
578
+ var containingExtent = (element, vertical) => {
579
+ const op = element.offsetParent;
580
+ if (op && op !== document.body && op !== document.documentElement) {
581
+ return vertical ? op.clientHeight : op.clientWidth;
582
+ }
583
+ return vertical ? window.innerHeight : window.innerWidth;
584
+ };
585
+ var measureFitSize = (deps) => {
586
+ const vertical = layoutAxis(deps.mode) === "height";
587
+ const natural = measureSheetNatural(
588
+ deps.element,
589
+ deps.scrollContainer,
590
+ vertical
591
+ );
592
+ const cap = deps.getMaxHeightCap();
593
+ const capped = cap !== void 0 ? Math.min(natural, cap) : natural;
594
+ if (typeof window === "undefined") return capped;
595
+ const viewport = containingExtent(deps.element, vertical);
596
+ return viewport > 0 ? Math.min(capped, viewport) : capped;
597
+ };
598
+
599
+ // src/core/features/slider-keyboard.ts
600
+ function installSliderKeyboard(deps) {
601
+ const isVerticalAxis = deps.mode === "bottom" || deps.mode === "top";
602
+ const stepUp = isVerticalAxis ? "ArrowUp" : "ArrowRight";
603
+ const stepDown = isVerticalAxis ? "ArrowDown" : "ArrowLeft";
604
+ const sheetExpandsKey = deps.mode === "top" ? stepDown : stepUp;
605
+ const sheetCollapsesKey = deps.mode === "top" ? stepUp : stepDown;
606
+ const onHandleKey = (e) => {
607
+ if (deps.isDestroyed()) return;
608
+ const allowed = deps.getAllowedIds();
609
+ const idx = allowed.indexOf(deps.getActiveId());
610
+ if (idx === -1) return;
611
+ if (e.key === sheetExpandsKey && idx < allowed.length - 1) {
612
+ e.preventDefault();
613
+ deps.snapTo(allowed[idx + 1]);
614
+ } else if (e.key === sheetCollapsesKey && idx > 0) {
615
+ e.preventDefault();
616
+ deps.snapTo(allowed[idx - 1]);
617
+ } else if (e.key === "Home") {
618
+ e.preventDefault();
619
+ deps.snapTo(allowed[0]);
620
+ } else if (e.key === "End") {
621
+ e.preventDefault();
622
+ deps.snapTo(allowed[allowed.length - 1]);
623
+ }
624
+ };
625
+ deps.handle.addEventListener("keydown", onHandleKey);
626
+ return () => deps.handle.removeEventListener("keydown", onHandleKey);
627
+ }
628
+
629
+ // src/core/types.ts
630
+ var SCRIM_PRESETS = Object.freeze({
631
+ subtle: {
632
+ color: "rgba(0,0,0,0.2)",
633
+ blur: void 0,
634
+ range: [0.3, 1],
635
+ interactive: false
636
+ },
637
+ standard: {
638
+ color: "rgba(0,0,0,0.4)",
639
+ blur: void 0,
640
+ range: [0, 1],
641
+ interactive: false
642
+ },
643
+ monitoring: {
644
+ color: "rgba(15,15,20,0.55)",
645
+ blur: "4px",
646
+ range: [0, 1],
647
+ interactive: false
648
+ },
649
+ cinematic: {
650
+ color: "rgba(0,0,0,0.7)",
651
+ blur: "12px",
652
+ range: [0, 1],
653
+ interactive: false
654
+ }
655
+ });
656
+
657
+ // src/core/primitives/hot-path-thresholds.ts
658
+ var SIZE_WRITE_EPSILON = 0.5;
659
+ var OPACITY_WRITE_EPSILON = 5e-3;
660
+ var POINTER_EVENTS_OPACITY_THRESHOLD = 0.05;
661
+ var RANGE_DIVISION_EPSILON = 1e-4;
662
+
663
+ // src/core/primitives/opacity-dedup.ts
664
+ var WriteSentinel = class {
665
+ constructor() {
666
+ __publicField(this, "last", -1);
667
+ }
668
+ shouldWrite(next, epsilon) {
669
+ if (Math.abs(next - this.last) <= epsilon) return false;
670
+ this.last = next;
671
+ return true;
672
+ }
673
+ invalidate() {
674
+ this.last = -1;
675
+ }
676
+ setLastWritten(value) {
677
+ this.last = value;
678
+ }
679
+ get value() {
680
+ return this.last;
681
+ }
682
+ };
683
+
684
+ // src/core/primitives/overlay-position.ts
685
+ function resolveSheetAnchoredStyle(mode, position, inset) {
686
+ const sizeOffset = `calc(var(--bs-size, 0px) + ${inset})`;
687
+ switch (mode) {
688
+ case "bottom":
689
+ if (position === "sheet-top-left") return { bottom: sizeOffset, left: inset };
690
+ if (position === "sheet-top-right") return { bottom: sizeOffset, right: inset };
691
+ return { bottom: sizeOffset, left: "50%", transform: "translateX(-50%)" };
692
+ case "top":
693
+ if (position === "sheet-top-left") return { top: sizeOffset, left: inset };
694
+ if (position === "sheet-top-right") return { top: sizeOffset, right: inset };
695
+ return { top: sizeOffset, left: "50%", transform: "translateX(-50%)" };
696
+ case "left":
697
+ if (position === "sheet-top-left") return { left: sizeOffset, top: inset };
698
+ if (position === "sheet-top-right") return { left: sizeOffset, bottom: inset };
699
+ return { left: sizeOffset, top: "50%", transform: "translateY(-50%)" };
700
+ case "right":
701
+ if (position === "sheet-top-left") return { right: sizeOffset, top: inset };
702
+ if (position === "sheet-top-right") return { right: sizeOffset, bottom: inset };
703
+ return { right: sizeOffset, top: "50%", transform: "translateY(-50%)" };
704
+ }
705
+ }
706
+ function applyOverlayPosition(ws, mode, position, inset) {
707
+ switch (position) {
708
+ case "top-left":
709
+ ws.top = inset;
710
+ ws.left = inset;
711
+ break;
712
+ case "top-center":
713
+ ws.top = inset;
714
+ ws.left = "50%";
715
+ ws.transform = "translateX(-50%)";
716
+ break;
717
+ case "top-right":
718
+ ws.top = inset;
719
+ ws.right = inset;
720
+ break;
721
+ case "center-left":
722
+ ws.top = "50%";
723
+ ws.left = inset;
724
+ ws.transform = "translateY(-50%)";
725
+ break;
726
+ case "center":
727
+ ws.top = "50%";
728
+ ws.left = "50%";
729
+ ws.transform = "translate(-50%, -50%)";
730
+ break;
731
+ case "center-right":
732
+ ws.top = "50%";
733
+ ws.right = inset;
734
+ ws.transform = "translateY(-50%)";
735
+ break;
736
+ case "bottom-left":
737
+ ws.bottom = inset;
738
+ ws.left = inset;
739
+ break;
740
+ case "bottom-center":
741
+ ws.bottom = inset;
742
+ ws.left = "50%";
743
+ ws.transform = "translateX(-50%)";
744
+ break;
745
+ case "bottom-right":
746
+ ws.bottom = inset;
747
+ ws.right = inset;
748
+ break;
749
+ case "sheet-top-left":
750
+ case "sheet-top-center":
751
+ case "sheet-top-right": {
752
+ const style = resolveSheetAnchoredStyle(mode, position, inset);
753
+ if (style.top !== void 0) ws.top = style.top;
754
+ if (style.bottom !== void 0) ws.bottom = style.bottom;
755
+ if (style.left !== void 0) ws.left = style.left;
756
+ if (style.right !== void 0) ws.right = style.right;
757
+ if (style.transform !== void 0) ws.transform = style.transform;
758
+ break;
759
+ }
760
+ }
761
+ }
762
+
763
+ // src/core/controllers/scrim-controller.ts
764
+ var VALID_CSS_LENGTH = /^-?\d+(?:\.\d+)?(?:px|em|rem|%)$/;
765
+ var DEFAULT_SCRIM_DIM = "rgba(0, 0, 0, 0.4)";
766
+ var ScrimController = class {
767
+ constructor(deps, opts) {
768
+ __publicField(this, "screenComponent");
769
+ __publicField(this, "backdrop");
770
+ __publicField(this, "mode");
771
+ __publicField(this, "isDestroyed");
772
+ __publicField(this, "isTopSheet");
773
+ __publicField(this, "getAllowedIds");
774
+ __publicField(this, "getResolvedSnaps");
775
+ __publicField(this, "snapToFn");
776
+ __publicField(this, "closeFn");
777
+ __publicField(this, "scrimInteractive", false);
778
+ __publicField(this, "scrimMode");
779
+ __publicField(this, "backdropRange");
780
+ __publicField(this, "screenRange");
781
+ __publicField(this, "scrimEnabled", true);
782
+ __publicField(this, "savedScrimRanges", null);
783
+ __publicField(this, "scrimTapToCloseEnabled", false);
784
+ __publicField(this, "detachScrimTap", null);
785
+ __publicField(this, "scrimOverlayTeardown", null);
786
+ __publicField(this, "backdropOpacitySentinel", new WriteSentinel());
787
+ __publicField(this, "screenOpacitySentinel", new WriteSentinel());
788
+ __publicField(this, "lastBackdropPointer", "");
789
+ __publicField(this, "lastScreenDisplay", "");
790
+ this.screenComponent = deps.screenComponent;
791
+ this.backdrop = deps.backdrop;
792
+ this.mode = deps.mode;
793
+ this.isDestroyed = deps.isDestroyed;
794
+ this.isTopSheet = deps.isTopSheet;
795
+ this.getAllowedIds = deps.getAllowedIds;
796
+ this.getResolvedSnaps = deps.getResolvedSnaps;
797
+ this.snapToFn = deps.snapTo;
798
+ this.closeFn = deps.close;
799
+ this.scrimMode = opts.scrimMode ?? "full";
800
+ const preset = opts.scrimPreset ? SCRIM_PRESETS[opts.scrimPreset] : null;
801
+ this.backdropRange = opts.backdropRange ?? preset?.range ?? [0, 1];
802
+ this.screenRange = opts.screenRange ?? preset?.range ?? [0, 1];
803
+ this.scrimTapToCloseEnabled = opts.scrimTapToClose ?? false;
804
+ const explicitColor = opts.scrimColor ?? preset?.color;
805
+ const effectiveColor = explicitColor ?? (this.scrimMode !== "off" && !this.backdrop ? DEFAULT_SCRIM_DIM : void 0);
806
+ if (this.screenComponent) {
807
+ this.applyScrimStyles({
808
+ color: effectiveColor,
809
+ blur: opts.scrimBlur ?? preset?.blur,
810
+ interactive: opts.scrimInteractive ?? preset?.interactive ?? false
811
+ });
812
+ if (this.scrimMode === "above-sheet") {
813
+ this.applyAboveSheetInset();
814
+ } else if (this.scrimMode === "off") {
815
+ const s = this.screenComponent.style;
816
+ s.opacity = "0";
817
+ s.display = "none";
818
+ this.screenOpacitySentinel.setLastWritten(0);
819
+ this.lastScreenDisplay = "none";
820
+ }
821
+ }
822
+ }
823
+ invalidateOpacityCache() {
824
+ this.backdropOpacitySentinel.invalidate();
825
+ this.screenOpacitySentinel.invalidate();
826
+ }
827
+ attach() {
828
+ if (this.scrimTapToCloseEnabled) {
829
+ this.setScrimTapToClose(true);
830
+ }
831
+ }
832
+ applyOpacity(progress, progressChanged) {
833
+ if (!progressChanged && this.backdropOpacitySentinel.value !== -1 && this.screenOpacitySentinel.value !== -1) {
834
+ return;
835
+ }
836
+ if (this.backdrop && this.isTopSheet()) {
837
+ const [s, e] = this.backdropRange;
838
+ const range = Math.max(e - s, RANGE_DIVISION_EPSILON);
839
+ const backdropOpacity = Math.min(
840
+ Math.max((progress - s) / range, 0),
841
+ 1
842
+ );
843
+ if (this.backdropOpacitySentinel.shouldWrite(
844
+ backdropOpacity,
845
+ OPACITY_WRITE_EPSILON
846
+ )) {
847
+ this.backdrop.style.opacity = String(backdropOpacity);
848
+ }
849
+ const nextPointer = backdropOpacity > POINTER_EVENTS_OPACITY_THRESHOLD ? "auto" : "none";
850
+ if (nextPointer !== this.lastBackdropPointer) {
851
+ this.backdrop.style.pointerEvents = nextPointer;
852
+ this.lastBackdropPointer = nextPointer;
853
+ }
854
+ }
855
+ if (this.screenComponent && this.scrimMode !== "off") {
856
+ const [ss, se] = this.screenRange;
857
+ const screenOpacity = Math.min(
858
+ 1,
859
+ Math.max(
860
+ 0,
861
+ (progress - ss) / Math.max(RANGE_DIVISION_EPSILON, se - ss)
862
+ )
863
+ );
864
+ if (this.screenOpacitySentinel.shouldWrite(
865
+ screenOpacity,
866
+ OPACITY_WRITE_EPSILON
867
+ )) {
868
+ this.screenComponent.style.opacity = String(screenOpacity);
869
+ }
870
+ const nextDisplay = screenOpacity > 0 ? "" : "none";
871
+ if (nextDisplay !== this.lastScreenDisplay) {
872
+ this.screenComponent.style.display = nextDisplay;
873
+ this.lastScreenDisplay = nextDisplay;
874
+ }
875
+ }
876
+ }
877
+ setBackdropRange(range, applySize) {
878
+ if (this.isDestroyed()) return;
879
+ this.backdropRange = range;
880
+ this.invalidateOpacityCache();
881
+ applySize();
882
+ }
883
+ setScreenRange(range, applySize) {
884
+ if (this.isDestroyed()) return;
885
+ this.screenRange = range;
886
+ this.invalidateOpacityCache();
887
+ applySize();
888
+ }
889
+ setScrimColor(color) {
890
+ if (this.isDestroyed()) return;
891
+ this.applyScrimStyles({ color: color === void 0 ? null : color });
892
+ }
893
+ setScrimBlur(blur) {
894
+ if (this.isDestroyed()) return;
895
+ this.applyScrimStyles({ blur: blur === void 0 ? null : blur });
896
+ }
897
+ setScrimInteractive(interactive) {
898
+ if (this.isDestroyed()) return;
899
+ this.applyScrimStyles({ interactive });
900
+ }
901
+ setScrimMode(mode, applySize) {
902
+ if (this.isDestroyed()) return;
903
+ if (mode === this.scrimMode) return;
904
+ this.invalidateOpacityCache();
905
+ const previous = this.scrimMode;
906
+ this.scrimMode = mode;
907
+ if (this.screenComponent) {
908
+ const s = this.screenComponent.style;
909
+ if (previous === "above-sheet" && mode !== "above-sheet") {
910
+ s.position = "";
911
+ s.inset = "";
912
+ s.pointerEvents = "";
913
+ }
914
+ if (mode === "above-sheet") {
915
+ this.applyAboveSheetInset();
916
+ } else if (mode === "off") {
917
+ s.opacity = "0";
918
+ s.display = "none";
919
+ this.screenOpacitySentinel.setLastWritten(0);
920
+ this.lastScreenDisplay = "none";
921
+ } else {
922
+ if (this.lastScreenDisplay === "none") {
923
+ s.display = "";
924
+ this.lastScreenDisplay = "";
925
+ }
926
+ }
927
+ }
928
+ applySize();
929
+ }
930
+ setScrimTapToClose(enabled) {
931
+ if (this.isDestroyed()) return;
932
+ if (!this.screenComponent) return;
933
+ const installed = this.detachScrimTap !== null;
934
+ if (enabled === installed) return;
935
+ if (enabled) {
936
+ const target = this.screenComponent;
937
+ if (target.style.pointerEvents === "none") {
938
+ target.style.pointerEvents = "auto";
939
+ }
940
+ const onClick = () => {
941
+ if (this.isDestroyed()) return;
942
+ if (this.closeFn) {
943
+ this.closeFn();
944
+ return;
945
+ }
946
+ const allowed = this.getAllowedIds();
947
+ const snaps = this.getResolvedSnaps();
948
+ const fallback = allowed.find((id) => {
949
+ const snap = findById(id, snaps);
950
+ return snap !== null && snap.size === 0;
951
+ });
952
+ if (fallback) this.snapToFn(fallback);
953
+ };
954
+ target.addEventListener("click", onClick);
955
+ this.detachScrimTap = () => target.removeEventListener("click", onClick);
956
+ this.scrimTapToCloseEnabled = true;
957
+ } else {
958
+ this.detachScrimTap?.();
959
+ this.detachScrimTap = null;
960
+ this.scrimTapToCloseEnabled = false;
961
+ }
962
+ }
963
+ setScrimEnabled(enabled, applySize) {
964
+ if (this.isDestroyed()) return;
965
+ if (enabled === this.scrimEnabled) return;
966
+ this.scrimEnabled = enabled;
967
+ this.invalidateOpacityCache();
968
+ if (!enabled) {
969
+ this.savedScrimRanges = {
970
+ screen: this.screenRange,
971
+ backdrop: this.backdropRange
972
+ };
973
+ this.screenRange = [1, 1];
974
+ this.backdropRange = [1, 1];
975
+ } else {
976
+ this.screenRange = this.savedScrimRanges?.screen ?? [0, 1];
977
+ this.backdropRange = this.savedScrimRanges?.backdrop ?? [0, 1];
978
+ this.savedScrimRanges = null;
979
+ }
980
+ applySize();
981
+ }
982
+ setScrim(opts, applySize) {
983
+ if (this.isDestroyed()) return;
984
+ if (opts.preset) {
985
+ const cfg = SCRIM_PRESETS[opts.preset];
986
+ this.applyScrimStyles({
987
+ color: cfg.color,
988
+ blur: cfg.blur ?? null,
989
+ interactive: cfg.interactive
990
+ });
991
+ if (!this.scrimEnabled && this.savedScrimRanges) {
992
+ this.savedScrimRanges = { screen: cfg.range, backdrop: cfg.range };
993
+ } else {
994
+ this.screenRange = cfg.range;
995
+ this.backdropRange = cfg.range;
996
+ }
997
+ }
998
+ const noop = () => {
999
+ };
1000
+ if (opts.mode !== void 0) this.setScrimMode(opts.mode, noop);
1001
+ if (opts.enabled !== void 0) this.setScrimEnabled(opts.enabled, noop);
1002
+ if (opts.tapToClose !== void 0) this.setScrimTapToClose(opts.tapToClose);
1003
+ this.applyScrimStyles({
1004
+ color: opts.color,
1005
+ blur: opts.blur,
1006
+ interactive: opts.interactive
1007
+ });
1008
+ if (opts.range !== void 0) {
1009
+ if (!this.scrimEnabled && this.savedScrimRanges) {
1010
+ this.savedScrimRanges = {
1011
+ screen: opts.range,
1012
+ backdrop: this.savedScrimRanges.backdrop
1013
+ };
1014
+ } else {
1015
+ this.screenRange = opts.range;
1016
+ }
1017
+ }
1018
+ this.invalidateOpacityCache();
1019
+ applySize();
1020
+ }
1021
+ setScrimOverlay(opts) {
1022
+ if (this.isDestroyed()) return () => {
1023
+ };
1024
+ if (!this.screenComponent) return () => {
1025
+ };
1026
+ this.scrimOverlayTeardown?.();
1027
+ const screen = this.screenComponent;
1028
+ const insetRaw = opts.inset ?? "16px";
1029
+ const inset = VALID_CSS_LENGTH.test(insetRaw) ? insetRaw : "16px";
1030
+ if (inset !== insetRaw) {
1031
+ devWarn(
1032
+ `[bottom-sheet] setScrimOverlay: invalid inset ${JSON.stringify(insetRaw)}; expected a CSS length like "16px" / "1rem" / "5%". Falling back to "16px".`
1033
+ );
1034
+ }
1035
+ if (opts.children.ownerDocument && opts.children.ownerDocument !== screen.ownerDocument) {
1036
+ devWarn(
1037
+ "[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."
1038
+ );
1039
+ }
1040
+ const position = opts.position ?? "top-right";
1041
+ const interactive = opts.interactive ?? true;
1042
+ const wrapper = screen.ownerDocument.createElement("div");
1043
+ wrapper.className = "bs-scrim-overlay";
1044
+ const ws = wrapper.style;
1045
+ ws.position = "absolute";
1046
+ applyOverlayPosition(ws, this.mode, position, inset);
1047
+ if (interactive) {
1048
+ ws.pointerEvents = "auto";
1049
+ }
1050
+ wrapper.appendChild(opts.children);
1051
+ const host = screen.parentElement;
1052
+ if (!host) {
1053
+ devWarn(
1054
+ "[bottom-sheet] setScrimOverlay: scrim element has no parent, skipping overlay injection. Mount the sheet root before calling setScrimOverlay."
1055
+ );
1056
+ return () => {
1057
+ };
1058
+ }
1059
+ host.appendChild(wrapper);
1060
+ const teardown = () => {
1061
+ if (wrapper.parentNode) wrapper.parentNode.removeChild(wrapper);
1062
+ if (this.scrimOverlayTeardown === teardown) {
1063
+ this.scrimOverlayTeardown = null;
1064
+ }
1065
+ };
1066
+ this.scrimOverlayTeardown = teardown;
1067
+ return teardown;
1068
+ }
1069
+ destroy() {
1070
+ this.detachScrimTap?.();
1071
+ this.detachScrimTap = null;
1072
+ this.scrimOverlayTeardown?.();
1073
+ this.scrimOverlayTeardown = null;
1074
+ if (this.screenComponent) {
1075
+ const s = this.screenComponent.style;
1076
+ s.opacity = "";
1077
+ s.display = "";
1078
+ s.position = "";
1079
+ s.inset = "";
1080
+ s.pointerEvents = "";
1081
+ }
1082
+ if (this.backdrop) {
1083
+ this.backdrop.style.opacity = "";
1084
+ this.backdrop.style.pointerEvents = "";
1085
+ }
1086
+ this.invalidateOpacityCache();
1087
+ this.lastBackdropPointer = "";
1088
+ this.lastScreenDisplay = "";
1089
+ }
1090
+ applyScrimStyles(opts) {
1091
+ if (!this.screenComponent) return;
1092
+ const s = this.screenComponent.style;
1093
+ if (opts.color !== void 0) {
1094
+ s.background = opts.color === null ? "" : opts.color;
1095
+ }
1096
+ if (opts.blur !== void 0) {
1097
+ const w = s;
1098
+ if (opts.blur === null) {
1099
+ s.backdropFilter = "";
1100
+ w.webkitBackdropFilter = "";
1101
+ } else {
1102
+ const filter = `blur(${opts.blur})`;
1103
+ s.backdropFilter = filter;
1104
+ w.webkitBackdropFilter = filter;
1105
+ }
1106
+ }
1107
+ if (opts.interactive !== void 0) {
1108
+ this.scrimInteractive = opts.interactive;
1109
+ s.pointerEvents = opts.interactive ? "auto" : "none";
1110
+ }
1111
+ }
1112
+ applyAboveSheetInset() {
1113
+ if (!this.screenComponent) return;
1114
+ const insetByMode = {
1115
+ bottom: "0 0 var(--bs-size) 0",
1116
+ top: "var(--bs-size) 0 0 0",
1117
+ left: "0 0 0 var(--bs-size)",
1118
+ right: "0 var(--bs-size) 0 0"
1119
+ };
1120
+ const s = this.screenComponent.style;
1121
+ s.position = "fixed";
1122
+ s.inset = insetByMode[this.mode];
1123
+ s.pointerEvents = this.scrimInteractive || this.scrimTapToCloseEnabled ? "auto" : "none";
1124
+ }
1125
+ };
1126
+
1127
+ // src/core/animation/animation.ts
1128
+ var easeOutBack = (t) => {
1129
+ const c1 = 1.40158;
1130
+ const c3 = c1 + 1;
1131
+ return 1 + c3 * Math.pow(t - 1, 3) + c1 * Math.pow(t - 1, 2);
1132
+ };
1133
+ var easeOutCubic = (t) => 1 - Math.pow(1 - t, 3);
1134
+ var easeLinear = (t) => t;
1135
+ var easeOutQuint = (t) => 1 - Math.pow(1 - t, 5);
1136
+ var tween = ({
1137
+ from,
1138
+ to,
1139
+ duration,
1140
+ easing = easeOutCubic,
1141
+ onUpdate
1142
+ }) => {
1143
+ let cancelled = false;
1144
+ let rafId = 0;
1145
+ let resolveFn = null;
1146
+ const start = performance.now();
1147
+ const promise = new Promise((resolve) => {
1148
+ resolveFn = resolve;
1149
+ if (duration <= 0) {
1150
+ onUpdate(to);
1151
+ resolve();
1152
+ return;
1153
+ }
1154
+ const step = (now) => {
1155
+ if (cancelled) return;
1156
+ const elapsed = now - start;
1157
+ const t = Math.min(elapsed / duration, 1);
1158
+ const value = from + (to - from) * easing(t);
1159
+ onUpdate(value);
1160
+ if (t < 1) {
1161
+ rafId = requestAnimationFrame(step);
1162
+ } else {
1163
+ resolve();
1164
+ }
1165
+ };
1166
+ rafId = requestAnimationFrame(step);
1167
+ });
1168
+ return {
1169
+ cancel: () => {
1170
+ cancelled = true;
1171
+ cancelAnimationFrame(rafId);
1172
+ resolveFn?.();
1173
+ },
1174
+ promise
1175
+ };
1176
+ };
1177
+ var prefersReducedMotion = () => {
1178
+ if (typeof window === "undefined" || !window.matchMedia) return false;
1179
+ return window.matchMedia("(prefers-reduced-motion: reduce)").matches;
1180
+ };
1181
+
1182
+ // src/core/animation/spring.ts
1183
+ var DEFAULT_SPRING = {
1184
+ stiffness: 220,
1185
+ damping: 26,
1186
+ mass: 1,
1187
+ restDelta: 0.5,
1188
+ restSpeed: 0.5
1189
+ };
1190
+ var MAX_DT = 1 / 30;
1191
+ var runSpring = ({
1192
+ from,
1193
+ to,
1194
+ velocity = 0,
1195
+ config,
1196
+ onUpdate
1197
+ }) => {
1198
+ const cfg = { ...DEFAULT_SPRING, ...config };
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
+
1246
+ // src/core/animation/animation-presets.ts
1247
+ var DEFAULT_DURATION = 220;
1248
+ function resolveAnimationPreset(animation) {
1249
+ switch (animation) {
1250
+ case "ios-spring":
1251
+ return {
1252
+ kind: "spring",
1253
+ spring: { stiffness: 300, damping: 30, mass: 1 }
1254
+ };
1255
+ case "material-bounce":
1256
+ return {
1257
+ kind: "spring",
1258
+ spring: { stiffness: 200, damping: 22, mass: 1 }
1259
+ };
1260
+ case "linear":
1261
+ return { kind: "tween", duration: DEFAULT_DURATION, easing: easeLinear };
1262
+ case "snappy":
1263
+ return { kind: "tween", duration: 180, easing: easeOutQuint };
1264
+ case "tween":
1265
+ return { kind: "tween" };
1266
+ case "spring":
1267
+ case void 0:
1268
+ default:
1269
+ return { kind: "spring" };
1270
+ }
1271
+ }
1272
+
1273
+ // src/core/animation/waapi-settle.ts
1274
+ var SIM_HZ = 240;
1275
+ var SAMPLE_STRIDE = 4;
1276
+ var MAX_SIM_MS = 3e3;
1277
+ function sampleSpringSettle(from, to, velocityPxPerS, config) {
1278
+ const cfg = { ...DEFAULT_SPRING, ...config };
1279
+ const subDt = 1 / SIM_HZ;
1280
+ const stepMs = SAMPLE_STRIDE / SIM_HZ * 1e3;
1281
+ const maxSteps = Math.ceil(MAX_SIM_MS / 1e3 * SIM_HZ);
1282
+ const values = [from];
1283
+ let x = from;
1284
+ let v = velocityPxPerS;
1285
+ for (let i = 1; i <= maxSteps; i += 1) {
1286
+ const force = -cfg.stiffness * (x - to) - cfg.damping * v;
1287
+ v += force / cfg.mass * subDt;
1288
+ x += v * subDt;
1289
+ if (!Number.isFinite(x) || !Number.isFinite(v)) {
1290
+ values.push(to);
1291
+ break;
1292
+ }
1293
+ if (i % SAMPLE_STRIDE === 0) values.push(x);
1294
+ const atRest = Math.abs(to - x) < (cfg.restDelta ?? 0.5) && Math.abs(v) < (cfg.restSpeed ?? 0.5);
1295
+ if (atRest) break;
1296
+ }
1297
+ if (values[values.length - 1] !== to) values.push(to);
1298
+ return {
1299
+ values,
1300
+ stepMs,
1301
+ durationMs: (values.length - 1) * stepMs
1302
+ };
1303
+ }
1304
+ function sampleTweenSettle(from, to, durationMs, easing) {
1305
+ const count = Math.max(2, Math.min(60, Math.round(durationMs / 16)));
1306
+ const values = [];
1307
+ for (let i = 0; i <= count; i += 1) {
1308
+ const t = i / count;
1309
+ values.push(from + (to - from) * easing(t));
1310
+ }
1311
+ values[values.length - 1] = to;
1312
+ return {
1313
+ values,
1314
+ stepMs: durationMs / count,
1315
+ durationMs
1316
+ };
1317
+ }
1318
+
1319
+ // src/core/controllers/animation-runner.ts
1320
+ var DEFAULT_DURATION2 = 220;
1321
+ var VELOCITY_PX_PER_S = 1e3;
1322
+ var AnimationRunner = class {
1323
+ constructor(deps, opts) {
1324
+ __publicField(this, "element");
1325
+ __publicField(this, "getRootEl");
1326
+ __publicField(this, "applySizeFn");
1327
+ __publicField(this, "getSize");
1328
+ __publicField(this, "isDragging");
1329
+ __publicField(this, "applyAux");
1330
+ __publicField(this, "getTransformFor");
1331
+ __publicField(this, "settleWaapi");
1332
+ __publicField(this, "currentWaapi", null);
1333
+ __publicField(this, "animationKind");
1334
+ __publicField(this, "springConfig");
1335
+ __publicField(this, "duration");
1336
+ __publicField(this, "easing");
1337
+ __publicField(this, "respectReducedMotion");
1338
+ __publicField(this, "reducedMotion", false);
1339
+ __publicField(this, "detachReducedMotion", null);
1340
+ __publicField(this, "viewTransitionsAvailable");
1341
+ __publicField(this, "currentTween", null);
1342
+ __publicField(this, "currentSpring", null);
1343
+ this.element = deps.element;
1344
+ this.getRootEl = deps.getRootEl;
1345
+ this.applySizeFn = deps.applySize;
1346
+ this.getSize = deps.getSize;
1347
+ this.isDragging = deps.isDragging;
1348
+ this.applyAux = deps.applyAux;
1349
+ this.getTransformFor = deps.getTransformFor;
1350
+ this.settleWaapi = opts.settleAnimation === "waapi";
1351
+ const preset = resolveAnimationPreset(opts.animation);
1352
+ this.animationKind = preset.kind;
1353
+ this.duration = opts.duration ?? preset.duration ?? DEFAULT_DURATION2;
1354
+ this.easing = opts.easing ?? preset.easing ?? easeOutBack;
1355
+ this.springConfig = { ...preset.spring ?? {}, ...opts.spring ?? {} };
1356
+ this.respectReducedMotion = opts.respectReducedMotion ?? true;
1357
+ const viewTransitionsEnabled = opts.viewTransitions ?? false;
1358
+ this.viewTransitionsAvailable = viewTransitionsEnabled && typeof document !== "undefined" && typeof document.startViewTransition === "function";
1359
+ if (this.respectReducedMotion && typeof window !== "undefined" && typeof window.matchMedia === "function") {
1360
+ const mq = window.matchMedia("(prefers-reduced-motion: reduce)");
1361
+ this.reducedMotion = mq.matches;
1362
+ const onMqChange = (e) => {
1363
+ this.reducedMotion = e.matches;
1364
+ };
1365
+ if (typeof mq.addEventListener === "function") {
1366
+ mq.addEventListener("change", onMqChange);
1367
+ this.detachReducedMotion = () => mq.removeEventListener("change", onMqChange);
1368
+ } else if (typeof mq.addListener === "function") {
1369
+ mq.addListener(onMqChange);
1370
+ this.detachReducedMotion = () => mq.removeListener(onMqChange);
1371
+ }
1372
+ }
1373
+ }
1374
+ get isAnimating() {
1375
+ return this.currentTween !== null || this.currentSpring !== null || this.currentWaapi !== null;
1376
+ }
1377
+ cancel() {
1378
+ this.currentTween?.cancel();
1379
+ this.currentSpring?.cancel();
1380
+ if (this.currentWaapi) {
1381
+ const entry = this.currentWaapi;
1382
+ this.currentWaapi = null;
1383
+ entry.stop();
1384
+ try {
1385
+ entry.anim.cancel();
1386
+ } catch {
1387
+ }
1388
+ }
1389
+ }
1390
+ async animateTo(target, velocityPxPerMs) {
1391
+ if (target === this.getSize()) {
1392
+ if (!this.isAnimating && !this.isDragging()) {
1393
+ this.element.style.willChange = "";
1394
+ }
1395
+ return;
1396
+ }
1397
+ this.currentTween?.cancel();
1398
+ this.currentSpring?.cancel();
1399
+ this.getRootEl()?.setAttribute("data-animating", "true");
1400
+ this.element.style.willChange = "transform";
1401
+ if (this.reducedMotion) {
1402
+ this.applySizeFn(target);
1403
+ this.getRootEl()?.removeAttribute("data-animating");
1404
+ if (!this.isDragging()) this.element.style.willChange = "";
1405
+ return;
1406
+ }
1407
+ if (this.settleWaapi && this.applyAux && this.getTransformFor && typeof this.element.animate === "function") {
1408
+ const samples = this.animationKind === "spring" ? sampleSpringSettle(
1409
+ this.getSize(),
1410
+ target,
1411
+ velocityPxPerMs * VELOCITY_PX_PER_S,
1412
+ this.springConfig
1413
+ ) : sampleTweenSettle(
1414
+ this.getSize(),
1415
+ target,
1416
+ this.duration,
1417
+ this.easing
1418
+ );
1419
+ if (samples.values.length >= 2 && samples.durationMs > 0) {
1420
+ await this.runWaapiSettle(samples, target);
1421
+ if (!this.isAnimating) {
1422
+ this.getRootEl()?.removeAttribute("data-animating");
1423
+ if (!this.isDragging()) this.element.style.willChange = "";
1424
+ }
1425
+ return;
1426
+ }
1427
+ }
1428
+ if (this.animationKind === "spring") {
1429
+ const spring = runSpring({
1430
+ from: this.getSize(),
1431
+ to: target,
1432
+ velocity: velocityPxPerMs * VELOCITY_PX_PER_S,
1433
+ config: this.springConfig,
1434
+ onUpdate: (v) => this.applySizeFn(v)
1435
+ });
1436
+ this.currentSpring = spring;
1437
+ await spring.promise;
1438
+ if (this.currentSpring === spring) this.currentSpring = null;
1439
+ } else {
1440
+ const tw = tween({
1441
+ from: this.getSize(),
1442
+ to: target,
1443
+ duration: this.duration,
1444
+ easing: this.easing,
1445
+ onUpdate: (v) => this.applySizeFn(v)
1446
+ });
1447
+ this.currentTween = tw;
1448
+ await tw.promise;
1449
+ if (this.currentTween === tw) this.currentTween = null;
1450
+ }
1451
+ if (!this.isAnimating) {
1452
+ this.getRootEl()?.removeAttribute("data-animating");
1453
+ if (!this.isDragging()) this.element.style.willChange = "";
1454
+ }
1455
+ }
1456
+ async runWaapiSettle(samples, target) {
1457
+ const applyAux = this.applyAux;
1458
+ const getTransformFor = this.getTransformFor;
1459
+ const frames = samples.values.map((v) => ({
1460
+ transform: getTransformFor(v)
1461
+ }));
1462
+ const anim = this.element.animate(frames, {
1463
+ duration: samples.durationMs,
1464
+ easing: "linear",
1465
+ fill: "forwards"
1466
+ });
1467
+ let stopped = false;
1468
+ let auxRaf = 0;
1469
+ const start = performance.now();
1470
+ const lastIdx = samples.values.length - 1;
1471
+ const auxTick = () => {
1472
+ if (stopped) return;
1473
+ const elapsed = performance.now() - start;
1474
+ const idx = Math.min(Math.floor(elapsed / samples.stepMs), lastIdx);
1475
+ applyAux(samples.values[idx]);
1476
+ auxRaf = requestAnimationFrame(auxTick);
1477
+ };
1478
+ auxRaf = requestAnimationFrame(auxTick);
1479
+ const entry = {
1480
+ anim,
1481
+ stop: () => {
1482
+ stopped = true;
1483
+ cancelAnimationFrame(auxRaf);
1484
+ }
1485
+ };
1486
+ this.currentWaapi = entry;
1487
+ try {
1488
+ await anim.finished;
1489
+ } catch {
1490
+ }
1491
+ entry.stop();
1492
+ const wasCurrent = this.currentWaapi === entry;
1493
+ if (wasCurrent) this.currentWaapi = null;
1494
+ if (wasCurrent && anim.playState === "finished") {
1495
+ this.applySizeFn(target);
1496
+ try {
1497
+ anim.cancel();
1498
+ } catch {
1499
+ }
1500
+ }
1501
+ }
1502
+ destroy() {
1503
+ this.detachReducedMotion?.();
1504
+ this.detachReducedMotion = null;
1505
+ this.currentTween = null;
1506
+ this.currentSpring = null;
1507
+ if (this.currentWaapi) {
1508
+ const entry = this.currentWaapi;
1509
+ this.currentWaapi = null;
1510
+ entry.stop();
1511
+ try {
1512
+ entry.anim.cancel();
1513
+ } catch {
1514
+ }
1515
+ }
1516
+ }
1517
+ };
1518
+
1519
+ // src/core/lifecycle/focusTrap.ts
1520
+ var FOCUSABLE = [
1521
+ "a[href]",
1522
+ "area[href]",
1523
+ "button:not([disabled])",
1524
+ "input:not([disabled]):not([type='hidden'])",
1525
+ "select:not([disabled])",
1526
+ "textarea:not([disabled])",
1527
+ "iframe",
1528
+ "object",
1529
+ "embed",
1530
+ "[contenteditable='true']",
1531
+ "[tabindex]:not([tabindex='-1'])"
1532
+ ].join(",");
1533
+ var isVisible = (el) => {
1534
+ if (typeof el.checkVisibility === "function") {
1535
+ return el.checkVisibility({ checkOpacity: false, checkVisibilityCSS: true });
1536
+ }
1537
+ return el.offsetParent !== null;
1538
+ };
1539
+ var collectFocusables = (root) => {
1540
+ const out = Array.from(root.querySelectorAll(FOCUSABLE));
1541
+ if (typeof HTMLSlotElement !== "undefined") {
1542
+ for (const slot of Array.from(root.querySelectorAll("slot"))) {
1543
+ for (const assigned of slot.assignedElements()) {
1544
+ if (assigned.matches(FOCUSABLE)) out.push(assigned);
1545
+ out.push(...collectFocusables(assigned));
1546
+ }
1547
+ }
1548
+ }
1549
+ return out;
1550
+ };
1551
+ var focusables = (root) => collectFocusables(root).filter(
1552
+ (el) => !el.hasAttribute("inert") && isVisible(el) && !el.matches("[aria-hidden='true']")
1553
+ );
1554
+ var containsComposed = (container, node) => {
1555
+ let n = node;
1556
+ while (n) {
1557
+ if (n === container) return true;
1558
+ const slot = n.assignedSlot;
1559
+ if (slot) {
1560
+ n = slot;
1561
+ continue;
1562
+ }
1563
+ const parent = n.parentNode;
1564
+ n = parent instanceof ShadowRoot ? parent.host : parent;
1565
+ }
1566
+ return false;
1567
+ };
1568
+ var trapStack = [];
1569
+ var installFocusTrap = (container, options = {}) => {
1570
+ if (typeof document === "undefined") return () => {
1571
+ };
1572
+ const token = {};
1573
+ trapStack.push(token);
1574
+ const isActive = () => trapStack[trapStack.length - 1] === token;
1575
+ const previouslyFocused = document.activeElement;
1576
+ const focusInitial = () => {
1577
+ const spec = options.initialFocus;
1578
+ if (spec === false) {
1579
+ if (!container.hasAttribute("tabindex")) container.tabIndex = -1;
1580
+ container.focus({ preventScroll: true });
1581
+ return;
1582
+ }
1583
+ const initial = typeof spec === "string" ? container.querySelector(spec) : spec ?? focusables(container)[0] ?? container;
1584
+ initial?.focus({ preventScroll: true });
1585
+ };
1586
+ focusInitial();
1587
+ const handleKey = (e) => {
1588
+ if (!isActive()) return;
1589
+ if (e.key === "Escape" && options.onEscape) {
1590
+ options.onEscape();
1591
+ return;
1592
+ }
1593
+ if (e.key !== "Tab") return;
1594
+ const list = focusables(container);
1595
+ if (list.length === 0) {
1596
+ e.preventDefault();
1597
+ return;
1598
+ }
1599
+ const first = list[0];
1600
+ const last = list[list.length - 1];
1601
+ const active = document.activeElement;
1602
+ if (e.shiftKey && active === first) {
1603
+ e.preventDefault();
1604
+ last.focus();
1605
+ } else if (!e.shiftKey && active === last) {
1606
+ e.preventDefault();
1607
+ first.focus();
1608
+ }
1609
+ };
1610
+ const handleFocusIn = (e) => {
1611
+ if (!isActive()) return;
1612
+ const target = e.target;
1613
+ if (target && !containsComposed(container, target)) {
1614
+ const list = focusables(container);
1615
+ list[0]?.focus({ preventScroll: true });
1616
+ }
1617
+ };
1618
+ document.addEventListener("keydown", handleKey, true);
1619
+ document.addEventListener("focusin", handleFocusIn, true);
1620
+ let released = false;
1621
+ return () => {
1622
+ if (released) return;
1623
+ released = true;
1624
+ const idx = trapStack.indexOf(token);
1625
+ if (idx !== -1) trapStack.splice(idx, 1);
1626
+ document.removeEventListener("keydown", handleKey, true);
1627
+ document.removeEventListener("focusin", handleFocusIn, true);
1628
+ if (previouslyFocused && document.contains(previouslyFocused)) {
1629
+ previouslyFocused.focus({ preventScroll: true });
1630
+ } else {
1631
+ const fallback = document.querySelector("[data-bs-restore-fallback]");
1632
+ fallback?.focus({ preventScroll: true });
1633
+ }
1634
+ };
1635
+ };
1636
+
1637
+ // src/core/lifecycle/scrollLock.ts
1638
+ var lockCount = 0;
1639
+ var savedStyles = null;
1640
+ var lockBodyScroll = () => {
1641
+ if (typeof document === "undefined") return () => {
1642
+ };
1643
+ if (lockCount === 0) {
1644
+ const body = document.body;
1645
+ savedStyles = {
1646
+ overflow: body.style.overflow,
1647
+ position: body.style.position,
1648
+ top: body.style.top,
1649
+ width: body.style.width,
1650
+ paddingRight: body.style.paddingRight,
1651
+ scrollY: window.scrollY
1652
+ };
1653
+ const scrollbarGap = window.innerWidth - document.documentElement.clientWidth;
1654
+ if (scrollbarGap > 0) {
1655
+ const current = parseFloat(getComputedStyle(body).paddingRight) || 0;
1656
+ body.style.paddingRight = `${current + scrollbarGap}px`;
1657
+ }
1658
+ body.style.overflow = "hidden";
1659
+ body.style.position = "fixed";
1660
+ body.style.top = `-${savedStyles.scrollY}px`;
1661
+ body.style.width = "100%";
1662
+ }
1663
+ lockCount++;
1664
+ let released = false;
1665
+ return () => {
1666
+ if (released) return;
1667
+ released = true;
1668
+ lockCount = Math.max(0, lockCount - 1);
1669
+ if (lockCount === 0 && savedStyles) {
1670
+ const body = document.body;
1671
+ body.style.overflow = savedStyles.overflow;
1672
+ body.style.position = savedStyles.position;
1673
+ body.style.top = savedStyles.top;
1674
+ body.style.width = savedStyles.width;
1675
+ body.style.paddingRight = savedStyles.paddingRight;
1676
+ window.scrollTo(0, savedStyles.scrollY);
1677
+ savedStyles = null;
1678
+ }
1679
+ };
1680
+ };
1681
+
1682
+ // src/core/features/inert-siblings.ts
1683
+ function createInertSiblings(rootProvider) {
1684
+ const tracked = [];
1685
+ return {
1686
+ apply() {
1687
+ if (typeof document === "undefined") return;
1688
+ let top = rootProvider();
1689
+ while (top.parentElement && top.parentElement !== document.body) {
1690
+ top = top.parentElement;
1691
+ }
1692
+ const parent = top.parentElement ?? document.body;
1693
+ for (const child of Array.from(parent.children)) {
1694
+ if (child === top) continue;
1695
+ if (child.hasAttribute("inert")) continue;
1696
+ if (child.classList.contains("bs-backdrop") || child.classList.contains("bs-screen") || child.classList.contains("bs-root")) {
1697
+ continue;
1698
+ }
1699
+ child.setAttribute("inert", "");
1700
+ tracked.push(child);
1701
+ }
1702
+ },
1703
+ remove() {
1704
+ for (const el of tracked) el.removeAttribute("inert");
1705
+ tracked.length = 0;
1706
+ }
1707
+ };
1708
+ }
1709
+
1710
+ // src/core/controllers/lifecycle-controller.ts
1711
+ var LifecycleController = class {
1712
+ constructor(deps, opts) {
1713
+ __publicField(this, "element");
1714
+ __publicField(this, "focusTrapEnabled");
1715
+ __publicField(this, "initialFocus");
1716
+ __publicField(this, "closeOnEscape");
1717
+ __publicField(this, "bodyScrollLockEnabled");
1718
+ __publicField(this, "inertSiblingsEnabled");
1719
+ __publicField(this, "inertSiblings");
1720
+ __publicField(this, "shouldApplyInertSiblings");
1721
+ __publicField(this, "returnFocus");
1722
+ __publicField(this, "releaseFocusTrap", null);
1723
+ __publicField(this, "releaseScrollLock", null);
1724
+ __publicField(this, "installed", false);
1725
+ __publicField(this, "destroyed", false);
1726
+ this.element = deps.element;
1727
+ this.focusTrapEnabled = opts.focusTrap ?? false;
1728
+ this.initialFocus = opts.initialFocus;
1729
+ this.closeOnEscape = opts.closeOnEscape ?? true;
1730
+ this.bodyScrollLockEnabled = opts.lockBodyScroll ?? true;
1731
+ this.inertSiblingsEnabled = opts.inertSiblings ?? false;
1732
+ this.shouldApplyInertSiblings = opts.shouldApplyInertSiblings ?? (() => true);
1733
+ this.returnFocus = opts.returnFocus;
1734
+ this.inertSiblings = createInertSiblings(() => this.element);
1735
+ }
1736
+ get isInstalled() {
1737
+ return this.installed;
1738
+ }
1739
+ install() {
1740
+ if (this.destroyed) return;
1741
+ this.installed = true;
1742
+ if (this.bodyScrollLockEnabled && !this.releaseScrollLock) {
1743
+ this.releaseScrollLock = lockBodyScroll();
1744
+ }
1745
+ if (this.focusTrapEnabled && !this.releaseFocusTrap) {
1746
+ this.releaseFocusTrap = installFocusTrap(this.element, {
1747
+ initialFocus: this.initialFocus
1748
+ });
1749
+ }
1750
+ if (this.inertSiblingsEnabled && this.shouldApplyInertSiblings()) {
1751
+ this.inertSiblings.apply();
1752
+ }
1753
+ }
1754
+ setReturnFocus(target) {
1755
+ if (this.destroyed) return;
1756
+ this.returnFocus = target;
1757
+ }
1758
+ release() {
1759
+ if (!this.installed) return;
1760
+ this.installed = false;
1761
+ this.releaseFocusTrap?.();
1762
+ this.releaseFocusTrap = null;
1763
+ this.releaseScrollLock?.();
1764
+ this.releaseScrollLock = null;
1765
+ this.inertSiblings.remove();
1766
+ if (this.returnFocus) {
1767
+ const target = typeof this.returnFocus === "function" ? this.returnFocus() : typeof this.returnFocus === "string" ? typeof document !== "undefined" ? document.querySelector(this.returnFocus) : null : this.returnFocus;
1768
+ target?.focus?.();
1769
+ }
1770
+ }
1771
+ destroy() {
1772
+ this.release();
1773
+ this.destroyed = true;
1774
+ }
1775
+ };
1776
+
1777
+ // src/core/gestures.ts
1778
+ var isAxisVertical = (mode) => mode === "bottom" || mode === "top";
1779
+ var SampleRing = class {
1780
+ constructor(capacity) {
1781
+ __publicField(this, "slots");
1782
+ __publicField(this, "capacity");
1783
+ __publicField(this, "head", 0);
1784
+ __publicField(this, "size", 0);
1785
+ this.capacity = capacity;
1786
+ this.slots = new Array(capacity);
1787
+ for (let i = 0; i < capacity; i++) this.slots[i] = { t: 0, v: 0 };
1788
+ }
1789
+ push(t, v) {
1790
+ const slot = this.slots[this.head];
1791
+ slot.t = t;
1792
+ slot.v = v;
1793
+ this.head = (this.head + 1) % this.capacity;
1794
+ if (this.size < this.capacity) this.size++;
1795
+ }
1796
+ pruneBefore(cutoff, minKeep) {
1797
+ while (this.size > minKeep) {
1798
+ const oldestIdx = (this.head - this.size + this.capacity) % this.capacity;
1799
+ if (this.slots[oldestIdx].t < cutoff) this.size--;
1800
+ else break;
1801
+ }
1802
+ }
1803
+ get length() {
1804
+ return this.size;
1805
+ }
1806
+ oldest() {
1807
+ if (this.size === 0) return void 0;
1808
+ return this.slots[(this.head - this.size + this.capacity) % this.capacity];
1809
+ }
1810
+ newest() {
1811
+ if (this.size === 0) return void 0;
1812
+ return this.slots[(this.head - 1 + this.capacity) % this.capacity];
1813
+ }
1814
+ reset() {
1815
+ this.head = 0;
1816
+ this.size = 0;
1817
+ }
1818
+ };
1819
+ var installGestures = (handle, mode, callbacks) => {
1820
+ let activePointerId = null;
1821
+ let activePointerType = "touch";
1822
+ let startCoord = 0;
1823
+ let lastCoord = 0;
1824
+ const VELOCITY_WINDOW_MS = (kind) => kind === "mouse" ? 160 : 120;
1825
+ const MAX_SAMPLES = 32;
1826
+ const samples = new SampleRing(MAX_SAMPLES);
1827
+ const axis = isAxisVertical(mode) ? "Y" : "X";
1828
+ const sign = mode === "bottom" || mode === "right" ? -1 : 1;
1829
+ const coordOf = (e) => axis === "Y" ? e.clientY : e.clientX;
1830
+ const onPointerDown = (e) => {
1831
+ if (activePointerId !== null) return;
1832
+ if (e.button !== void 0 && e.button !== 0) return;
1833
+ activePointerId = e.pointerId;
1834
+ activePointerType = e.pointerType || "touch";
1835
+ startCoord = coordOf(e);
1836
+ lastCoord = startCoord;
1837
+ samples.reset();
1838
+ samples.push(e.timeStamp, startCoord);
1839
+ try {
1840
+ handle.setPointerCapture(e.pointerId);
1841
+ } catch {
1842
+ }
1843
+ callbacks.onStart(startCoord, activePointerType);
1844
+ };
1845
+ const onPointerMove = (e) => {
1846
+ if (e.pointerId !== activePointerId) return;
1847
+ const coord = coordOf(e);
1848
+ const rawDelta = coord - startCoord;
1849
+ const delta = rawDelta * sign;
1850
+ lastCoord = coord;
1851
+ samples.push(e.timeStamp, coord);
1852
+ const cutoff = e.timeStamp - VELOCITY_WINDOW_MS(activePointerType);
1853
+ samples.pruneBefore(cutoff, 2);
1854
+ callbacks.onMove(delta);
1855
+ };
1856
+ const finishGesture = (e) => {
1857
+ if (e.pointerId !== activePointerId) return;
1858
+ const rawDelta = lastCoord - startCoord;
1859
+ const delta = rawDelta * sign;
1860
+ let velocity = 0;
1861
+ if (samples.length >= 3) {
1862
+ const first = samples.oldest();
1863
+ const last = samples.newest();
1864
+ const dt = last.t - first.t;
1865
+ if (dt > 0) velocity = (last.v - first.v) / dt * sign;
1866
+ }
1867
+ const finishedKind = activePointerType;
1868
+ activePointerId = null;
1869
+ samples.reset();
1870
+ try {
1871
+ if (handle.hasPointerCapture(e.pointerId)) {
1872
+ handle.releasePointerCapture(e.pointerId);
1873
+ }
1874
+ } catch {
1875
+ }
1876
+ callbacks.onEnd(delta, velocity, finishedKind);
1877
+ };
1878
+ const onPointerCancel = (e) => {
1879
+ if (e.pointerId !== activePointerId) return;
1880
+ activePointerId = null;
1881
+ samples.reset();
1882
+ callbacks.onCancel?.();
1883
+ };
1884
+ handle.addEventListener("pointerdown", onPointerDown);
1885
+ handle.addEventListener("pointermove", onPointerMove);
1886
+ handle.addEventListener("pointerup", finishGesture);
1887
+ handle.addEventListener("pointercancel", onPointerCancel);
1888
+ const prevTouchAction = handle.style.touchAction;
1889
+ handle.style.touchAction = isAxisVertical(mode) ? "pan-x" : "pan-y";
1890
+ return () => {
1891
+ handle.removeEventListener("pointerdown", onPointerDown);
1892
+ handle.removeEventListener("pointermove", onPointerMove);
1893
+ handle.removeEventListener("pointerup", finishGesture);
1894
+ handle.removeEventListener("pointercancel", onPointerCancel);
1895
+ handle.style.touchAction = prevTouchAction;
1896
+ if (activePointerId !== null && handle.hasPointerCapture(activePointerId)) {
1897
+ handle.releasePointerCapture(activePointerId);
1898
+ }
1899
+ };
1900
+ };
1901
+
1902
+ // src/core/primitives/rubber-band.ts
1903
+ function rubberBand(overshoot, maxAxisSize) {
1904
+ const cap = Math.min(maxAxisSize * 0.15, 80);
1905
+ if (cap <= 0) return 0;
1906
+ const abs = Math.abs(overshoot);
1907
+ return Math.sign(overshoot) * (cap * abs / (abs + cap));
1908
+ }
1909
+
1910
+ // src/core/features/soft-keyboard.ts
1911
+ function dismissSoftKeyboardIfFocused(root) {
1912
+ if (typeof document === "undefined") return false;
1913
+ const active = document.activeElement;
1914
+ if (!active) return false;
1915
+ if (!root.contains(active)) return false;
1916
+ const tag = active.tagName;
1917
+ const isEditable = tag === "INPUT" || tag === "TEXTAREA" || active.isContentEditable === true;
1918
+ if (!isEditable) return false;
1919
+ try {
1920
+ active.blur();
1921
+ return true;
1922
+ } catch {
1923
+ return false;
1924
+ }
1925
+ }
1926
+
1927
+ // src/core/controllers/gesture-controller.ts
1928
+ var GestureController = class {
1929
+ constructor(deps) {
1930
+ __publicField(this, "deps");
1931
+ __publicField(this, "detach", null);
1932
+ __publicField(this, "isDragging_", false);
1933
+ __publicField(this, "dragStartSize", 0);
1934
+ __publicField(this, "currentPointerKind", "touch");
1935
+ __publicField(this, "keyboardDismissed", false);
1936
+ __publicField(this, "dragSuppressed", false);
1937
+ __publicField(this, "dragPayload", { size: 0, delta: 0 });
1938
+ this.deps = deps;
1939
+ }
1940
+ get isDragging() {
1941
+ return this.isDragging_;
1942
+ }
1943
+ install() {
1944
+ if (this.detach) return this.detach;
1945
+ this.detach = installGestures(this.deps.handle, this.deps.mode, {
1946
+ onStart: (_coord, kind) => {
1947
+ if (this.deps.getDisableDrag?.()) {
1948
+ this.dragSuppressed = true;
1949
+ return;
1950
+ }
1951
+ this.dragSuppressed = false;
1952
+ this.deps.cancelAnimation();
1953
+ this.isDragging_ = true;
1954
+ this.dragStartSize = this.deps.getDragContext().size;
1955
+ this.currentPointerKind = kind;
1956
+ this.keyboardDismissed = false;
1957
+ this.deps.getRoot()?.setAttribute("data-dragging", "true");
1958
+ this.deps.element.style.willChange = "transform";
1959
+ this.deps.emit("dragstart", { size: this.dragStartSize });
1960
+ },
1961
+ onMove: (delta) => {
1962
+ if (this.dragSuppressed) return;
1963
+ const ctx = this.deps.getDragContext();
1964
+ const { min, max } = ctx.range;
1965
+ const maxAxis = ctx.maxAxisSize;
1966
+ const rubberOn = ctx.rubberBandEnabled;
1967
+ let next = this.dragStartSize + delta;
1968
+ if (next > max) {
1969
+ next = rubberOn ? max + rubberBand(next - max, maxAxis) : max;
1970
+ } else if (next < min) {
1971
+ next = rubberOn ? min - rubberBand(min - next, maxAxis) : min;
1972
+ }
1973
+ this.deps.applySize(next);
1974
+ if (!this.keyboardDismissed && this.currentPointerKind === "touch" && this.deps.mode === "bottom" && next < this.dragStartSize) {
1975
+ if (dismissSoftKeyboardIfFocused(this.deps.element)) {
1976
+ this.keyboardDismissed = true;
1977
+ }
1978
+ }
1979
+ if (this.deps.listenerCount("drag") > 0) {
1980
+ this.dragPayload.size = next;
1981
+ this.dragPayload.delta = delta;
1982
+ this.deps.emit("drag", this.dragPayload);
1983
+ }
1984
+ },
1985
+ onEnd: (delta, velocity, kind) => {
1986
+ if (this.dragSuppressed) {
1987
+ this.dragSuppressed = false;
1988
+ return;
1989
+ }
1990
+ this.isDragging_ = false;
1991
+ this.keyboardDismissed = false;
1992
+ this.deps.getRoot()?.removeAttribute("data-dragging");
1993
+ this.deps.emit("dragend", {
1994
+ size: this.deps.getDragContext().size,
1995
+ velocity
1996
+ });
1997
+ this.deps.settleAfterDrag(delta, velocity, kind);
1998
+ },
1999
+ onCancel: () => {
2000
+ if (this.dragSuppressed) {
2001
+ this.dragSuppressed = false;
2002
+ return;
2003
+ }
2004
+ this.isDragging_ = false;
2005
+ this.keyboardDismissed = false;
2006
+ this.deps.getRoot()?.removeAttribute("data-dragging");
2007
+ this.deps.emit("dragend", {
2008
+ size: this.deps.getDragContext().size,
2009
+ velocity: 0
2010
+ });
2011
+ const restoreTo = this.dragStartSize;
2012
+ void this.deps.animateTo(restoreTo, 0);
2013
+ }
2014
+ });
2015
+ return this.detach;
2016
+ }
2017
+ forceClearDragState() {
2018
+ if (!this.isDragging_) return;
2019
+ this.isDragging_ = false;
2020
+ this.keyboardDismissed = false;
2021
+ this.deps.getRoot()?.removeAttribute("data-dragging");
2022
+ this.deps.element.style.willChange = "";
2023
+ }
2024
+ };
2025
+
2026
+ // src/core/primitives/aria-slider-writer.ts
2027
+ var AriaSliderWriter = class {
2028
+ constructor(handle, mode) {
2029
+ __publicField(this, "handle", handle);
2030
+ handle.setAttribute(
2031
+ "aria-orientation",
2032
+ mode === "bottom" || mode === "top" ? "vertical" : "horizontal"
2033
+ );
2034
+ }
2035
+ setValue(allowedIds, activeId) {
2036
+ if (allowedIds.length === 0) {
2037
+ this.handle.removeAttribute("aria-valuemin");
2038
+ this.handle.removeAttribute("aria-valuemax");
2039
+ this.handle.removeAttribute("aria-valuenow");
2040
+ this.handle.removeAttribute("aria-valuetext");
2041
+ return;
2042
+ }
2043
+ const max = Math.max(allowedIds.length - 1, 0);
2044
+ const idx = Math.max(allowedIds.indexOf(activeId), 0);
2045
+ this.handle.setAttribute("aria-valuemin", "0");
2046
+ this.handle.setAttribute("aria-valuemax", String(max));
2047
+ this.handle.setAttribute("aria-valuenow", String(idx));
2048
+ this.handle.setAttribute("aria-valuetext", activeId);
2049
+ }
2050
+ };
2051
+
2052
+ // src/core/features/scroll-cache.ts
2053
+ function createScrollCache(deps) {
2054
+ const positions = /* @__PURE__ */ new Map();
2055
+ const isSmallSize = (size) => {
2056
+ const max = deps.getMaxAxisSize();
2057
+ if (max <= 0) return false;
2058
+ return size < max / 2;
2059
+ };
2060
+ return {
2061
+ cache(fromId, prevSize, nextSize) {
2062
+ const container = deps.scrollContainer;
2063
+ if (!container) return;
2064
+ if (isSmallSize(prevSize)) return;
2065
+ if (!isSmallSize(nextSize)) return;
2066
+ positions.set(fromId, container.scrollTop);
2067
+ },
2068
+ restore(toId, prevSize, nextSize) {
2069
+ const container = deps.scrollContainer;
2070
+ if (!container) return;
2071
+ if (isSmallSize(nextSize)) return;
2072
+ if (!isSmallSize(prevSize)) return;
2073
+ const cached = positions.get(toId);
2074
+ if (cached === void 0) return;
2075
+ container.scrollTop = cached;
2076
+ positions.delete(toId);
2077
+ },
2078
+ clear() {
2079
+ positions.clear();
2080
+ }
2081
+ };
2082
+ }
2083
+
2084
+ // src/core/primitives/anchor-animations.ts
2085
+ var DEFAULT_DURATION3 = 200;
2086
+ var DEFAULT_EASING = "cubic-bezier(0.22, 1, 0.36, 1)";
2087
+ var PRESET_FRAMES = {
2088
+ fade: {
2089
+ enter: [{ opacity: 0 }, { opacity: 1 }],
2090
+ exit: [{ opacity: 1 }, { opacity: 0 }]
2091
+ },
2092
+ scale: {
2093
+ enter: [
2094
+ { opacity: 0, transform: "scale(0.85)" },
2095
+ { opacity: 1, transform: "scale(1)" }
2096
+ ],
2097
+ exit: [
2098
+ { opacity: 1, transform: "scale(1)" },
2099
+ { opacity: 0, transform: "scale(0.85)" }
2100
+ ]
2101
+ },
2102
+ slide: {
2103
+ enter: [
2104
+ { opacity: 0, transform: "translateY(10px)" },
2105
+ { opacity: 1, transform: "translateY(0)" }
2106
+ ],
2107
+ exit: [
2108
+ { opacity: 1, transform: "translateY(0)" },
2109
+ { opacity: 0, transform: "translateY(10px)" }
2110
+ ]
2111
+ },
2112
+ pop: {
2113
+ enter: [
2114
+ { opacity: 0, transform: "scale(0.6)", offset: 0 },
2115
+ { opacity: 1, transform: "scale(1.06)", offset: 0.7 },
2116
+ { opacity: 1, transform: "scale(1)", offset: 1 }
2117
+ ],
2118
+ exit: [
2119
+ { opacity: 1, transform: "scale(1)" },
2120
+ { opacity: 0, transform: "scale(0.6)" }
2121
+ ]
2122
+ }
2123
+ };
2124
+ var INSTANT = {
2125
+ finished: Promise.resolve(),
2126
+ cancel: () => {
2127
+ }
2128
+ };
2129
+ function resolveTransitionFrames(spec, phase) {
2130
+ const fallback = { frames: null, duration: 0, easing: DEFAULT_EASING };
2131
+ if (spec === "none") return fallback;
2132
+ if (spec === void 0 || typeof spec === "string") {
2133
+ const preset2 = PRESET_FRAMES[spec ?? "fade"];
2134
+ return {
2135
+ frames: preset2[phase],
2136
+ duration: DEFAULT_DURATION3,
2137
+ easing: DEFAULT_EASING
2138
+ };
2139
+ }
2140
+ const custom = phase === "enter" ? spec.enter : spec.exit;
2141
+ if (custom) {
2142
+ return {
2143
+ frames: custom,
2144
+ duration: spec.duration ?? DEFAULT_DURATION3,
2145
+ easing: spec.easing ?? DEFAULT_EASING
2146
+ };
2147
+ }
2148
+ if (spec.preset === "none") return fallback;
2149
+ const preset = PRESET_FRAMES[spec.preset ?? "fade"];
2150
+ return {
2151
+ frames: preset[phase],
2152
+ duration: spec.duration ?? DEFAULT_DURATION3,
2153
+ easing: spec.easing ?? DEFAULT_EASING
2154
+ };
2155
+ }
2156
+ function runAnchorTransition(el, spec, phase) {
2157
+ const respectReduced = typeof spec === "object" ? spec.respectReducedMotion !== false : true;
2158
+ if (respectReduced && prefersReducedMotion()) return INSTANT;
2159
+ const { frames, duration, easing } = resolveTransitionFrames(spec, phase);
2160
+ if (!frames || duration <= 0) return INSTANT;
2161
+ if (typeof el.animate !== "function") return INSTANT;
2162
+ const animation = el.animate(frames, {
2163
+ duration,
2164
+ easing,
2165
+ fill: "both"
2166
+ });
2167
+ const finished = animation.finished.then(() => void 0).catch(() => void 0);
2168
+ return {
2169
+ finished,
2170
+ cancel: () => {
2171
+ try {
2172
+ animation.cancel();
2173
+ } catch {
2174
+ return;
2175
+ }
2176
+ }
2177
+ };
2178
+ }
2179
+
2180
+ // src/core/features/sheet-anchors.ts
2181
+ function attachAnchor(deps, opts) {
2182
+ const doc = deps.host.ownerDocument;
2183
+ const wrapper = doc.createElement("div");
2184
+ wrapper.className = "bs-anchor";
2185
+ const interactive = opts.interactive ?? true;
2186
+ const position = opts.position ?? "sheet-top-right";
2187
+ const isDock = position === "dock-bottom" || position === "dock-top";
2188
+ const ws = wrapper.style;
2189
+ ws.position = "fixed";
2190
+ if (isDock) {
2191
+ ws.left = "0";
2192
+ ws.right = "0";
2193
+ if (position === "dock-bottom") {
2194
+ ws.bottom = "0";
2195
+ } else {
2196
+ ws.top = "0";
2197
+ }
2198
+ } else {
2199
+ applyOverlayPosition(ws, deps.mode, position, opts.inset ?? "16px");
2200
+ }
2201
+ if (opts.fadeRange) {
2202
+ const [r0, r1] = opts.fadeRange;
2203
+ const span = Math.max(r1 - r0, 1e-4);
2204
+ ws.opacity = `clamp(0, calc((var(--bs-progress, 0) - ${r0}) / ${span}), 1)`;
2205
+ }
2206
+ wrapper.appendChild(opts.element);
2207
+ deps.host.appendChild(wrapper);
2208
+ let visible = null;
2209
+ let inFlight = null;
2210
+ const shouldShow = (state) => {
2211
+ if (typeof opts.showOn === "function") return opts.showOn(state);
2212
+ if (Array.isArray(opts.showOn)) return opts.showOn.includes(state.activeId);
2213
+ return isDock || state.size > 0;
2214
+ };
2215
+ const applyVisibility = (next, animate) => {
2216
+ if (next === visible) return;
2217
+ visible = next;
2218
+ inFlight?.cancel();
2219
+ inFlight = null;
2220
+ wrapper.style.pointerEvents = next && interactive ? "auto" : "none";
2221
+ if (next) {
2222
+ wrapper.style.visibility = "";
2223
+ if (animate) {
2224
+ inFlight = runAnchorTransition(opts.element, opts.animation, "enter");
2225
+ }
2226
+ return;
2227
+ }
2228
+ if (!animate) {
2229
+ wrapper.style.visibility = "hidden";
2230
+ return;
2231
+ }
2232
+ const handle = runAnchorTransition(opts.element, opts.animation, "exit");
2233
+ inFlight = handle;
2234
+ void handle.finished.then(() => {
2235
+ if (inFlight === handle && visible === false) {
2236
+ wrapper.style.visibility = "hidden";
2237
+ }
2238
+ });
2239
+ };
2240
+ const evaluate = (animate) => {
2241
+ if (deps.isDestroyed()) return;
2242
+ applyVisibility(shouldShow(deps.getState()), animate);
2243
+ };
2244
+ const offs = [
2245
+ deps.on("snap", () => evaluate(true)),
2246
+ deps.on("open", () => evaluate(true)),
2247
+ deps.on("close", () => evaluate(true))
2248
+ ];
2249
+ evaluate(false);
2250
+ return {
2251
+ wrapper,
2252
+ detach: () => {
2253
+ offs.forEach((off) => off());
2254
+ inFlight?.cancel();
2255
+ inFlight = null;
2256
+ if (wrapper.parentNode) wrapper.parentNode.removeChild(wrapper);
2257
+ },
2258
+ syncZ: (z) => {
2259
+ wrapper.style.zIndex = String(z);
2260
+ }
2261
+ };
2262
+ }
2263
+
2264
+ // src/core/features/scrim-stages.ts
2265
+ var matchesId = (def, activeId) => {
2266
+ if (def.for === void 0) return false;
2267
+ return Array.isArray(def.for) ? def.for.includes(activeId) : def.for === activeId;
2268
+ };
2269
+ var matchesRange = (def, progress) => {
2270
+ if (!def.forRange) return false;
2271
+ const [from, to] = def.forRange;
2272
+ return progress >= from && progress <= to;
2273
+ };
2274
+ function installScrimStages(deps, opts) {
2275
+ const doc = deps.host.ownerDocument;
2276
+ const entries = opts.stages.map((def) => {
2277
+ const wrapper = doc.createElement("div");
2278
+ wrapper.className = "bs-scrim-stage";
2279
+ const ws = wrapper.style;
2280
+ ws.position = "absolute";
2281
+ applyOverlayPosition(
2282
+ ws,
2283
+ deps.mode,
2284
+ def.position ?? opts.position ?? "center",
2285
+ def.inset ?? opts.inset ?? "16px"
2286
+ );
2287
+ ws.visibility = "hidden";
2288
+ ws.pointerEvents = "none";
2289
+ wrapper.appendChild(def.element);
2290
+ deps.host.appendChild(wrapper);
2291
+ return { def, wrapper, inFlight: null };
2292
+ });
2293
+ let active = null;
2294
+ const resolveActive = () => {
2295
+ const state = deps.getState();
2296
+ if (state.size === 0) {
2297
+ return entries.find((e) => matchesId(e.def, state.activeId)) ?? null;
2298
+ }
2299
+ return entries.find((e) => matchesId(e.def, state.activeId)) ?? entries.find((e) => matchesRange(e.def, state.progress)) ?? null;
2300
+ };
2301
+ const hide = (entry, animate) => {
2302
+ entry.inFlight?.cancel();
2303
+ entry.wrapper.style.pointerEvents = "none";
2304
+ if (!animate) {
2305
+ entry.inFlight = null;
2306
+ entry.wrapper.style.visibility = "hidden";
2307
+ return;
2308
+ }
2309
+ const handle = runAnchorTransition(
2310
+ entry.def.element,
2311
+ entry.def.animation ?? opts.animation,
2312
+ "exit"
2313
+ );
2314
+ entry.inFlight = handle;
2315
+ void handle.finished.then(() => {
2316
+ if (entry.inFlight === handle && active !== entry) {
2317
+ entry.wrapper.style.visibility = "hidden";
2318
+ }
2319
+ });
2320
+ };
2321
+ const show = (entry, animate) => {
2322
+ entry.inFlight?.cancel();
2323
+ entry.inFlight = null;
2324
+ entry.wrapper.style.visibility = "";
2325
+ entry.wrapper.style.pointerEvents = entry.def.interactive ?? opts.interactive ?? false ? "auto" : "none";
2326
+ if (animate) {
2327
+ entry.inFlight = runAnchorTransition(
2328
+ entry.def.element,
2329
+ entry.def.animation ?? opts.animation,
2330
+ "enter"
2331
+ );
2332
+ }
2333
+ };
2334
+ const update = (animate) => {
2335
+ if (deps.isDestroyed()) return;
2336
+ const next = resolveActive();
2337
+ if (next === active) return;
2338
+ const prev = active;
2339
+ active = next;
2340
+ if (prev) hide(prev, animate);
2341
+ if (next) show(next, animate);
2342
+ };
2343
+ const offs = [
2344
+ deps.on("snap", () => update(true)),
2345
+ deps.on("open", () => update(true)),
2346
+ deps.on("close", () => update(true))
2347
+ ];
2348
+ if (entries.some((e) => e.def.forRange)) {
2349
+ offs.push(deps.on("progress", () => update(true)));
2350
+ }
2351
+ update(false);
2352
+ return () => {
2353
+ offs.forEach((off) => off());
2354
+ for (const entry of entries) {
2355
+ entry.inFlight?.cancel();
2356
+ entry.inFlight = null;
2357
+ if (entry.wrapper.parentNode) {
2358
+ entry.wrapper.parentNode.removeChild(entry.wrapper);
2359
+ }
2360
+ }
2361
+ active = null;
2362
+ };
2363
+ }
2364
+
2365
+ // src/core/primitives/engine-options.ts
2366
+ var DEFAULT_FLICK_VELOCITY = 0.65;
2367
+ var DEFAULT_DRAG_THRESHOLD = 18;
2368
+ function resolveEngineOptions(opts, extras) {
2369
+ const initialAllowed = opts.allowed ?? opts.snapPoints.map((p) => p.id);
2370
+ let initialId = opts.initial ?? initialAllowed[0] ?? opts.snapPoints[0]?.id ?? "default";
2371
+ if (opts.persistKey) {
2372
+ const restored = readPersistedIdSafe(opts.persistKey);
2373
+ if (restored && initialAllowed.includes(restored)) {
2374
+ initialId = restored;
2375
+ }
2376
+ }
2377
+ return {
2378
+ mode: opts.mode ?? "bottom",
2379
+ flickVelocity: opts.flickVelocity ?? DEFAULT_FLICK_VELOCITY,
2380
+ dragThreshold: opts.dragThreshold ?? DEFAULT_DRAG_THRESHOLD,
2381
+ rubberBandEnabled: opts.rubberBand ?? true,
2382
+ closeOnBack: opts.closeOnBack ?? false,
2383
+ persistent: opts.persistent ?? false,
2384
+ disableClose: opts.disableClose ?? false,
2385
+ disableDrag: opts.disableDrag ?? false,
2386
+ closeOnRouteChange: opts.closeOnRouteChange ?? false,
2387
+ radius: opts.radius,
2388
+ maxHeight: opts.maxHeight,
2389
+ initialAllowed,
2390
+ initialId,
2391
+ scrim: {
2392
+ scrimMode: opts.scrimMode,
2393
+ scrimColor: opts.scrimColor,
2394
+ scrimBlur: opts.scrimBlur,
2395
+ scrimInteractive: opts.scrimInteractive ?? (opts.scrimTapToClose ? true : void 0),
2396
+ scrimTapToClose: opts.scrimTapToClose,
2397
+ scrimPreset: opts.scrimPreset,
2398
+ screenRange: opts.screenRange,
2399
+ backdropRange: opts.backdropRange
2400
+ },
2401
+ animation: {
2402
+ animation: opts.animation,
2403
+ settleAnimation: opts.settleAnimation,
2404
+ duration: opts.duration,
2405
+ easing: opts.easing,
2406
+ spring: opts.spring,
2407
+ respectReducedMotion: opts.respectReducedMotion,
2408
+ viewTransitions: opts.viewTransitions
2409
+ },
2410
+ lifecycle: {
2411
+ focusTrap: opts.focusTrap,
2412
+ initialFocus: opts.initialFocus,
2413
+ closeOnEscape: opts.closeOnEscape,
2414
+ lockBodyScroll: opts.lockBodyScroll,
2415
+ inertSiblings: opts.inertSiblings,
2416
+ shouldApplyInertSiblings: extras?.shouldApplyInertSiblings,
2417
+ returnFocus: opts.returnFocusTo
2418
+ }
2419
+ };
2420
+ }
2421
+ var readPersistedIdSafe = (key) => {
2422
+ if (typeof window === "undefined") return null;
2423
+ if (typeof localStorage === "undefined") return null;
2424
+ try {
2425
+ return localStorage.getItem(key);
2426
+ } catch {
2427
+ return null;
2428
+ }
2429
+ };
2430
+
2431
+ // src/core/primitives/teardown-stack.ts
2432
+ var TeardownStack = class {
2433
+ constructor() {
2434
+ __publicField(this, "fns", []);
2435
+ }
2436
+ add(fn) {
2437
+ if (fn) this.fns.push(fn);
2438
+ }
2439
+ drain() {
2440
+ while (this.fns.length) {
2441
+ try {
2442
+ this.fns.pop()();
2443
+ } catch (err) {
2444
+ queueMicrotask(() => {
2445
+ throw err;
2446
+ });
2447
+ }
2448
+ }
2449
+ }
2450
+ };
2451
+
2452
+ // src/core/BottomSheetCore.ts
2453
+ var HAPTIC_DURATION_MS = 8;
2454
+ var BottomSheetCore = class {
2455
+ constructor(opts) {
2456
+ __publicField(this, "id", nextInstanceId("bs"));
2457
+ __publicField(this, "element");
2458
+ __publicField(this, "handle");
2459
+ __publicField(this, "scrollContainer");
2460
+ __publicField(this, "backdrop");
2461
+ __publicField(this, "screenComponent");
2462
+ __publicField(this, "scrimParent", null);
2463
+ __publicField(this, "mode");
2464
+ __publicField(this, "flickVelocity");
2465
+ __publicField(this, "dragThreshold");
2466
+ __publicField(this, "rubberBandEnabled");
2467
+ __publicField(this, "scrim");
2468
+ __publicField(this, "aria");
2469
+ __publicField(this, "animation");
2470
+ __publicField(this, "lifecycle");
2471
+ __publicField(this, "closeOnBack");
2472
+ __publicField(this, "routedTo");
2473
+ __publicField(this, "persistent");
2474
+ __publicField(this, "disableCloseFlag");
2475
+ __publicField(this, "disableDragFlag");
2476
+ __publicField(this, "closeOnRouteChange");
2477
+ __publicField(this, "maxHeight");
2478
+ __publicField(this, "snapPointsRaw");
2479
+ __publicField(this, "snaps");
2480
+ __publicField(this, "activeId");
2481
+ __publicField(this, "size", 0);
2482
+ __publicField(this, "gesture");
2483
+ __publicField(this, "rootEl", null);
2484
+ __publicField(this, "destroyed", false);
2485
+ __publicField(this, "currentViewTransition", null);
2486
+ __publicField(this, "currentAbort", new AbortController());
2487
+ __publicField(this, "sizeWriteSentinel", new WriteSentinel());
2488
+ __publicField(this, "progressWriteSentinel", new WriteSentinel());
2489
+ __publicField(this, "isTopSheet", true);
2490
+ __publicField(this, "opening", false);
2491
+ __publicField(this, "allowOvershoot", false);
2492
+ __publicField(this, "progressPayload", {
2493
+ value: 0,
2494
+ size: 0
2495
+ });
2496
+ __publicField(this, "dragContextBuf", {
2497
+ size: 0,
2498
+ maxAxisSize: 0,
2499
+ range: { min: 0, max: 0 },
2500
+ rubberBandEnabled: false
2501
+ });
2502
+ __publicField(this, "bus", createEventBus());
2503
+ __publicField(this, "persistKey");
2504
+ __publicField(this, "linkedSheets", []);
2505
+ __publicField(this, "scrollCache");
2506
+ __publicField(this, "teardowns", new TeardownStack());
2507
+ __publicField(this, "anchors", []);
2508
+ __publicField(this, "anchorHost", null);
2509
+ __publicField(this, "stackZ", 100);
2510
+ __publicField(this, "detachScrimStages", null);
2511
+ __publicField(this, "stackEffectEnabled", false);
2512
+ __publicField(this, "stackEffectPrimed", false);
2513
+ __publicField(this, "fitDeps");
2514
+ __publicField(this, "transformTemplate");
2515
+ __publicField(this, "featureList", []);
2516
+ __publicField(this, "featureCtx");
2517
+ const resolved = resolveEngineOptions(opts, {
2518
+ shouldApplyInertSiblings: () => {
2519
+ if (typeof document === "undefined") return false;
2520
+ const bodyChildren = Array.from(document.body.children);
2521
+ return bodyChildren.some(
2522
+ (c) => c === opts.element || c.contains(opts.element)
2523
+ );
2524
+ }
2525
+ });
2526
+ this.element = opts.element;
2527
+ this.handle = opts.handle ?? opts.element;
2528
+ this.scrollContainer = opts.scrollContainer;
2529
+ this.backdrop = opts.backdrop;
2530
+ this.screenComponent = opts.scrim ?? opts.screenComponent;
2531
+ this.scrimParent = this.screenComponent?.parentElement ?? null;
2532
+ this.mode = resolved.mode;
2533
+ this.transformTemplate = buildTransformTemplate(this.mode);
2534
+ this.aria = new AriaSliderWriter(this.handle, this.mode);
2535
+ this.flickVelocity = resolved.flickVelocity;
2536
+ this.dragThreshold = resolved.dragThreshold;
2537
+ this.rubberBandEnabled = resolved.rubberBandEnabled;
2538
+ this.scrim = new ScrimController(
2539
+ {
2540
+ mode: this.mode,
2541
+ screenComponent: this.screenComponent,
2542
+ backdrop: this.backdrop,
2543
+ isDestroyed: () => this.destroyed,
2544
+ isTopSheet: () => this.isTopSheet,
2545
+ getAllowedIds: () => this.snaps.getAllowedIds().slice(),
2546
+ getResolvedSnaps: () => this.snaps.getResolvedSnaps().slice(),
2547
+ snapTo: (id) => {
2548
+ void this.snapTo(id);
2549
+ },
2550
+ close: () => {
2551
+ if (!this.canDismiss()) return;
2552
+ void this.close("backdrop");
2553
+ }
2554
+ },
2555
+ resolved.scrim
2556
+ );
2557
+ this.animation = new AnimationRunner(
2558
+ {
2559
+ element: this.element,
2560
+ getRootEl: () => this.rootEl,
2561
+ applySize: (size) => this.applySize(size),
2562
+ getSize: () => this.size,
2563
+ isDragging: () => this.gesture?.isDragging ?? false,
2564
+ applyAux: (size) => this.applySize(size, true),
2565
+ getTransformFor: (size) => this.transformTemplate(this.snaps.getMaxAxisSize() - size)
2566
+ },
2567
+ resolved.animation
2568
+ );
2569
+ this.lifecycle = new LifecycleController(
2570
+ {
2571
+ element: this.element,
2572
+ close: () => this.close()
2573
+ },
2574
+ resolved.lifecycle
2575
+ );
2576
+ this.closeOnBack = resolved.closeOnBack;
2577
+ this.routedTo = opts.routedTo;
2578
+ this.persistent = resolved.persistent;
2579
+ this.disableCloseFlag = resolved.disableClose;
2580
+ this.disableDragFlag = resolved.disableDrag;
2581
+ this.closeOnRouteChange = resolved.closeOnRouteChange;
2582
+ this.stackEffectEnabled = opts.stackEffect ?? false;
2583
+ this.snapPointsRaw = opts.snapPoints;
2584
+ this.persistKey = opts.persistKey;
2585
+ if (opts.linkedSheets) {
2586
+ this.linkedSheets = opts.linkedSheets;
2587
+ }
2588
+ const byName = /* @__PURE__ */ new Map();
2589
+ for (const feature of opts.features ?? []) {
2590
+ byName.set(feature.name, feature);
2591
+ }
2592
+ this.featureList = Array.from(byName.values());
2593
+ this.activeId = resolved.initialId;
2594
+ auditVhUsage(this.snapPointsRaw);
2595
+ this.auditDuplicateSnapIds(this.snapPointsRaw);
2596
+ this.rootEl = this.element.closest(".bs-root");
2597
+ this.maxHeight = createMaxHeightController({
2598
+ element: this.element,
2599
+ mode: this.mode,
2600
+ getMaxAxisSize: () => this.snaps.getMaxAxisSize(),
2601
+ setMaxAxisSize: (size) => {
2602
+ this.snaps.setMaxAxisSize(size);
2603
+ },
2604
+ recompute: () => this.recompute()
2605
+ });
2606
+ this.fitDeps = {
2607
+ element: this.element,
2608
+ scrollContainer: this.scrollContainer,
2609
+ mode: this.mode,
2610
+ getMaxHeightCap: () => this.maxHeight.getCap()
2611
+ };
2612
+ this.snaps = new SnapResolver(
2613
+ this.snapPointsRaw,
2614
+ resolved.initialAllowed,
2615
+ this.mode,
2616
+ () => measureFitSize(this.fitDeps),
2617
+ (maxAxisSize) => {
2618
+ this.element.style[layoutAxis(this.mode)] = `${maxAxisSize}px`;
2619
+ }
2620
+ );
2621
+ const initial = this.snaps.findById(this.activeId);
2622
+ if (initial) this.size = initial.size;
2623
+ this.applySize(this.size);
2624
+ this.updateAriaSlider();
2625
+ this.scrollCache = createScrollCache({
2626
+ scrollContainer: this.scrollContainer,
2627
+ getMaxAxisSize: () => this.snaps.getMaxAxisSize()
2628
+ });
2629
+ this.featureCtx = this.buildFeatureContext(opts.autoCollapseAfter);
2630
+ this.attach();
2631
+ this.registerInStack();
2632
+ if (this.snaps.getMaxAxisSize() === 0 && typeof IntersectionObserver !== "undefined" && typeof document !== "undefined") {
2633
+ const io = new IntersectionObserver((entries) => {
2634
+ for (const entry of entries) {
2635
+ if (!entry.isIntersecting) continue;
2636
+ if (this.destroyed) return;
2637
+ this.snaps.recompute();
2638
+ this.healActiveSnap();
2639
+ io.disconnect();
2640
+ return;
2641
+ }
2642
+ });
2643
+ io.observe(this.element);
2644
+ this.teardowns.add(() => io.disconnect());
2645
+ }
2646
+ this.teardowns.add(
2647
+ installFitObserver({
2648
+ handle: this.handle,
2649
+ scrollContainer: this.scrollContainer,
2650
+ hasFitSnap: () => this.snapPointsRaw.some(
2651
+ (p) => p.size === "fit" || p.size === "content"
2652
+ ),
2653
+ isDestroyed: () => this.destroyed,
2654
+ isDragging: () => this.gesture?.isDragging ?? false,
2655
+ recompute: () => this.recompute()
2656
+ })
2657
+ );
2658
+ if (resolved.radius !== void 0) this.setRadius(resolved.radius);
2659
+ if (resolved.maxHeight !== void 0) this.setMaxHeight(resolved.maxHeight);
2660
+ this.applyAutoAriaLabelledBy();
2661
+ this.runFeatureStage("post");
2662
+ this.warnOrphanFeatureOptions();
2663
+ if (this.size > 0) {
2664
+ this.handleOpen();
2665
+ }
2666
+ }
2667
+ buildFeatureContext(autoCollapseAfter) {
2668
+ return {
2669
+ element: this.element,
2670
+ scrollContainer: this.scrollContainer,
2671
+ sheetId: this.id,
2672
+ options: {
2673
+ routedTo: this.routedTo,
2674
+ closeOnBack: this.closeOnBack,
2675
+ closeOnRouteChange: this.closeOnRouteChange,
2676
+ persistKey: this.persistKey,
2677
+ autoCollapseAfter
2678
+ },
2679
+ isDestroyed: () => this.destroyed,
2680
+ isDragging: () => this.gesture?.isDragging ?? false,
2681
+ isAnimating: () => this.animation.isAnimating,
2682
+ isTopSheet: () => this.isTopSheet,
2683
+ isVerticalAxis: () => this.mode === "bottom" || this.mode === "top",
2684
+ getSize: () => this.size,
2685
+ getActiveId: () => this.activeId,
2686
+ getAllowedIds: () => this.snaps.getAllowedIds().slice(),
2687
+ allowedIdsBySize: () => this.allowedIdsBySize(),
2688
+ getMaxAxisSize: () => this.snaps.getMaxAxisSize(),
2689
+ resolveSnap: (id) => this.snaps.findById(id),
2690
+ resolveActiveSnap: () => this.snaps.findById(this.activeId),
2691
+ setSize: (size) => {
2692
+ this.size = size;
2693
+ },
2694
+ setMaxAxisSize: (size) => {
2695
+ this.snaps.setMaxAxisSize(size);
2696
+ },
2697
+ applySize: (size) => this.applySize(size),
2698
+ recomputeSnaps: () => this.recomputeSnaps(),
2699
+ newCycle: () => {
2700
+ this.newCycle();
2701
+ },
2702
+ cancelInFlight: () => {
2703
+ this.animation.cancel();
2704
+ },
2705
+ resyncAfterResize: () => this.resyncAfterResize(),
2706
+ snapTo: (id) => {
2707
+ void this.snapTo(id);
2708
+ },
2709
+ close: (reason) => this.close(reason),
2710
+ on: (event, fn) => this.on(event, fn),
2711
+ addTeardown: (fn) => this.teardowns.add(fn)
2712
+ };
2713
+ }
2714
+ runFeatureStage(stage) {
2715
+ for (const feature of this.featureList) {
2716
+ if ((feature.stage ?? "post") !== stage) continue;
2717
+ const teardown = feature.install(this.featureCtx);
2718
+ if (teardown) this.teardowns.add(teardown);
2719
+ }
2720
+ }
2721
+ warnOrphanFeatureOptions() {
2722
+ const names = new Set(this.featureList.map((f) => f.name));
2723
+ if ((this.closeOnBack || this.routedTo !== void 0 || this.closeOnRouteChange) && !names.has("route")) {
2724
+ devWarn(
2725
+ "[BottomSheet] closeOnBack/routedTo/closeOnRouteChange need routeFeature() \u2014 option ignored"
2726
+ );
2727
+ }
2728
+ if (this.persistKey && !names.has("persist")) {
2729
+ devWarn(
2730
+ "[BottomSheet] persistKey needs persistFeature() \u2014 snaps will not be saved"
2731
+ );
2732
+ }
2733
+ }
2734
+ get state() {
2735
+ return {
2736
+ size: this.size,
2737
+ activeId: this.activeId,
2738
+ isDragging: this.gesture?.isDragging ?? false,
2739
+ isAnimating: this.animation.isAnimating,
2740
+ progress: this.computeProgress(this.size)
2741
+ };
2742
+ }
2743
+ on(event, fn) {
2744
+ return this.bus.on(event, fn);
2745
+ }
2746
+ use(plugin) {
2747
+ if (this.destroyed) {
2748
+ devWarn(
2749
+ `[BottomSheet] use("${plugin.name}") called on destroyed engine \u2014 ignored.`
2750
+ );
2751
+ return this;
2752
+ }
2753
+ const scoped = [];
2754
+ const scope = {
2755
+ add: (fn) => {
2756
+ if (typeof fn === "function") scoped.push(fn);
2757
+ }
2758
+ };
2759
+ let teardown;
2760
+ try {
2761
+ teardown = plugin.install(this, scope);
2762
+ } catch (err) {
2763
+ for (let i = scoped.length - 1; i >= 0; i--) {
2764
+ try {
2765
+ scoped[i]();
2766
+ } catch (cleanupErr) {
2767
+ queueMicrotask(() => {
2768
+ throw cleanupErr;
2769
+ });
2770
+ }
2771
+ }
2772
+ queueMicrotask(() => {
2773
+ throw err;
2774
+ });
2775
+ return this;
2776
+ }
2777
+ for (const fn of scoped) this.teardowns.add(fn);
2778
+ if (typeof teardown === "function") {
2779
+ this.teardowns.add(teardown);
2780
+ }
2781
+ return this;
2782
+ }
2783
+ setLinkedSheets(sheets) {
2784
+ if (this.destroyed) return;
2785
+ this.linkedSheets = sheets;
2786
+ }
2787
+ getAllowedIds() {
2788
+ return this.snaps.getAllowedIds().slice();
2789
+ }
2790
+ getResolvedSnaps() {
2791
+ return this.snaps.getResolvedSnaps();
2792
+ }
2793
+ async snapTo(id, velocityOrOpts = 0, _skipBeforeSnap = false) {
2794
+ if (this.destroyed) return;
2795
+ const opts = typeof velocityOrOpts === "object" ? velocityOrOpts : { velocity: velocityOrOpts };
2796
+ const velocityPxPerMs = opts.velocity ?? 0;
2797
+ const externalSignal = opts.signal;
2798
+ if (externalSignal?.aborted) return;
2799
+ const target = this.snaps.findById(id);
2800
+ if (!target) {
2801
+ devWarn(`[BottomSheet] unknown snap id: ${id}`);
2802
+ return;
2803
+ }
2804
+ if (!this.snaps.getAllowedIds().includes(id)) {
2805
+ devWarn(`[BottomSheet] snap "${id}" is not in allowed list`);
2806
+ return;
2807
+ }
2808
+ if (!_skipBeforeSnap && this.emitBeforeSnap(target, this.activeId)) {
2809
+ return;
2810
+ }
2811
+ const signal = this.newCycle();
2812
+ const onExternalAbort = () => {
2813
+ this.animation.cancel();
2814
+ this.newCycle();
2815
+ };
2816
+ externalSignal?.addEventListener("abort", onExternalAbort, { once: true });
2817
+ const wasClosed = this.size === 0;
2818
+ const previousId = this.activeId;
2819
+ const previousSize = this.size;
2820
+ this.scrollCache.cache(previousId, previousSize, target.size);
2821
+ this.activeId = id;
2822
+ const rawTargetSize = this.snapPointsRaw.find((p) => p.id === id)?.size;
2823
+ const opensToRest = id === "closed" || rawTargetSize === 0;
2824
+ if (wasClosed && !opensToRest) {
2825
+ this.opening = true;
2826
+ sheetStack.promote(this.id);
2827
+ }
2828
+ if (this.animation.viewTransitionsAvailable) {
2829
+ this.currentViewTransition?.skipTransition?.();
2830
+ const vt = document.startViewTransition(() => {
2831
+ this.applySize(target.size);
2832
+ });
2833
+ vt.ready?.catch(() => {
2834
+ });
2835
+ this.currentViewTransition = vt;
2836
+ try {
2837
+ await vt.finished;
2838
+ } catch {
2839
+ }
2840
+ if (this.currentViewTransition === vt) this.currentViewTransition = null;
2841
+ } else {
2842
+ await this.animation.animateTo(target.size, velocityPxPerMs);
2843
+ }
2844
+ externalSignal?.removeEventListener("abort", onExternalAbort);
2845
+ if (signal.aborted) return;
2846
+ this.opening = false;
2847
+ this.completeSnap(id, target.size, previousSize, wasClosed, false);
2848
+ }
2849
+ completeSnap(id, targetSize, previousSize, wasClosed, withHaptic) {
2850
+ this.scrollCache.restore(id, previousSize, targetSize);
2851
+ this.updateAriaSlider();
2852
+ this.emit("snap", {
2853
+ id,
2854
+ size: this.size,
2855
+ progress: this.computeProgress(this.size)
2856
+ });
2857
+ if (withHaptic) this.haptic();
2858
+ if (wasClosed && targetSize > 0) this.emitOpenSequence(id);
2859
+ if (targetSize === 0) this.emitCloseSequence();
2860
+ }
2861
+ emitOpenSequence(id) {
2862
+ this.emit("open", { id });
2863
+ this.handleOpen();
2864
+ notifyLinkedSheets(this.linkedSheets, this);
2865
+ this.emit("opened", { id });
2866
+ }
2867
+ emitCloseSequence() {
2868
+ this.emit("close", void 0);
2869
+ this.handleClose();
2870
+ this.emit("closed", void 0);
2871
+ }
2872
+ async dragTo(targetSize, velocityOrOpts = 0) {
2873
+ if (this.destroyed) return;
2874
+ const opts = typeof velocityOrOpts === "object" ? velocityOrOpts : { velocity: velocityOrOpts };
2875
+ const velocityPxPerMs = opts.velocity ?? 0;
2876
+ const externalSignal = opts.signal;
2877
+ if (externalSignal?.aborted) return;
2878
+ this.newCycle();
2879
+ const onExternalAbort = () => {
2880
+ this.animation.cancel();
2881
+ this.newCycle();
2882
+ };
2883
+ externalSignal?.addEventListener("abort", onExternalAbort, { once: true });
2884
+ const clamped = Math.max(0, Math.min(targetSize, this.snaps.getMaxAxisSize()));
2885
+ await this.animation.animateTo(clamped, velocityPxPerMs);
2886
+ externalSignal?.removeEventListener("abort", onExternalAbort);
2887
+ if (this.destroyed || externalSignal?.aborted) return;
2888
+ }
2889
+ open(id) {
2890
+ if (this.destroyed) return Promise.resolve();
2891
+ const target = id ?? this.snaps.getAllowedIds().find((a) => (this.snaps.findById(a)?.size ?? 0) > 0) ?? this.activeId;
2892
+ return this.snapTo(target);
2893
+ }
2894
+ close(reason = "programmatic") {
2895
+ if (this.destroyed) return Promise.resolve();
2896
+ if (this.disableCloseFlag) return Promise.resolve();
2897
+ if (this.size > 0 && this.emitBeforeClose(reason)) return Promise.resolve();
2898
+ 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];
2899
+ return this.snapTo(closedId ?? this.activeId);
2900
+ }
2901
+ canDismiss() {
2902
+ return !this.persistent && !this.disableCloseFlag && !this.destroyed;
2903
+ }
2904
+ setPersistent(value) {
2905
+ if (this.destroyed) return;
2906
+ this.persistent = value;
2907
+ }
2908
+ setDisableClose(value) {
2909
+ if (this.destroyed) return;
2910
+ this.disableCloseFlag = value;
2911
+ }
2912
+ setDisableDrag(value) {
2913
+ if (this.destroyed) return;
2914
+ this.disableDragFlag = value;
2915
+ }
2916
+ isTop() {
2917
+ return this.isTopSheet;
2918
+ }
2919
+ depth() {
2920
+ return sheetStack.depthOf(this.id);
2921
+ }
2922
+ expand() {
2923
+ if (this.destroyed) return Promise.resolve();
2924
+ const ids = this.allowedIdsBySize();
2925
+ const target = ids[ids.length - 1];
2926
+ if (!target) return Promise.resolve();
2927
+ return this.snapTo(target);
2928
+ }
2929
+ collapse() {
2930
+ if (this.destroyed) return Promise.resolve();
2931
+ const ids = this.allowedIdsBySize();
2932
+ const nonZero = ids.find((id) => (this.snaps.findById(id)?.size ?? 0) > 0);
2933
+ const target = nonZero ?? ids[0];
2934
+ if (!target) return Promise.resolve();
2935
+ return this.snapTo(target);
2936
+ }
2937
+ setRadius(r) {
2938
+ if (this.destroyed) return;
2939
+ this.maxHeight.setRadius(r);
2940
+ }
2941
+ setMaxHeight(h) {
2942
+ if (this.destroyed) return;
2943
+ this.maxHeight.setMaxHeight(h);
2944
+ }
2945
+ setAllowed(ids, snap) {
2946
+ if (this.destroyed) return;
2947
+ this.snaps.setAllowedIds(ids);
2948
+ this.updateAriaSlider();
2949
+ if (snap && ids.includes(snap)) {
2950
+ void this.snapTo(snap, 0, true);
2951
+ } else if (!ids.includes(this.activeId)) {
2952
+ const fallback = ids[0];
2953
+ if (fallback) void this.snapTo(fallback, 0, true);
2954
+ }
2955
+ }
2956
+ setBackdropRange(range) {
2957
+ this.scrim.setBackdropRange(range, () => this.applySize(this.size));
2958
+ }
2959
+ setScreenRange(range) {
2960
+ this.scrim.setScreenRange(range, () => this.applySize(this.size));
2961
+ }
2962
+ setScrimColor(color) {
2963
+ this.scrim.setScrimColor(color);
2964
+ }
2965
+ setScrimBlur(blur) {
2966
+ this.scrim.setScrimBlur(blur);
2967
+ }
2968
+ setScrimInteractive(interactive) {
2969
+ this.scrim.setScrimInteractive(interactive);
2970
+ }
2971
+ setScrim(opts) {
2972
+ this.scrim.setScrim(opts, () => this.applySize(this.size));
2973
+ }
2974
+ setScrimMode(mode) {
2975
+ this.scrim.setScrimMode(mode, () => this.applySize(this.size));
2976
+ }
2977
+ setScrimTapToClose(enabled) {
2978
+ this.scrim.setScrimTapToClose(enabled);
2979
+ }
2980
+ setScrimEnabled(enabled) {
2981
+ this.scrim.setScrimEnabled(enabled, () => this.applySize(this.size));
2982
+ }
2983
+ setScrimOverlay(opts) {
2984
+ return this.scrim.setScrimOverlay(opts);
2985
+ }
2986
+ addAnchor(opts) {
2987
+ if (this.destroyed || typeof document === "undefined") return () => {
2988
+ };
2989
+ if (!this.anchorHost) {
2990
+ this.anchorHost = this.rootEl ?? this.element.parentElement ?? document.body;
2991
+ this.anchorHost.style.setProperty("--bs-size", `${this.size}px`);
2992
+ this.anchorHost.style.setProperty(
2993
+ "--bs-progress",
2994
+ String(this.computeProgress(this.size))
2995
+ );
2996
+ }
2997
+ const handle = attachAnchor(
2998
+ {
2999
+ mode: this.mode,
3000
+ host: this.anchorHost,
3001
+ getState: () => ({
3002
+ activeId: this.activeId,
3003
+ size: this.size,
3004
+ progress: this.computeProgress(this.size)
3005
+ }),
3006
+ on: (event, fn) => this.on(event, fn),
3007
+ isDestroyed: () => this.destroyed
3008
+ },
3009
+ opts
3010
+ );
3011
+ handle.syncZ(this.stackZ + 1);
3012
+ this.anchors.push(handle);
3013
+ const detach = () => {
3014
+ const idx = this.anchors.indexOf(handle);
3015
+ if (idx === -1) return;
3016
+ this.anchors.splice(idx, 1);
3017
+ handle.detach();
3018
+ };
3019
+ this.teardowns.add(detach);
3020
+ return detach;
3021
+ }
3022
+ setScrimStages(opts) {
3023
+ if (this.destroyed || typeof document === "undefined") return () => {
3024
+ };
3025
+ this.detachScrimStages?.();
3026
+ this.detachScrimStages = null;
3027
+ if (!opts) return () => {
3028
+ };
3029
+ const host = this.screenComponent?.parentElement;
3030
+ if (!host) {
3031
+ devWarn(
3032
+ "[BottomSheet] setScrimStages: no scrim element mounted \u2014 pass `scrim` to the engine first."
3033
+ );
3034
+ return () => {
3035
+ };
3036
+ }
3037
+ const detachInstalled = installScrimStages(
3038
+ {
3039
+ mode: this.mode,
3040
+ host,
3041
+ getState: () => ({
3042
+ activeId: this.activeId,
3043
+ size: this.size,
3044
+ progress: this.computeProgress(this.size)
3045
+ }),
3046
+ on: (event, fn) => this.on(event, fn),
3047
+ isDestroyed: () => this.destroyed
3048
+ },
3049
+ opts
3050
+ );
3051
+ const detach = () => {
3052
+ if (this.detachScrimStages === detach) this.detachScrimStages = null;
3053
+ detachInstalled();
3054
+ };
3055
+ this.detachScrimStages = detach;
3056
+ this.teardowns.add(() => this.detachScrimStages?.());
3057
+ return detach;
3058
+ }
3059
+ getScrimState() {
3060
+ return { mode: this.scrim.scrimMode, enabled: this.scrim.scrimEnabled };
3061
+ }
3062
+ setSnapPoints(points, allowed) {
3063
+ if (this.destroyed) return;
3064
+ this.animation.cancel();
3065
+ this.newCycle();
3066
+ this.snapPointsRaw = points;
3067
+ auditVhUsage(points);
3068
+ this.auditDuplicateSnapIds(points);
3069
+ this.snaps.setRaw(points);
3070
+ if (allowed) this.snaps.setAllowedIds(allowed);
3071
+ this.scrollCache.clear();
3072
+ this.scrim.invalidateOpacityCache();
3073
+ const current = this.snaps.findById(this.activeId);
3074
+ if (current) {
3075
+ this.applySize(current.size);
3076
+ }
3077
+ this.updateAriaSlider();
3078
+ }
3079
+ recompute() {
3080
+ if (this.destroyed) return;
3081
+ this.maxHeight.resolveCap();
3082
+ this.snaps.recompute();
3083
+ this.maxHeight.clampTo();
3084
+ this.scrim.invalidateOpacityCache();
3085
+ if (this.gesture?.isDragging) return;
3086
+ this.healActiveSnap();
3087
+ }
3088
+ healActiveSnap() {
3089
+ const current = this.snaps.findById(this.activeId);
3090
+ if (!current) return;
3091
+ this.size = current.size;
3092
+ this.applySize(this.size);
3093
+ this.updateAriaSlider();
3094
+ if (this.size > 0 && !this.lifecycle.isInstalled && !this.opening && !this.animation.isAnimating) {
3095
+ this.emitOpenSequence(this.activeId);
3096
+ }
3097
+ }
3098
+ destroy() {
3099
+ this.destroyed = true;
3100
+ this.opening = false;
3101
+ this.currentAbort.abort();
3102
+ this.currentViewTransition?.skipTransition?.();
3103
+ this.currentViewTransition = null;
3104
+ this.bus.clear();
3105
+ this.animation.cancel();
3106
+ this.gesture?.forceClearDragState();
3107
+ this.teardowns.drain();
3108
+ this.scrim.destroy();
3109
+ this.animation.destroy();
3110
+ this.lifecycle.destroy();
3111
+ if (this.backdrop) {
3112
+ this.backdrop.style.opacity = "";
3113
+ this.backdrop.style.pointerEvents = "";
3114
+ }
3115
+ for (const host of [this.anchorHost, this.scrimParent, this.rootEl]) {
3116
+ if (!host) continue;
3117
+ host.style.removeProperty("--bs-size");
3118
+ host.style.removeProperty("--bs-progress");
3119
+ }
3120
+ if (this.screenComponent) {
3121
+ this.screenComponent.style.opacity = "";
3122
+ this.screenComponent.style.display = "";
3123
+ }
3124
+ this.sizeWriteSentinel.invalidate();
3125
+ this.progressWriteSentinel.invalidate();
3126
+ }
3127
+ emit(event, payload) {
3128
+ this.bus.emit(event, payload);
3129
+ }
3130
+ newCycle() {
3131
+ this.allowOvershoot = false;
3132
+ this.opening = false;
3133
+ this.currentAbort.abort();
3134
+ this.currentAbort = new AbortController();
3135
+ return this.currentAbort.signal;
3136
+ }
3137
+ emitBeforeSnap(target, previousId) {
3138
+ return emitCancelable(
3139
+ (payload) => this.emit("before-snap", payload),
3140
+ { id: target.id, size: target.size, previousId },
3141
+ "before-snap"
3142
+ );
3143
+ }
3144
+ emitBeforeClose(reason) {
3145
+ return emitCancelable(
3146
+ (payload) => this.emit("before-close", payload),
3147
+ { reason },
3148
+ "before-close"
3149
+ );
3150
+ }
3151
+ applyAutoAriaLabelledBy() {
3152
+ if (typeof document === "undefined") return;
3153
+ if (!this.lifecycle.focusTrapEnabled) return;
3154
+ if (this.element.getAttribute("aria-labelledby")) return;
3155
+ const scope = this.handle ?? this.element;
3156
+ const titled = scope.querySelector("[data-bs-title]") ?? scope.querySelector("h1,h2,h3,h4,h5,h6");
3157
+ if (!titled) return;
3158
+ if (!titled.id) {
3159
+ titled.id = nextInstanceId("bs-title");
3160
+ }
3161
+ this.element.setAttribute("aria-labelledby", titled.id);
3162
+ }
3163
+ recomputeSnaps() {
3164
+ this.maxHeight.resolveCap();
3165
+ this.snaps.setRaw(this.snapPointsRaw);
3166
+ this.maxHeight.clampTo();
3167
+ this.scrim.invalidateOpacityCache();
3168
+ }
3169
+ auditDuplicateSnapIds(points) {
3170
+ const seen = /* @__PURE__ */ new Set();
3171
+ for (const p of points) {
3172
+ if (seen.has(p.id)) {
3173
+ devWarn(
3174
+ `[BottomSheet] duplicate snap id "${p.id}" \u2014 only the first occurrence is reachable.`
3175
+ );
3176
+ }
3177
+ seen.add(p.id);
3178
+ }
3179
+ }
3180
+ getAllowedRange() {
3181
+ return this.snaps.getAllowedRange();
3182
+ }
3183
+ allowedIdsBySize() {
3184
+ return this.snaps.getAllowedIds().slice().sort(
3185
+ (a, b) => (this.snaps.findById(a)?.size ?? 0) - (this.snaps.findById(b)?.size ?? 0)
3186
+ );
3187
+ }
3188
+ updateAriaSlider() {
3189
+ this.aria.setValue(this.allowedIdsBySize(), this.activeId);
3190
+ }
3191
+ registerInStack() {
3192
+ this.teardowns.add(sheetStack.push({
3193
+ id: this.id,
3194
+ setZIndex: (z) => {
3195
+ this.stackZ = z;
3196
+ this.element.style.zIndex = String(z);
3197
+ if (this.backdrop) this.backdrop.style.zIndex = String(z - 1);
3198
+ for (const anchor of this.anchors) anchor.syncZ(z + 1);
3199
+ },
3200
+ setIsTop: (isTop) => {
3201
+ this.isTopSheet = isTop;
3202
+ },
3203
+ isOpen: () => this.size > 0 || this.opening,
3204
+ setDepth: (depth) => this.applyStackDepth(depth)
3205
+ }));
3206
+ }
3207
+ applyStackDepth(depth) {
3208
+ if (!this.stackEffectEnabled) return;
3209
+ this.element.setAttribute("data-stack-depth", String(depth));
3210
+ if (!this.stackEffectPrimed) {
3211
+ this.stackEffectPrimed = true;
3212
+ const isVertical = this.mode === "bottom" || this.mode === "top";
3213
+ this.element.style.transformOrigin = isVertical ? "50% 100%" : "0% 50%";
3214
+ this.element.style.transition = "scale 320ms cubic-bezier(0.29, 1.04, 0.84, 0.99)";
3215
+ }
3216
+ const scale = Math.max(1 - depth * 0.04, 0.86);
3217
+ this.element.style.scale = scale === 1 ? "" : String(scale);
3218
+ }
3219
+ attach() {
3220
+ this.gesture = new GestureController({
3221
+ handle: this.handle,
3222
+ element: this.element,
3223
+ mode: this.mode,
3224
+ getRoot: () => this.rootEl,
3225
+ getDragContext: () => {
3226
+ const buf = this.dragContextBuf;
3227
+ const r = this.getAllowedRange();
3228
+ buf.size = this.size;
3229
+ buf.maxAxisSize = this.snaps.getMaxAxisSize();
3230
+ buf.range.min = r.min;
3231
+ buf.range.max = r.max;
3232
+ buf.rubberBandEnabled = this.rubberBandEnabled;
3233
+ return buf;
3234
+ },
3235
+ getDisableDrag: () => this.disableDragFlag,
3236
+ cancelAnimation: () => this.animation.cancel(),
3237
+ applySize: (size) => this.applySize(size),
3238
+ animateTo: (size, velocity) => this.animation.animateTo(size, velocity),
3239
+ settleAfterDrag: (delta, velocity, kind) => this.settleAfterDrag(delta, velocity, kind),
3240
+ emit: (event, payload) => this.emit(event, payload),
3241
+ listenerCount: (event) => this.bus.listenerCount(event)
3242
+ });
3243
+ this.teardowns.add(this.gesture.install());
3244
+ try {
3245
+ this.attachFeatures();
3246
+ } catch (err) {
3247
+ this.teardowns.drain();
3248
+ try {
3249
+ this.scrim.destroy();
3250
+ } catch (destroyErr) {
3251
+ queueMicrotask(() => {
3252
+ throw destroyErr;
3253
+ });
3254
+ }
3255
+ try {
3256
+ this.animation.destroy();
3257
+ } catch (destroyErr) {
3258
+ queueMicrotask(() => {
3259
+ throw destroyErr;
3260
+ });
3261
+ }
3262
+ try {
3263
+ this.lifecycle.destroy();
3264
+ } catch (destroyErr) {
3265
+ queueMicrotask(() => {
3266
+ throw destroyErr;
3267
+ });
3268
+ }
3269
+ this.destroyed = true;
3270
+ throw err;
3271
+ }
3272
+ }
3273
+ attachFeatures() {
3274
+ this.runFeatureStage("attach");
3275
+ if (typeof window !== "undefined") {
3276
+ this.teardowns.add(installResizeObserver({
3277
+ element: this.element,
3278
+ getMode: () => this.mode,
3279
+ isDestroyed: () => this.destroyed,
3280
+ isDragging: () => this.gesture?.isDragging ?? false,
3281
+ resolveActiveSnap: () => this.snaps.findById(this.activeId),
3282
+ getMaxAxisSize: () => this.snaps.getMaxAxisSize(),
3283
+ getSize: () => this.size,
3284
+ setMaxAxisSize: (size) => {
3285
+ this.snaps.setMaxAxisSize(size);
3286
+ },
3287
+ setSize: (size) => {
3288
+ this.size = size;
3289
+ },
3290
+ recomputeSnaps: () => this.recomputeSnaps(),
3291
+ applySize: (size) => this.applySize(size),
3292
+ cancelInFlight: () => {
3293
+ this.animation.cancel();
3294
+ },
3295
+ newCycle: () => {
3296
+ this.newCycle();
3297
+ },
3298
+ isAnimating: () => this.animation.isAnimating,
3299
+ resyncAfterCancel: () => this.resyncAfterResize()
3300
+ }));
3301
+ if (this.lifecycle.closeOnEscape) {
3302
+ const onKey = (e) => {
3303
+ if (this.destroyed || e.defaultPrevented) return;
3304
+ if (!this.canDismiss()) return;
3305
+ if (e.key === "Escape" && this.isTopSheet && this.size > 0) {
3306
+ void this.close("escape");
3307
+ }
3308
+ };
3309
+ document.addEventListener("keydown", onKey);
3310
+ this.teardowns.add(
3311
+ () => document.removeEventListener("keydown", onKey)
3312
+ );
3313
+ }
3314
+ }
3315
+ this.teardowns.add(installSliderKeyboard({
3316
+ handle: this.handle,
3317
+ mode: this.mode,
3318
+ isDestroyed: () => this.destroyed,
3319
+ getAllowedIds: () => this.allowedIdsBySize(),
3320
+ getActiveId: () => this.activeId,
3321
+ snapTo: (id) => {
3322
+ void this.snapTo(id);
3323
+ }
3324
+ }));
3325
+ this.scrim.attach();
3326
+ }
3327
+ settleAfterDrag(delta, velocity, kind = "touch") {
3328
+ const signal = this.newCycle();
3329
+ const target = this.snaps.findDragSettleTarget({
3330
+ delta,
3331
+ velocity,
3332
+ pointerKind: kind,
3333
+ size: this.size,
3334
+ activeId: this.activeId,
3335
+ flickVelocity: this.flickVelocity,
3336
+ dragThreshold: this.dragThreshold
3337
+ });
3338
+ if (!target) {
3339
+ this.clearWillChangeIfIdle();
3340
+ return;
3341
+ }
3342
+ const previousId = this.activeId;
3343
+ if (target.id === previousId && target.size === this.size) {
3344
+ this.clearWillChangeIfIdle();
3345
+ return;
3346
+ }
3347
+ if (this.emitBeforeSnap(target, previousId)) {
3348
+ const restore = this.snaps.findById(previousId);
3349
+ if (restore) {
3350
+ void this.animation.animateTo(restore.size, 0);
3351
+ }
3352
+ return;
3353
+ }
3354
+ const previousSize = this.size;
3355
+ const previousSnapSize = this.snaps.findById(previousId)?.size;
3356
+ this.scrollCache.cache(previousId, previousSize, target.size);
3357
+ this.activeId = target.id;
3358
+ if (previousSnapSize === 0 && target.size > 0) {
3359
+ this.opening = true;
3360
+ sheetStack.promote(this.id);
3361
+ }
3362
+ const settleCap = this.snaps.getMaxAxisSize();
3363
+ const settleSize = settleCap > 0 ? Math.min(target.size, settleCap) : target.size;
3364
+ this.allowOvershoot = true;
3365
+ void this.animation.animateTo(settleSize, velocity).then(() => {
3366
+ if (signal.aborted) return;
3367
+ this.allowOvershoot = false;
3368
+ this.opening = false;
3369
+ this.completeSnap(
3370
+ target.id,
3371
+ target.size,
3372
+ previousSize,
3373
+ previousSnapSize === 0,
3374
+ true
3375
+ );
3376
+ });
3377
+ }
3378
+ clearWillChangeIfIdle() {
3379
+ if (this.animation.isAnimating || this.gesture?.isDragging) return;
3380
+ this.element.style.willChange = "";
3381
+ }
3382
+ haptic() {
3383
+ if (typeof navigator !== "undefined" && "vibrate" in navigator) {
3384
+ try {
3385
+ navigator.vibrate(HAPTIC_DURATION_MS);
3386
+ } catch {
3387
+ }
3388
+ }
3389
+ }
3390
+ applySize(size, skipTransform = false) {
3391
+ const cap = this.snaps.getMaxAxisSize();
3392
+ const clamped = cap > 0 && size > cap && !this.gesture?.isDragging && !this.allowOvershoot ? cap : size;
3393
+ this.size = clamped;
3394
+ const offset = cap - clamped;
3395
+ const style = this.element.style;
3396
+ if (!skipTransform) style.transform = this.transformTemplate(offset);
3397
+ if (this.sizeWriteSentinel.shouldWrite(clamped, SIZE_WRITE_EPSILON)) {
3398
+ style.setProperty("--bs-size", `${clamped}px`);
3399
+ if (this.scrimParent) {
3400
+ this.scrimParent.style.setProperty("--bs-size", `${clamped}px`);
3401
+ }
3402
+ if (this.anchorHost && this.anchorHost !== this.scrimParent) {
3403
+ this.anchorHost.style.setProperty("--bs-size", `${clamped}px`);
3404
+ }
3405
+ if (this.rootEl && this.rootEl !== this.scrimParent && this.rootEl !== this.anchorHost) {
3406
+ this.rootEl.style.setProperty("--bs-size", `${clamped}px`);
3407
+ }
3408
+ }
3409
+ const progress = this.computeProgress(clamped);
3410
+ const progressChanged = this.progressWriteSentinel.shouldWrite(
3411
+ progress,
3412
+ OPACITY_WRITE_EPSILON
3413
+ );
3414
+ if (progressChanged) {
3415
+ style.setProperty("--bs-progress", String(progress));
3416
+ if (this.anchorHost) {
3417
+ this.anchorHost.style.setProperty("--bs-progress", String(progress));
3418
+ }
3419
+ if (this.rootEl && this.rootEl !== this.anchorHost) {
3420
+ this.rootEl.style.setProperty("--bs-progress", String(progress));
3421
+ }
3422
+ }
3423
+ this.scrim.applyOpacity(progress, progressChanged);
3424
+ if (progressChanged && this.bus.listenerCount("progress") > 0) {
3425
+ this.progressPayload.value = progress;
3426
+ this.progressPayload.size = clamped;
3427
+ this.emit("progress", this.progressPayload);
3428
+ }
3429
+ }
3430
+ computeProgress(size) {
3431
+ const { min, max } = this.getAllowedRange();
3432
+ const reachableMax = Math.min(max, this.snaps.getMaxAxisSize());
3433
+ if (reachableMax <= min) return 0;
3434
+ return Math.min(Math.max((size - min) / (reachableMax - min), 0), 1);
3435
+ }
3436
+ handleOpen() {
3437
+ sheetStack.promote(this.id);
3438
+ try {
3439
+ this.lifecycle.install();
3440
+ } catch (err) {
3441
+ if (!this.destroyed) this.newCycle();
3442
+ this.handleClose();
3443
+ this.applySize(0);
3444
+ this.activeId = this.snapPointsRaw.find((p) => p.id === "closed")?.id ?? this.activeId;
3445
+ this.size = 0;
3446
+ queueMicrotask(() => {
3447
+ throw err;
3448
+ });
3449
+ }
3450
+ }
3451
+ resyncAfterResize() {
3452
+ if (this.destroyed) return;
3453
+ const target = this.snaps.findById(this.activeId);
3454
+ if (!target) return;
3455
+ this.updateAriaSlider();
3456
+ this.emit("snap", {
3457
+ id: target.id,
3458
+ size: this.size,
3459
+ progress: this.computeProgress(this.size)
3460
+ });
3461
+ if (target.size > 0 && !this.lifecycle.isInstalled) {
3462
+ this.emitOpenSequence(target.id);
3463
+ } else if (target.size === 0 && this.lifecycle.isInstalled) {
3464
+ this.emitCloseSequence();
3465
+ }
3466
+ }
3467
+ handleClose() {
3468
+ this.opening = false;
3469
+ this.lifecycle.release();
3470
+ sheetStack.update();
3471
+ }
3472
+ };
3473
+
3474
+ exports.BottomSheetCore = BottomSheetCore;
3475
+ //# sourceMappingURL=core.cjs.map
3476
+ //# sourceMappingURL=core.cjs.map