@surdeddd/bottom-sheet 0.1.0 → 0.3.0

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