@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.
@@ -438,46 +438,198 @@ 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 runInternal = (fn) => {
456
+ internalDepth += 1;
457
+ try {
458
+ fn();
459
+ } finally {
460
+ internalDepth -= 1;
461
+ }
462
+ };
463
+ var notifyRouteChange = () => {
464
+ const snapshot = Array.from(subscribers);
465
+ for (const fn of snapshot) fn();
466
+ };
467
+ var ensureListener = () => {
468
+ if (listenerInstalled || !hasWindow()) return;
469
+ window.addEventListener("popstate", onPopState);
470
+ listenerInstalled = true;
471
+ };
472
+ var maybeTeardownListener = () => {
473
+ if (!listenerInstalled) return;
474
+ if (entries.length > 0 || subscribers.size > 0 || suppress > 0) return;
475
+ if (hasWindow()) window.removeEventListener("popstate", onPopState);
476
+ listenerInstalled = false;
477
+ };
478
+ var installPatch = () => {
479
+ if (patchInstalled || !hasHistory()) return;
480
+ origPushState = history.pushState;
481
+ origReplaceState = history.replaceState;
482
+ patchedPushState = function(...args) {
483
+ origPushState.apply(this, args);
484
+ if (internalDepth === 0) notifyRouteChange();
485
+ };
486
+ patchedReplaceState = function(...args) {
487
+ origReplaceState.apply(this, args);
488
+ if (internalDepth === 0) notifyRouteChange();
489
+ };
490
+ history.pushState = patchedPushState;
491
+ history.replaceState = patchedReplaceState;
492
+ patchInstalled = true;
493
+ };
494
+ var removePatch = () => {
495
+ if (!patchInstalled) return;
496
+ if (history.pushState === patchedPushState && origPushState) {
497
+ history.pushState = origPushState;
498
+ }
499
+ if (history.replaceState === patchedReplaceState && origReplaceState) {
500
+ history.replaceState = origReplaceState;
501
+ }
502
+ origPushState = null;
503
+ origReplaceState = null;
504
+ patchedPushState = null;
505
+ patchedReplaceState = null;
506
+ patchInstalled = false;
507
+ };
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;
452
530
  }
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
- }
531
+ if (entry.surface.isTop()) {
532
+ target = entry;
533
+ targetIdx = i;
534
+ break;
462
535
  }
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
- });
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);
471
548
  });
472
- };
473
- var programmaticBack = () => {
474
- suppress += 1;
475
- try {
476
- history.back();
477
- } catch {
478
- suppress = Math.max(0, suppress - 1);
549
+ maybeTeardownListener();
550
+ }
551
+ function restore(entry) {
552
+ if (!hasWindow() || !hasHistory()) return;
553
+ entry.live = true;
554
+ runInternal(() => {
555
+ try {
556
+ history.pushState(entry.surface.markerState(), "", entry.surface.url);
557
+ } catch {
558
+ entry.live = false;
559
+ }
560
+ });
561
+ if (entry.live) {
562
+ entries.push(entry);
563
+ ensureListener();
479
564
  }
480
- };
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(surface.markerState(), "", 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
+ rebrandSurface.markerState(),
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
+
632
+ // src/core/features/route.ts
481
633
  function installRoute(deps) {
482
634
  if (typeof window === "undefined") {
483
635
  return () => {
@@ -487,92 +639,39 @@ function installRoute(deps) {
487
639
  return () => {
488
640
  };
489
641
  }
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
- };
642
+ let handle = null;
518
643
  const onOpen = () => {
519
- if (closer) return;
520
- const self = {
644
+ if (handle) return;
645
+ handle = pushBackMarker({
521
646
  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();
647
+ isTop: deps.isTopSheet,
648
+ close: deps.close,
649
+ markerState: () => deps.routedTo !== void 0 ? { __bsRouted: deps.sheetId } : { __bsSheet: deps.sheetId },
650
+ url: deps.routedTo
651
+ });
652
+ };
653
+ const onClose = () => {
654
+ if (!handle) return;
655
+ popBackMarker(handle);
656
+ handle = null;
536
657
  };
537
658
  const unsubscribeOpen = deps.on("open", onOpen);
538
- const unsubscribeClose = deps.on("close", teardownEntry);
659
+ const unsubscribeClose = deps.on("close", onClose);
539
660
  if (deps.getSize() > 0) onOpen();
540
661
  return () => {
541
662
  unsubscribeOpen();
542
663
  unsubscribeClose();
543
- teardownEntry();
664
+ if (handle) {
665
+ popBackMarker(handle);
666
+ handle = null;
667
+ }
544
668
  };
545
669
  }
546
670
  function installRouteChange(deps) {
547
- if (typeof window === "undefined" || typeof history === "undefined") {
548
- return () => {
549
- };
550
- }
551
- const onChange = () => {
671
+ return subscribeRouteChange(() => {
552
672
  if (deps.isDestroyed() || deps.getSize() <= 0) return;
553
673
  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
- };
674
+ });
576
675
  }
577
676
 
578
677
  // src/core/features/content-swipe.ts
@@ -2425,7 +2524,7 @@ var matchesRange = (def, progress) => {
2425
2524
  };
2426
2525
  function installScrimStages(deps, opts) {
2427
2526
  const doc = deps.host.ownerDocument;
2428
- const entries = opts.stages.map((def) => {
2527
+ const entries2 = opts.stages.map((def) => {
2429
2528
  const wrapper = doc.createElement("div");
2430
2529
  wrapper.className = "bs-scrim-stage";
2431
2530
  const ws = wrapper.style;
@@ -2446,9 +2545,9 @@ function installScrimStages(deps, opts) {
2446
2545
  const resolveActive = () => {
2447
2546
  const state = deps.getState();
2448
2547
  if (state.size === 0) {
2449
- return entries.find((e) => matchesId(e.def, state.activeId)) ?? null;
2548
+ return entries2.find((e) => matchesId(e.def, state.activeId)) ?? null;
2450
2549
  }
2451
- return entries.find((e) => matchesId(e.def, state.activeId)) ?? entries.find((e) => matchesRange(e.def, state.progress)) ?? null;
2550
+ return entries2.find((e) => matchesId(e.def, state.activeId)) ?? entries2.find((e) => matchesRange(e.def, state.progress)) ?? null;
2452
2551
  };
2453
2552
  const hide = (entry, animate) => {
2454
2553
  entry.inFlight?.cancel();
@@ -2497,13 +2596,13 @@ function installScrimStages(deps, opts) {
2497
2596
  deps.on("open", () => update(true)),
2498
2597
  deps.on("close", () => update(true))
2499
2598
  ];
2500
- if (entries.some((e) => e.def.forRange)) {
2599
+ if (entries2.some((e) => e.def.forRange)) {
2501
2600
  offs.push(deps.on("progress", () => update(true)));
2502
2601
  }
2503
2602
  update(false);
2504
2603
  return () => {
2505
2604
  offs.forEach((off) => off());
2506
- for (const entry of entries) {
2605
+ for (const entry of entries2) {
2507
2606
  entry.inFlight?.cancel();
2508
2607
  entry.inFlight = null;
2509
2608
  if (entry.wrapper.parentNode) {
@@ -2762,8 +2861,8 @@ var BottomSheetEngine = class {
2762
2861
  this.attach();
2763
2862
  this.registerInStack();
2764
2863
  if (this.snaps.getMaxAxisSize() === 0 && typeof IntersectionObserver !== "undefined" && typeof document !== "undefined") {
2765
- const io = new IntersectionObserver((entries) => {
2766
- for (const entry of entries) {
2864
+ const io = new IntersectionObserver((entries2) => {
2865
+ for (const entry of entries2) {
2767
2866
  if (!entry.isIntersecting) continue;
2768
2867
  if (this.destroyed) return;
2769
2868
  this.snaps.recompute();
@@ -3480,9 +3579,9 @@ var BottomSheetEngine = class {
3480
3579
  return;
3481
3580
  }
3482
3581
  if (this.emitBeforeSnap(target, previousId)) {
3483
- const restore = this.snaps.findById(previousId);
3484
- if (restore) {
3485
- void this.animation.animateTo(restore.size, 0);
3582
+ const restore2 = this.snaps.findById(previousId);
3583
+ if (restore2) {
3584
+ void this.animation.animateTo(restore2.size, 0);
3486
3585
  }
3487
3586
  return;
3488
3587
  }