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