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