@surdeddd/bottom-sheet 0.10.0 → 0.12.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 (42) hide show
  1. package/README.md +52 -35
  2. package/dist/{BottomSheetEngine-DzsIwD28.d.cts → BottomSheetEngine-D6oiEfZ4.d.cts} +1 -0
  3. package/dist/{BottomSheetEngine-DRjCU73e.d.ts → BottomSheetEngine-Dhy0X_e9.d.ts} +1 -0
  4. package/dist/element.cjs +250 -136
  5. package/dist/element.d.cts +1 -1
  6. package/dist/element.d.ts +1 -1
  7. package/dist/element.iife.global.js +2 -2
  8. package/dist/element.js +250 -136
  9. package/dist/index.cjs +257 -164
  10. package/dist/index.d.cts +2 -2
  11. package/dist/index.d.ts +2 -2
  12. package/dist/index.js +257 -164
  13. package/dist/integrations/formik.d.cts +1 -1
  14. package/dist/integrations/formik.d.ts +1 -1
  15. package/dist/integrations/react-hook-form.d.cts +1 -1
  16. package/dist/integrations/react-hook-form.d.ts +1 -1
  17. package/dist/overlay.cjs +161 -33
  18. package/dist/overlay.d.cts +1 -3
  19. package/dist/overlay.d.ts +1 -3
  20. package/dist/overlay.js +161 -33
  21. package/dist/preact.d.cts +1 -1
  22. package/dist/preact.d.ts +1 -1
  23. package/dist/qwik.cjs +240 -131
  24. package/dist/qwik.d.cts +2 -2
  25. package/dist/qwik.d.ts +2 -2
  26. package/dist/qwik.js +240 -131
  27. package/dist/react.cjs +257 -164
  28. package/dist/react.d.cts +2 -2
  29. package/dist/react.d.ts +2 -2
  30. package/dist/react.js +257 -164
  31. package/dist/solid.cjs +240 -131
  32. package/dist/solid.d.cts +1 -0
  33. package/dist/solid.d.ts +1 -0
  34. package/dist/solid.js +240 -131
  35. package/dist/solid.source.jsx +240 -131
  36. package/dist/svelte-core.cjs +240 -131
  37. package/dist/svelte-core.js +240 -131
  38. package/dist/svelte.cjs +252 -143
  39. package/dist/svelte.js +252 -143
  40. package/dist/vue.cjs +240 -131
  41. package/dist/vue.js +240 -131
  42. package/package.json +10 -8
package/README.md CHANGED
@@ -2,8 +2,8 @@
2
2
 
3
3
  > Universal, headless bottom-sheet engine. **One core, eight adapters, GPU-accelerated, fully tested.**
4
4
 
5
- ![bundle](https://img.shields.io/badge/core-16.8%20KB%20gzip-1a1614?style=flat-square)
6
- ![tests](https://img.shields.io/badge/tests-501%20unit-2ea44f?style=flat-square)
5
+ ![bundle](https://img.shields.io/badge/core-%E2%89%A422.5%20KB%20gzip%20budget-1a1614?style=flat-square)
6
+ ![tests](https://img.shields.io/badge/tests-500%2B%20unit-2ea44f?style=flat-square)
7
7
  ![ts](https://img.shields.io/badge/TypeScript-strict-3178c6?style=flat-square&logo=typescript&logoColor=white)
8
8
  ![license](https://img.shields.io/badge/license-MIT-dc3522?style=flat-square)
9
9
 
@@ -14,40 +14,41 @@ stacking — and the exact same engine behind every adapter.
14
14
  ## 🚀 [Live demo → bottom-sheet-demo.vercel.app](https://bottom-sheet-demo.vercel.app)
15
15
 
16
16
  <p align="center">
17
- <img src="https://raw.githubusercontent.com/Surdeddd/BottomSheet/main/docs/gifs/01-drag-to-close.gif" alt="drag to close — spring physics" width="380" />
17
+ <picture>
18
+ <source media="(prefers-color-scheme: dark)" srcset="docs/screenshots/sheet-dark.png" />
19
+ <img src="docs/screenshots/sheet-light.png" alt="bottom sheet at a mid snap point over a dimmed page" width="420" />
20
+ </picture>
18
21
  </p>
19
22
 
20
- ```
23
+ <p align="center">
24
+ <img src="https://raw.githubusercontent.com/Surdeddd/BottomSheet/main/docs/gifs/01-drag-to-close.gif" alt="drag to close — spring physics" width="380" />
25
+ </p>
21
26
 
22
- ┌───────────────────────────────────────┐
23
- │ ENGINE (~9.15 KB gzip) │
24
- │ spring · gestures · snap math │
25
- │ focus trap · scroll lock · stack │
26
- └────────────┬──────────────────────────┘
27
- │
28
- ┌──────┬──────┬──────┬──────┬──────┬─────────────┬─────────┐
29
- ▼ ▼ ▼ ▼ ▼ ▼ ▼ ▼
30
- React Vue 3 Svelte Solid Lit Web Comp. Vanilla Angular
31
- (recipe)
32
- ```
27
+ <p align="center">
28
+ <img src="docs/assets/architecture.svg" alt="One engine core feeding eight adapters — React, Preact, Vue 3, Svelte, Solid, Qwik, Web Component and Vanilla — plus a standalone /overlay subpath" width="720" />
29
+ </p>
33
30
 
34
- ## Why another bottom-sheet?
35
-
36
- | Feature | vaul | react-modal-sheet | **this lib** |
37
- | ------------------------------------ | ------- | ----------------- | ------------- |
38
- | React adapter | ✓ | ✓ | ✓ |
39
- | Vue / Svelte / Web Component | — | — | **✓ ✓ ✓** |
40
- | Vanilla / no framework | — | — | **✓** |
41
- | `bottom` mode | ✓ | ✓ | ✓ |
42
- | `top` / `left` / `right` / overlay | — | — | **✓** |
43
- | Spring physics with velocity carry | ✓ | partial | **✓** |
44
- | Pointer-type-aware tuning | — | — | **✓** |
45
- | Hardware-back interception (Android) | — | — | **✓** |
46
- | `env(safe-area-inset-*)` | partial | — | **✓** |
47
- | WCAG 2.1 AA keyboard slider | partial | — | **✓** |
48
- | Multi-sheet stacking | — | — | **✓** |
49
- | Sheet-manager registry (typed) | — | — | **✓** |
50
- | Tests (unit + e2e) | partial | partial | **501 + 195** |
31
+ ## What it does
32
+
33
+ Every item below is verifiable in this repo's `src/`:
34
+
35
+ - **8 build targets** — React, Preact, Vue 3, Svelte 5, Solid, Qwik, Web
36
+ Component (Lit-compatible `<bottom-sheet>`) and the vanilla core, plus an
37
+ `/overlay` subpath (standalone slide-up panel).
38
+ - **4 axes** — `bottom`, `top`, `left`, `right` (via `mode`).
39
+ - **Spring physics with velocity carry**, plus pointer-type-aware tuning
40
+ (separate touch / mouse / pen drag thresholds).
41
+ - **Hardware-back interception** on Android — opt-in via `closeOnBack`.
42
+ - **`env(safe-area-inset-*)`-aware padding** so action rows clear the home
43
+ indicator.
44
+ - **WCAG 2.1 AA keyboard slider** — Arrow/Home/End/Esc stepping, focus trap
45
+ with restore.
46
+ - **Multi-sheet stacking** plus a typed sheet-manager registry.
47
+ - **Cancelable `before-snap` / `before-close`** — veto a transition
48
+ synchronously.
49
+ - Backed by 500+ unit tests across the suite and ~200 Playwright e2e runs
50
+ across three browser engines (`mobile-chrome` / `mobile-safari` /
51
+ `firefox`).
51
52
 
52
53
  ## Install
53
54
 
@@ -316,6 +317,14 @@ engine.depth(); // open sheets above this one
316
317
  engine.destroy(); // remove listeners + cancel animation
317
318
  ```
318
319
 
320
+ `initial` starts the sheet **born open** at any snap whose size resolves `> 0`
321
+ (no `open` / `opened` event fires — there is no transition to announce). Calling
322
+ `open()` / `snapTo()` **synchronously in the mount tick is safe** — no
323
+ `requestAnimationFrame` / `nextTick` delay is needed, including `fit` / `content`
324
+ and teleported sheets. A sheet mounted hidden (`display:none` ancestor) heals
325
+ and emits its open sequence once on reveal. See
326
+ [Architecture → Opening at construction](docs/ARCHITECTURE.md).
327
+
319
328
  ### Scrim runtime API
320
329
 
321
330
  The scrim is the dim layer behind/around the sheet (`screenComponent`). All
@@ -407,6 +416,14 @@ element — drive any CSS animation from drag without touching JS:
407
416
  }
408
417
  ```
409
418
 
419
+ > **z-index is engine-owned.** The stack controller rewrites inline `z-index` on
420
+ > `.bs-sheet`, `.bs-backdrop` and anchor wrappers on every stack change (base
421
+ > `100`, step `10` per depth), so a value you set is overwritten and races the
422
+ > stack. To position the sheet layer against app UI, move the container
423
+ > (teleport target / `.bs-root` parent) or change the open order — don't style
424
+ > `z-index` on bs-managed elements. See
425
+ > [Architecture → z-index & the sheet stack](docs/ARCHITECTURE.md).
426
+
410
427
  ### Scrim, blur & high contrast
411
428
 
412
429
  | Token | Default | Region |
@@ -456,19 +473,19 @@ npm run demo
456
473
  # http://localhost:5173
457
474
  ```
458
475
 
459
- The editorial demo showcases all five adapters, every gesture, every mode
476
+ The editorial demo showcases the adapters, every gesture, every mode
460
477
  (`bottom · top · left · right · overlay`), and live engine readouts (active
461
478
  snap, progress, velocity, FPS). EN/RU and light/dark toggles in the corner.
462
479
 
463
480
  ## Testing
464
481
 
465
482
  ```bash
466
- npm test # 501 unit tests via vitest + happy-dom (~6s)
483
+ npm test # 500+ unit tests via vitest + happy-dom
467
484
  npx playwright test # e2e via Playwright on mobile-Chrome / mobile-Safari / Firefox
468
485
  npm run typecheck # TypeScript --noEmit
469
486
  ```
470
487
 
471
- CI runs all three across Node 20/22 on every PR and tag.
488
+ CI runs all three across Node 20.19 / 22 on every PR and tag.
472
489
 
473
490
  ## Contributing
474
491
 
@@ -304,6 +304,7 @@ declare class BottomSheetEngine {
304
304
  };
305
305
  setSnapPoints(points: EngineOptions["snapPoints"], allowed?: string[]): void;
306
306
  recompute(): void;
307
+ private healActiveSnap;
307
308
  destroy(): void;
308
309
  private emit;
309
310
  private newCycle;
@@ -304,6 +304,7 @@ declare class BottomSheetEngine {
304
304
  };
305
305
  setSnapPoints(points: EngineOptions["snapPoints"], allowed?: string[]): void;
306
306
  recompute(): void;
307
+ private healActiveSnap;
307
308
  destroy(): void;
308
309
  private emit;
309
310
  private newCycle;
package/dist/element.cjs CHANGED
@@ -504,6 +504,206 @@ var init_linked_sheets = __esm({
504
504
  }
505
505
  });
506
506
 
507
+ // src/core/features/history-coordinator.ts
508
+ function onPopState() {
509
+ if (suppress > 0) {
510
+ suppress -= 1;
511
+ const rebrand = pendingRebrand;
512
+ pendingRebrand = null;
513
+ if (rebrand) rebrand();
514
+ maybeTeardownListener();
515
+ return;
516
+ }
517
+ if (entries.length === 0) {
518
+ notifyRouteChange();
519
+ maybeTeardownListener();
520
+ return;
521
+ }
522
+ let target = null;
523
+ let targetIdx = -1;
524
+ for (let i = entries.length - 1; i >= 0; i -= 1) {
525
+ const entry = entries[i];
526
+ if (!entry.surface.isOpen()) continue;
527
+ if (!target) {
528
+ target = entry;
529
+ targetIdx = i;
530
+ }
531
+ if (entry.surface.isTop()) {
532
+ target = entry;
533
+ targetIdx = i;
534
+ break;
535
+ }
536
+ }
537
+ if (!target) {
538
+ const removed = entries.pop();
539
+ if (removed) removed.live = false;
540
+ maybeTeardownListener();
541
+ return;
542
+ }
543
+ target.live = false;
544
+ entries.splice(targetIdx, 1);
545
+ const settled = target;
546
+ void Promise.resolve(settled.surface.close()).then(() => {
547
+ if (settled.surface.isOpen()) restore(settled);
548
+ });
549
+ maybeTeardownListener();
550
+ }
551
+ function restore(entry) {
552
+ if (!hasWindow() || !hasHistory()) return;
553
+ entry.live = true;
554
+ runInternal(() => {
555
+ try {
556
+ history.pushState(brandMarker(entry.surface), "", entry.surface.url);
557
+ } catch {
558
+ entry.live = false;
559
+ }
560
+ });
561
+ if (entry.live) {
562
+ entries.push(entry);
563
+ ensureListener();
564
+ }
565
+ }
566
+ function pushBackMarker(surface) {
567
+ const entry = { surface, priorUrl: void 0, live: false };
568
+ if (!hasWindow() || !hasHistory()) {
569
+ return entry;
570
+ }
571
+ entry.priorUrl = surface.url !== void 0 ? location.href : void 0;
572
+ runInternal(() => {
573
+ try {
574
+ history.pushState(brandMarker(surface), "", surface.url);
575
+ entry.live = true;
576
+ } catch {
577
+ entry.live = false;
578
+ }
579
+ });
580
+ if (entry.live) {
581
+ entries.push(entry);
582
+ ensureListener();
583
+ }
584
+ return entry;
585
+ }
586
+ function popBackMarker(handle) {
587
+ if (!hasWindow() || !hasHistory()) return;
588
+ const entry = handle;
589
+ const idx = entries.indexOf(entry);
590
+ if (idx === -1 || !entry.live) return;
591
+ const last = entries.length - 1;
592
+ entry.live = false;
593
+ entries.splice(idx, 1);
594
+ if (idx !== last) {
595
+ const rebrandSurface = entries[entries.length - 1].surface;
596
+ const rebrandUrl = entry.priorUrl;
597
+ pendingRebrand = () => {
598
+ runInternal(() => {
599
+ history.replaceState(
600
+ brandMarker(rebrandSurface),
601
+ "",
602
+ rebrandUrl !== void 0 ? rebrandUrl : location.href
603
+ );
604
+ });
605
+ };
606
+ }
607
+ suppress += 1;
608
+ runInternal(() => {
609
+ try {
610
+ history.back();
611
+ } catch {
612
+ suppress = Math.max(0, suppress - 1);
613
+ pendingRebrand = null;
614
+ }
615
+ });
616
+ maybeTeardownListener();
617
+ }
618
+ function subscribeRouteChange(fn) {
619
+ if (!hasWindow() || !hasHistory()) return () => {
620
+ };
621
+ subscribers.add(fn);
622
+ if (subscribers.size === 1) installPatch();
623
+ ensureListener();
624
+ return () => {
625
+ if (!subscribers.has(fn)) return;
626
+ subscribers.delete(fn);
627
+ if (subscribers.size === 0) removePatch();
628
+ maybeTeardownListener();
629
+ };
630
+ }
631
+ var entries, subscribers, suppress, internalDepth, pendingRebrand, listenerInstalled, patchInstalled, origPushState, origReplaceState, patchedPushState, patchedReplaceState, hasWindow, hasHistory, brandMarker, runInternal, notifyRouteChange, ensureListener, maybeTeardownListener, installPatch, removePatch;
632
+ var init_history_coordinator = __esm({
633
+ "src/core/features/history-coordinator.ts"() {
634
+ entries = [];
635
+ subscribers = /* @__PURE__ */ new Set();
636
+ suppress = 0;
637
+ internalDepth = 0;
638
+ pendingRebrand = null;
639
+ listenerInstalled = false;
640
+ patchInstalled = false;
641
+ origPushState = null;
642
+ origReplaceState = null;
643
+ patchedPushState = null;
644
+ patchedReplaceState = null;
645
+ hasWindow = () => typeof window !== "undefined";
646
+ hasHistory = () => typeof history !== "undefined";
647
+ brandMarker = (surface) => ({
648
+ ...surface.markerState(),
649
+ __bs: true
650
+ });
651
+ runInternal = (fn) => {
652
+ internalDepth += 1;
653
+ try {
654
+ fn();
655
+ } finally {
656
+ internalDepth -= 1;
657
+ }
658
+ };
659
+ notifyRouteChange = () => {
660
+ const snapshot = Array.from(subscribers);
661
+ for (const fn of snapshot) fn();
662
+ };
663
+ ensureListener = () => {
664
+ if (listenerInstalled || !hasWindow()) return;
665
+ window.addEventListener("popstate", onPopState);
666
+ listenerInstalled = true;
667
+ };
668
+ maybeTeardownListener = () => {
669
+ if (!listenerInstalled) return;
670
+ if (entries.length > 0 || subscribers.size > 0 || suppress > 0) return;
671
+ if (hasWindow()) window.removeEventListener("popstate", onPopState);
672
+ listenerInstalled = false;
673
+ };
674
+ installPatch = () => {
675
+ if (patchInstalled || !hasHistory()) return;
676
+ origPushState = history.pushState;
677
+ origReplaceState = history.replaceState;
678
+ patchedPushState = function(...args) {
679
+ origPushState.apply(this, args);
680
+ if (internalDepth === 0) notifyRouteChange();
681
+ };
682
+ patchedReplaceState = function(...args) {
683
+ origReplaceState.apply(this, args);
684
+ if (internalDepth === 0) notifyRouteChange();
685
+ };
686
+ history.pushState = patchedPushState;
687
+ history.replaceState = patchedReplaceState;
688
+ patchInstalled = true;
689
+ };
690
+ removePatch = () => {
691
+ if (!patchInstalled) return;
692
+ if (history.pushState === patchedPushState && origPushState) {
693
+ history.pushState = origPushState;
694
+ }
695
+ if (history.replaceState === patchedReplaceState && origReplaceState) {
696
+ history.replaceState = origReplaceState;
697
+ }
698
+ origPushState = null;
699
+ origReplaceState = null;
700
+ patchedPushState = null;
701
+ patchedReplaceState = null;
702
+ patchInstalled = false;
703
+ };
704
+ }
705
+ });
706
+
507
707
  // src/core/features/route.ts
508
708
  function installRoute(deps) {
509
709
  if (typeof window === "undefined") {
@@ -514,135 +714,43 @@ function installRoute(deps) {
514
714
  return () => {
515
715
  };
516
716
  }
517
- let pushed = false;
518
- let closer = null;
519
- const teardownEntry = () => {
520
- if (closer) {
521
- const idx = closers.indexOf(closer);
522
- if (idx !== -1) closers.splice(idx, 1);
523
- }
524
- const wasConsumed = closer?.consumed ?? false;
525
- closer = null;
526
- if (pushed && !wasConsumed) {
527
- pushed = false;
528
- programmaticBack();
529
- } else {
530
- pushed = false;
531
- }
532
- };
533
- const pushEntry = () => {
534
- try {
535
- if (deps.routedTo !== void 0) {
536
- history.pushState({ __bsRouted: deps.sheetId }, "", deps.routedTo);
537
- } else {
538
- history.pushState({ __bsSheet: deps.sheetId }, "");
539
- }
540
- pushed = true;
541
- } catch {
542
- pushed = false;
543
- }
544
- };
717
+ let handle = null;
545
718
  const onOpen = () => {
546
- if (closer) return;
547
- const self = {
719
+ if (handle) return;
720
+ handle = pushBackMarker({
548
721
  isOpen: () => !deps.isDestroyed() && deps.getSize() > 0,
549
- isTop: () => deps.isTopSheet(),
550
- close: () => deps.close(),
551
- restore: () => {
552
- if (deps.isDestroyed() || closer !== self) return;
553
- self.consumed = false;
554
- if (!closers.includes(self)) closers.push(self);
555
- pushEntry();
556
- },
557
- consumed: false
558
- };
559
- closer = self;
560
- ensureHandler();
561
- closers.push(self);
562
- pushEntry();
722
+ isTop: deps.isTopSheet,
723
+ close: deps.close,
724
+ markerState: () => deps.routedTo !== void 0 ? { __bsRouted: deps.sheetId } : { __bsSheet: deps.sheetId },
725
+ url: deps.routedTo
726
+ });
727
+ };
728
+ const onClose = () => {
729
+ if (!handle) return;
730
+ popBackMarker(handle);
731
+ handle = null;
563
732
  };
564
733
  const unsubscribeOpen = deps.on("open", onOpen);
565
- const unsubscribeClose = deps.on("close", teardownEntry);
734
+ const unsubscribeClose = deps.on("close", onClose);
566
735
  if (deps.getSize() > 0) onOpen();
567
736
  return () => {
568
737
  unsubscribeOpen();
569
738
  unsubscribeClose();
570
- teardownEntry();
739
+ if (handle) {
740
+ popBackMarker(handle);
741
+ handle = null;
742
+ }
571
743
  };
572
744
  }
573
745
  function installRouteChange(deps) {
574
- if (typeof window === "undefined" || typeof history === "undefined") {
575
- return () => {
576
- };
577
- }
578
- const onChange = () => {
746
+ return subscribeRouteChange(() => {
579
747
  if (deps.isDestroyed() || deps.getSize() <= 0) return;
580
748
  void deps.close();
581
- };
582
- const wrap = (key) => {
583
- const original = history[key];
584
- const patched = function(...args) {
585
- original.apply(this, args);
586
- onChange();
587
- };
588
- history[key] = patched;
589
- return () => {
590
- if (history[key] === patched) {
591
- history[key] = original;
592
- }
593
- };
594
- };
595
- window.addEventListener("popstate", onChange);
596
- const restorePush = wrap("pushState");
597
- const restoreReplace = wrap("replaceState");
598
- return () => {
599
- window.removeEventListener("popstate", onChange);
600
- restorePush();
601
- restoreReplace();
602
- };
749
+ });
603
750
  }
604
- var closers, handlerInstalled, suppress, ensureHandler, programmaticBack;
605
751
  var init_route = __esm({
606
752
  "src/core/features/route.ts"() {
607
- closers = [];
608
- handlerInstalled = false;
609
- suppress = 0;
610
- ensureHandler = () => {
611
- if (handlerInstalled || typeof window === "undefined") return;
612
- handlerInstalled = true;
613
- window.addEventListener("popstate", () => {
614
- if (suppress > 0) {
615
- suppress -= 1;
616
- return;
617
- }
618
- let target = null;
619
- for (let i = closers.length - 1; i >= 0; i -= 1) {
620
- const c = closers[i];
621
- if (!c.isOpen()) continue;
622
- if (!target) target = c;
623
- if (c.isTop()) {
624
- target = c;
625
- break;
626
- }
627
- }
628
- if (!target) return;
629
- target.consumed = true;
630
- const idx = closers.indexOf(target);
631
- if (idx !== -1) closers.splice(idx, 1);
632
- const settled = target;
633
- void Promise.resolve(settled.close()).then(() => {
634
- if (settled.isOpen()) settled.restore();
635
- });
636
- });
637
- };
638
- programmaticBack = () => {
639
- suppress += 1;
640
- try {
641
- history.back();
642
- } catch {
643
- suppress = Math.max(0, suppress - 1);
644
- }
645
- };
753
+ init_history_coordinator();
646
754
  }
647
755
  });
648
756
 
@@ -942,11 +1050,15 @@ var init_fit_measurement = __esm({
942
1050
  "src/core/features/fit-measurement.ts"() {
943
1051
  init_transform();
944
1052
  measureSheetNatural = (element, content, vertical) => {
945
- if (!content) {
946
- return vertical ? element.offsetHeight : element.offsetWidth;
947
- }
948
1053
  const axis = vertical ? "height" : "width";
949
1054
  const sheetStyle = element.style;
1055
+ if (!content) {
1056
+ const prev = sheetStyle[axis];
1057
+ sheetStyle[axis] = "auto";
1058
+ const poked2 = vertical ? element.offsetHeight : element.offsetWidth;
1059
+ sheetStyle[axis] = prev;
1060
+ return poked2;
1061
+ }
950
1062
  const cs = content.style;
951
1063
  const prevSheet = sheetStyle[axis];
952
1064
  const prevFlex = cs.flex;
@@ -2640,7 +2752,7 @@ var init_sheet_anchors = __esm({
2640
2752
  // src/core/features/scrim-stages.ts
2641
2753
  function installScrimStages(deps, opts) {
2642
2754
  const doc = deps.host.ownerDocument;
2643
- const entries = opts.stages.map((def) => {
2755
+ const entries2 = opts.stages.map((def) => {
2644
2756
  const wrapper = doc.createElement("div");
2645
2757
  wrapper.className = "bs-scrim-stage";
2646
2758
  const ws = wrapper.style;
@@ -2661,9 +2773,9 @@ function installScrimStages(deps, opts) {
2661
2773
  const resolveActive = () => {
2662
2774
  const state = deps.getState();
2663
2775
  if (state.size === 0) {
2664
- return entries.find((e) => matchesId(e.def, state.activeId)) ?? null;
2776
+ return entries2.find((e) => matchesId(e.def, state.activeId)) ?? null;
2665
2777
  }
2666
- return entries.find((e) => matchesId(e.def, state.activeId)) ?? entries.find((e) => matchesRange(e.def, state.progress)) ?? null;
2778
+ return entries2.find((e) => matchesId(e.def, state.activeId)) ?? entries2.find((e) => matchesRange(e.def, state.progress)) ?? null;
2667
2779
  };
2668
2780
  const hide = (entry, animate) => {
2669
2781
  entry.inFlight?.cancel();
@@ -2712,13 +2824,13 @@ function installScrimStages(deps, opts) {
2712
2824
  deps.on("open", () => update(true)),
2713
2825
  deps.on("close", () => update(true))
2714
2826
  ];
2715
- if (entries.some((e) => e.def.forRange)) {
2827
+ if (entries2.some((e) => e.def.forRange)) {
2716
2828
  offs.push(deps.on("progress", () => update(true)));
2717
2829
  }
2718
2830
  update(false);
2719
2831
  return () => {
2720
2832
  offs.forEach((off) => off());
2721
- for (const entry of entries) {
2833
+ for (const entry of entries2) {
2722
2834
  entry.inFlight?.cancel();
2723
2835
  entry.inFlight = null;
2724
2836
  if (entry.wrapper.parentNode) {
@@ -3038,17 +3150,12 @@ var init_BottomSheetEngine = __esm({
3038
3150
  this.attach();
3039
3151
  this.registerInStack();
3040
3152
  if (this.snaps.getMaxAxisSize() === 0 && typeof IntersectionObserver !== "undefined" && typeof document !== "undefined") {
3041
- const io = new IntersectionObserver((entries) => {
3042
- for (const entry of entries) {
3153
+ const io = new IntersectionObserver((entries2) => {
3154
+ for (const entry of entries2) {
3043
3155
  if (!entry.isIntersecting) continue;
3044
3156
  if (this.destroyed) return;
3045
3157
  this.snaps.recompute();
3046
- const current = this.snaps.findById(this.activeId);
3047
- if (current) {
3048
- this.size = current.size;
3049
- this.applySize(this.size);
3050
- this.updateAriaSlider();
3051
- }
3158
+ this.healActiveSnap();
3052
3159
  io.disconnect();
3053
3160
  return;
3054
3161
  }
@@ -3212,6 +3319,8 @@ var init_BottomSheetEngine = __esm({
3212
3319
  const vt = document.startViewTransition(() => {
3213
3320
  this.applySize(target.size);
3214
3321
  });
3322
+ vt.ready?.catch(() => {
3323
+ });
3215
3324
  this.currentViewTransition = vt;
3216
3325
  try {
3217
3326
  await vt.finished;
@@ -3463,11 +3572,16 @@ var init_BottomSheetEngine = __esm({
3463
3572
  this.maxHeight.clampTo();
3464
3573
  this.scrim.invalidateOpacityCache();
3465
3574
  if (this.gesture?.isDragging) return;
3575
+ this.healActiveSnap();
3576
+ }
3577
+ healActiveSnap() {
3466
3578
  const current = this.snaps.findById(this.activeId);
3467
- if (current) {
3468
- this.size = current.size;
3469
- this.applySize(this.size);
3470
- this.updateAriaSlider();
3579
+ if (!current) return;
3580
+ this.size = current.size;
3581
+ this.applySize(this.size);
3582
+ this.updateAriaSlider();
3583
+ if (this.size > 0 && !this.lifecycle.isInstalled && !this.opening && !this.animation.isAnimating) {
3584
+ this.emitOpenSequence(this.activeId);
3471
3585
  }
3472
3586
  }
3473
3587
  destroy() {
@@ -3756,9 +3870,9 @@ var init_BottomSheetEngine = __esm({
3756
3870
  return;
3757
3871
  }
3758
3872
  if (this.emitBeforeSnap(target, previousId)) {
3759
- const restore = this.snaps.findById(previousId);
3760
- if (restore) {
3761
- void this.animation.animateTo(restore.size, 0);
3873
+ const restore2 = this.snaps.findById(previousId);
3874
+ if (restore2) {
3875
+ void this.animation.animateTo(restore2.size, 0);
3762
3876
  }
3763
3877
  return;
3764
3878
  }
@@ -1,4 +1,4 @@
1
- import { B as BottomSheetEngine, C as CloseReason, A as AnchorOptions, S as ScrimStagesOptions, E as EngineState } from './BottomSheetEngine-DzsIwD28.cjs';
1
+ import { B as BottomSheetEngine, C as CloseReason, A as AnchorOptions, S as ScrimStagesOptions, E as EngineState } from './BottomSheetEngine-D6oiEfZ4.cjs';
2
2
  import './transform-jVfAikE9.cjs';
3
3
 
4
4
  declare const BaseHTMLElement: typeof HTMLElement;
package/dist/element.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { B as BottomSheetEngine, C as CloseReason, A as AnchorOptions, S as ScrimStagesOptions, E as EngineState } from './BottomSheetEngine-DRjCU73e.js';
1
+ import { B as BottomSheetEngine, C as CloseReason, A as AnchorOptions, S as ScrimStagesOptions, E as EngineState } from './BottomSheetEngine-Dhy0X_e9.js';
2
2
  import './transform-jVfAikE9.js';
3
3
 
4
4
  declare const BaseHTMLElement: typeof HTMLElement;