@surdeddd/bottom-sheet 0.3.0 → 0.4.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 (45) hide show
  1. package/dist/{BottomSheetEngine-CAymrH-P.d.cts → BottomSheetEngine-BJrH7qY3.d.cts} +39 -3
  2. package/dist/{BottomSheetEngine-DcqcuudN.d.ts → BottomSheetEngine-L05jGZvU.d.ts} +39 -3
  3. package/dist/element.cjs +480 -89
  4. package/dist/element.d.cts +23 -4
  5. package/dist/element.d.ts +23 -4
  6. package/dist/element.iife.global.js +2 -2
  7. package/dist/element.js +480 -89
  8. package/dist/index.cjs +341 -80
  9. package/dist/index.d.cts +3 -2
  10. package/dist/index.d.ts +3 -2
  11. package/dist/index.js +341 -80
  12. package/dist/integrations/formik.d.cts +1 -1
  13. package/dist/integrations/formik.d.ts +1 -1
  14. package/dist/integrations/react-hook-form.d.cts +1 -1
  15. package/dist/integrations/react-hook-form.d.ts +1 -1
  16. package/dist/overlay.cjs +13 -2
  17. package/dist/overlay.js +13 -2
  18. package/dist/preact.d.cts +1 -1
  19. package/dist/preact.d.ts +1 -1
  20. package/dist/qwik.cjs +352 -81
  21. package/dist/qwik.d.cts +4 -2
  22. package/dist/qwik.d.ts +4 -2
  23. package/dist/qwik.js +352 -81
  24. package/dist/react.cjs +427 -87
  25. package/dist/react.d.cts +29 -4
  26. package/dist/react.d.ts +29 -4
  27. package/dist/react.js +427 -87
  28. package/dist/solid.cjs +341 -80
  29. package/dist/solid.d.cts +38 -2
  30. package/dist/solid.d.ts +38 -2
  31. package/dist/solid.js +341 -80
  32. package/dist/solid.source.jsx +341 -80
  33. package/dist/styles.css +57 -56
  34. package/dist/svelte-core.cjs +346 -80
  35. package/dist/svelte-core.js +346 -80
  36. package/dist/svelte-src/BottomSheet.svelte +103 -5
  37. package/dist/svelte.cjs +421 -88
  38. package/dist/svelte.d.cts +21 -0
  39. package/dist/svelte.d.ts +21 -0
  40. package/dist/svelte.js +421 -88
  41. package/dist/vue.cjs +584 -91
  42. package/dist/vue.d.cts +51 -2
  43. package/dist/vue.d.ts +51 -2
  44. package/dist/vue.js +585 -92
  45. package/package.json +6 -6
package/dist/svelte.cjs CHANGED
@@ -61,6 +61,15 @@ var SheetStack = class {
61
61
  size() {
62
62
  return this.entries.length;
63
63
  }
64
+ depthOf(id) {
65
+ const open = [];
66
+ for (const entry of this.entries) {
67
+ if (!entry.isOpen || entry.isOpen()) open.push(entry);
68
+ }
69
+ const idx = open.findIndex((e) => e.id === id);
70
+ if (idx === -1) return 0;
71
+ return open.length - 1 - idx;
72
+ }
64
73
  clear() {
65
74
  this.entries = [];
66
75
  }
@@ -179,7 +188,9 @@ var PERCENT = /^(-?\d+(?:\.\d+)?)%$/;
179
188
  var resolveSnap = (point, mode, measureFit) => {
180
189
  if (typeof point === "number") return Math.max(0, point);
181
190
  if (point === "full") return viewportSize(mode);
182
- if (point === "fit") return Math.max(0, measureFit?.() ?? 0);
191
+ if (point === "fit" || point === "content") {
192
+ return Math.max(0, measureFit?.() ?? 0);
193
+ }
183
194
  const pctMatch = point.match(PERCENT);
184
195
  if (pctMatch) {
185
196
  const pct = parseFloat(pctMatch[1]) / 100;
@@ -416,6 +427,42 @@ function notifyLinkedSheets(sheets, self) {
416
427
  }
417
428
 
418
429
  // src/core/features/route.ts
430
+ var closers = [];
431
+ var handlerInstalled = false;
432
+ var suppress = 0;
433
+ var ensureHandler = () => {
434
+ if (handlerInstalled || typeof window === "undefined") return;
435
+ handlerInstalled = true;
436
+ window.addEventListener("popstate", () => {
437
+ if (suppress > 0) {
438
+ suppress -= 1;
439
+ return;
440
+ }
441
+ let target = null;
442
+ for (let i = closers.length - 1; i >= 0; i -= 1) {
443
+ const c = closers[i];
444
+ if (!c.isOpen()) continue;
445
+ if (!target) target = c;
446
+ if (c.isTop()) {
447
+ target = c;
448
+ break;
449
+ }
450
+ }
451
+ if (!target) return;
452
+ target.consumed = true;
453
+ const idx = closers.indexOf(target);
454
+ if (idx !== -1) closers.splice(idx, 1);
455
+ target.close();
456
+ });
457
+ };
458
+ var programmaticBack = () => {
459
+ suppress += 1;
460
+ try {
461
+ history.back();
462
+ } catch {
463
+ suppress = Math.max(0, suppress - 1);
464
+ }
465
+ };
419
466
  function installRoute(deps) {
420
467
  if (typeof window === "undefined") {
421
468
  return () => {
@@ -425,82 +472,81 @@ function installRoute(deps) {
425
472
  return () => {
426
473
  };
427
474
  }
428
- let routePushed = false;
429
- let closeOnBackPushed = false;
430
- let detachPopstate = null;
431
- const popOurMarker = (key, expected) => {
432
- const state2 = history.state;
433
- if (state2 && state2[key] === expected) {
434
- try {
435
- history.back();
436
- } catch {
437
- }
475
+ let pushed = false;
476
+ let closer = null;
477
+ const teardownEntry = () => {
478
+ if (closer) {
479
+ const idx = closers.indexOf(closer);
480
+ if (idx !== -1) closers.splice(idx, 1);
481
+ }
482
+ const wasConsumed = closer?.consumed ?? false;
483
+ closer = null;
484
+ if (pushed && !wasConsumed) {
485
+ pushed = false;
486
+ programmaticBack();
487
+ } else {
488
+ pushed = false;
438
489
  }
439
490
  };
440
491
  const onOpen = () => {
441
- if (deps.routedTo && !detachPopstate) {
442
- const target = deps.routedTo;
443
- try {
444
- history.pushState({ __bsRouted: target }, "", target);
445
- routePushed = true;
446
- } catch {
447
- }
448
- const onPop = (event) => {
449
- if (deps.isDestroyed()) return;
450
- const state2 = event.state;
451
- if ((!state2 || state2.__bsRouted !== target) && deps.getSize() > 0) {
452
- routePushed = false;
453
- void deps.close();
454
- }
455
- };
456
- window.addEventListener("popstate", onPop);
457
- detachPopstate = () => window.removeEventListener("popstate", onPop);
458
- } else if (deps.closeOnBack && !detachPopstate) {
459
- try {
492
+ if (closer) return;
493
+ closer = {
494
+ isOpen: () => !deps.isDestroyed() && deps.getSize() > 0,
495
+ isTop: () => deps.isTopSheet(),
496
+ close: () => void deps.close(),
497
+ consumed: false
498
+ };
499
+ ensureHandler();
500
+ closers.push(closer);
501
+ try {
502
+ if (deps.routedTo !== void 0) {
503
+ history.pushState({ __bsRouted: deps.sheetId }, "", deps.routedTo);
504
+ } else {
460
505
  history.pushState({ __bsSheet: deps.sheetId }, "");
461
- closeOnBackPushed = true;
462
- } catch {
463
506
  }
464
- const onPop = () => {
465
- if (deps.isDestroyed()) return;
466
- if (deps.getSize() > 0 && deps.isTopSheet()) {
467
- const state2 = history.state;
468
- if (state2?.__bsSheet !== deps.sheetId) closeOnBackPushed = false;
469
- void deps.close();
470
- }
471
- };
472
- window.addEventListener("popstate", onPop);
473
- detachPopstate = () => window.removeEventListener("popstate", onPop);
474
- }
475
- };
476
- const onClose = () => {
477
- detachPopstate?.();
478
- detachPopstate = null;
479
- if (routePushed) {
480
- routePushed = false;
481
- if (deps.routedTo !== void 0) popOurMarker("__bsRouted", deps.routedTo);
482
- }
483
- if (closeOnBackPushed) {
484
- closeOnBackPushed = false;
485
- popOurMarker("__bsSheet", deps.sheetId);
507
+ pushed = true;
508
+ } catch {
509
+ pushed = false;
486
510
  }
487
511
  };
488
512
  const unsubscribeOpen = deps.on("open", onOpen);
489
- const unsubscribeClose = deps.on("close", onClose);
513
+ const unsubscribeClose = deps.on("close", teardownEntry);
490
514
  if (deps.getSize() > 0) onOpen();
491
515
  return () => {
492
516
  unsubscribeOpen();
493
517
  unsubscribeClose();
494
- detachPopstate?.();
495
- detachPopstate = null;
496
- if (routePushed) {
497
- routePushed = false;
498
- if (deps.routedTo !== void 0) popOurMarker("__bsRouted", deps.routedTo);
499
- }
500
- if (closeOnBackPushed) {
501
- closeOnBackPushed = false;
502
- popOurMarker("__bsSheet", deps.sheetId);
503
- }
518
+ teardownEntry();
519
+ };
520
+ }
521
+ function installRouteChange(deps) {
522
+ if (typeof window === "undefined" || typeof history === "undefined") {
523
+ return () => {
524
+ };
525
+ }
526
+ const onChange = () => {
527
+ if (deps.isDestroyed() || deps.getSize() <= 0) return;
528
+ void deps.close();
529
+ };
530
+ const wrap = (key) => {
531
+ const original = history[key];
532
+ const patched = function(...args) {
533
+ original.apply(this, args);
534
+ onChange();
535
+ };
536
+ history[key] = patched;
537
+ return () => {
538
+ if (history[key] === patched) {
539
+ history[key] = original;
540
+ }
541
+ };
542
+ };
543
+ window.addEventListener("popstate", onChange);
544
+ const restorePush = wrap("pushState");
545
+ const restoreReplace = wrap("replaceState");
546
+ return () => {
547
+ window.removeEventListener("popstate", onChange);
548
+ restorePush();
549
+ restoreReplace();
504
550
  };
505
551
  }
506
552
 
@@ -808,6 +854,7 @@ function applyOverlayPosition(ws, mode, position, inset) {
808
854
 
809
855
  // src/core/controllers/scrim-controller.ts
810
856
  var VALID_CSS_LENGTH = /^-?\d+(?:\.\d+)?(?:px|em|rem|%)$/;
857
+ var DEFAULT_SCRIM_DIM = "rgba(0, 0, 0, 0.4)";
811
858
  var ScrimController = class {
812
859
  constructor(deps, opts) {
813
860
  __publicField(this, "screenComponent");
@@ -846,9 +893,11 @@ var ScrimController = class {
846
893
  this.backdropRange = opts.backdropRange ?? preset?.range ?? [0, 1];
847
894
  this.screenRange = opts.screenRange ?? preset?.range ?? [0, 1];
848
895
  this.scrimTapToCloseEnabled = opts.scrimTapToClose ?? false;
896
+ const explicitColor = opts.scrimColor ?? preset?.color;
897
+ const effectiveColor = explicitColor ?? (this.scrimMode !== "off" && !this.backdrop ? DEFAULT_SCRIM_DIM : void 0);
849
898
  if (this.screenComponent) {
850
899
  this.applyScrimStyles({
851
- color: opts.scrimColor ?? preset?.color,
900
+ color: effectiveColor,
852
901
  blur: opts.scrimBlur ?? preset?.blur,
853
902
  interactive: opts.scrimInteractive ?? preset?.interactive ?? false
854
903
  });
@@ -1622,7 +1671,7 @@ var LifecycleController = class {
1622
1671
  this.releaseScrollLock = null;
1623
1672
  this.inertSiblings.remove();
1624
1673
  if (this.returnFocus) {
1625
- const target = typeof this.returnFocus === "function" ? this.returnFocus() : this.returnFocus;
1674
+ const target = typeof this.returnFocus === "function" ? this.returnFocus() : typeof this.returnFocus === "string" ? typeof document !== "undefined" ? document.querySelector(this.returnFocus) : null : this.returnFocus;
1626
1675
  target?.focus?.();
1627
1676
  }
1628
1677
  }
@@ -1789,6 +1838,7 @@ var GestureController = class {
1789
1838
  __publicField(this, "dragStartSize", 0);
1790
1839
  __publicField(this, "currentPointerKind", "touch");
1791
1840
  __publicField(this, "keyboardDismissed", false);
1841
+ __publicField(this, "dragSuppressed", false);
1792
1842
  __publicField(this, "dragPayload", { size: 0, delta: 0 });
1793
1843
  this.deps = deps;
1794
1844
  }
@@ -1799,6 +1849,11 @@ var GestureController = class {
1799
1849
  if (this.detach) return this.detach;
1800
1850
  this.detach = installGestures(this.deps.handle, this.deps.mode, {
1801
1851
  onStart: (_coord, kind) => {
1852
+ if (this.deps.getDisableDrag?.()) {
1853
+ this.dragSuppressed = true;
1854
+ return;
1855
+ }
1856
+ this.dragSuppressed = false;
1802
1857
  this.deps.cancelAnimation();
1803
1858
  this.isDragging_ = true;
1804
1859
  this.dragStartSize = this.deps.getDragContext().size;
@@ -1809,6 +1864,7 @@ var GestureController = class {
1809
1864
  this.deps.emit("dragstart", { size: this.dragStartSize });
1810
1865
  },
1811
1866
  onMove: (delta) => {
1867
+ if (this.dragSuppressed) return;
1812
1868
  const ctx = this.deps.getDragContext();
1813
1869
  const { min, max } = ctx.range;
1814
1870
  const maxAxis = ctx.maxAxisSize;
@@ -1832,6 +1888,10 @@ var GestureController = class {
1832
1888
  }
1833
1889
  },
1834
1890
  onEnd: (delta, velocity, kind) => {
1891
+ if (this.dragSuppressed) {
1892
+ this.dragSuppressed = false;
1893
+ return;
1894
+ }
1835
1895
  this.isDragging_ = false;
1836
1896
  this.keyboardDismissed = false;
1837
1897
  this.deps.getRoot()?.removeAttribute("data-dragging");
@@ -1842,6 +1902,10 @@ var GestureController = class {
1842
1902
  this.deps.settleAfterDrag(delta, velocity, kind);
1843
1903
  },
1844
1904
  onCancel: () => {
1905
+ if (this.dragSuppressed) {
1906
+ this.dragSuppressed = false;
1907
+ return;
1908
+ }
1845
1909
  this.isDragging_ = false;
1846
1910
  this.keyboardDismissed = false;
1847
1911
  this.deps.getRoot()?.removeAttribute("data-dragging");
@@ -2221,6 +2285,12 @@ function resolveEngineOptions(opts, extras) {
2221
2285
  dragThreshold: opts.dragThreshold ?? DEFAULT_DRAG_THRESHOLD,
2222
2286
  rubberBandEnabled: opts.rubberBand ?? true,
2223
2287
  closeOnBack: opts.closeOnBack ?? false,
2288
+ persistent: opts.persistent ?? false,
2289
+ disableClose: opts.disableClose ?? false,
2290
+ disableDrag: opts.disableDrag ?? false,
2291
+ closeOnRouteChange: opts.closeOnRouteChange ?? false,
2292
+ radius: opts.radius,
2293
+ maxHeight: opts.maxHeight,
2224
2294
  initialAllowed,
2225
2295
  initialId,
2226
2296
  scrim: {
@@ -2247,7 +2317,8 @@ function resolveEngineOptions(opts, extras) {
2247
2317
  closeOnEscape: opts.closeOnEscape,
2248
2318
  lockBodyScroll: opts.lockBodyScroll,
2249
2319
  inertSiblings: opts.inertSiblings,
2250
- shouldApplyInertSiblings: extras?.shouldApplyInertSiblings
2320
+ shouldApplyInertSiblings: extras?.shouldApplyInertSiblings,
2321
+ returnFocus: opts.returnFocusTo
2251
2322
  }
2252
2323
  };
2253
2324
  }
@@ -2292,6 +2363,11 @@ var BottomSheetEngine = class {
2292
2363
  __publicField(this, "lifecycle");
2293
2364
  __publicField(this, "closeOnBack");
2294
2365
  __publicField(this, "routedTo");
2366
+ __publicField(this, "persistent");
2367
+ __publicField(this, "disableCloseFlag");
2368
+ __publicField(this, "disableDragFlag");
2369
+ __publicField(this, "closeOnRouteChange");
2370
+ __publicField(this, "maxHeightCap");
2295
2371
  __publicField(this, "snapPointsRaw");
2296
2372
  __publicField(this, "snaps");
2297
2373
  __publicField(this, "activeId");
@@ -2304,6 +2380,7 @@ var BottomSheetEngine = class {
2304
2380
  __publicField(this, "sizeWriteSentinel", new WriteSentinel());
2305
2381
  __publicField(this, "progressWriteSentinel", new WriteSentinel());
2306
2382
  __publicField(this, "isTopSheet", true);
2383
+ __publicField(this, "opening", false);
2307
2384
  __publicField(this, "progressPayload", {
2308
2385
  value: 0,
2309
2386
  size: 0
@@ -2360,7 +2437,8 @@ var BottomSheetEngine = class {
2360
2437
  void this.snapTo(id);
2361
2438
  },
2362
2439
  close: () => {
2363
- void this.close();
2440
+ if (!this.canDismiss()) return;
2441
+ void this.close("backdrop");
2364
2442
  }
2365
2443
  },
2366
2444
  resolved.scrim
@@ -2384,6 +2462,10 @@ var BottomSheetEngine = class {
2384
2462
  );
2385
2463
  this.closeOnBack = resolved.closeOnBack;
2386
2464
  this.routedTo = opts.routedTo;
2465
+ this.persistent = resolved.persistent;
2466
+ this.disableCloseFlag = resolved.disableClose;
2467
+ this.disableDragFlag = resolved.disableDrag;
2468
+ this.closeOnRouteChange = resolved.closeOnRouteChange;
2387
2469
  this.stackEffectEnabled = opts.stackEffect ?? false;
2388
2470
  this.snapPointsRaw = opts.snapPoints;
2389
2471
  this.persistKey = opts.persistKey;
@@ -2397,7 +2479,7 @@ var BottomSheetEngine = class {
2397
2479
  this.snapPointsRaw,
2398
2480
  resolved.initialAllowed,
2399
2481
  this.mode,
2400
- () => layoutAxis(this.mode) === "width" ? this.handle.offsetWidth : this.handle.offsetHeight,
2482
+ () => this.measureFitSize(),
2401
2483
  (maxAxisSize) => {
2402
2484
  this.element.style[layoutAxis(this.mode)] = `${maxAxisSize}px`;
2403
2485
  }
@@ -2431,6 +2513,19 @@ var BottomSheetEngine = class {
2431
2513
  io.observe(this.element);
2432
2514
  this.teardowns.add(() => io.disconnect());
2433
2515
  }
2516
+ this.installFitObserver();
2517
+ if (resolved.radius !== void 0) this.setRadius(resolved.radius);
2518
+ if (resolved.maxHeight !== void 0) this.setMaxHeight(resolved.maxHeight);
2519
+ this.applyAutoAriaLabelledBy();
2520
+ if (this.closeOnRouteChange) {
2521
+ this.teardowns.add(
2522
+ installRouteChange({
2523
+ isDestroyed: () => this.destroyed,
2524
+ getSize: () => this.size,
2525
+ close: () => this.close()
2526
+ })
2527
+ );
2528
+ }
2434
2529
  if (this.persistKey) {
2435
2530
  this.teardowns.add(installPersist(this, this.persistKey));
2436
2531
  }
@@ -2455,7 +2550,7 @@ var BottomSheetEngine = class {
2455
2550
  isTopSheet: () => this.isTopSheet,
2456
2551
  getSize: () => this.size,
2457
2552
  isDestroyed: () => this.destroyed,
2458
- close: () => this.close(),
2553
+ close: () => this.close("back"),
2459
2554
  on: (event, fn) => this.on(event, fn),
2460
2555
  sheetId: this.id
2461
2556
  })
@@ -2549,6 +2644,10 @@ var BottomSheetEngine = class {
2549
2644
  const previousSize = this.size;
2550
2645
  this.scrollCache.cache(previousId, previousSize, target.size);
2551
2646
  this.activeId = id;
2647
+ if (wasClosed && target.size > 0) {
2648
+ this.opening = true;
2649
+ sheetStack.promote(this.id);
2650
+ }
2552
2651
  if (this.animation.viewTransitionsAvailable) {
2553
2652
  this.currentViewTransition?.skipTransition?.();
2554
2653
  const vt = document.startViewTransition(() => {
@@ -2563,19 +2662,26 @@ var BottomSheetEngine = class {
2563
2662
  } else {
2564
2663
  await this.animation.animateTo(target.size, velocityPxPerMs);
2565
2664
  }
2665
+ this.opening = false;
2566
2666
  externalSignal?.removeEventListener("abort", onExternalAbort);
2567
2667
  if (signal.aborted) return;
2568
2668
  this.scrollCache.restore(id, previousSize, target.size);
2569
2669
  this.updateAriaSlider();
2570
- this.emit("snap", { id, size: target.size });
2670
+ this.emit("snap", {
2671
+ id,
2672
+ size: target.size,
2673
+ progress: this.computeProgress(target.size)
2674
+ });
2571
2675
  if (wasClosed && target.size > 0) {
2572
2676
  this.emit("open", { id });
2573
2677
  this.handleOpen();
2574
2678
  notifyLinkedSheets(this.linkedSheets, this);
2679
+ this.emit("opened", { id });
2575
2680
  }
2576
2681
  if (target.size === 0) {
2577
2682
  this.emit("close", void 0);
2578
2683
  this.handleClose();
2684
+ this.emit("closed", void 0);
2579
2685
  }
2580
2686
  }
2581
2687
  async dragTo(targetSize, velocityOrOpts = 0) {
@@ -2600,11 +2706,74 @@ var BottomSheetEngine = class {
2600
2706
  const target = id ?? this.snaps.getAllowedIds().find((a) => a !== "closed") ?? this.activeId;
2601
2707
  return this.snapTo(target);
2602
2708
  }
2603
- close() {
2709
+ close(reason = "programmatic") {
2604
2710
  if (this.destroyed) return Promise.resolve();
2711
+ if (this.disableCloseFlag) return Promise.resolve();
2712
+ if (this.size > 0 && this.emitBeforeClose(reason)) return Promise.resolve();
2605
2713
  const closedId = this.snapPointsRaw.find((p) => p.id === "closed")?.id ?? this.snaps.getAllowedIds()[0];
2606
2714
  return this.snapTo(closedId ?? this.activeId);
2607
2715
  }
2716
+ canDismiss() {
2717
+ return !this.persistent && !this.disableCloseFlag && !this.destroyed;
2718
+ }
2719
+ isTop() {
2720
+ return this.isTopSheet;
2721
+ }
2722
+ depth() {
2723
+ return sheetStack.depthOf(this.id);
2724
+ }
2725
+ expand() {
2726
+ if (this.destroyed) return Promise.resolve();
2727
+ const ids = this.allowedIdsBySize();
2728
+ const target = ids[ids.length - 1];
2729
+ if (!target) return Promise.resolve();
2730
+ return this.snapTo(target);
2731
+ }
2732
+ collapse() {
2733
+ if (this.destroyed) return Promise.resolve();
2734
+ const ids = this.allowedIdsBySize();
2735
+ const nonZero = ids.find((id) => (this.snaps.findById(id)?.size ?? 0) > 0);
2736
+ const target = nonZero ?? ids[0];
2737
+ if (!target) return Promise.resolve();
2738
+ return this.snapTo(target);
2739
+ }
2740
+ setRadius(r) {
2741
+ if (this.destroyed) return;
2742
+ this.element.style.setProperty(
2743
+ "--bs-radius",
2744
+ typeof r === "number" ? `${r}px` : r
2745
+ );
2746
+ }
2747
+ setMaxHeight(h) {
2748
+ if (this.destroyed) return;
2749
+ const value = typeof h === "number" ? `${h}px` : h;
2750
+ const axis = layoutAxis(this.mode);
2751
+ this.element.style.setProperty(
2752
+ axis === "height" ? "maxHeight" : "maxWidth",
2753
+ value
2754
+ );
2755
+ if (typeof h === "number") {
2756
+ this.maxHeightCap = h;
2757
+ this.clampToMaxHeight();
2758
+ } else if (typeof window !== "undefined") {
2759
+ const probe2 = document.createElement("div");
2760
+ probe2.style.cssText = `position:absolute;visibility:hidden;pointer-events:none;${axis === "height" ? "height" : "width"}:${value};`;
2761
+ document.body.appendChild(probe2);
2762
+ const measured = axis === "height" ? probe2.offsetHeight : probe2.offsetWidth;
2763
+ probe2.remove();
2764
+ this.maxHeightCap = measured > 0 ? measured : void 0;
2765
+ this.clampToMaxHeight();
2766
+ }
2767
+ }
2768
+ clampToMaxHeight() {
2769
+ if (this.maxHeightCap === void 0) return;
2770
+ if (this.snaps.getMaxAxisSize() > this.maxHeightCap) {
2771
+ this.snaps.setMaxAxisSize(this.maxHeightCap);
2772
+ }
2773
+ if (this.size > this.maxHeightCap && !this.gesture?.isDragging) {
2774
+ this.applySize(this.maxHeightCap);
2775
+ }
2776
+ }
2608
2777
  setAllowed(ids, snap) {
2609
2778
  if (this.destroyed) return;
2610
2779
  this.snaps.setAllowedIds(ids);
@@ -2738,8 +2907,53 @@ var BottomSheetEngine = class {
2738
2907
  }
2739
2908
  this.updateAriaSlider();
2740
2909
  }
2910
+ recompute() {
2911
+ if (this.destroyed) return;
2912
+ this.snaps.recompute();
2913
+ this.scrim.invalidateOpacityCache();
2914
+ if (this.gesture?.isDragging) return;
2915
+ const current = this.snaps.findById(this.activeId);
2916
+ if (current) {
2917
+ this.size = current.size;
2918
+ this.applySize(this.size);
2919
+ this.updateAriaSlider();
2920
+ }
2921
+ }
2922
+ measureFitSize() {
2923
+ const vertical = layoutAxis(this.mode) === "height";
2924
+ const handleSize = vertical ? this.handle.offsetHeight : this.handle.offsetWidth;
2925
+ const content = this.scrollContainer;
2926
+ const natural = content ? handleSize + (vertical ? content.scrollHeight : content.scrollWidth) : handleSize;
2927
+ if (typeof window === "undefined") return natural;
2928
+ const viewport = vertical ? window.innerHeight : window.innerWidth;
2929
+ return viewport > 0 ? Math.min(natural, viewport) : natural;
2930
+ }
2931
+ installFitObserver() {
2932
+ if (typeof ResizeObserver === "undefined") return;
2933
+ if (!this.snapPointsRaw.some((p) => p.size === "fit" || p.size === "content"))
2934
+ return;
2935
+ const targets = [this.handle];
2936
+ if (this.scrollContainer && this.scrollContainer !== this.handle) {
2937
+ targets.push(this.scrollContainer);
2938
+ }
2939
+ let raf = 0;
2940
+ const ro = new ResizeObserver(() => {
2941
+ if (raf) return;
2942
+ raf = requestAnimationFrame(() => {
2943
+ raf = 0;
2944
+ if (this.destroyed || this.gesture?.isDragging) return;
2945
+ this.recompute();
2946
+ });
2947
+ });
2948
+ for (const t of targets) ro.observe(t);
2949
+ this.teardowns.add(() => {
2950
+ ro.disconnect();
2951
+ if (raf) cancelAnimationFrame(raf);
2952
+ });
2953
+ }
2741
2954
  destroy() {
2742
2955
  this.destroyed = true;
2956
+ this.opening = false;
2743
2957
  this.currentAbort.abort();
2744
2958
  this.currentViewTransition?.skipTransition?.();
2745
2959
  this.currentViewTransition = null;
@@ -2789,6 +3003,36 @@ var BottomSheetEngine = class {
2789
3003
  frozen = true;
2790
3004
  return cancelled;
2791
3005
  }
3006
+ emitBeforeClose(reason) {
3007
+ let cancelled = false;
3008
+ let frozen = false;
3009
+ this.emit("before-close", {
3010
+ reason,
3011
+ cancel: () => {
3012
+ if (frozen) {
3013
+ console.warn(
3014
+ "[BottomSheet] before-close.cancel() called asynchronously \u2014 ignored. cancel() must be invoked synchronously inside the listener."
3015
+ );
3016
+ return;
3017
+ }
3018
+ cancelled = true;
3019
+ }
3020
+ });
3021
+ frozen = true;
3022
+ return cancelled;
3023
+ }
3024
+ applyAutoAriaLabelledBy() {
3025
+ if (typeof document === "undefined") return;
3026
+ if (!this.lifecycle.focusTrapEnabled) return;
3027
+ if (this.element.getAttribute("aria-labelledby")) return;
3028
+ const scope = this.handle ?? this.element;
3029
+ const titled = scope.querySelector("[data-bs-title]") ?? scope.querySelector("h1,h2,h3,h4,h5,h6");
3030
+ if (!titled) return;
3031
+ if (!titled.id) {
3032
+ titled.id = nextInstanceId("bs-title");
3033
+ }
3034
+ this.element.setAttribute("aria-labelledby", titled.id);
3035
+ }
2792
3036
  recomputeSnaps() {
2793
3037
  const seen = /* @__PURE__ */ new Set();
2794
3038
  for (const p of this.snapPointsRaw) {
@@ -2824,11 +3068,8 @@ var BottomSheetEngine = class {
2824
3068
  },
2825
3069
  setIsTop: (isTop) => {
2826
3070
  this.isTopSheet = isTop;
2827
- if (this.backdrop) {
2828
- this.backdrop.style.display = isTop ? "" : "none";
2829
- }
2830
3071
  },
2831
- isOpen: () => this.size > 0,
3072
+ isOpen: () => this.size > 0 || this.opening,
2832
3073
  setDepth: (depth) => this.applyStackDepth(depth)
2833
3074
  }));
2834
3075
  }
@@ -2860,6 +3101,7 @@ var BottomSheetEngine = class {
2860
3101
  buf.rubberBandEnabled = this.rubberBandEnabled;
2861
3102
  return buf;
2862
3103
  },
3104
+ getDisableDrag: () => this.disableDragFlag,
2863
3105
  cancelAnimation: () => this.animation.cancel(),
2864
3106
  applySize: (size) => this.applySize(size),
2865
3107
  animateTo: (size, velocity) => this.animation.animateTo(size, velocity),
@@ -2939,8 +3181,9 @@ var BottomSheetEngine = class {
2939
3181
  if (this.lifecycle.closeOnEscape) {
2940
3182
  const onKey = (e) => {
2941
3183
  if (this.destroyed || e.defaultPrevented) return;
3184
+ if (!this.canDismiss()) return;
2942
3185
  if (e.key === "Escape" && this.isTopSheet && this.size > 0) {
2943
- void this.close();
3186
+ void this.close("escape");
2944
3187
  }
2945
3188
  };
2946
3189
  document.addEventListener("keydown", onKey);
@@ -3003,20 +3246,31 @@ var BottomSheetEngine = class {
3003
3246
  const previousSnapSize = this.snaps.findById(previousId)?.size;
3004
3247
  this.scrollCache.cache(previousId, previousSize, target.size);
3005
3248
  this.activeId = target.id;
3249
+ if (previousSnapSize === 0 && target.size > 0) {
3250
+ this.opening = true;
3251
+ sheetStack.promote(this.id);
3252
+ }
3006
3253
  void this.animation.animateTo(target.size, velocity).then(() => {
3254
+ this.opening = false;
3007
3255
  if (signal.aborted) return;
3008
3256
  this.scrollCache.restore(target.id, previousSize, target.size);
3009
3257
  this.updateAriaSlider();
3010
- this.emit("snap", { id: target.id, size: target.size });
3258
+ this.emit("snap", {
3259
+ id: target.id,
3260
+ size: target.size,
3261
+ progress: this.computeProgress(target.size)
3262
+ });
3011
3263
  this.haptic();
3012
3264
  if (previousSnapSize === 0 && target.size > 0) {
3013
3265
  this.emit("open", { id: target.id });
3014
3266
  this.handleOpen();
3015
3267
  notifyLinkedSheets(this.linkedSheets, this);
3268
+ this.emit("opened", { id: target.id });
3016
3269
  }
3017
3270
  if (target.size === 0) {
3018
3271
  this.emit("close", void 0);
3019
3272
  this.handleClose();
3273
+ this.emit("closed", void 0);
3020
3274
  }
3021
3275
  });
3022
3276
  }
@@ -3085,17 +3339,24 @@ var BottomSheetEngine = class {
3085
3339
  const target = this.snaps.findById(this.activeId);
3086
3340
  if (!target) return;
3087
3341
  this.updateAriaSlider();
3088
- this.emit("snap", { id: target.id, size: target.size });
3342
+ this.emit("snap", {
3343
+ id: target.id,
3344
+ size: target.size,
3345
+ progress: this.computeProgress(target.size)
3346
+ });
3089
3347
  if (target.size > 0 && !this.lifecycle.isInstalled) {
3090
3348
  this.emit("open", { id: target.id });
3091
3349
  this.handleOpen();
3092
3350
  notifyLinkedSheets(this.linkedSheets, this);
3351
+ this.emit("opened", { id: target.id });
3093
3352
  } else if (target.size === 0 && this.lifecycle.isInstalled) {
3094
3353
  this.emit("close", void 0);
3095
3354
  this.handleClose();
3355
+ this.emit("closed", void 0);
3096
3356
  }
3097
3357
  }
3098
3358
  handleClose() {
3359
+ this.opening = false;
3099
3360
  this.lifecycle.release();
3100
3361
  sheetStack.update();
3101
3362
  }
@@ -3149,6 +3410,10 @@ function createBottomSheet(opts) {
3149
3410
  snapTo: (id) => engine?.snapTo(id) ?? Promise.resolve(),
3150
3411
  open: (id) => engine?.open(id) ?? Promise.resolve(),
3151
3412
  close: () => engine?.close() ?? Promise.resolve(),
3413
+ expand: () => engine?.expand() ?? Promise.resolve(),
3414
+ collapse: () => engine?.collapse() ?? Promise.resolve(),
3415
+ isTop: () => engine?.isTop() ?? false,
3416
+ depth: () => engine?.depth() ?? 0,
3152
3417
  setAllowed: (ids, snap) => engine?.setAllowed(ids, snap),
3153
3418
  setSnapPoints: (points, allowed) => engine?.setSnapPoints(points, allowed),
3154
3419
  setScrim: (scrimOpts) => engine?.setScrim(scrimOpts),
@@ -3158,6 +3423,7 @@ function createBottomSheet(opts) {
3158
3423
  }),
3159
3424
  setScrimStages: (stagesOpts) => engine?.setScrimStages(stagesOpts) ?? (() => {
3160
3425
  }),
3426
+ recompute: () => engine?.recompute(),
3161
3427
  destroy: () => {
3162
3428
  engine?.destroy();
3163
3429
  engine = null;
@@ -3171,7 +3437,7 @@ var root_4 = $2__namespace.from_html(`<div class="bs-button-slot" data-side="rig
3171
3437
  var root = $2__namespace.from_html(`<div class="bs-root"><!> <div class="bs-screen"><!></div> <section class="bs-sheet" role="dialog"><!> <!> <div class="bs-handle" role="slider" tabindex="0" aria-label="Resize sheet"><!></div> <div class="bs-content" tabindex="0" role="region" aria-label="Sheet content"><!></div> <span class="bs-sr-only" role="status" aria-live="polite"> </span></section></div>`);
3172
3438
  function BottomSheet($$anchor, $$props) {
3173
3439
  $2__namespace.push($$props, true);
3174
- let mode = $2__namespace.prop($$props, "mode", 3, "bottom"), animation = $2__namespace.prop($$props, "animation", 3, "spring"), focusTrap = $2__namespace.prop($$props, "focusTrap", 3, false), closeOnEscape = $2__namespace.prop($$props, "closeOnEscape", 3, true), closeOnBack = $2__namespace.prop($$props, "closeOnBack", 3, false), lockBodyScroll2 = $2__namespace.prop($$props, "lockBodyScroll", 3, true), rubberBand2 = $2__namespace.prop($$props, "rubberBand", 3, true), backdrop = $2__namespace.prop($$props, "backdrop", 3, true), closeOnBackdrop = $2__namespace.prop($$props, "closeOnBackdrop", 3, true), ariaLabel = $2__namespace.prop($$props, "ariaLabel", 3, "Bottom sheet");
3440
+ let mode = $2__namespace.prop($$props, "mode", 3, "bottom"), animation = $2__namespace.prop($$props, "animation", 3, "spring"), focusTrap = $2__namespace.prop($$props, "focusTrap", 3, false), closeOnEscape = $2__namespace.prop($$props, "closeOnEscape", 3, true), closeOnBack = $2__namespace.prop($$props, "closeOnBack", 3, false), lockBodyScroll2 = $2__namespace.prop($$props, "lockBodyScroll", 3, true), rubberBand2 = $2__namespace.prop($$props, "rubberBand", 3, true), backdrop = $2__namespace.prop($$props, "backdrop", 3, true), closeOnBackdrop = $2__namespace.prop($$props, "closeOnBackdrop", 3, true), persistent = $2__namespace.prop($$props, "persistent", 3, false), disableClose = $2__namespace.prop($$props, "disableClose", 3, false), disableDrag = $2__namespace.prop($$props, "disableDrag", 3, false), closeOnRouteChange = $2__namespace.prop($$props, "closeOnRouteChange", 3, false), openProp = $2__namespace.prop($$props, "open", 15), snap = $2__namespace.prop($$props, "snap", 15), ariaLabel = $2__namespace.prop($$props, "ariaLabel", 3, "Bottom sheet");
3175
3441
  let sheetEl = $2__namespace.state(void 0);
3176
3442
  let handleEl = $2__namespace.state(void 0);
3177
3443
  let contentEl = $2__namespace.state(void 0);
@@ -3204,7 +3470,14 @@ function BottomSheet($$anchor, $$props) {
3204
3470
  closeOnBack: closeOnBack(),
3205
3471
  lockBodyScroll: lockBodyScroll2(),
3206
3472
  rubberBand: rubberBand2(),
3207
- backdropRange: $$props.backdropRange
3473
+ backdropRange: $$props.backdropRange,
3474
+ persistent: persistent(),
3475
+ disableClose: disableClose(),
3476
+ disableDrag: disableDrag(),
3477
+ closeOnRouteChange: closeOnRouteChange(),
3478
+ radius: $$props.radius,
3479
+ maxHeight: $$props.maxHeight,
3480
+ returnFocusTo: $$props.returnFocusTo
3208
3481
  });
3209
3482
  const sync = () => {
3210
3483
  if (!engine) return;
@@ -3212,14 +3485,32 @@ function BottomSheet($$anchor, $$props) {
3212
3485
  $$props.onchange?.($2__namespace.get(viewState));
3213
3486
  };
3214
3487
  sync();
3488
+ if ($$props.backdropColor !== void 0) engine.setScrimColor($$props.backdropColor);
3489
+ if ($$props.backdropOpacity !== void 0) engine.setBackdropRange([0, $$props.backdropOpacity]);
3215
3490
  engine.on("snap", (payload) => {
3216
3491
  sync();
3492
+ snap(payload.id);
3217
3493
  $$props.onsnap?.(payload.id);
3218
3494
  });
3219
- engine.on("open", (payload) => $$props.onopen?.(payload.id));
3220
- engine.on("close", () => $$props.onclose?.());
3221
- engine.on("dragstart", sync);
3222
- engine.on("dragend", sync);
3495
+ engine.on("open", (payload) => {
3496
+ openProp(true);
3497
+ $$props.onopen?.(payload.id);
3498
+ });
3499
+ engine.on("close", () => {
3500
+ openProp(false);
3501
+ $$props.onclose?.();
3502
+ });
3503
+ engine.on("before-close", (payload) => $$props.onbeforeclose?.(payload));
3504
+ engine.on("opened", (payload) => $$props.onopened?.(payload.id));
3505
+ engine.on("closed", () => $$props.onclosed?.());
3506
+ engine.on("dragstart", () => {
3507
+ sync();
3508
+ $$props.ondragstart?.();
3509
+ });
3510
+ engine.on("dragend", () => {
3511
+ sync();
3512
+ $$props.ondragend?.();
3513
+ });
3223
3514
  });
3224
3515
  svelte.onDestroy(() => {
3225
3516
  engine?.destroy();
@@ -3228,13 +3519,46 @@ function BottomSheet($$anchor, $$props) {
3228
3519
  const snapTo = (id) => engine?.snapTo(id) ?? Promise.resolve();
3229
3520
  const open = (id) => engine?.open(id) ?? Promise.resolve();
3230
3521
  const close = () => engine?.close() ?? Promise.resolve();
3231
- const setAllowed = (ids, snap) => engine?.setAllowed(ids, snap);
3522
+ const expand = () => engine?.expand() ?? Promise.resolve();
3523
+ const collapse = () => engine?.collapse() ?? Promise.resolve();
3524
+ const isTop = () => engine?.isTop() ?? false;
3525
+ const depth = () => engine?.depth() ?? 0;
3526
+ const setAllowed = (ids, snap2) => engine?.setAllowed(ids, snap2);
3232
3527
  const setSnapPoints = (points, nextAllowed) => engine?.setSnapPoints(points, nextAllowed);
3233
3528
  const setScrim = (opts) => engine?.setScrim(opts);
3234
3529
  const setScrimOverlay = (opts) => engine?.setScrimOverlay(opts) ?? (() => {
3235
3530
  });
3531
+ const setRadius = (r) => engine?.setRadius(r);
3532
+ const setMaxHeight = (h) => engine?.setMaxHeight(h);
3533
+ const canDismiss = () => engine?.canDismiss() ?? false;
3236
3534
  const getState = () => engine?.state ?? $2__namespace.get(viewState);
3237
3535
  const getEngine = () => engine;
3536
+ $2__namespace.user_effect(() => {
3537
+ if ($$props.radius !== void 0) engine?.setRadius($$props.radius);
3538
+ });
3539
+ $2__namespace.user_effect(() => {
3540
+ if ($$props.maxHeight !== void 0) engine?.setMaxHeight($$props.maxHeight);
3541
+ });
3542
+ $2__namespace.user_effect(() => {
3543
+ if ($$props.backdropColor !== void 0) engine?.setScrimColor($$props.backdropColor);
3544
+ });
3545
+ $2__namespace.user_effect(() => {
3546
+ if ($$props.backdropOpacity !== void 0) engine?.setBackdropRange([0, $$props.backdropOpacity]);
3547
+ });
3548
+ $2__namespace.user_effect(() => {
3549
+ if (snap() === void 0) return;
3550
+ const next = snap();
3551
+ if (!engine || svelte.untrack(() => $2__namespace.get(viewState).activeId) === next) return;
3552
+ void engine.snapTo(next);
3553
+ });
3554
+ $2__namespace.user_effect(() => {
3555
+ if (openProp() === void 0) return;
3556
+ const next = openProp();
3557
+ if (!engine) return;
3558
+ const isOpen = svelte.untrack(() => $2__namespace.get(viewState).size) > 0;
3559
+ if (next && !isOpen) void engine.open();
3560
+ else if (!next && isOpen) void engine.close();
3561
+ });
3238
3562
  let allowedIds = $2__namespace.derived(() => $$props.allowed && $$props.allowed.length > 0 ? $$props.allowed : $$props.snapPoints.map((s) => s.id));
3239
3563
  let activeIdx = $2__namespace.derived(() => $2__namespace.get(allowedIds).indexOf($2__namespace.get(viewState).activeId));
3240
3564
  let isVerticalAxis = $2__namespace.derived(() => mode() === "bottom" || mode() === "top");
@@ -3242,10 +3566,17 @@ function BottomSheet($$anchor, $$props) {
3242
3566
  snapTo,
3243
3567
  open,
3244
3568
  close,
3569
+ expand,
3570
+ collapse,
3571
+ isTop,
3572
+ depth,
3245
3573
  setAllowed,
3246
3574
  setSnapPoints,
3247
3575
  setScrim,
3248
3576
  setScrimOverlay,
3577
+ setRadius,
3578
+ setMaxHeight,
3579
+ canDismiss,
3249
3580
  getState,
3250
3581
  getEngine
3251
3582
  };
@@ -3256,7 +3587,9 @@ function BottomSheet($$anchor, $$props) {
3256
3587
  var div_1 = root_1();
3257
3588
  $2__namespace.bind_this(div_1, ($$value) => $2__namespace.set(backdropEl, $$value), () => $2__namespace.get(backdropEl));
3258
3589
  $2__namespace.delegated("click", div_1, function(...$$args) {
3259
- (closeOnBackdrop() ? () => engine?.close() : void 0)?.apply(this, $$args);
3590
+ (closeOnBackdrop() ? () => {
3591
+ if (engine?.canDismiss()) void engine.close("backdrop");
3592
+ } : void 0)?.apply(this, $$args);
3260
3593
  });
3261
3594
  $2__namespace.append($$anchor2, div_1);
3262
3595
  };