@surdeddd/bottom-sheet 0.10.0 → 0.11.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.
@@ -444,46 +444,198 @@ function notifyLinkedSheets(sheets, self) {
444
444
  }
445
445
  }
446
446
 
447
- // src/core/features/route.ts
448
- var closers = [];
449
- var handlerInstalled = false;
447
+ // src/core/features/history-coordinator.ts
448
+ var entries = [];
449
+ var subscribers = /* @__PURE__ */ new Set();
450
450
  var suppress = 0;
451
- var ensureHandler = () => {
452
- if (handlerInstalled || typeof window === "undefined") return;
453
- handlerInstalled = true;
454
- window.addEventListener("popstate", () => {
455
- if (suppress > 0) {
456
- suppress -= 1;
457
- return;
451
+ var internalDepth = 0;
452
+ var pendingRebrand = null;
453
+ var listenerInstalled = false;
454
+ var patchInstalled = false;
455
+ var origPushState = null;
456
+ var origReplaceState = null;
457
+ var patchedPushState = null;
458
+ var patchedReplaceState = null;
459
+ var hasWindow = () => typeof window !== "undefined";
460
+ var hasHistory = () => typeof history !== "undefined";
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;
458
536
  }
459
- let target = null;
460
- for (let i = closers.length - 1; i >= 0; i -= 1) {
461
- const c = closers[i];
462
- if (!c.isOpen()) continue;
463
- if (!target) target = c;
464
- if (c.isTop()) {
465
- target = c;
466
- break;
467
- }
537
+ if (entry.surface.isTop()) {
538
+ target = entry;
539
+ targetIdx = i;
540
+ break;
468
541
  }
469
- if (!target) return;
470
- target.consumed = true;
471
- const idx = closers.indexOf(target);
472
- if (idx !== -1) closers.splice(idx, 1);
473
- const settled = target;
474
- void Promise.resolve(settled.close()).then(() => {
475
- if (settled.isOpen()) settled.restore();
476
- });
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);
477
554
  });
478
- };
479
- var programmaticBack = () => {
480
- suppress += 1;
481
- try {
482
- history.back();
483
- } catch {
484
- 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(entry.surface.markerState(), "", entry.surface.url);
563
+ } catch {
564
+ entry.live = false;
565
+ }
566
+ });
567
+ if (entry.live) {
568
+ entries.push(entry);
569
+ ensureListener();
485
570
  }
486
- };
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(surface.markerState(), "", 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
+ rebrandSurface.markerState(),
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
487
639
  function installRoute(deps) {
488
640
  if (typeof window === "undefined") {
489
641
  return () => {
@@ -493,92 +645,39 @@ function installRoute(deps) {
493
645
  return () => {
494
646
  };
495
647
  }
496
- let pushed = false;
497
- let closer = null;
498
- const teardownEntry = () => {
499
- if (closer) {
500
- const idx = closers.indexOf(closer);
501
- if (idx !== -1) closers.splice(idx, 1);
502
- }
503
- const wasConsumed = closer?.consumed ?? false;
504
- closer = null;
505
- if (pushed && !wasConsumed) {
506
- pushed = false;
507
- programmaticBack();
508
- } else {
509
- pushed = false;
510
- }
511
- };
512
- const pushEntry = () => {
513
- try {
514
- if (deps.routedTo !== void 0) {
515
- history.pushState({ __bsRouted: deps.sheetId }, "", deps.routedTo);
516
- } else {
517
- history.pushState({ __bsSheet: deps.sheetId }, "");
518
- }
519
- pushed = true;
520
- } catch {
521
- pushed = false;
522
- }
523
- };
648
+ let handle = null;
524
649
  const onOpen = () => {
525
- if (closer) return;
526
- const self = {
650
+ if (handle) return;
651
+ handle = pushBackMarker({
527
652
  isOpen: () => !deps.isDestroyed() && deps.getSize() > 0,
528
- isTop: () => deps.isTopSheet(),
529
- close: () => deps.close(),
530
- restore: () => {
531
- if (deps.isDestroyed() || closer !== self) return;
532
- self.consumed = false;
533
- if (!closers.includes(self)) closers.push(self);
534
- pushEntry();
535
- },
536
- consumed: false
537
- };
538
- closer = self;
539
- ensureHandler();
540
- closers.push(self);
541
- 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;
542
663
  };
543
664
  const unsubscribeOpen = deps.on("open", onOpen);
544
- const unsubscribeClose = deps.on("close", teardownEntry);
665
+ const unsubscribeClose = deps.on("close", onClose);
545
666
  if (deps.getSize() > 0) onOpen();
546
667
  return () => {
547
668
  unsubscribeOpen();
548
669
  unsubscribeClose();
549
- teardownEntry();
670
+ if (handle) {
671
+ popBackMarker(handle);
672
+ handle = null;
673
+ }
550
674
  };
551
675
  }
552
676
  function installRouteChange(deps) {
553
- if (typeof window === "undefined" || typeof history === "undefined") {
554
- return () => {
555
- };
556
- }
557
- const onChange = () => {
677
+ return subscribeRouteChange(() => {
558
678
  if (deps.isDestroyed() || deps.getSize() <= 0) return;
559
679
  void deps.close();
560
- };
561
- const wrap = (key) => {
562
- const original = history[key];
563
- const patched = function(...args) {
564
- original.apply(this, args);
565
- onChange();
566
- };
567
- history[key] = patched;
568
- return () => {
569
- if (history[key] === patched) {
570
- history[key] = original;
571
- }
572
- };
573
- };
574
- window.addEventListener("popstate", onChange);
575
- const restorePush = wrap("pushState");
576
- const restoreReplace = wrap("replaceState");
577
- return () => {
578
- window.removeEventListener("popstate", onChange);
579
- restorePush();
580
- restoreReplace();
581
- };
680
+ });
582
681
  }
583
682
 
584
683
  // src/core/features/content-swipe.ts
@@ -2430,7 +2529,7 @@ var matchesRange = (def, progress) => {
2430
2529
  };
2431
2530
  function installScrimStages(deps, opts) {
2432
2531
  const doc = deps.host.ownerDocument;
2433
- const entries = opts.stages.map((def) => {
2532
+ const entries2 = opts.stages.map((def) => {
2434
2533
  const wrapper = doc.createElement("div");
2435
2534
  wrapper.className = "bs-scrim-stage";
2436
2535
  const ws = wrapper.style;
@@ -2451,9 +2550,9 @@ function installScrimStages(deps, opts) {
2451
2550
  const resolveActive = () => {
2452
2551
  const state = deps.getState();
2453
2552
  if (state.size === 0) {
2454
- return entries.find((e) => matchesId(e.def, state.activeId)) ?? null;
2553
+ return entries2.find((e) => matchesId(e.def, state.activeId)) ?? null;
2455
2554
  }
2456
- return entries.find((e) => matchesId(e.def, state.activeId)) ?? entries.find((e) => matchesRange(e.def, state.progress)) ?? null;
2555
+ return entries2.find((e) => matchesId(e.def, state.activeId)) ?? entries2.find((e) => matchesRange(e.def, state.progress)) ?? null;
2457
2556
  };
2458
2557
  const hide = (entry, animate) => {
2459
2558
  entry.inFlight?.cancel();
@@ -2502,13 +2601,13 @@ function installScrimStages(deps, opts) {
2502
2601
  deps.on("open", () => update(true)),
2503
2602
  deps.on("close", () => update(true))
2504
2603
  ];
2505
- if (entries.some((e) => e.def.forRange)) {
2604
+ if (entries2.some((e) => e.def.forRange)) {
2506
2605
  offs.push(deps.on("progress", () => update(true)));
2507
2606
  }
2508
2607
  update(false);
2509
2608
  return () => {
2510
2609
  offs.forEach((off) => off());
2511
- for (const entry of entries) {
2610
+ for (const entry of entries2) {
2512
2611
  entry.inFlight?.cancel();
2513
2612
  entry.inFlight = null;
2514
2613
  if (entry.wrapper.parentNode) {
@@ -2765,8 +2864,8 @@ var BottomSheetEngine = class {
2765
2864
  this.attach();
2766
2865
  this.registerInStack();
2767
2866
  if (this.snaps.getMaxAxisSize() === 0 && typeof IntersectionObserver !== "undefined" && typeof document !== "undefined") {
2768
- const io = new IntersectionObserver((entries) => {
2769
- for (const entry of entries) {
2867
+ const io = new IntersectionObserver((entries2) => {
2868
+ for (const entry of entries2) {
2770
2869
  if (!entry.isIntersecting) continue;
2771
2870
  if (this.destroyed) return;
2772
2871
  this.snaps.recompute();
@@ -3483,9 +3582,9 @@ var BottomSheetEngine = class {
3483
3582
  return;
3484
3583
  }
3485
3584
  if (this.emitBeforeSnap(target, previousId)) {
3486
- const restore = this.snaps.findById(previousId);
3487
- if (restore) {
3488
- void this.animation.animateTo(restore.size, 0);
3585
+ const restore2 = this.snaps.findById(previousId);
3586
+ if (restore2) {
3587
+ void this.animation.animateTo(restore2.size, 0);
3489
3588
  }
3490
3589
  return;
3491
3590
  }