@surdeddd/bottom-sheet 0.8.2 → 0.9.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 (46) hide show
  1. package/dist/{BottomSheetEngine-7BDK2OWX.d.cts → BottomSheetEngine-BcyruNP1.d.cts} +12 -6
  2. package/dist/{BottomSheetEngine-BFTb0-4Z.d.ts → BottomSheetEngine-CBRcBwV-.d.ts} +12 -6
  3. package/dist/element.cjs +530 -349
  4. package/dist/element.d.cts +2 -1
  5. package/dist/element.d.ts +2 -1
  6. package/dist/element.iife.global.js +2 -2
  7. package/dist/element.js +530 -349
  8. package/dist/index.cjs +438 -294
  9. package/dist/index.d.cts +3 -3
  10. package/dist/index.d.ts +3 -3
  11. package/dist/index.js +438 -294
  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 +160 -96
  17. package/dist/overlay.d.cts +2 -0
  18. package/dist/overlay.d.ts +2 -0
  19. package/dist/overlay.js +160 -96
  20. package/dist/preact.d.cts +1 -1
  21. package/dist/preact.d.ts +1 -1
  22. package/dist/qwik.cjs +387 -211
  23. package/dist/qwik.d.cts +4 -3
  24. package/dist/qwik.d.ts +4 -3
  25. package/dist/qwik.js +387 -211
  26. package/dist/react.cjs +472 -296
  27. package/dist/react.d.cts +3 -2
  28. package/dist/react.d.ts +3 -2
  29. package/dist/react.js +472 -296
  30. package/dist/solid.cjs +417 -215
  31. package/dist/solid.d.cts +21 -7
  32. package/dist/solid.d.ts +21 -7
  33. package/dist/solid.js +417 -215
  34. package/dist/solid.source.jsx +412 -209
  35. package/dist/svelte-core.cjs +328 -209
  36. package/dist/svelte-core.js +328 -209
  37. package/dist/svelte-src/BottomSheet.svelte +56 -0
  38. package/dist/svelte.cjs +365 -210
  39. package/dist/svelte.d.cts +4 -0
  40. package/dist/svelte.d.ts +4 -0
  41. package/dist/svelte.js +365 -210
  42. package/dist/vue.cjs +358 -211
  43. package/dist/vue.d.cts +6 -0
  44. package/dist/vue.d.ts +6 -0
  45. package/dist/vue.js +358 -211
  46. package/package.json +6 -6
package/dist/element.cjs CHANGED
@@ -306,18 +306,25 @@ var init_snap_resolver = __esm({
306
306
  __publicField(this, "resolved", []);
307
307
  __publicField(this, "maxAxisSize", 0);
308
308
  __publicField(this, "rangeCache", null);
309
+ __publicField(this, "notified", false);
309
310
  this.raw = raw;
310
311
  this.allowedIds = allowed ?? raw.map((p) => p.id);
311
312
  this.recompute();
312
313
  }
313
314
  recompute() {
314
- this.resolved = resolveSnapList(this.raw, this.mode, this.measureFit);
315
- this.maxAxisSize = this.resolved.reduce(
315
+ const measure = this.measureFit;
316
+ let measured;
317
+ const measureOnce = measure ? () => measured ?? (measured = measure()) : void 0;
318
+ this.resolved = resolveSnapList(this.raw, this.mode, measureOnce);
319
+ const nextMax = this.resolved.reduce(
316
320
  (m, s) => s.size > m ? s.size : m,
317
321
  0
318
322
  );
319
323
  this.rangeCache = null;
320
- this.onMaxAxisSizeChange?.(this.maxAxisSize);
324
+ const changed = nextMax !== this.maxAxisSize || !this.notified;
325
+ this.maxAxisSize = nextMax;
326
+ this.notified = true;
327
+ if (changed) this.onMaxAxisSizeChange?.(this.maxAxisSize);
321
328
  }
322
329
  setRaw(raw) {
323
330
  this.raw = raw;
@@ -484,16 +491,7 @@ function installRoute(deps) {
484
491
  pushed = false;
485
492
  }
486
493
  };
487
- const onOpen = () => {
488
- if (closer) return;
489
- closer = {
490
- isOpen: () => !deps.isDestroyed() && deps.getSize() > 0,
491
- isTop: () => deps.isTopSheet(),
492
- close: () => void deps.close(),
493
- consumed: false
494
- };
495
- ensureHandler();
496
- closers.push(closer);
494
+ const pushEntry = () => {
497
495
  try {
498
496
  if (deps.routedTo !== void 0) {
499
497
  history.pushState({ __bsRouted: deps.sheetId }, "", deps.routedTo);
@@ -505,6 +503,25 @@ function installRoute(deps) {
505
503
  pushed = false;
506
504
  }
507
505
  };
506
+ const onOpen = () => {
507
+ if (closer) return;
508
+ const self = {
509
+ isOpen: () => !deps.isDestroyed() && deps.getSize() > 0,
510
+ isTop: () => deps.isTopSheet(),
511
+ close: () => deps.close(),
512
+ restore: () => {
513
+ if (deps.isDestroyed() || closer !== self) return;
514
+ self.consumed = false;
515
+ if (!closers.includes(self)) closers.push(self);
516
+ pushEntry();
517
+ },
518
+ consumed: false
519
+ };
520
+ closer = self;
521
+ ensureHandler();
522
+ closers.push(self);
523
+ pushEntry();
524
+ };
508
525
  const unsubscribeOpen = deps.on("open", onOpen);
509
526
  const unsubscribeClose = deps.on("close", teardownEntry);
510
527
  if (deps.getSize() > 0) onOpen();
@@ -573,7 +590,10 @@ var init_route = __esm({
573
590
  target.consumed = true;
574
591
  const idx = closers.indexOf(target);
575
592
  if (idx !== -1) closers.splice(idx, 1);
576
- target.close();
593
+ const settled = target;
594
+ void Promise.resolve(settled.close()).then(() => {
595
+ if (settled.isOpen()) settled.restore();
596
+ });
577
597
  });
578
598
  };
579
599
  programmaticBack = () => {
@@ -591,11 +611,13 @@ var init_route = __esm({
591
611
  function installContentSwipe(deps) {
592
612
  const { container } = deps;
593
613
  let startY = 0;
614
+ let startScrollTop = 0;
594
615
  let moved = false;
595
616
  const onTouchStart = (e) => {
596
617
  const t = e.touches[0];
597
618
  if (!t) return;
598
619
  startY = t.clientY;
620
+ startScrollTop = container.scrollTop;
599
621
  moved = false;
600
622
  };
601
623
  const onTouchMove = (e) => {
@@ -606,6 +628,7 @@ function installContentSwipe(deps) {
606
628
  const onTouchEnd = (e) => {
607
629
  if (!moved) return;
608
630
  if (deps.isDragging() || deps.isAnimating()) return;
631
+ if (container.scrollTop !== startScrollTop) return;
609
632
  const t = e.changedTouches[0];
610
633
  if (!t) return;
611
634
  const deltaY = t.clientY - startY;
@@ -732,6 +755,142 @@ var init_resize_observer = __esm({
732
755
  }
733
756
  });
734
757
 
758
+ // src/core/features/fit-observer.ts
759
+ function installFitObserver(deps) {
760
+ if (typeof ResizeObserver === "undefined") return () => {
761
+ };
762
+ if (!deps.hasFitSnap()) return () => {
763
+ };
764
+ let raf = 0;
765
+ const schedule = () => {
766
+ if (raf) return;
767
+ raf = requestAnimationFrame(() => {
768
+ raf = 0;
769
+ if (deps.isDestroyed() || deps.isDragging()) return;
770
+ deps.recompute();
771
+ });
772
+ };
773
+ const ro = new ResizeObserver(schedule);
774
+ ro.observe(deps.handle);
775
+ const scroller = deps.scrollContainer;
776
+ const observed = /* @__PURE__ */ new Set();
777
+ const slots = /* @__PURE__ */ new Set();
778
+ const hasSlot = typeof HTMLSlotElement !== "undefined";
779
+ const onSlotChange = () => resync();
780
+ const trackSlots = () => {
781
+ if (!scroller || !hasSlot) return;
782
+ for (const child of Array.from(scroller.children)) {
783
+ if (child instanceof HTMLSlotElement && !slots.has(child)) {
784
+ child.addEventListener("slotchange", onSlotChange);
785
+ slots.add(child);
786
+ }
787
+ }
788
+ };
789
+ const resync = () => {
790
+ trackSlots();
791
+ const targets = contentTargets(scroller);
792
+ for (const el of observed) {
793
+ if (!targets.includes(el)) {
794
+ ro.unobserve(el);
795
+ observed.delete(el);
796
+ }
797
+ }
798
+ for (const el of targets) {
799
+ if (!observed.has(el)) {
800
+ ro.observe(el);
801
+ observed.add(el);
802
+ }
803
+ }
804
+ schedule();
805
+ };
806
+ let mo;
807
+ if (scroller) {
808
+ resync();
809
+ if (typeof MutationObserver !== "undefined") {
810
+ mo = new MutationObserver(resync);
811
+ mo.observe(scroller, { childList: true });
812
+ }
813
+ }
814
+ return () => {
815
+ ro.disconnect();
816
+ mo?.disconnect();
817
+ for (const slot of slots) {
818
+ slot.removeEventListener("slotchange", onSlotChange);
819
+ }
820
+ if (raf) cancelAnimationFrame(raf);
821
+ };
822
+ }
823
+ var contentTargets;
824
+ var init_fit_observer = __esm({
825
+ "src/core/features/fit-observer.ts"() {
826
+ contentTargets = (scroller) => {
827
+ if (!scroller) return [];
828
+ const hasSlot = typeof HTMLSlotElement !== "undefined";
829
+ const out = [];
830
+ for (const child of Array.from(scroller.children)) {
831
+ if (hasSlot && child instanceof HTMLSlotElement) {
832
+ for (const el of child.assignedElements()) out.push(el);
833
+ } else {
834
+ out.push(child);
835
+ }
836
+ }
837
+ return out;
838
+ };
839
+ }
840
+ });
841
+
842
+ // src/core/features/fit-measurement.ts
843
+ var measureSheetNatural, containingExtent, measureFitSize;
844
+ var init_fit_measurement = __esm({
845
+ "src/core/features/fit-measurement.ts"() {
846
+ init_transform();
847
+ measureSheetNatural = (element, content, vertical) => {
848
+ if (!content) {
849
+ return vertical ? element.offsetHeight : element.offsetWidth;
850
+ }
851
+ const axis = vertical ? "height" : "width";
852
+ const sheetStyle = element.style;
853
+ const cs = content.style;
854
+ const prevSheet = sheetStyle[axis];
855
+ const prevFlex = cs.flex;
856
+ const prevContent = vertical ? cs.height : cs.width;
857
+ sheetStyle[axis] = "auto";
858
+ cs.flex = "none";
859
+ if (vertical) cs.height = "auto";
860
+ else cs.width = "auto";
861
+ const poked = vertical ? element.offsetHeight : element.offsetWidth;
862
+ const contentBox = vertical ? content.clientHeight : content.clientWidth;
863
+ const contentScroll = vertical ? content.scrollHeight : content.scrollWidth;
864
+ const natural = Math.max(poked, poked - contentBox + contentScroll);
865
+ sheetStyle[axis] = prevSheet;
866
+ cs.flex = prevFlex;
867
+ if (vertical) cs.height = prevContent;
868
+ else cs.width = prevContent;
869
+ return natural;
870
+ };
871
+ containingExtent = (element, vertical) => {
872
+ const op = element.offsetParent;
873
+ if (op && op !== document.body && op !== document.documentElement) {
874
+ return vertical ? op.clientHeight : op.clientWidth;
875
+ }
876
+ return vertical ? window.innerHeight : window.innerWidth;
877
+ };
878
+ measureFitSize = (deps) => {
879
+ const vertical = layoutAxis(deps.mode) === "height";
880
+ const natural = measureSheetNatural(
881
+ deps.element,
882
+ deps.scrollContainer,
883
+ vertical
884
+ );
885
+ const cap = deps.getMaxHeightCap();
886
+ const capped = cap !== void 0 ? Math.min(natural, cap) : natural;
887
+ if (typeof window === "undefined") return capped;
888
+ const viewport = containingExtent(deps.element, vertical);
889
+ return viewport > 0 ? Math.min(capped, viewport) : capped;
890
+ };
891
+ }
892
+ });
893
+
735
894
  // src/core/features/slider-keyboard.ts
736
895
  function installSliderKeyboard(deps) {
737
896
  const isVerticalAxis = deps.mode === "bottom" || deps.mode === "top";
@@ -1254,6 +1413,9 @@ var init_scrim_controller = __esm({
1254
1413
  const s = this.screenComponent.style;
1255
1414
  s.opacity = "";
1256
1415
  s.display = "";
1416
+ s.position = "";
1417
+ s.inset = "";
1418
+ s.pointerEvents = "";
1257
1419
  }
1258
1420
  if (this.backdrop) {
1259
1421
  this.backdrop.style.opacity = "";
@@ -1526,7 +1688,12 @@ var init_animation_runner = __esm({
1526
1688
  this.currentSpring?.cancel();
1527
1689
  }
1528
1690
  async animateTo(target, velocityPxPerMs) {
1529
- if (target === this.getSize()) return;
1691
+ if (target === this.getSize()) {
1692
+ if (!this.isAnimating && !this.isDragging()) {
1693
+ this.element.style.willChange = "";
1694
+ }
1695
+ return;
1696
+ }
1530
1697
  this.currentTween?.cancel();
1531
1698
  this.currentSpring?.cancel();
1532
1699
  this.getRootEl()?.setAttribute("data-animating", "true");
@@ -1534,7 +1701,7 @@ var init_animation_runner = __esm({
1534
1701
  if (this.reducedMotion) {
1535
1702
  this.applySizeFn(target);
1536
1703
  this.getRootEl()?.removeAttribute("data-animating");
1537
- if (!this.isDragging()) this.element.style.willChange = "auto";
1704
+ if (!this.isDragging()) this.element.style.willChange = "";
1538
1705
  return;
1539
1706
  }
1540
1707
  if (this.animationKind === "spring") {
@@ -1562,7 +1729,7 @@ var init_animation_runner = __esm({
1562
1729
  }
1563
1730
  if (!this.isAnimating) {
1564
1731
  this.getRootEl()?.removeAttribute("data-animating");
1565
- if (!this.isDragging()) this.element.style.willChange = "auto";
1732
+ if (!this.isDragging()) this.element.style.willChange = "";
1566
1733
  }
1567
1734
  }
1568
1735
  destroy() {
@@ -1576,7 +1743,7 @@ var init_animation_runner = __esm({
1576
1743
  });
1577
1744
 
1578
1745
  // src/core/lifecycle/focusTrap.ts
1579
- var FOCUSABLE, isVisible, focusables, trapStack, installFocusTrap;
1746
+ var FOCUSABLE, isVisible, collectFocusables, focusables, containsComposed, trapStack, installFocusTrap;
1580
1747
  var init_focusTrap = __esm({
1581
1748
  "src/core/lifecycle/focusTrap.ts"() {
1582
1749
  FOCUSABLE = [
@@ -1598,9 +1765,35 @@ var init_focusTrap = __esm({
1598
1765
  }
1599
1766
  return el.offsetParent !== null;
1600
1767
  };
1601
- focusables = (root) => Array.from(root.querySelectorAll(FOCUSABLE)).filter(
1768
+ collectFocusables = (root) => {
1769
+ const out = Array.from(root.querySelectorAll(FOCUSABLE));
1770
+ if (typeof HTMLSlotElement !== "undefined") {
1771
+ for (const slot of Array.from(root.querySelectorAll("slot"))) {
1772
+ for (const assigned of slot.assignedElements()) {
1773
+ if (assigned.matches(FOCUSABLE)) out.push(assigned);
1774
+ out.push(...collectFocusables(assigned));
1775
+ }
1776
+ }
1777
+ }
1778
+ return out;
1779
+ };
1780
+ focusables = (root) => collectFocusables(root).filter(
1602
1781
  (el) => !el.hasAttribute("inert") && isVisible(el) && !el.matches("[aria-hidden='true']")
1603
1782
  );
1783
+ containsComposed = (container, node) => {
1784
+ let n = node;
1785
+ while (n) {
1786
+ if (n === container) return true;
1787
+ const slot = n.assignedSlot;
1788
+ if (slot) {
1789
+ n = slot;
1790
+ continue;
1791
+ }
1792
+ const parent = n.parentNode;
1793
+ n = parent instanceof ShadowRoot ? parent.host : parent;
1794
+ }
1795
+ return false;
1796
+ };
1604
1797
  trapStack = [];
1605
1798
  installFocusTrap = (container, options = {}) => {
1606
1799
  if (typeof document === "undefined") return () => {
@@ -1610,7 +1803,13 @@ var init_focusTrap = __esm({
1610
1803
  const isActive = () => trapStack[trapStack.length - 1] === token;
1611
1804
  const previouslyFocused = document.activeElement;
1612
1805
  const focusInitial = () => {
1613
- const initial = typeof options.initialFocus === "string" ? container.querySelector(options.initialFocus) : options.initialFocus ?? focusables(container)[0] ?? container;
1806
+ const spec = options.initialFocus;
1807
+ if (spec === false) {
1808
+ if (!container.hasAttribute("tabindex")) container.tabIndex = -1;
1809
+ container.focus({ preventScroll: true });
1810
+ return;
1811
+ }
1812
+ const initial = typeof spec === "string" ? container.querySelector(spec) : spec ?? focusables(container)[0] ?? container;
1614
1813
  initial?.focus({ preventScroll: true });
1615
1814
  };
1616
1815
  focusInitial();
@@ -1640,7 +1839,7 @@ var init_focusTrap = __esm({
1640
1839
  const handleFocusIn = (e) => {
1641
1840
  if (!isActive()) return;
1642
1841
  const target = e.target;
1643
- if (target && !container.contains(target)) {
1842
+ if (target && !containsComposed(container, target)) {
1644
1843
  const list = focusables(container);
1645
1844
  list[0]?.focus({ preventScroll: true });
1646
1845
  }
@@ -1682,8 +1881,14 @@ var init_scrollLock = __esm({
1682
1881
  position: body.style.position,
1683
1882
  top: body.style.top,
1684
1883
  width: body.style.width,
1884
+ paddingRight: body.style.paddingRight,
1685
1885
  scrollY: window.scrollY
1686
1886
  };
1887
+ const scrollbarGap = window.innerWidth - document.documentElement.clientWidth;
1888
+ if (scrollbarGap > 0) {
1889
+ const current = parseFloat(getComputedStyle(body).paddingRight) || 0;
1890
+ body.style.paddingRight = `${current + scrollbarGap}px`;
1891
+ }
1687
1892
  body.style.overflow = "hidden";
1688
1893
  body.style.position = "fixed";
1689
1894
  body.style.top = `-${savedStyles.scrollY}px`;
@@ -1701,6 +1906,7 @@ var init_scrollLock = __esm({
1701
1906
  body.style.position = savedStyles.position;
1702
1907
  body.style.top = savedStyles.top;
1703
1908
  body.style.width = savedStyles.width;
1909
+ body.style.paddingRight = savedStyles.paddingRight;
1704
1910
  window.scrollTo(0, savedStyles.scrollY);
1705
1911
  savedStyles = null;
1706
1912
  }
@@ -1930,12 +2136,14 @@ var init_gestures = __esm({
1930
2136
  handle.addEventListener("pointermove", onPointerMove);
1931
2137
  handle.addEventListener("pointerup", finishGesture);
1932
2138
  handle.addEventListener("pointercancel", onPointerCancel);
2139
+ const prevTouchAction = handle.style.touchAction;
1933
2140
  handle.style.touchAction = isAxisVertical(mode) ? "pan-x" : "pan-y";
1934
2141
  return () => {
1935
2142
  handle.removeEventListener("pointerdown", onPointerDown);
1936
2143
  handle.removeEventListener("pointermove", onPointerMove);
1937
2144
  handle.removeEventListener("pointerup", finishGesture);
1938
2145
  handle.removeEventListener("pointercancel", onPointerCancel);
2146
+ handle.style.touchAction = prevTouchAction;
1939
2147
  if (activePointerId !== null && handle.hasPointerCapture(activePointerId)) {
1940
2148
  handle.releasePointerCapture(activePointerId);
1941
2149
  }
@@ -2078,7 +2286,7 @@ var init_gesture_controller = __esm({
2078
2286
  this.isDragging_ = false;
2079
2287
  this.keyboardDismissed = false;
2080
2288
  this.deps.getRoot()?.removeAttribute("data-dragging");
2081
- this.deps.element.style.willChange = "auto";
2289
+ this.deps.element.style.willChange = "";
2082
2290
  }
2083
2291
  };
2084
2292
  }
@@ -2513,8 +2721,34 @@ var init_engine_options = __esm({
2513
2721
  }
2514
2722
  });
2515
2723
 
2724
+ // src/core/primitives/teardown-stack.ts
2725
+ var TeardownStack;
2726
+ var init_teardown_stack = __esm({
2727
+ "src/core/primitives/teardown-stack.ts"() {
2728
+ TeardownStack = class {
2729
+ constructor() {
2730
+ __publicField(this, "fns", []);
2731
+ }
2732
+ add(fn) {
2733
+ if (fn) this.fns.push(fn);
2734
+ }
2735
+ drain() {
2736
+ while (this.fns.length) {
2737
+ try {
2738
+ this.fns.pop()();
2739
+ } catch (err) {
2740
+ queueMicrotask(() => {
2741
+ throw err;
2742
+ });
2743
+ }
2744
+ }
2745
+ }
2746
+ };
2747
+ }
2748
+ });
2749
+
2516
2750
  // src/core/BottomSheetEngine.ts
2517
- var HAPTIC_DURATION_MS, TeardownStack, BottomSheetEngine;
2751
+ var HAPTIC_DURATION_MS, BottomSheetEngine;
2518
2752
  var init_BottomSheetEngine = __esm({
2519
2753
  "src/core/BottomSheetEngine.ts"() {
2520
2754
  init_sheetStack();
@@ -2530,6 +2764,8 @@ var init_BottomSheetEngine = __esm({
2530
2764
  init_content_swipe();
2531
2765
  init_visual_viewport();
2532
2766
  init_resize_observer();
2767
+ init_fit_observer();
2768
+ init_fit_measurement();
2533
2769
  init_slider_keyboard();
2534
2770
  init_scrim_controller();
2535
2771
  init_animation_runner();
@@ -2541,27 +2777,9 @@ var init_BottomSheetEngine = __esm({
2541
2777
  init_sheet_anchors();
2542
2778
  init_scrim_stages();
2543
2779
  init_engine_options();
2780
+ init_teardown_stack();
2544
2781
  init_opacity_dedup();
2545
2782
  HAPTIC_DURATION_MS = 8;
2546
- TeardownStack = class {
2547
- constructor() {
2548
- __publicField(this, "fns", []);
2549
- }
2550
- add(fn) {
2551
- if (fn) this.fns.push(fn);
2552
- }
2553
- drain() {
2554
- while (this.fns.length) {
2555
- try {
2556
- this.fns.pop()();
2557
- } catch (err) {
2558
- queueMicrotask(() => {
2559
- throw err;
2560
- });
2561
- }
2562
- }
2563
- }
2564
- };
2565
2783
  BottomSheetEngine = class {
2566
2784
  constructor(opts) {
2567
2785
  __publicField(this, "id", nextInstanceId("bs"));
@@ -2586,6 +2804,7 @@ var init_BottomSheetEngine = __esm({
2586
2804
  __publicField(this, "disableDragFlag");
2587
2805
  __publicField(this, "closeOnRouteChange");
2588
2806
  __publicField(this, "maxHeightCap");
2807
+ __publicField(this, "maxHeightRaw");
2589
2808
  __publicField(this, "snapPointsRaw");
2590
2809
  __publicField(this, "snaps");
2591
2810
  __publicField(this, "activeId");
@@ -2621,6 +2840,7 @@ var init_BottomSheetEngine = __esm({
2621
2840
  __publicField(this, "detachScrimStages", null);
2622
2841
  __publicField(this, "stackEffectEnabled", false);
2623
2842
  __publicField(this, "stackEffectPrimed", false);
2843
+ __publicField(this, "fitDeps");
2624
2844
  __publicField(this, "transformTemplate");
2625
2845
  const resolved = resolveEngineOptions(opts, {
2626
2846
  shouldApplyInertSiblings: () => {
@@ -2693,12 +2913,19 @@ var init_BottomSheetEngine = __esm({
2693
2913
  }
2694
2914
  this.activeId = resolved.initialId;
2695
2915
  auditVhUsage(this.snapPointsRaw);
2916
+ this.auditDuplicateSnapIds(this.snapPointsRaw);
2696
2917
  this.rootEl = this.element.closest(".bs-root");
2918
+ this.fitDeps = {
2919
+ element: this.element,
2920
+ scrollContainer: this.scrollContainer,
2921
+ mode: this.mode,
2922
+ getMaxHeightCap: () => this.maxHeightCap
2923
+ };
2697
2924
  this.snaps = new SnapResolver(
2698
2925
  this.snapPointsRaw,
2699
2926
  resolved.initialAllowed,
2700
2927
  this.mode,
2701
- () => this.measureFitSize(),
2928
+ () => measureFitSize(this.fitDeps),
2702
2929
  (maxAxisSize) => {
2703
2930
  this.element.style[layoutAxis(this.mode)] = `${maxAxisSize}px`;
2704
2931
  }
@@ -2732,7 +2959,18 @@ var init_BottomSheetEngine = __esm({
2732
2959
  io.observe(this.element);
2733
2960
  this.teardowns.add(() => io.disconnect());
2734
2961
  }
2735
- this.installFitObserver();
2962
+ this.teardowns.add(
2963
+ installFitObserver({
2964
+ handle: this.handle,
2965
+ scrollContainer: this.scrollContainer,
2966
+ hasFitSnap: () => this.snapPointsRaw.some(
2967
+ (p) => p.size === "fit" || p.size === "content"
2968
+ ),
2969
+ isDestroyed: () => this.destroyed,
2970
+ isDragging: () => this.gesture?.isDragging ?? false,
2971
+ recompute: () => this.recompute()
2972
+ })
2973
+ );
2736
2974
  if (resolved.radius !== void 0) this.setRadius(resolved.radius);
2737
2975
  if (resolved.maxHeight !== void 0) this.setMaxHeight(resolved.maxHeight);
2738
2976
  this.applyAutoAriaLabelledBy();
@@ -2883,27 +3121,33 @@ var init_BottomSheetEngine = __esm({
2883
3121
  } else {
2884
3122
  await this.animation.animateTo(target.size, velocityPxPerMs);
2885
3123
  }
2886
- this.opening = false;
2887
3124
  externalSignal?.removeEventListener("abort", onExternalAbort);
2888
3125
  if (signal.aborted) return;
2889
- this.scrollCache.restore(id, previousSize, target.size);
3126
+ this.opening = false;
3127
+ this.completeSnap(id, target.size, previousSize, wasClosed, false);
3128
+ }
3129
+ completeSnap(id, targetSize, previousSize, wasClosed, withHaptic) {
3130
+ this.scrollCache.restore(id, previousSize, targetSize);
2890
3131
  this.updateAriaSlider();
2891
3132
  this.emit("snap", {
2892
3133
  id,
2893
3134
  size: this.size,
2894
3135
  progress: this.computeProgress(this.size)
2895
3136
  });
2896
- if (wasClosed && target.size > 0) {
2897
- this.emit("open", { id });
2898
- this.handleOpen();
2899
- notifyLinkedSheets(this.linkedSheets, this);
2900
- this.emit("opened", { id });
2901
- }
2902
- if (target.size === 0) {
2903
- this.emit("close", void 0);
2904
- this.handleClose();
2905
- this.emit("closed", void 0);
2906
- }
3137
+ if (withHaptic) this.haptic();
3138
+ if (wasClosed && targetSize > 0) this.emitOpenSequence(id);
3139
+ if (targetSize === 0) this.emitCloseSequence();
3140
+ }
3141
+ emitOpenSequence(id) {
3142
+ this.emit("open", { id });
3143
+ this.handleOpen();
3144
+ notifyLinkedSheets(this.linkedSheets, this);
3145
+ this.emit("opened", { id });
3146
+ }
3147
+ emitCloseSequence() {
3148
+ this.emit("close", void 0);
3149
+ this.handleClose();
3150
+ this.emit("closed", void 0);
2907
3151
  }
2908
3152
  async dragTo(targetSize, velocityOrOpts = 0) {
2909
3153
  if (this.destroyed) return;
@@ -2924,19 +3168,31 @@ var init_BottomSheetEngine = __esm({
2924
3168
  }
2925
3169
  open(id) {
2926
3170
  if (this.destroyed) return Promise.resolve();
2927
- const target = id ?? this.snaps.getAllowedIds().find((a) => a !== "closed") ?? this.activeId;
3171
+ const target = id ?? this.snaps.getAllowedIds().find((a) => (this.snaps.findById(a)?.size ?? 0) > 0) ?? this.activeId;
2928
3172
  return this.snapTo(target);
2929
3173
  }
2930
3174
  close(reason = "programmatic") {
2931
3175
  if (this.destroyed) return Promise.resolve();
2932
3176
  if (this.disableCloseFlag) return Promise.resolve();
2933
3177
  if (this.size > 0 && this.emitBeforeClose(reason)) return Promise.resolve();
2934
- const closedId = this.snapPointsRaw.find((p) => p.id === "closed")?.id ?? this.snaps.getAllowedIds()[0];
3178
+ const closedId = this.snapPointsRaw.find((p) => p.id === "closed")?.id ?? this.snaps.getAllowedIds().find((a) => this.snaps.findById(a)?.size === 0) ?? this.snaps.getAllowedIds()[0];
2935
3179
  return this.snapTo(closedId ?? this.activeId);
2936
3180
  }
2937
3181
  canDismiss() {
2938
3182
  return !this.persistent && !this.disableCloseFlag && !this.destroyed;
2939
3183
  }
3184
+ setPersistent(value) {
3185
+ if (this.destroyed) return;
3186
+ this.persistent = value;
3187
+ }
3188
+ setDisableClose(value) {
3189
+ if (this.destroyed) return;
3190
+ this.disableCloseFlag = value;
3191
+ }
3192
+ setDisableDrag(value) {
3193
+ if (this.destroyed) return;
3194
+ this.disableDragFlag = value;
3195
+ }
2940
3196
  isTop() {
2941
3197
  return this.isTopSheet;
2942
3198
  }
@@ -2973,18 +3229,23 @@ var init_BottomSheetEngine = __esm({
2973
3229
  axis === "height" ? "max-height" : "max-width",
2974
3230
  value
2975
3231
  );
2976
- if (typeof h === "number") {
2977
- this.maxHeightCap = h;
2978
- this.recompute();
2979
- } else if (typeof window !== "undefined") {
2980
- const probe2 = document.createElement("div");
2981
- probe2.style.cssText = `position:absolute;visibility:hidden;pointer-events:none;${axis === "height" ? "height" : "width"}:${value};`;
2982
- document.body.appendChild(probe2);
2983
- const measured = axis === "height" ? probe2.offsetHeight : probe2.offsetWidth;
2984
- probe2.remove();
2985
- this.maxHeightCap = measured > 0 ? measured : void 0;
2986
- this.recompute();
3232
+ this.maxHeightRaw = h;
3233
+ this.resolveMaxHeightCap();
3234
+ this.recompute();
3235
+ }
3236
+ resolveMaxHeightCap() {
3237
+ const raw = this.maxHeightRaw;
3238
+ if (raw === void 0) {
3239
+ this.maxHeightCap = void 0;
3240
+ return;
3241
+ }
3242
+ if (typeof raw === "number") {
3243
+ this.maxHeightCap = raw;
3244
+ return;
2987
3245
  }
3246
+ if (typeof window === "undefined") return;
3247
+ const measured = resolveSnap(raw, this.mode);
3248
+ this.maxHeightCap = measured > 0 ? measured : void 0;
2988
3249
  }
2989
3250
  clampToMaxHeight() {
2990
3251
  if (this.maxHeightCap === void 0) return;
@@ -3115,6 +3376,7 @@ var init_BottomSheetEngine = __esm({
3115
3376
  this.newCycle();
3116
3377
  this.snapPointsRaw = points;
3117
3378
  auditVhUsage(points);
3379
+ this.auditDuplicateSnapIds(points);
3118
3380
  this.snaps.setRaw(points);
3119
3381
  if (allowed) this.snaps.setAllowedIds(allowed);
3120
3382
  this.scrollCache.clear();
@@ -3127,6 +3389,7 @@ var init_BottomSheetEngine = __esm({
3127
3389
  }
3128
3390
  recompute() {
3129
3391
  if (this.destroyed) return;
3392
+ this.resolveMaxHeightCap();
3130
3393
  this.snaps.recompute();
3131
3394
  this.clampToMaxHeight();
3132
3395
  this.scrim.invalidateOpacityCache();
@@ -3138,129 +3401,6 @@ var init_BottomSheetEngine = __esm({
3138
3401
  this.updateAriaSlider();
3139
3402
  }
3140
3403
  }
3141
- contentTargets() {
3142
- const scroller = this.scrollContainer;
3143
- if (!scroller) return [];
3144
- const hasSlot = typeof HTMLSlotElement !== "undefined";
3145
- const out = [];
3146
- for (const child of Array.from(scroller.children)) {
3147
- if (hasSlot && child instanceof HTMLSlotElement) {
3148
- for (const el of child.assignedElements()) out.push(el);
3149
- } else {
3150
- out.push(child);
3151
- }
3152
- }
3153
- return out;
3154
- }
3155
- measureSheetNatural(vertical) {
3156
- const el = this.element;
3157
- const content = this.scrollContainer;
3158
- if (!content) {
3159
- return vertical ? el.offsetHeight : el.offsetWidth;
3160
- }
3161
- const axis = vertical ? "height" : "width";
3162
- const sheetStyle = el.style;
3163
- const cs = content.style;
3164
- const prevSheet = sheetStyle[axis];
3165
- const prevFlex = cs.flex;
3166
- const prevContent = vertical ? cs.height : cs.width;
3167
- sheetStyle[axis] = "auto";
3168
- cs.flex = "none";
3169
- if (vertical) cs.height = "auto";
3170
- else cs.width = "auto";
3171
- const poked = vertical ? el.offsetHeight : el.offsetWidth;
3172
- const contentBox = vertical ? content.clientHeight : content.clientWidth;
3173
- const contentScroll = vertical ? content.scrollHeight : content.scrollWidth;
3174
- const natural = Math.max(poked, poked - contentBox + contentScroll);
3175
- sheetStyle[axis] = prevSheet;
3176
- cs.flex = prevFlex;
3177
- if (vertical) cs.height = prevContent;
3178
- else cs.width = prevContent;
3179
- return natural;
3180
- }
3181
- measureFitSize() {
3182
- const vertical = layoutAxis(this.mode) === "height";
3183
- const natural = this.measureSheetNatural(vertical);
3184
- const capped = this.maxHeightCap !== void 0 ? Math.min(natural, this.maxHeightCap) : natural;
3185
- if (typeof window === "undefined") return capped;
3186
- const viewport = this.containingExtent(vertical);
3187
- return viewport > 0 ? Math.min(capped, viewport) : capped;
3188
- }
3189
- // The fit cap is the sheet's containing block, not always the window: a
3190
- // position:fixed sheet nested under a transform/filter/will-change ancestor
3191
- // is clipped to that ancestor, so capping by window.innerHeight oversizes the
3192
- // sheet and the content/footer get clipped. offsetParent resolves to that
3193
- // ancestor when present, and is null (→ viewport) for a plain fixed sheet.
3194
- containingExtent(vertical) {
3195
- const op = this.element.offsetParent;
3196
- if (op && op !== document.body && op !== document.documentElement) {
3197
- return vertical ? op.clientHeight : op.clientWidth;
3198
- }
3199
- return vertical ? window.innerHeight : window.innerWidth;
3200
- }
3201
- installFitObserver() {
3202
- if (typeof ResizeObserver === "undefined") return;
3203
- if (!this.snapPointsRaw.some((p) => p.size === "fit" || p.size === "content"))
3204
- return;
3205
- let raf = 0;
3206
- const schedule = () => {
3207
- if (raf) return;
3208
- raf = requestAnimationFrame(() => {
3209
- raf = 0;
3210
- if (this.destroyed || this.gesture?.isDragging) return;
3211
- this.recompute();
3212
- });
3213
- };
3214
- const ro = new ResizeObserver(schedule);
3215
- ro.observe(this.handle);
3216
- const scroller = this.scrollContainer;
3217
- const observed = /* @__PURE__ */ new Set();
3218
- const slots = /* @__PURE__ */ new Set();
3219
- const hasSlot = typeof HTMLSlotElement !== "undefined";
3220
- const onSlotChange = () => resync();
3221
- const trackSlots = () => {
3222
- if (!scroller || !hasSlot) return;
3223
- for (const child of Array.from(scroller.children)) {
3224
- if (child instanceof HTMLSlotElement && !slots.has(child)) {
3225
- child.addEventListener("slotchange", onSlotChange);
3226
- slots.add(child);
3227
- }
3228
- }
3229
- };
3230
- const resync = () => {
3231
- trackSlots();
3232
- const targets = this.contentTargets();
3233
- for (const el of observed) {
3234
- if (!targets.includes(el)) {
3235
- ro.unobserve(el);
3236
- observed.delete(el);
3237
- }
3238
- }
3239
- for (const el of targets) {
3240
- if (!observed.has(el)) {
3241
- ro.observe(el);
3242
- observed.add(el);
3243
- }
3244
- }
3245
- schedule();
3246
- };
3247
- let mo;
3248
- if (scroller) {
3249
- resync();
3250
- if (typeof MutationObserver !== "undefined") {
3251
- mo = new MutationObserver(resync);
3252
- mo.observe(scroller, { childList: true });
3253
- }
3254
- }
3255
- this.teardowns.add(() => {
3256
- ro.disconnect();
3257
- mo?.disconnect();
3258
- for (const slot of slots) {
3259
- slot.removeEventListener("slotchange", onSlotChange);
3260
- }
3261
- if (raf) cancelAnimationFrame(raf);
3262
- });
3263
- }
3264
3404
  destroy() {
3265
3405
  this.destroyed = true;
3266
3406
  this.opening = false;
@@ -3278,9 +3418,10 @@ var init_BottomSheetEngine = __esm({
3278
3418
  this.backdrop.style.opacity = "";
3279
3419
  this.backdrop.style.pointerEvents = "";
3280
3420
  }
3281
- if (this.anchorHost) {
3282
- this.anchorHost.style.removeProperty("--bs-size");
3283
- this.anchorHost.style.removeProperty("--bs-progress");
3421
+ for (const host of [this.anchorHost, this.scrimParent, this.rootEl]) {
3422
+ if (!host) continue;
3423
+ host.style.removeProperty("--bs-size");
3424
+ host.style.removeProperty("--bs-progress");
3284
3425
  }
3285
3426
  if (this.screenComponent) {
3286
3427
  this.screenComponent.style.opacity = "";
@@ -3294,6 +3435,7 @@ var init_BottomSheetEngine = __esm({
3294
3435
  }
3295
3436
  newCycle() {
3296
3437
  this.allowOvershoot = false;
3438
+ this.opening = false;
3297
3439
  this.currentAbort.abort();
3298
3440
  this.currentAbort = new AbortController();
3299
3441
  return this.currentAbort.signal;
@@ -3349,8 +3491,14 @@ var init_BottomSheetEngine = __esm({
3349
3491
  this.element.setAttribute("aria-labelledby", titled.id);
3350
3492
  }
3351
3493
  recomputeSnaps() {
3494
+ this.resolveMaxHeightCap();
3495
+ this.snaps.setRaw(this.snapPointsRaw);
3496
+ this.clampToMaxHeight();
3497
+ this.scrim.invalidateOpacityCache();
3498
+ }
3499
+ auditDuplicateSnapIds(points) {
3352
3500
  const seen = /* @__PURE__ */ new Set();
3353
- for (const p of this.snapPointsRaw) {
3501
+ for (const p of points) {
3354
3502
  if (seen.has(p.id)) {
3355
3503
  console.warn(
3356
3504
  `[BottomSheet] duplicate snap id "${p.id}" \u2014 only the first occurrence is reachable.`
@@ -3358,9 +3506,6 @@ var init_BottomSheetEngine = __esm({
3358
3506
  }
3359
3507
  seen.add(p.id);
3360
3508
  }
3361
- this.snaps.setRaw(this.snapPointsRaw);
3362
- this.clampToMaxHeight();
3363
- this.scrim.invalidateOpacityCache();
3364
3509
  }
3365
3510
  getAllowedRange() {
3366
3511
  return this.snaps.getAllowedRange();
@@ -3548,9 +3693,15 @@ var init_BottomSheetEngine = __esm({
3548
3693
  flickVelocity: this.flickVelocity,
3549
3694
  dragThreshold: this.dragThreshold
3550
3695
  });
3551
- if (!target) return;
3696
+ if (!target) {
3697
+ this.clearWillChangeIfIdle();
3698
+ return;
3699
+ }
3552
3700
  const previousId = this.activeId;
3553
- if (target.id === previousId && target.size === this.size) return;
3701
+ if (target.id === previousId && target.size === this.size) {
3702
+ this.clearWillChangeIfIdle();
3703
+ return;
3704
+ }
3554
3705
  if (this.emitBeforeSnap(target, previousId)) {
3555
3706
  const restore = this.snaps.findById(previousId);
3556
3707
  if (restore) {
@@ -3573,27 +3724,19 @@ var init_BottomSheetEngine = __esm({
3573
3724
  if (signal.aborted) return;
3574
3725
  this.allowOvershoot = false;
3575
3726
  this.opening = false;
3576
- this.scrollCache.restore(target.id, previousSize, target.size);
3577
- this.updateAriaSlider();
3578
- this.emit("snap", {
3579
- id: target.id,
3580
- size: this.size,
3581
- progress: this.computeProgress(this.size)
3582
- });
3583
- this.haptic();
3584
- if (previousSnapSize === 0 && target.size > 0) {
3585
- this.emit("open", { id: target.id });
3586
- this.handleOpen();
3587
- notifyLinkedSheets(this.linkedSheets, this);
3588
- this.emit("opened", { id: target.id });
3589
- }
3590
- if (target.size === 0) {
3591
- this.emit("close", void 0);
3592
- this.handleClose();
3593
- this.emit("closed", void 0);
3594
- }
3727
+ this.completeSnap(
3728
+ target.id,
3729
+ target.size,
3730
+ previousSize,
3731
+ previousSnapSize === 0,
3732
+ true
3733
+ );
3595
3734
  });
3596
3735
  }
3736
+ clearWillChangeIfIdle() {
3737
+ if (this.animation.isAnimating || this.gesture?.isDragging) return;
3738
+ this.element.style.willChange = "";
3739
+ }
3597
3740
  haptic() {
3598
3741
  if (typeof navigator !== "undefined" && "vibrate" in navigator) {
3599
3742
  try {
@@ -3674,14 +3817,9 @@ var init_BottomSheetEngine = __esm({
3674
3817
  progress: this.computeProgress(this.size)
3675
3818
  });
3676
3819
  if (target.size > 0 && !this.lifecycle.isInstalled) {
3677
- this.emit("open", { id: target.id });
3678
- this.handleOpen();
3679
- notifyLinkedSheets(this.linkedSheets, this);
3680
- this.emit("opened", { id: target.id });
3820
+ this.emitOpenSequence(target.id);
3681
3821
  } else if (target.size === 0 && this.lifecycle.isInstalled) {
3682
- this.emit("close", void 0);
3683
- this.handleClose();
3684
- this.emit("closed", void 0);
3822
+ this.emitCloseSequence();
3685
3823
  }
3686
3824
  }
3687
3825
  handleClose() {
@@ -3701,17 +3839,102 @@ var init_baseStyles = __esm({
3701
3839
  }
3702
3840
  });
3703
3841
 
3704
- // src/web-component/BottomSheetElement.ts
3705
- var BottomSheetElement_exports = {};
3706
- __export(BottomSheetElement_exports, {
3707
- BottomSheetElement: () => exports.BottomSheetElement,
3708
- defineBottomSheet: () => exports.defineBottomSheet
3842
+ // src/web-component/shadow-tree.ts
3843
+ var buildSlot, buildSlotRegion, buildShadowTree;
3844
+ var init_shadow_tree = __esm({
3845
+ "src/web-component/shadow-tree.ts"() {
3846
+ buildSlot = (name) => {
3847
+ const slot = document.createElement("slot");
3848
+ slot.name = name;
3849
+ return slot;
3850
+ };
3851
+ buildSlotRegion = (name, className) => {
3852
+ const region = document.createElement("div");
3853
+ region.className = className;
3854
+ region.setAttribute("part", name);
3855
+ const slot = buildSlot(name);
3856
+ region.appendChild(slot);
3857
+ const sync = () => {
3858
+ region.hidden = slot.assignedNodes({ flatten: true }).length === 0;
3859
+ };
3860
+ slot.addEventListener("slotchange", sync);
3861
+ sync();
3862
+ return region;
3863
+ };
3864
+ buildShadowTree = () => {
3865
+ const root = document.createElement("div");
3866
+ root.className = "bs-root";
3867
+ const backdrop = document.createElement("div");
3868
+ backdrop.className = "bs-backdrop";
3869
+ backdrop.setAttribute("part", "backdrop");
3870
+ const screen = document.createElement("div");
3871
+ screen.className = "bs-screen";
3872
+ screen.setAttribute("part", "screen");
3873
+ screen.appendChild(buildSlot("screen"));
3874
+ const sheet = document.createElement("section");
3875
+ sheet.className = "bs-sheet";
3876
+ sheet.setAttribute("part", "sheet");
3877
+ sheet.setAttribute("role", "dialog");
3878
+ sheet.setAttribute("aria-modal", "false");
3879
+ const leftButton = document.createElement("div");
3880
+ leftButton.className = "bs-button-slot";
3881
+ leftButton.setAttribute("data-side", "left");
3882
+ leftButton.setAttribute("part", "left-button");
3883
+ leftButton.appendChild(buildSlot("leftButton"));
3884
+ const rightButton = document.createElement("div");
3885
+ rightButton.className = "bs-button-slot";
3886
+ rightButton.setAttribute("data-side", "right");
3887
+ rightButton.setAttribute("part", "right-button");
3888
+ rightButton.appendChild(buildSlot("rightButton"));
3889
+ const handle = document.createElement("div");
3890
+ handle.className = "bs-handle";
3891
+ handle.setAttribute("part", "handle");
3892
+ handle.setAttribute("role", "slider");
3893
+ handle.setAttribute("tabindex", "0");
3894
+ handle.setAttribute("aria-label", "Resize sheet");
3895
+ const header = buildSlotRegion("header", "bs-header");
3896
+ const content = document.createElement("div");
3897
+ content.className = "bs-content";
3898
+ content.setAttribute("part", "content");
3899
+ content.setAttribute("tabindex", "0");
3900
+ content.setAttribute("role", "region");
3901
+ content.setAttribute("aria-label", "Sheet content");
3902
+ const defaultSlot = document.createElement("slot");
3903
+ content.appendChild(defaultSlot);
3904
+ const footer = buildSlotRegion("footer", "bs-footer");
3905
+ const announcer = document.createElement("span");
3906
+ announcer.className = "bs-sr-announce";
3907
+ announcer.setAttribute("role", "status");
3908
+ announcer.setAttribute("aria-live", "polite");
3909
+ announcer.setAttribute("aria-atomic", "true");
3910
+ announcer.style.cssText = "position:absolute;left:-10000px;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);";
3911
+ sheet.append(handle, header, content, footer);
3912
+ root.append(backdrop, screen, sheet, leftButton, rightButton, announcer);
3913
+ const fragment = document.createDocumentFragment();
3914
+ fragment.appendChild(root);
3915
+ return {
3916
+ fragment,
3917
+ refs: {
3918
+ sheet,
3919
+ handle,
3920
+ header,
3921
+ content,
3922
+ footer,
3923
+ backdrop,
3924
+ screen,
3925
+ leftButton,
3926
+ rightButton,
3927
+ announcer
3928
+ }
3929
+ };
3930
+ };
3931
+ }
3709
3932
  });
3710
- var ATTR_SNAP_POINTS, ATTR_ALLOWED, ATTR_INITIAL, ATTR_MODE, ATTR_BACKDROP, ATTR_STYLESHEET, ATTR_ANIMATION, ATTR_FOCUS_TRAP, ATTR_CLOSE_ON_ESCAPE, ATTR_LOCK_BODY_SCROLL, ATTR_SHEET_LABEL, ATTR_STACK_EFFECT, ATTR_PERSISTENT, ATTR_DISABLE_CLOSE, ATTR_DISABLE_DRAG, ATTR_CLOSE_ON_ROUTE_CHANGE, ATTR_RADIUS, ATTR_MAX_HEIGHT, ATTR_RETURN_FOCUS_TO, ATTR_BACKDROP_COLOR, ATTR_SCRIM_COLOR, ATTR_SNAP, LIVE_ATTRS, isValidSnapPoint, DEFAULT_SNAP_POINTS, parseSnapPoints, VALID_MODES, VALID_ANIMATIONS, parseMode, parseAnimation, parseList, parseDimension, ANCHOR_POSITIONS, ANCHOR_ANIMATIONS, parseAnchorOptions, buildSlot, buildSlotRegion, buildShadowTree, BaseHTMLElement; exports.BottomSheetElement = void 0; exports.defineBottomSheet = void 0;
3711
- var init_BottomSheetElement = __esm({
3712
- "src/web-component/BottomSheetElement.ts"() {
3713
- init_BottomSheetEngine();
3714
- init_baseStyles();
3933
+
3934
+ // src/web-component/attributes.ts
3935
+ var ATTR_SNAP_POINTS, ATTR_ALLOWED, ATTR_INITIAL, ATTR_MODE, ATTR_BACKDROP, ATTR_STYLESHEET, ATTR_ANIMATION, ATTR_FOCUS_TRAP, ATTR_CLOSE_ON_ESCAPE, ATTR_LOCK_BODY_SCROLL, ATTR_SHEET_LABEL, ATTR_STACK_EFFECT, ATTR_PERSISTENT, ATTR_DISABLE_CLOSE, ATTR_DISABLE_DRAG, ATTR_CLOSE_ON_ROUTE_CHANGE, ATTR_RADIUS, ATTR_MAX_HEIGHT, ATTR_RETURN_FOCUS_TO, ATTR_BACKDROP_COLOR, ATTR_SCRIM_COLOR, ATTR_SNAP, LIVE_ATTRS, OBSERVED_ATTRS, isValidSnapPoint, DEFAULT_SNAP_POINTS, parseSnapPoints, VALID_MODES, VALID_ANIMATIONS, parseMode, parseAnimation, parseList, parseDimension, ANCHOR_POSITIONS, ANCHOR_ANIMATIONS, parseAnchorOptions;
3936
+ var init_attributes = __esm({
3937
+ "src/web-component/attributes.ts"() {
3715
3938
  ATTR_SNAP_POINTS = "snap-points";
3716
3939
  ATTR_ALLOWED = "allowed";
3717
3940
  ATTR_INITIAL = "initial";
@@ -3739,8 +3962,34 @@ var init_BottomSheetElement = __esm({
3739
3962
  ATTR_MAX_HEIGHT,
3740
3963
  ATTR_BACKDROP_COLOR,
3741
3964
  ATTR_SCRIM_COLOR,
3742
- ATTR_SNAP
3965
+ ATTR_SNAP,
3966
+ ATTR_PERSISTENT,
3967
+ ATTR_DISABLE_CLOSE,
3968
+ ATTR_DISABLE_DRAG
3743
3969
  ]);
3970
+ OBSERVED_ATTRS = [
3971
+ ATTR_SNAP_POINTS,
3972
+ ATTR_ALLOWED,
3973
+ ATTR_INITIAL,
3974
+ ATTR_MODE,
3975
+ ATTR_BACKDROP,
3976
+ ATTR_ANIMATION,
3977
+ ATTR_FOCUS_TRAP,
3978
+ ATTR_CLOSE_ON_ESCAPE,
3979
+ ATTR_LOCK_BODY_SCROLL,
3980
+ ATTR_SHEET_LABEL,
3981
+ ATTR_STACK_EFFECT,
3982
+ ATTR_PERSISTENT,
3983
+ ATTR_DISABLE_CLOSE,
3984
+ ATTR_DISABLE_DRAG,
3985
+ ATTR_CLOSE_ON_ROUTE_CHANGE,
3986
+ ATTR_RADIUS,
3987
+ ATTR_MAX_HEIGHT,
3988
+ ATTR_RETURN_FOCUS_TO,
3989
+ ATTR_BACKDROP_COLOR,
3990
+ ATTR_SCRIM_COLOR,
3991
+ ATTR_SNAP
3992
+ ];
3744
3993
  isValidSnapPoint = (p) => !!p && typeof p === "object" && typeof p.id === "string" && (typeof p.size === "number" || typeof p.size === "string");
3745
3994
  DEFAULT_SNAP_POINTS = [{ id: "full", size: "full" }];
3746
3995
  parseSnapPoints = (raw) => {
@@ -3813,91 +4062,22 @@ var init_BottomSheetElement = __esm({
3813
4062
  animation: animation && ANCHOR_ANIMATIONS.includes(animation) ? animation : void 0
3814
4063
  };
3815
4064
  };
3816
- buildSlot = (name) => {
3817
- const slot = document.createElement("slot");
3818
- slot.name = name;
3819
- return slot;
3820
- };
3821
- buildSlotRegion = (name, className) => {
3822
- const region = document.createElement("div");
3823
- region.className = className;
3824
- region.setAttribute("part", name);
3825
- const slot = buildSlot(name);
3826
- region.appendChild(slot);
3827
- const sync = () => {
3828
- region.hidden = slot.assignedNodes({ flatten: true }).length === 0;
3829
- };
3830
- slot.addEventListener("slotchange", sync);
3831
- sync();
3832
- return region;
3833
- };
3834
- buildShadowTree = () => {
3835
- const root = document.createElement("div");
3836
- root.className = "bs-root";
3837
- const backdrop = document.createElement("div");
3838
- backdrop.className = "bs-backdrop";
3839
- backdrop.setAttribute("part", "backdrop");
3840
- const screen = document.createElement("div");
3841
- screen.className = "bs-screen";
3842
- screen.setAttribute("part", "screen");
3843
- screen.appendChild(buildSlot("screen"));
3844
- const sheet = document.createElement("section");
3845
- sheet.className = "bs-sheet";
3846
- sheet.setAttribute("part", "sheet");
3847
- sheet.setAttribute("role", "dialog");
3848
- sheet.setAttribute("aria-modal", "false");
3849
- const leftButton = document.createElement("div");
3850
- leftButton.className = "bs-button-slot";
3851
- leftButton.setAttribute("data-side", "left");
3852
- leftButton.setAttribute("part", "left-button");
3853
- leftButton.appendChild(buildSlot("leftButton"));
3854
- const rightButton = document.createElement("div");
3855
- rightButton.className = "bs-button-slot";
3856
- rightButton.setAttribute("data-side", "right");
3857
- rightButton.setAttribute("part", "right-button");
3858
- rightButton.appendChild(buildSlot("rightButton"));
3859
- const handle = document.createElement("div");
3860
- handle.className = "bs-handle";
3861
- handle.setAttribute("part", "handle");
3862
- handle.setAttribute("role", "slider");
3863
- handle.setAttribute("tabindex", "0");
3864
- handle.setAttribute("aria-label", "Resize sheet");
3865
- const header = buildSlotRegion("header", "bs-header");
3866
- const content = document.createElement("div");
3867
- content.className = "bs-content";
3868
- content.setAttribute("part", "content");
3869
- content.setAttribute("tabindex", "0");
3870
- content.setAttribute("role", "region");
3871
- content.setAttribute("aria-label", "Sheet content");
3872
- const defaultSlot = document.createElement("slot");
3873
- content.appendChild(defaultSlot);
3874
- const footer = buildSlotRegion("footer", "bs-footer");
3875
- const announcer = document.createElement("span");
3876
- announcer.className = "bs-sr-announce";
3877
- announcer.setAttribute("role", "status");
3878
- announcer.setAttribute("aria-live", "polite");
3879
- announcer.setAttribute("aria-atomic", "true");
3880
- announcer.style.cssText = "position:absolute;left:-10000px;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);";
3881
- sheet.append(handle, header, content, footer);
3882
- root.append(backdrop, screen, sheet, leftButton, rightButton, announcer);
3883
- const fragment = document.createDocumentFragment();
3884
- fragment.appendChild(root);
3885
- return {
3886
- fragment,
3887
- refs: {
3888
- sheet,
3889
- handle,
3890
- header,
3891
- content,
3892
- footer,
3893
- backdrop,
3894
- screen,
3895
- leftButton,
3896
- rightButton,
3897
- announcer
3898
- }
3899
- };
3900
- };
4065
+ }
4066
+ });
4067
+
4068
+ // src/web-component/BottomSheetElement.ts
4069
+ var BottomSheetElement_exports = {};
4070
+ __export(BottomSheetElement_exports, {
4071
+ BottomSheetElement: () => exports.BottomSheetElement,
4072
+ defineBottomSheet: () => exports.defineBottomSheet
4073
+ });
4074
+ var BaseHTMLElement; exports.BottomSheetElement = void 0; exports.defineBottomSheet = void 0;
4075
+ var init_BottomSheetElement = __esm({
4076
+ "src/web-component/BottomSheetElement.ts"() {
4077
+ init_BottomSheetEngine();
4078
+ init_baseStyles();
4079
+ init_shadow_tree();
4080
+ init_attributes();
3901
4081
  BaseHTMLElement = typeof HTMLElement !== "undefined" ? HTMLElement : class {
3902
4082
  };
3903
4083
  exports.BottomSheetElement = class extends BaseHTMLElement {
@@ -3910,6 +4090,7 @@ var init_BottomSheetElement = __esm({
3910
4090
  __publicField(this, "backdropClickHandler", null);
3911
4091
  __publicField(this, "stylesheetLinked", false);
3912
4092
  __publicField(this, "declarativeAnchors", []);
4093
+ __publicField(this, "lastActiveId", null);
3913
4094
  this.root = this.attachShadow({ mode: "open" });
3914
4095
  const baseStyle = document.createElement("style");
3915
4096
  baseStyle.textContent = baseStyles;
@@ -3932,29 +4113,7 @@ var init_BottomSheetElement = __esm({
3932
4113
  this.root.appendChild(fragment);
3933
4114
  }
3934
4115
  static get observedAttributes() {
3935
- return [
3936
- ATTR_SNAP_POINTS,
3937
- ATTR_ALLOWED,
3938
- ATTR_INITIAL,
3939
- ATTR_MODE,
3940
- ATTR_BACKDROP,
3941
- ATTR_ANIMATION,
3942
- ATTR_FOCUS_TRAP,
3943
- ATTR_CLOSE_ON_ESCAPE,
3944
- ATTR_LOCK_BODY_SCROLL,
3945
- ATTR_SHEET_LABEL,
3946
- ATTR_STACK_EFFECT,
3947
- ATTR_PERSISTENT,
3948
- ATTR_DISABLE_CLOSE,
3949
- ATTR_DISABLE_DRAG,
3950
- ATTR_CLOSE_ON_ROUTE_CHANGE,
3951
- ATTR_RADIUS,
3952
- ATTR_MAX_HEIGHT,
3953
- ATTR_RETURN_FOCUS_TO,
3954
- ATTR_BACKDROP_COLOR,
3955
- ATTR_SCRIM_COLOR,
3956
- ATTR_SNAP
3957
- ];
4116
+ return OBSERVED_ATTRS.slice();
3958
4117
  }
3959
4118
  connectedCallback() {
3960
4119
  if (!this.stylesheetLinked) {
@@ -3998,6 +4157,15 @@ var init_BottomSheetElement = __esm({
3998
4157
  case ATTR_SCRIM_COLOR:
3999
4158
  this.engine.setScrimColor(value);
4000
4159
  break;
4160
+ case ATTR_PERSISTENT:
4161
+ this.engine.setPersistent(value === "true");
4162
+ break;
4163
+ case ATTR_DISABLE_CLOSE:
4164
+ this.engine.setDisableClose(value === "true");
4165
+ break;
4166
+ case ATTR_DISABLE_DRAG:
4167
+ this.engine.setDisableDrag(value === "true");
4168
+ break;
4001
4169
  case ATTR_SNAP:
4002
4170
  if (value && value !== this.engine.state.activeId) {
4003
4171
  void this.engine.snapTo(value);
@@ -4009,6 +4177,7 @@ var init_BottomSheetElement = __esm({
4009
4177
  return this.engine;
4010
4178
  }
4011
4179
  teardownEngine() {
4180
+ this.lastActiveId = this.engine?.state.activeId ?? this.lastActiveId;
4012
4181
  this.offHandlers.forEach((off) => off());
4013
4182
  this.offHandlers = [];
4014
4183
  if (this.backdropClickHandler) {
@@ -4046,6 +4215,9 @@ var init_BottomSheetElement = __esm({
4046
4215
  };
4047
4216
  backdrop.addEventListener("click", this.backdropClickHandler);
4048
4217
  }
4218
+ const snapPoints = parseSnapPoints(this.getAttribute(ATTR_SNAP_POINTS));
4219
+ const initialAttr = this.getAttribute(ATTR_INITIAL) ?? void 0;
4220
+ const initial = this.lastActiveId && snapPoints.some((p) => p.id === this.lastActiveId) ? this.lastActiveId : initialAttr;
4049
4221
  const opts = {
4050
4222
  element: sheet,
4051
4223
  handle,
@@ -4053,9 +4225,9 @@ var init_BottomSheetElement = __esm({
4053
4225
  backdrop: showBackdrop ? backdrop : void 0,
4054
4226
  scrim: this.refs.screen,
4055
4227
  mode,
4056
- snapPoints: parseSnapPoints(this.getAttribute(ATTR_SNAP_POINTS)),
4228
+ snapPoints,
4057
4229
  allowed: parseList(this.getAttribute(ATTR_ALLOWED)),
4058
- initial: this.getAttribute(ATTR_INITIAL) ?? void 0,
4230
+ initial,
4059
4231
  animation: parseAnimation(this.getAttribute(ATTR_ANIMATION)),
4060
4232
  focusTrap,
4061
4233
  closeOnEscape: this.getAttribute(ATTR_CLOSE_ON_ESCAPE) !== "false",
@@ -4085,6 +4257,15 @@ var init_BottomSheetElement = __esm({
4085
4257
  announcer.textContent = payload.id;
4086
4258
  this.dispatchEvent(new CustomEvent("snap", { detail: payload }));
4087
4259
  }),
4260
+ this.engine.on("before-snap", (payload) => {
4261
+ const accepted = this.dispatchEvent(
4262
+ new CustomEvent("before-snap", {
4263
+ detail: payload,
4264
+ cancelable: true
4265
+ })
4266
+ );
4267
+ if (!accepted) payload.cancel();
4268
+ }),
4088
4269
  this.engine.on(
4089
4270
  "open",
4090
4271
  (payload) => this.dispatchEvent(new CustomEvent("open", { detail: payload }))