@surdeddd/bottom-sheet 0.23.4 → 0.24.1

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 (80) hide show
  1. package/README.md +513 -527
  2. package/README.ru.md +533 -545
  3. package/dist/{BottomSheetCore-C5tJYVMY.d.cts → BottomSheetCore-B_r4KCQL.d.cts} +48 -13
  4. package/dist/{BottomSheetCore-DxSrC4QC.d.ts → BottomSheetCore-B_r4KCQL.d.ts} +48 -13
  5. package/dist/{BottomSheetEngine-B3F9qukX.d.cts → BottomSheetEngine-0F7mjyvF.d.cts} +1 -1
  6. package/dist/{BottomSheetEngine-BchzgqGG.d.ts → BottomSheetEngine-CJV_DDrM.d.ts} +1 -1
  7. package/dist/core.cjs +810 -436
  8. package/dist/core.d.cts +2 -3
  9. package/dist/core.d.ts +2 -3
  10. package/dist/core.js +810 -436
  11. package/dist/debug.d.cts +0 -1
  12. package/dist/debug.d.ts +0 -1
  13. package/dist/{default-features-DwVTzDwP.d.ts → default-features-4Xwv6aVt.d.ts} +4 -2
  14. package/dist/{default-features-BGiCSmV1.d.cts → default-features-DX2NOxP6.d.cts} +4 -2
  15. package/dist/element.cjs +1421 -697
  16. package/dist/element.d.cts +11 -3
  17. package/dist/element.d.ts +11 -3
  18. package/dist/element.iife.global.js +2 -2
  19. package/dist/element.js +1421 -697
  20. package/dist/features.cjs +334 -190
  21. package/dist/features.d.cts +3 -4
  22. package/dist/features.d.ts +3 -4
  23. package/dist/features.js +334 -191
  24. package/dist/{index-C7TSQkVK.d.cts → index-2shlxhcG.d.cts} +1 -1
  25. package/dist/{index-CdM1tke5.d.ts → index-CNCRQnpc.d.ts} +1 -1
  26. package/dist/index.cjs +1129 -631
  27. package/dist/index.d.cts +11 -6
  28. package/dist/index.d.ts +11 -6
  29. package/dist/index.js +1129 -632
  30. package/dist/integrations/formik.cjs +21 -10
  31. package/dist/integrations/formik.d.cts +0 -1
  32. package/dist/integrations/formik.d.ts +0 -1
  33. package/dist/integrations/formik.js +22 -11
  34. package/dist/integrations/react-hook-form.cjs +21 -12
  35. package/dist/integrations/react-hook-form.d.cts +0 -1
  36. package/dist/integrations/react-hook-form.d.ts +0 -1
  37. package/dist/integrations/react-hook-form.js +21 -12
  38. package/dist/option-keys-DItuAjUw.d.cts +6 -0
  39. package/dist/option-keys-a5DYNwwO.d.ts +6 -0
  40. package/dist/overlay.cjs +386 -189
  41. package/dist/overlay.d.cts +1 -3
  42. package/dist/overlay.d.ts +1 -3
  43. package/dist/overlay.js +386 -189
  44. package/dist/preact.d.cts +3 -4
  45. package/dist/preact.d.ts +3 -4
  46. package/dist/qwik-lib/index.qwik.cjs +1306 -671
  47. package/dist/qwik-lib/index.qwik.mjs +1306 -671
  48. package/dist/qwik.cjs +1326 -701
  49. package/dist/qwik.d.cts +12 -30
  50. package/dist/qwik.d.ts +12 -30
  51. package/dist/qwik.js +1326 -701
  52. package/dist/react.cjs +1373 -750
  53. package/dist/react.d.cts +26 -12
  54. package/dist/react.d.ts +26 -12
  55. package/dist/react.js +1375 -752
  56. package/dist/solid.cjs +1332 -727
  57. package/dist/solid.d.cts +13 -438
  58. package/dist/solid.d.ts +13 -438
  59. package/dist/solid.js +1334 -729
  60. package/dist/solid.source.jsx +1374 -751
  61. package/dist/styles.css +20 -6
  62. package/dist/svelte-core.cjs +1209 -676
  63. package/dist/svelte-core.js +1207 -677
  64. package/dist/svelte-src/BottomSheet.svelte +101 -58
  65. package/dist/svelte.cjs +1323 -705
  66. package/dist/svelte.d.cts +45 -25
  67. package/dist/svelte.d.ts +45 -25
  68. package/dist/svelte.js +1323 -705
  69. package/dist/vue.cjs +1354 -773
  70. package/dist/vue.d.cts +20 -3
  71. package/dist/vue.d.ts +20 -3
  72. package/dist/vue.js +1354 -773
  73. package/dist/webgl.cjs +159 -53
  74. package/dist/webgl.d.cts +2 -2
  75. package/dist/webgl.d.ts +2 -2
  76. package/dist/webgl.js +159 -53
  77. package/llms.txt +330 -0
  78. package/package.json +42 -20
  79. package/dist/transform-jVfAikE9.d.cts +0 -3
  80. package/dist/transform-jVfAikE9.d.ts +0 -3
package/dist/overlay.js CHANGED
@@ -2,6 +2,15 @@ var __defProp = Object.defineProperty;
2
2
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
3
3
  var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
4
4
 
5
+ // src/core/primitives/shared.ts
6
+ var STORE_KEY = /* @__PURE__ */ Symbol.for("@surdeddd/bottom-sheet/shared/1");
7
+ function shared(name, init) {
8
+ const host = globalThis;
9
+ const store = host[STORE_KEY] ?? (host[STORE_KEY] = {});
10
+ if (!(name in store)) store[name] = init();
11
+ return store[name];
12
+ }
13
+
5
14
  // src/core/lifecycle/sheet-stack.ts
6
15
  var BASE_Z = 100;
7
16
  var STEP = 10;
@@ -69,10 +78,25 @@ var SheetStack = class {
69
78
  });
70
79
  }
71
80
  };
72
- var sheetStack = new SheetStack();
81
+ var sheetStack = shared("stack", () => new SheetStack());
82
+
83
+ // src/core/primitives/dev-warn.ts
84
+ function devWarn(...args) {
85
+ if (typeof process !== "undefined" && process.env && process.env.NODE_ENV !== "production" && typeof console !== "undefined") {
86
+ console.warn(...args);
87
+ }
88
+ }
73
89
 
74
90
  // src/core/primitives/css-length.ts
75
91
  var probe = null;
92
+ var rejected = /* @__PURE__ */ new Set();
93
+ var warnRejected = (value) => {
94
+ if (rejected.has(value)) return;
95
+ rejected.add(value);
96
+ devWarn(
97
+ `[BottomSheet] snap size "${value}" is not a CSS length the browser accepts \u2014 it resolves to 0.`
98
+ );
99
+ };
76
100
  var PROBE_ATTR = "data-bs-probe";
77
101
  var ensureProbe = (axis) => {
78
102
  if (typeof document === "undefined") return null;
@@ -114,19 +138,13 @@ var resolveSnap = (point, mode, measureFit) => {
114
138
  if (!el) return 0;
115
139
  {
116
140
  el.style.height = point;
141
+ if (el.style.height === "") warnRejected(point);
117
142
  return Math.max(0, el.getBoundingClientRect().height);
118
143
  }
119
144
  };
120
145
 
121
- // src/core/primitives/dev-warn.ts
122
- function devWarn(...args) {
123
- if (typeof process !== "undefined" && process.env && process.env.NODE_ENV !== "production" && typeof console !== "undefined") {
124
- console.warn(...args);
125
- }
126
- }
127
-
128
146
  // src/core/primitives/instance-id.ts
129
- var counters = {};
147
+ var counters = shared("instanceIds", () => ({}));
130
148
  function nextInstanceId(prefix) {
131
149
  if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
132
150
  return `${prefix}-${crypto.randomUUID()}`;
@@ -154,7 +172,13 @@ function createEventBus() {
154
172
  const set = listeners.get(event);
155
173
  if (!set) return;
156
174
  for (const fn of [...set]) {
157
- fn(payload);
175
+ try {
176
+ fn(payload);
177
+ } catch (err) {
178
+ queueMicrotask(() => {
179
+ throw err;
180
+ });
181
+ }
158
182
  }
159
183
  },
160
184
  listenerCount(event) {
@@ -201,27 +225,51 @@ var collectFocusables = (root) => {
201
225
  var focusables = (root) => collectFocusables(root).filter(
202
226
  (el) => !el.hasAttribute("inert") && isVisible(el) && !el.matches("[aria-hidden='true']")
203
227
  );
228
+ var findDeep = (root, selector) => {
229
+ const direct = root.querySelector(selector);
230
+ if (direct || typeof HTMLSlotElement === "undefined") return direct;
231
+ for (const slot of Array.from(root.querySelectorAll("slot"))) {
232
+ for (const assigned of slot.assignedElements()) {
233
+ if (assigned.matches(selector)) return assigned;
234
+ const inner = findDeep(assigned, selector);
235
+ if (inner) return inner;
236
+ }
237
+ }
238
+ return null;
239
+ };
240
+ var FOCUS_ALLOW_ATTR = "data-bs-focus-allow";
241
+ var composedParent = (node) => {
242
+ const slot = node.assignedSlot;
243
+ if (slot) return slot;
244
+ const parent = node.parentNode;
245
+ return parent instanceof ShadowRoot ? parent.host : parent;
246
+ };
204
247
  var containsComposed = (container, node) => {
205
- let n = node;
206
- while (n) {
248
+ for (let n = node; n; n = composedParent(n)) {
207
249
  if (n === container) return true;
208
- const slot = n.assignedSlot;
209
- if (slot) {
210
- n = slot;
211
- continue;
212
- }
213
- const parent = n.parentNode;
214
- n = parent instanceof ShadowRoot ? parent.host : parent;
215
250
  }
216
251
  return false;
217
252
  };
218
- var trapStack = [];
253
+ var isAllowedOutside = (node) => {
254
+ for (let n = node; n; n = composedParent(n)) {
255
+ if (n instanceof Element && n.hasAttribute(FOCUS_ALLOW_ATTR)) return true;
256
+ }
257
+ return false;
258
+ };
259
+ var deepActiveElement = () => {
260
+ let active = document.activeElement;
261
+ while (active?.shadowRoot?.activeElement) {
262
+ active = active.shadowRoot.activeElement;
263
+ }
264
+ return active;
265
+ };
266
+ var trapStack = shared("focusTraps", () => []);
219
267
  var installFocusTrap = (container, options = {}) => {
220
268
  if (typeof document === "undefined") return () => {
221
269
  };
222
270
  const token = {};
223
- trapStack.push(token);
224
- const isActive = () => trapStack[trapStack.length - 1] === token;
271
+ if (!options.isActive) trapStack.push(token);
272
+ const isActive = options.isActive ?? (() => trapStack[trapStack.length - 1] === token);
225
273
  const previouslyFocused = document.activeElement;
226
274
  const focusInitial = () => {
227
275
  const spec = options.initialFocus;
@@ -230,10 +278,11 @@ var installFocusTrap = (container, options = {}) => {
230
278
  container.focus({ preventScroll: true });
231
279
  return;
232
280
  }
233
- const initial = typeof spec === "string" ? container.querySelector(spec) : spec ?? focusables(container)[0] ?? container;
281
+ const initial = typeof spec === "string" ? findDeep(container, spec) : spec ?? focusables(container)[0] ?? container;
234
282
  initial?.focus({ preventScroll: true });
235
283
  };
236
- focusInitial();
284
+ if (!containsComposed(container, deepActiveElement())) focusInitial();
285
+ let backwards = false;
237
286
  const handleKey = (e) => {
238
287
  if (!isActive()) return;
239
288
  if (e.key === "Escape" && options.onEscape) {
@@ -241,6 +290,10 @@ var installFocusTrap = (container, options = {}) => {
241
290
  return;
242
291
  }
243
292
  if (e.key !== "Tab") return;
293
+ backwards = e.shiftKey;
294
+ setTimeout(() => {
295
+ backwards = false;
296
+ }, 0);
244
297
  const list = focusables(container);
245
298
  if (list.length === 0) {
246
299
  e.preventDefault();
@@ -248,7 +301,7 @@ var installFocusTrap = (container, options = {}) => {
248
301
  }
249
302
  const first = list[0];
250
303
  const last = list[list.length - 1];
251
- const active = document.activeElement;
304
+ const active = deepActiveElement();
252
305
  if (e.shiftKey && active === first) {
253
306
  e.preventDefault();
254
307
  last.focus();
@@ -259,11 +312,11 @@ var installFocusTrap = (container, options = {}) => {
259
312
  };
260
313
  const handleFocusIn = (e) => {
261
314
  if (!isActive()) return;
262
- const target = e.target;
263
- if (target && !containsComposed(container, target)) {
264
- const list = focusables(container);
265
- list[0]?.focus({ preventScroll: true });
266
- }
315
+ const target = e.composedPath()[0] ?? e.target;
316
+ if (!target || containsComposed(container, target)) return;
317
+ if (isAllowedOutside(target)) return;
318
+ const list = focusables(container);
319
+ list[backwards ? list.length - 1 : 0]?.focus({ preventScroll: true });
267
320
  };
268
321
  document.addEventListener("keydown", handleKey, true);
269
322
  document.addEventListener("focusin", handleFocusIn, true);
@@ -285,21 +338,25 @@ var installFocusTrap = (container, options = {}) => {
285
338
  };
286
339
 
287
340
  // src/core/lifecycle/scroll-lock.ts
288
- var lockCount = 0;
289
- var savedStyles = null;
341
+ var state = shared("scrollLock", () => ({
342
+ count: 0,
343
+ saved: null
344
+ }));
290
345
  var lockBodyScroll = () => {
291
346
  if (typeof document === "undefined") return () => {
292
347
  };
293
- if (lockCount === 0) {
348
+ if (state.count === 0) {
294
349
  const body = document.body;
295
- savedStyles = {
350
+ const saved = {
296
351
  overflow: body.style.overflow,
297
352
  position: body.style.position,
298
353
  top: body.style.top,
299
354
  width: body.style.width,
300
355
  paddingRight: body.style.paddingRight,
356
+ scrollX: window.scrollX,
301
357
  scrollY: window.scrollY
302
358
  };
359
+ state.saved = saved;
303
360
  const scrollbarGap = window.innerWidth - document.documentElement.clientWidth;
304
361
  if (scrollbarGap > 0) {
305
362
  const current = parseFloat(getComputedStyle(body).paddingRight) || 0;
@@ -307,53 +364,72 @@ var lockBodyScroll = () => {
307
364
  }
308
365
  body.style.overflow = "hidden";
309
366
  body.style.position = "fixed";
310
- body.style.top = `-${savedStyles.scrollY}px`;
367
+ body.style.top = `-${saved.scrollY}px`;
311
368
  body.style.width = "100%";
312
369
  }
313
- lockCount++;
370
+ state.count++;
314
371
  let released = false;
315
372
  return () => {
316
373
  if (released) return;
317
374
  released = true;
318
- lockCount = Math.max(0, lockCount - 1);
319
- if (lockCount === 0 && savedStyles) {
375
+ state.count = Math.max(0, state.count - 1);
376
+ if (state.count === 0 && state.saved) {
320
377
  const body = document.body;
321
- body.style.overflow = savedStyles.overflow;
322
- body.style.position = savedStyles.position;
323
- body.style.top = savedStyles.top;
324
- body.style.width = savedStyles.width;
325
- body.style.paddingRight = savedStyles.paddingRight;
326
- window.scrollTo(0, savedStyles.scrollY);
327
- savedStyles = null;
378
+ const saved = state.saved;
379
+ body.style.overflow = saved.overflow;
380
+ body.style.position = saved.position;
381
+ body.style.top = saved.top;
382
+ body.style.width = saved.width;
383
+ body.style.paddingRight = saved.paddingRight;
384
+ window.scrollTo({
385
+ top: saved.scrollY,
386
+ left: saved.scrollX,
387
+ behavior: "instant"
388
+ });
389
+ state.saved = null;
328
390
  }
329
391
  };
330
392
  };
331
393
 
332
394
  // src/core/features/inert-siblings.ts
395
+ var OWN_ROOTS = ".bs-root, .ovl-root";
396
+ var LAYER_CLASSES = [
397
+ "bs-backdrop",
398
+ "bs-screen",
399
+ "bs-root",
400
+ "ovl-backdrop",
401
+ "ovl-root"
402
+ ];
403
+ var isLayer = (el) => LAYER_CLASSES.some((name) => el.classList.contains(name));
333
404
  function createInertSiblings(rootProvider) {
334
405
  const tracked = [];
406
+ const remove = () => {
407
+ for (const el of tracked) el.removeAttribute("inert");
408
+ tracked.length = 0;
409
+ };
335
410
  return {
336
411
  apply() {
337
412
  if (typeof document === "undefined") return;
338
- let top = rootProvider();
339
- while (top.parentElement && top.parentElement !== document.body) {
340
- top = top.parentElement;
341
- }
342
- const parent = top.parentElement ?? document.body;
343
- for (const child of Array.from(parent.children)) {
344
- if (child === top) continue;
345
- if (child.hasAttribute("inert")) continue;
346
- if (child.classList.contains("bs-backdrop") || child.classList.contains("bs-screen") || child.classList.contains("bs-root")) {
413
+ remove();
414
+ const surface = rootProvider();
415
+ let node = surface.closest(OWN_ROOTS) ?? surface;
416
+ while (node && node !== document.body) {
417
+ const parent = node.parentElement;
418
+ if (!parent) {
419
+ const root = node.getRootNode();
420
+ node = typeof ShadowRoot !== "undefined" && root instanceof ShadowRoot ? root.host : null;
347
421
  continue;
348
422
  }
349
- child.setAttribute("inert", "");
350
- tracked.push(child);
423
+ for (const sibling of Array.from(parent.children)) {
424
+ if (sibling === node) continue;
425
+ if (sibling.hasAttribute("inert") || isLayer(sibling)) continue;
426
+ sibling.setAttribute("inert", "");
427
+ tracked.push(sibling);
428
+ }
429
+ node = parent;
351
430
  }
352
431
  },
353
- remove() {
354
- for (const el of tracked) el.removeAttribute("inert");
355
- tracked.length = 0;
356
- }
432
+ remove
357
433
  };
358
434
  }
359
435
 
@@ -361,6 +437,7 @@ function createInertSiblings(rootProvider) {
361
437
  var LifecycleController = class {
362
438
  constructor(deps, opts) {
363
439
  __publicField(this, "element");
440
+ __publicField(this, "isTop");
364
441
  __publicField(this, "focusTrapEnabled");
365
442
  __publicField(this, "initialFocus");
366
443
  __publicField(this, "closeOnEscape");
@@ -374,6 +451,7 @@ var LifecycleController = class {
374
451
  __publicField(this, "installed", false);
375
452
  __publicField(this, "destroyed", false);
376
453
  this.element = deps.element;
454
+ this.isTop = deps.isTop;
377
455
  this.focusTrapEnabled = opts.focusTrap ?? false;
378
456
  this.initialFocus = opts.initialFocus;
379
457
  this.closeOnEscape = opts.closeOnEscape ?? true;
@@ -394,12 +472,17 @@ var LifecycleController = class {
394
472
  }
395
473
  if (this.focusTrapEnabled && !this.releaseFocusTrap) {
396
474
  this.releaseFocusTrap = installFocusTrap(this.element, {
397
- initialFocus: this.initialFocus
475
+ initialFocus: this.initialFocus,
476
+ isActive: this.isTop
398
477
  });
399
478
  }
400
- if (this.inertSiblingsEnabled && this.shouldApplyInertSiblings()) {
401
- this.inertSiblings.apply();
402
- }
479
+ this.syncTop();
480
+ }
481
+ syncTop() {
482
+ if (!this.installed || !this.inertSiblingsEnabled) return;
483
+ if (!this.shouldApplyInertSiblings()) return;
484
+ if (this.isTop?.() ?? true) this.inertSiblings.apply();
485
+ else this.inertSiblings.remove();
403
486
  }
404
487
  setReturnFocus(target) {
405
488
  if (this.destroyed) return;
@@ -528,148 +611,266 @@ function installOverlaySwipe(deps) {
528
611
  }
529
612
 
530
613
  // src/core/features/history-coordinator.ts
531
- var entries = [];
532
- var subscribers = /* @__PURE__ */ new Set();
533
- var suppress = 0;
534
- var pendingRebrands = [];
535
- var listenerInstalled = false;
536
- var hasWindow = () => typeof window !== "undefined";
537
- var hasHistory = () => typeof history !== "undefined";
538
- var brandMarker = (surface) => ({
614
+ var MARKER = "__bs";
615
+ var STACK = "__bsStack";
616
+ var DEPTH = "__bsDepth";
617
+ var ROUTED = "__bsRouted";
618
+ var SETTLE_TIMEOUT_MS = 1e3;
619
+ var state2 = shared(
620
+ "history",
621
+ () => ({
622
+ live: [],
623
+ stackId: void 0,
624
+ baseUrl: void 0,
625
+ position: null,
626
+ knownLength: 0,
627
+ href: "",
628
+ subscribers: /* @__PURE__ */ new Set(),
629
+ internal: 0,
630
+ pending: [],
631
+ deferred: [],
632
+ waiters: [],
633
+ timer: null,
634
+ listening: false,
635
+ leftovers: false,
636
+ orphanChecked: false,
637
+ push: null,
638
+ replace: null
639
+ })
640
+ );
641
+ var hasHistory = () => typeof window !== "undefined" && typeof history !== "undefined";
642
+ var plain = (value) => value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
643
+ var depthOf = (value) => {
644
+ const depth = value?.[DEPTH];
645
+ return typeof depth === "number" ? depth : null;
646
+ };
647
+ var isMarker = (value) => value?.[MARKER] === true;
648
+ var inStack = (value) => value !== null && state2.stackId !== void 0 && value[STACK] === state2.stackId && depthOf(value) !== null;
649
+ var foreignKeys = (value) => {
650
+ const out = {};
651
+ const source = plain(value);
652
+ if (!source) return out;
653
+ for (const key of Object.keys(source)) {
654
+ if (!key.startsWith(MARKER)) out[key] = source[key];
655
+ }
656
+ return out;
657
+ };
658
+ var markerFor = (surface, depth) => ({
659
+ ...foreignKeys(history.state),
539
660
  ...surface.markerState(),
540
- __bs: true
661
+ [MARKER]: true,
662
+ [STACK]: state2.stackId,
663
+ [DEPTH]: depth
541
664
  });
665
+ var urlForDepth = (depth) => {
666
+ for (let i = depth - 1; i >= 0; i -= 1) {
667
+ const url = state2.live[i]?.surface.url;
668
+ if (url !== void 0) return url;
669
+ }
670
+ return state2.baseUrl;
671
+ };
542
672
  var runInternal = (fn) => {
673
+ state2.internal += 1;
543
674
  try {
544
675
  fn();
676
+ return true;
677
+ } catch {
678
+ return false;
545
679
  } finally {
680
+ state2.internal -= 1;
546
681
  }
547
682
  };
548
683
  var notifyRouteChange = () => {
549
- const snapshot = Array.from(subscribers);
550
- for (const fn of snapshot) fn();
684
+ for (const fn of Array.from(state2.subscribers)) {
685
+ try {
686
+ fn();
687
+ } catch (err) {
688
+ queueMicrotask(() => {
689
+ throw err;
690
+ });
691
+ }
692
+ }
551
693
  };
552
694
  var ensureListener = () => {
553
- if (listenerInstalled || !hasWindow()) return;
695
+ if (state2.listening || !hasHistory()) return;
554
696
  window.addEventListener("popstate", onPopState);
555
- listenerInstalled = true;
697
+ state2.listening = true;
556
698
  };
557
699
  var maybeTeardownListener = () => {
558
- if (!listenerInstalled) return;
559
- if (entries.length > 0 || subscribers.size > 0 || suppress > 0) return;
560
- if (hasWindow()) window.removeEventListener("popstate", onPopState);
561
- listenerInstalled = false;
562
- };
563
- function onPopState() {
564
- if (suppress > 0) {
565
- suppress -= 1;
566
- const rebrand = pendingRebrands.shift();
567
- if (rebrand) rebrand();
568
- maybeTeardownListener();
569
- return;
570
- }
571
- if (entries.length === 0) {
572
- notifyRouteChange();
573
- maybeTeardownListener();
700
+ if (!state2.listening) return;
701
+ if (state2.live.length > 0 || state2.subscribers.size > 0 || state2.pending.length > 0 || state2.leftovers) {
574
702
  return;
575
703
  }
576
- let target = null;
577
- let targetIdx = -1;
578
- for (let i = entries.length - 1; i >= 0; i -= 1) {
579
- const entry = entries[i];
580
- if (!entry.surface.isOpen()) continue;
581
- if (!target) {
582
- target = entry;
583
- targetIdx = i;
584
- }
585
- if (entry.surface.isTop()) {
586
- target = entry;
587
- targetIdx = i;
588
- break;
589
- }
590
- }
591
- if (!target) {
592
- const removed = entries.pop();
593
- if (removed) removed.live = false;
704
+ if (hasHistory()) window.removeEventListener("popstate", onPopState);
705
+ state2.listening = false;
706
+ };
707
+ var settleIfIdle = () => {
708
+ if (state2.pending.length > 0) return;
709
+ if (state2.timer !== null) {
710
+ clearTimeout(state2.timer);
711
+ state2.timer = null;
712
+ }
713
+ for (const entry of state2.deferred.splice(0)) pushEntry(entry);
714
+ for (const resolve of state2.waiters.splice(0)) resolve();
715
+ };
716
+ var traverse = (delta, kind) => {
717
+ state2.pending.push(kind);
718
+ if (state2.timer !== null) clearTimeout(state2.timer);
719
+ state2.timer = setTimeout(() => {
720
+ state2.timer = null;
721
+ state2.pending.length = 0;
722
+ settleIfIdle();
594
723
  maybeTeardownListener();
595
- return;
596
- }
597
- target.live = false;
598
- entries.splice(targetIdx, 1);
599
- const settled = target;
600
- void Promise.resolve(settled.surface.close()).then(() => {
601
- if (settled.surface.isOpen()) restore(settled);
724
+ }, SETTLE_TIMEOUT_MS);
725
+ ensureListener();
726
+ const moved = runInternal(() => {
727
+ if (delta < 0) history.back();
728
+ else history.forward();
602
729
  });
603
- maybeTeardownListener();
604
- }
605
- function restore(entry) {
606
- if (!hasWindow() || !hasHistory()) return;
607
- entry.live = true;
730
+ if (!moved) {
731
+ state2.pending.pop();
732
+ settleIfIdle();
733
+ }
734
+ };
735
+ var newStackId = () => `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
736
+ var startStack = () => {
737
+ state2.stackId = newStackId();
738
+ const current = history.state;
739
+ const leftover = plain(current);
740
+ state2.baseUrl = isMarker(leftover) && leftover?.[ROUTED] !== void 0 ? void 0 : location.href;
741
+ if (isMarker(leftover)) return;
742
+ if (current !== null && current !== void 0 && !leftover) return;
608
743
  runInternal(() => {
609
- try {
610
- history.pushState(brandMarker(entry.surface), "", entry.surface.url);
611
- } catch {
612
- entry.live = false;
613
- }
744
+ history.replaceState(
745
+ { ...foreignKeys(current), [STACK]: state2.stackId, [DEPTH]: 0 },
746
+ "",
747
+ location.href
748
+ );
614
749
  });
615
- if (entry.live) {
616
- entries.push(entry);
617
- ensureListener();
618
- }
750
+ };
751
+ function pushEntry(entry) {
752
+ const depth = state2.live.length + 1;
753
+ if (state2.live.length === 0) startStack();
754
+ const written = runInternal(() => {
755
+ history.pushState(markerFor(entry.surface, depth), "", entry.surface.url);
756
+ });
757
+ if (!written) return false;
758
+ state2.live.push(entry);
759
+ state2.position = depth;
760
+ state2.knownLength = history.length;
761
+ state2.href = location.href;
762
+ ensureListener();
763
+ return true;
619
764
  }
620
- function pushBackMarker(surface) {
621
- const entry = { surface, priorUrl: void 0, live: false };
622
- if (!hasWindow() || !hasHistory()) {
623
- return entry;
765
+ var enlist = (entry) => {
766
+ if (state2.pending.length > 0) state2.deferred.push(entry);
767
+ else pushEntry(entry);
768
+ };
769
+ var sameBrand = (a, b) => {
770
+ for (const key of /* @__PURE__ */ new Set([...Object.keys(a), ...Object.keys(b)])) {
771
+ if (key.startsWith(MARKER) && a[key] !== b[key]) return false;
624
772
  }
625
- entry.priorUrl = surface.url !== void 0 ? location.href : void 0;
773
+ return true;
774
+ };
775
+ var rebrand = (depth) => {
776
+ const entry = state2.live[depth - 1];
777
+ if (!entry) return;
778
+ const url = urlForDepth(depth);
779
+ const marker = markerFor(entry.surface, depth);
780
+ const atUrl = url === void 0 || new URL(url, location.href).href === location.href;
781
+ if (atUrl && sameBrand(plain(history.state) ?? {}, marker)) return;
626
782
  runInternal(() => {
783
+ history.replaceState(marker, "", url);
784
+ });
785
+ state2.href = location.href;
786
+ };
787
+ var closeAbove = (depth) => {
788
+ const dropped = state2.live.splice(depth);
789
+ for (let i = dropped.length - 1; i >= 0; i -= 1) {
790
+ const entry = dropped[i];
791
+ let closing = void 0;
627
792
  try {
628
- history.pushState(brandMarker(surface), "", surface.url);
629
- entry.live = true;
630
- } catch {
631
- entry.live = false;
793
+ closing = entry.surface.close();
794
+ } catch (err) {
795
+ queueMicrotask(() => {
796
+ throw err;
797
+ });
632
798
  }
633
- });
634
- if (entry.live) {
635
- entries.push(entry);
636
- ensureListener();
799
+ void Promise.resolve(closing).then(() => {
800
+ if (!entry.surface.isOpen()) return;
801
+ const known = (e) => e.surface === entry.surface;
802
+ if (state2.live.some(known) || state2.deferred.some(known)) return;
803
+ enlist(entry);
804
+ });
805
+ }
806
+ };
807
+ function onPopState(event) {
808
+ const kind = state2.pending.shift();
809
+ const byUser = kind !== "pop";
810
+ const landed = plain(event.state);
811
+ const depth = depthOf(landed);
812
+ const from = state2.position;
813
+ const liveBefore = state2.live.length;
814
+ const grew = history.length > state2.knownLength;
815
+ state2.knownLength = history.length;
816
+ const within = inStack(landed);
817
+ if (within && depth !== null) {
818
+ state2.position = depth;
819
+ if (depth > state2.live.length) {
820
+ const forward = from !== null && from < depth;
821
+ traverse(forward ? 1 : -1, byUser ? "skip" : "pop");
822
+ } else if (depth < state2.live.length) {
823
+ if (byUser) closeAbove(depth);
824
+ } else {
825
+ rebrand(depth);
826
+ }
827
+ } else if (isMarker(landed)) {
828
+ state2.position = null;
829
+ traverse(-1, byUser ? "skip" : "pop");
830
+ } else {
831
+ state2.position = null;
832
+ if (byUser && !grew && state2.live.length > 0) {
833
+ closeAbove(state2.live.length - 1);
834
+ }
835
+ }
836
+ const moved = location.href !== state2.href;
837
+ state2.href = location.href;
838
+ if (kind === void 0 && moved && (liveBefore === 0 || !within)) {
839
+ notifyRouteChange();
637
840
  }
841
+ settleIfIdle();
842
+ maybeTeardownListener();
843
+ }
844
+ function pushBackMarker(surface) {
845
+ const entry = { surface };
846
+ if (hasHistory()) enlist(entry);
638
847
  return entry;
639
848
  }
640
849
  function popBackMarker(handle) {
641
- if (!hasWindow() || !hasHistory()) return;
850
+ if (!hasHistory()) return Promise.resolve();
642
851
  const entry = handle;
643
- const idx = entries.indexOf(entry);
644
- if (idx === -1 || !entry.live) return;
645
- const last = entries.length - 1;
646
- entry.live = false;
647
- entries.splice(idx, 1);
648
- let rebrand = null;
649
- if (idx !== last) {
650
- const rebrandSurface = entries[entries.length - 1].surface;
651
- const rebrandUrl = entry.priorUrl;
652
- rebrand = () => {
653
- runInternal(() => {
654
- history.replaceState(
655
- brandMarker(rebrandSurface),
656
- "",
657
- rebrandUrl !== void 0 ? rebrandUrl : location.href
658
- );
659
- });
660
- };
852
+ const waiting = state2.deferred.indexOf(entry);
853
+ if (waiting !== -1) state2.deferred.splice(waiting, 1);
854
+ const idx = state2.live.indexOf(entry);
855
+ if (idx === -1) return historySettled();
856
+ const top = state2.live.length;
857
+ state2.live.splice(idx, 1);
858
+ const current = plain(history.state);
859
+ const onTop = isMarker(current) && inStack(current) && depthOf(current) === top;
860
+ if (onTop && state2.pending.length === 0) {
861
+ traverse(-1, "pop");
862
+ } else {
863
+ state2.leftovers = true;
864
+ ensureListener();
661
865
  }
662
- pendingRebrands.push(rebrand);
663
- suppress += 1;
664
- runInternal(() => {
665
- try {
666
- history.back();
667
- } catch {
668
- suppress = Math.max(0, suppress - 1);
669
- pendingRebrands.pop();
670
- }
671
- });
672
866
  maybeTeardownListener();
867
+ return historySettled();
868
+ }
869
+ function historySettled() {
870
+ if (state2.pending.length === 0) return Promise.resolve();
871
+ return new Promise((resolve) => {
872
+ state2.waiters.push(resolve);
873
+ });
673
874
  }
674
875
 
675
876
  // src/core/overlay-options.ts
@@ -791,7 +992,8 @@ var OverlayEngine = class {
791
992
  this.lifecycle = new LifecycleController(
792
993
  {
793
994
  element: this.element,
794
- close: () => this.close("escape")
995
+ close: () => this.close("escape"),
996
+ isTop: () => this.isTop
795
997
  },
796
998
  {
797
999
  focusTrap: this.focusTrapEnabled,
@@ -799,12 +1001,7 @@ var OverlayEngine = class {
799
1001
  closeOnEscape: this.closeOnEscape,
800
1002
  lockBodyScroll: this.bodyScrollLockEnabled,
801
1003
  inertSiblings: this.inertSiblingsEnabled,
802
- shouldApplyInertSiblings: () => {
803
- const bodyChildren = Array.from(document.body.children);
804
- return bodyChildren.some(
805
- (c) => c === this.element || c.contains(this.element)
806
- );
807
- },
1004
+ shouldApplyInertSiblings: () => this.element.isConnected,
808
1005
  returnFocus: opts.returnFocus
809
1006
  }
810
1007
  );
@@ -821,9 +1018,9 @@ var OverlayEngine = class {
821
1018
  open() {
822
1019
  if (this.destroyed || this.isOpen_) return Promise.resolve();
823
1020
  this.maybeMount();
824
- this.emit("before-open", void 0);
825
1021
  this.isOpen_ = true;
826
1022
  this.isAnimating_ = true;
1023
+ this.emit("before-open", void 0);
827
1024
  sheetStack.promote(this.id);
828
1025
  this.element.removeAttribute("hidden");
829
1026
  this.element.setAttribute("data-state", "entering");
@@ -900,13 +1097,11 @@ var OverlayEngine = class {
900
1097
  }
901
1098
  close(reason = "programmatic") {
902
1099
  if (this.destroyed || !this.isOpen_) return Promise.resolve();
903
- this.emit("before-close", void 0);
904
1100
  this.isOpen_ = false;
905
1101
  this.isAnimating_ = true;
1102
+ this.emit("before-close", void 0);
906
1103
  sheetStack.update();
907
- if (reason !== "back" && this.backMarker) {
908
- popBackMarker(this.backMarker);
909
- }
1104
+ const historyDone = reason !== "back" && this.backMarker ? popBackMarker(this.backMarker) : Promise.resolve();
910
1105
  this.backMarker = null;
911
1106
  this.element.setAttribute("data-state", "exiting");
912
1107
  const dur = this.effectiveDuration();
@@ -917,7 +1112,7 @@ var OverlayEngine = class {
917
1112
  }
918
1113
  this.setWillChange(true);
919
1114
  const cycle = ++this.cycleNonce;
920
- return new Promise((resolve) => {
1115
+ const exited = new Promise((resolve) => {
921
1116
  requestAnimationFrame(() => {
922
1117
  if (this.destroyed || cycle !== this.cycleNonce) return resolve();
923
1118
  this.element.style.transform = closedTransformFor(
@@ -954,6 +1149,7 @@ var OverlayEngine = class {
954
1149
  });
955
1150
  });
956
1151
  });
1152
+ return Promise.all([exited, historyDone]).then(() => void 0);
957
1153
  }
958
1154
  toggle() {
959
1155
  if (this.destroyed) return Promise.resolve();
@@ -966,7 +1162,7 @@ var OverlayEngine = class {
966
1162
  this.setWillChange(false);
967
1163
  this.releaseInteractiveListeners();
968
1164
  if (this.backMarker) {
969
- popBackMarker(this.backMarker);
1165
+ void popBackMarker(this.backMarker);
970
1166
  this.backMarker = null;
971
1167
  }
972
1168
  this.releaseStackEntry?.();
@@ -1260,7 +1456,6 @@ var OverlayEngine = class {
1260
1456
  if (this.closeOnBackEnabled && this.backMarker === null && typeof window !== "undefined") {
1261
1457
  this.backMarker = pushBackMarker({
1262
1458
  isOpen: () => this.isOpen_,
1263
- isTop: () => this.isTop,
1264
1459
  close: () => this.close("back"),
1265
1460
  markerState: () => ({ __bsOverlay: this.id })
1266
1461
  });
@@ -1296,7 +1491,9 @@ var OverlayEngine = class {
1296
1491
  if (this.backdrop) this.backdrop.style.zIndex = String(z - 1);
1297
1492
  },
1298
1493
  setIsTop: (isTop) => {
1494
+ const changed = this.isTop !== isTop;
1299
1495
  this.isTop = isTop;
1496
+ if (changed) this.lifecycle?.syncTop();
1300
1497
  if (this.backdrop) {
1301
1498
  this.backdrop.style.display = isTop ? "" : "none";
1302
1499
  }