@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
@@ -440,46 +440,202 @@ function notifyLinkedSheets(sheets, self) {
440
440
  }
441
441
  }
442
442
 
443
- // src/core/features/route.ts
444
- var closers = [];
445
- var handlerInstalled = false;
443
+ // src/core/features/history-coordinator.ts
444
+ var entries = [];
445
+ var subscribers = /* @__PURE__ */ new Set();
446
446
  var suppress = 0;
447
- var ensureHandler = () => {
448
- if (handlerInstalled || typeof window === "undefined") return;
449
- handlerInstalled = true;
450
- window.addEventListener("popstate", () => {
451
- if (suppress > 0) {
452
- suppress -= 1;
453
- return;
447
+ var internalDepth = 0;
448
+ var pendingRebrand = null;
449
+ var listenerInstalled = false;
450
+ var patchInstalled = false;
451
+ var origPushState = null;
452
+ var origReplaceState = null;
453
+ var patchedPushState = null;
454
+ var patchedReplaceState = null;
455
+ var hasWindow = () => typeof window !== "undefined";
456
+ var hasHistory = () => typeof history !== "undefined";
457
+ var brandMarker = (surface) => ({
458
+ ...surface.markerState(),
459
+ __bs: true
460
+ });
461
+ var runInternal = (fn) => {
462
+ internalDepth += 1;
463
+ try {
464
+ fn();
465
+ } finally {
466
+ internalDepth -= 1;
467
+ }
468
+ };
469
+ var notifyRouteChange = () => {
470
+ const snapshot = Array.from(subscribers);
471
+ for (const fn of snapshot) fn();
472
+ };
473
+ var ensureListener = () => {
474
+ if (listenerInstalled || !hasWindow()) return;
475
+ window.addEventListener("popstate", onPopState);
476
+ listenerInstalled = true;
477
+ };
478
+ var maybeTeardownListener = () => {
479
+ if (!listenerInstalled) return;
480
+ if (entries.length > 0 || subscribers.size > 0 || suppress > 0) return;
481
+ if (hasWindow()) window.removeEventListener("popstate", onPopState);
482
+ listenerInstalled = false;
483
+ };
484
+ var installPatch = () => {
485
+ if (patchInstalled || !hasHistory()) return;
486
+ origPushState = history.pushState;
487
+ origReplaceState = history.replaceState;
488
+ patchedPushState = function(...args) {
489
+ origPushState.apply(this, args);
490
+ if (internalDepth === 0) notifyRouteChange();
491
+ };
492
+ patchedReplaceState = function(...args) {
493
+ origReplaceState.apply(this, args);
494
+ if (internalDepth === 0) notifyRouteChange();
495
+ };
496
+ history.pushState = patchedPushState;
497
+ history.replaceState = patchedReplaceState;
498
+ patchInstalled = true;
499
+ };
500
+ var removePatch = () => {
501
+ if (!patchInstalled) return;
502
+ if (history.pushState === patchedPushState && origPushState) {
503
+ history.pushState = origPushState;
504
+ }
505
+ if (history.replaceState === patchedReplaceState && origReplaceState) {
506
+ history.replaceState = origReplaceState;
507
+ }
508
+ origPushState = null;
509
+ origReplaceState = null;
510
+ patchedPushState = null;
511
+ patchedReplaceState = null;
512
+ patchInstalled = false;
513
+ };
514
+ function onPopState() {
515
+ if (suppress > 0) {
516
+ suppress -= 1;
517
+ const rebrand = pendingRebrand;
518
+ pendingRebrand = null;
519
+ if (rebrand) rebrand();
520
+ maybeTeardownListener();
521
+ return;
522
+ }
523
+ if (entries.length === 0) {
524
+ notifyRouteChange();
525
+ maybeTeardownListener();
526
+ return;
527
+ }
528
+ let target = null;
529
+ let targetIdx = -1;
530
+ for (let i = entries.length - 1; i >= 0; i -= 1) {
531
+ const entry = entries[i];
532
+ if (!entry.surface.isOpen()) continue;
533
+ if (!target) {
534
+ target = entry;
535
+ targetIdx = i;
454
536
  }
455
- let target = null;
456
- for (let i = closers.length - 1; i >= 0; i -= 1) {
457
- const c = closers[i];
458
- if (!c.isOpen()) continue;
459
- if (!target) target = c;
460
- if (c.isTop()) {
461
- target = c;
462
- break;
463
- }
537
+ if (entry.surface.isTop()) {
538
+ target = entry;
539
+ targetIdx = i;
540
+ break;
464
541
  }
465
- if (!target) return;
466
- target.consumed = true;
467
- const idx = closers.indexOf(target);
468
- if (idx !== -1) closers.splice(idx, 1);
469
- const settled = target;
470
- void Promise.resolve(settled.close()).then(() => {
471
- if (settled.isOpen()) settled.restore();
472
- });
542
+ }
543
+ if (!target) {
544
+ const removed = entries.pop();
545
+ if (removed) removed.live = false;
546
+ maybeTeardownListener();
547
+ return;
548
+ }
549
+ target.live = false;
550
+ entries.splice(targetIdx, 1);
551
+ const settled = target;
552
+ void Promise.resolve(settled.surface.close()).then(() => {
553
+ if (settled.surface.isOpen()) restore(settled);
473
554
  });
474
- };
475
- var programmaticBack = () => {
476
- suppress += 1;
477
- try {
478
- history.back();
479
- } catch {
480
- suppress = Math.max(0, suppress - 1);
555
+ maybeTeardownListener();
556
+ }
557
+ function restore(entry) {
558
+ if (!hasWindow() || !hasHistory()) return;
559
+ entry.live = true;
560
+ runInternal(() => {
561
+ try {
562
+ history.pushState(brandMarker(entry.surface), "", entry.surface.url);
563
+ } catch {
564
+ entry.live = false;
565
+ }
566
+ });
567
+ if (entry.live) {
568
+ entries.push(entry);
569
+ ensureListener();
481
570
  }
482
- };
571
+ }
572
+ function pushBackMarker(surface) {
573
+ const entry = { surface, priorUrl: void 0, live: false };
574
+ if (!hasWindow() || !hasHistory()) {
575
+ return entry;
576
+ }
577
+ entry.priorUrl = surface.url !== void 0 ? location.href : void 0;
578
+ runInternal(() => {
579
+ try {
580
+ history.pushState(brandMarker(surface), "", surface.url);
581
+ entry.live = true;
582
+ } catch {
583
+ entry.live = false;
584
+ }
585
+ });
586
+ if (entry.live) {
587
+ entries.push(entry);
588
+ ensureListener();
589
+ }
590
+ return entry;
591
+ }
592
+ function popBackMarker(handle) {
593
+ if (!hasWindow() || !hasHistory()) return;
594
+ const entry = handle;
595
+ const idx = entries.indexOf(entry);
596
+ if (idx === -1 || !entry.live) return;
597
+ const last = entries.length - 1;
598
+ entry.live = false;
599
+ entries.splice(idx, 1);
600
+ if (idx !== last) {
601
+ const rebrandSurface = entries[entries.length - 1].surface;
602
+ const rebrandUrl = entry.priorUrl;
603
+ pendingRebrand = () => {
604
+ runInternal(() => {
605
+ history.replaceState(
606
+ brandMarker(rebrandSurface),
607
+ "",
608
+ rebrandUrl !== void 0 ? rebrandUrl : location.href
609
+ );
610
+ });
611
+ };
612
+ }
613
+ suppress += 1;
614
+ runInternal(() => {
615
+ try {
616
+ history.back();
617
+ } catch {
618
+ suppress = Math.max(0, suppress - 1);
619
+ pendingRebrand = null;
620
+ }
621
+ });
622
+ maybeTeardownListener();
623
+ }
624
+ function subscribeRouteChange(fn) {
625
+ if (!hasWindow() || !hasHistory()) return () => {
626
+ };
627
+ subscribers.add(fn);
628
+ if (subscribers.size === 1) installPatch();
629
+ ensureListener();
630
+ return () => {
631
+ if (!subscribers.has(fn)) return;
632
+ subscribers.delete(fn);
633
+ if (subscribers.size === 0) removePatch();
634
+ maybeTeardownListener();
635
+ };
636
+ }
637
+
638
+ // src/core/features/route.ts
483
639
  function installRoute(deps) {
484
640
  if (typeof window === "undefined") {
485
641
  return () => {
@@ -489,92 +645,39 @@ function installRoute(deps) {
489
645
  return () => {
490
646
  };
491
647
  }
492
- let pushed = false;
493
- let closer = null;
494
- const teardownEntry = () => {
495
- if (closer) {
496
- const idx = closers.indexOf(closer);
497
- if (idx !== -1) closers.splice(idx, 1);
498
- }
499
- const wasConsumed = closer?.consumed ?? false;
500
- closer = null;
501
- if (pushed && !wasConsumed) {
502
- pushed = false;
503
- programmaticBack();
504
- } else {
505
- pushed = false;
506
- }
507
- };
508
- const pushEntry = () => {
509
- try {
510
- if (deps.routedTo !== void 0) {
511
- history.pushState({ __bsRouted: deps.sheetId }, "", deps.routedTo);
512
- } else {
513
- history.pushState({ __bsSheet: deps.sheetId }, "");
514
- }
515
- pushed = true;
516
- } catch {
517
- pushed = false;
518
- }
519
- };
648
+ let handle = null;
520
649
  const onOpen = () => {
521
- if (closer) return;
522
- const self = {
650
+ if (handle) return;
651
+ handle = pushBackMarker({
523
652
  isOpen: () => !deps.isDestroyed() && deps.getSize() > 0,
524
- isTop: () => deps.isTopSheet(),
525
- close: () => deps.close(),
526
- restore: () => {
527
- if (deps.isDestroyed() || closer !== self) return;
528
- self.consumed = false;
529
- if (!closers.includes(self)) closers.push(self);
530
- pushEntry();
531
- },
532
- consumed: false
533
- };
534
- closer = self;
535
- ensureHandler();
536
- closers.push(self);
537
- pushEntry();
653
+ isTop: deps.isTopSheet,
654
+ close: deps.close,
655
+ markerState: () => deps.routedTo !== void 0 ? { __bsRouted: deps.sheetId } : { __bsSheet: deps.sheetId },
656
+ url: deps.routedTo
657
+ });
658
+ };
659
+ const onClose = () => {
660
+ if (!handle) return;
661
+ popBackMarker(handle);
662
+ handle = null;
538
663
  };
539
664
  const unsubscribeOpen = deps.on("open", onOpen);
540
- const unsubscribeClose = deps.on("close", teardownEntry);
665
+ const unsubscribeClose = deps.on("close", onClose);
541
666
  if (deps.getSize() > 0) onOpen();
542
667
  return () => {
543
668
  unsubscribeOpen();
544
669
  unsubscribeClose();
545
- teardownEntry();
670
+ if (handle) {
671
+ popBackMarker(handle);
672
+ handle = null;
673
+ }
546
674
  };
547
675
  }
548
676
  function installRouteChange(deps) {
549
- if (typeof window === "undefined" || typeof history === "undefined") {
550
- return () => {
551
- };
552
- }
553
- const onChange = () => {
677
+ return subscribeRouteChange(() => {
554
678
  if (deps.isDestroyed() || deps.getSize() <= 0) return;
555
679
  void deps.close();
556
- };
557
- const wrap = (key) => {
558
- const original = history[key];
559
- const patched = function(...args) {
560
- original.apply(this, args);
561
- onChange();
562
- };
563
- history[key] = patched;
564
- return () => {
565
- if (history[key] === patched) {
566
- history[key] = original;
567
- }
568
- };
569
- };
570
- window.addEventListener("popstate", onChange);
571
- const restorePush = wrap("pushState");
572
- const restoreReplace = wrap("replaceState");
573
- return () => {
574
- window.removeEventListener("popstate", onChange);
575
- restorePush();
576
- restoreReplace();
577
- };
680
+ });
578
681
  }
579
682
 
580
683
  // src/core/features/content-swipe.ts
@@ -844,11 +947,15 @@ function createMaxHeightController(deps) {
844
947
 
845
948
  // src/core/features/fit-measurement.ts
846
949
  var measureSheetNatural = (element, content, vertical) => {
847
- if (!content) {
848
- return vertical ? element.offsetHeight : element.offsetWidth;
849
- }
850
950
  const axis = vertical ? "height" : "width";
851
951
  const sheetStyle = element.style;
952
+ if (!content) {
953
+ const prev = sheetStyle[axis];
954
+ sheetStyle[axis] = "auto";
955
+ const poked2 = vertical ? element.offsetHeight : element.offsetWidth;
956
+ sheetStyle[axis] = prev;
957
+ return poked2;
958
+ }
852
959
  const cs = content.style;
853
960
  const prevSheet = sheetStyle[axis];
854
961
  const prevFlex = cs.flex;
@@ -2427,7 +2534,7 @@ var matchesRange = (def, progress) => {
2427
2534
  };
2428
2535
  function installScrimStages(deps, opts) {
2429
2536
  const doc = deps.host.ownerDocument;
2430
- const entries = opts.stages.map((def) => {
2537
+ const entries2 = opts.stages.map((def) => {
2431
2538
  const wrapper = doc.createElement("div");
2432
2539
  wrapper.className = "bs-scrim-stage";
2433
2540
  const ws = wrapper.style;
@@ -2448,9 +2555,9 @@ function installScrimStages(deps, opts) {
2448
2555
  const resolveActive = () => {
2449
2556
  const state = deps.getState();
2450
2557
  if (state.size === 0) {
2451
- return entries.find((e) => matchesId(e.def, state.activeId)) ?? null;
2558
+ return entries2.find((e) => matchesId(e.def, state.activeId)) ?? null;
2452
2559
  }
2453
- return entries.find((e) => matchesId(e.def, state.activeId)) ?? entries.find((e) => matchesRange(e.def, state.progress)) ?? null;
2560
+ return entries2.find((e) => matchesId(e.def, state.activeId)) ?? entries2.find((e) => matchesRange(e.def, state.progress)) ?? null;
2454
2561
  };
2455
2562
  const hide = (entry, animate) => {
2456
2563
  entry.inFlight?.cancel();
@@ -2499,13 +2606,13 @@ function installScrimStages(deps, opts) {
2499
2606
  deps.on("open", () => update(true)),
2500
2607
  deps.on("close", () => update(true))
2501
2608
  ];
2502
- if (entries.some((e) => e.def.forRange)) {
2609
+ if (entries2.some((e) => e.def.forRange)) {
2503
2610
  offs.push(deps.on("progress", () => update(true)));
2504
2611
  }
2505
2612
  update(false);
2506
2613
  return () => {
2507
2614
  offs.forEach((off) => off());
2508
- for (const entry of entries) {
2615
+ for (const entry of entries2) {
2509
2616
  entry.inFlight?.cancel();
2510
2617
  entry.inFlight = null;
2511
2618
  if (entry.wrapper.parentNode) {
@@ -2764,17 +2871,12 @@ var BottomSheetEngine = class {
2764
2871
  this.attach();
2765
2872
  this.registerInStack();
2766
2873
  if (this.snaps.getMaxAxisSize() === 0 && typeof IntersectionObserver !== "undefined" && typeof document !== "undefined") {
2767
- const io = new IntersectionObserver((entries) => {
2768
- for (const entry of entries) {
2874
+ const io = new IntersectionObserver((entries2) => {
2875
+ for (const entry of entries2) {
2769
2876
  if (!entry.isIntersecting) continue;
2770
2877
  if (this.destroyed) return;
2771
2878
  this.snaps.recompute();
2772
- const current = this.snaps.findById(this.activeId);
2773
- if (current) {
2774
- this.size = current.size;
2775
- this.applySize(this.size);
2776
- this.updateAriaSlider();
2777
- }
2879
+ this.healActiveSnap();
2778
2880
  io.disconnect();
2779
2881
  return;
2780
2882
  }
@@ -2938,6 +3040,8 @@ var BottomSheetEngine = class {
2938
3040
  const vt = document.startViewTransition(() => {
2939
3041
  this.applySize(target.size);
2940
3042
  });
3043
+ vt.ready?.catch(() => {
3044
+ });
2941
3045
  this.currentViewTransition = vt;
2942
3046
  try {
2943
3047
  await vt.finished;
@@ -3189,11 +3293,16 @@ var BottomSheetEngine = class {
3189
3293
  this.maxHeight.clampTo();
3190
3294
  this.scrim.invalidateOpacityCache();
3191
3295
  if (this.gesture?.isDragging) return;
3296
+ this.healActiveSnap();
3297
+ }
3298
+ healActiveSnap() {
3192
3299
  const current = this.snaps.findById(this.activeId);
3193
- if (current) {
3194
- this.size = current.size;
3195
- this.applySize(this.size);
3196
- this.updateAriaSlider();
3300
+ if (!current) return;
3301
+ this.size = current.size;
3302
+ this.applySize(this.size);
3303
+ this.updateAriaSlider();
3304
+ if (this.size > 0 && !this.lifecycle.isInstalled && !this.opening && !this.animation.isAnimating) {
3305
+ this.emitOpenSequence(this.activeId);
3197
3306
  }
3198
3307
  }
3199
3308
  destroy() {
@@ -3482,9 +3591,9 @@ var BottomSheetEngine = class {
3482
3591
  return;
3483
3592
  }
3484
3593
  if (this.emitBeforeSnap(target, previousId)) {
3485
- const restore = this.snaps.findById(previousId);
3486
- if (restore) {
3487
- void this.animation.animateTo(restore.size, 0);
3594
+ const restore2 = this.snaps.findById(previousId);
3595
+ if (restore2) {
3596
+ void this.animation.animateTo(restore2.size, 0);
3488
3597
  }
3489
3598
  return;
3490
3599
  }