sibujs 1.4.0 → 2.0.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 (190) hide show
  1. package/README.md +105 -119
  2. package/dist/browser.cjs +288 -80
  3. package/dist/browser.d.cts +19 -9
  4. package/dist/browser.d.ts +19 -9
  5. package/dist/browser.js +6 -6
  6. package/dist/build.cjs +1019 -313
  7. package/dist/build.d.cts +1 -1
  8. package/dist/build.d.ts +1 -1
  9. package/dist/build.js +15 -13
  10. package/dist/cdn.global.js +17 -16
  11. package/dist/chunk-2RA7SHDA.js +65 -0
  12. package/dist/chunk-2UPRY23K.js +80 -0
  13. package/dist/chunk-3JHCYHWN.js +125 -0
  14. package/dist/{chunk-ZWKZCBO6.js → chunk-3LR7GLWQ.js} +154 -33
  15. package/dist/{chunk-3AIRKM3B.js → chunk-3NSGB5JN.js} +115 -34
  16. package/dist/{chunk-3ARAQO7B.js → chunk-52YJLLRO.js} +29 -6
  17. package/dist/chunk-54EDRCEF.js +93 -0
  18. package/dist/chunk-7JDB7I65.js +1327 -0
  19. package/dist/{chunk-WZSPOOER.js → chunk-CC65Y57T.js} +8 -5
  20. package/dist/{chunk-23VV7YD3.js → chunk-DFPFITST.js} +25 -30
  21. package/dist/{chunk-WR5D4EGH.js → chunk-GTBNNBJ6.js} +14 -2
  22. package/dist/chunk-HB24TBAF.js +121 -0
  23. package/dist/{chunk-CZUGLNJS.js → chunk-ITX6OO3F.js} +3 -3
  24. package/dist/{chunk-JAKHTMQU.js → chunk-JA6667UN.js} +206 -46
  25. package/dist/{chunk-77L6NL3X.js → chunk-JXMMDLBY.js} +306 -183
  26. package/dist/{chunk-3X2YG6YM.js → chunk-JYD2PWXH.js} +59 -28
  27. package/dist/{chunk-F3FA4F32.js → chunk-KLRMB5ZS.js} +135 -79
  28. package/dist/{chunk-5X6PP2UK.js → chunk-LMLD24FC.js} +2 -2
  29. package/dist/{chunk-M4NLBH4I.js → chunk-LYTCUZ7H.js} +3 -2
  30. package/dist/{chunk-TSOKIX5Z.js → chunk-MIUAXB7K.js} +126 -74
  31. package/dist/{chunk-QWZG56ET.js → chunk-ND2664SF.js} +558 -190
  32. package/dist/{chunk-JCI5M6U6.js → chunk-O2MNQFLP.js} +261 -79
  33. package/dist/{chunk-EWFVA3TJ.js → chunk-R73P76YZ.js} +1 -1
  34. package/dist/{chunk-2BYQDGN3.js → chunk-SAHNHTFC.js} +234 -63
  35. package/dist/chunk-UCS6AMJ7.js +79 -0
  36. package/dist/{chunk-ZD6OAMTH.js → chunk-VLPPXTYG.js} +90 -35
  37. package/dist/{chunk-OUZZEE4S.js → chunk-WOMYAHHI.js} +17 -11
  38. package/dist/{contracts-xo5ckdRP.d.cts → contracts-ey_Qh8ef.d.cts} +7 -8
  39. package/dist/{contracts-xo5ckdRP.d.ts → contracts-ey_Qh8ef.d.ts} +7 -8
  40. package/dist/{customElement-D2DJp_xn.d.cts → customElement-CPfIrbvg.d.cts} +18 -9
  41. package/dist/{customElement-D2DJp_xn.d.ts → customElement-CPfIrbvg.d.ts} +18 -9
  42. package/dist/data.cjs +452 -100
  43. package/dist/data.d.cts +20 -2
  44. package/dist/data.d.ts +20 -2
  45. package/dist/data.js +11 -9
  46. package/dist/devtools.cjs +535 -247
  47. package/dist/devtools.d.cts +1 -1
  48. package/dist/devtools.d.ts +1 -1
  49. package/dist/devtools.js +34 -30
  50. package/dist/ecosystem.cjs +499 -143
  51. package/dist/ecosystem.d.cts +13 -11
  52. package/dist/ecosystem.d.ts +13 -11
  53. package/dist/ecosystem.js +12 -11
  54. package/dist/extras.cjs +3639 -1629
  55. package/dist/extras.d.cts +11 -11
  56. package/dist/extras.d.ts +11 -11
  57. package/dist/extras.js +58 -45
  58. package/dist/index.cjs +1023 -313
  59. package/dist/index.d.cts +128 -55
  60. package/dist/index.d.ts +128 -55
  61. package/dist/index.js +28 -16
  62. package/dist/{introspect-BumjnBKr.d.cts → introspect-BWNjNw64.d.cts} +22 -2
  63. package/dist/{introspect-CZrlcaYy.d.ts → introspect-cY2pg9pW.d.ts} +22 -2
  64. package/dist/motion.cjs +90 -36
  65. package/dist/motion.d.cts +1 -1
  66. package/dist/motion.d.ts +1 -1
  67. package/dist/motion.js +4 -4
  68. package/dist/patterns.cjs +414 -81
  69. package/dist/patterns.d.cts +53 -20
  70. package/dist/patterns.d.ts +53 -20
  71. package/dist/patterns.js +7 -7
  72. package/dist/performance.cjs +364 -108
  73. package/dist/performance.d.cts +29 -17
  74. package/dist/performance.d.ts +29 -17
  75. package/dist/performance.js +13 -6
  76. package/dist/plugin-D30wlGW5.d.cts +71 -0
  77. package/dist/plugin-D30wlGW5.d.ts +71 -0
  78. package/dist/plugins.cjs +652 -271
  79. package/dist/plugins.d.cts +13 -6
  80. package/dist/plugins.d.ts +13 -6
  81. package/dist/plugins.js +116 -50
  82. package/dist/{ssr-Do_SiVoL.d.cts → ssr-CrVNy6Pa.d.cts} +9 -15
  83. package/dist/{ssr-Do_SiVoL.d.ts → ssr-CrVNy6Pa.d.ts} +9 -15
  84. package/dist/{ssr-4PBXAOO3.js → ssr-FXD2PPMC.js} +4 -3
  85. package/dist/ssr.cjs +648 -219
  86. package/dist/ssr.d.cts +27 -7
  87. package/dist/ssr.d.ts +27 -7
  88. package/dist/ssr.js +12 -11
  89. package/dist/{tagFactory-DaJ0YWX6.d.ts → tagFactory-S17H2qxu.d.cts} +9 -1
  90. package/dist/{tagFactory-DaJ0YWX6.d.cts → tagFactory-S17H2qxu.d.ts} +9 -1
  91. package/dist/testing.cjs +252 -63
  92. package/dist/testing.d.cts +17 -4
  93. package/dist/testing.d.ts +17 -4
  94. package/dist/testing.js +100 -44
  95. package/dist/ui.cjs +576 -168
  96. package/dist/ui.d.cts +13 -16
  97. package/dist/ui.d.ts +13 -16
  98. package/dist/ui.js +20 -17
  99. package/dist/widgets.cjs +1001 -93
  100. package/dist/widgets.d.cts +104 -2
  101. package/dist/widgets.d.ts +104 -2
  102. package/dist/widgets.js +9 -7
  103. package/package.json +8 -2
  104. package/dist/chunk-32DY64NT.js +0 -282
  105. package/dist/chunk-3CRQALYP.js +0 -877
  106. package/dist/chunk-4EI4AG32.js +0 -482
  107. package/dist/chunk-4MYMUBRS.js +0 -21
  108. package/dist/chunk-6HLLIF3K.js +0 -398
  109. package/dist/chunk-6LSNVCS2.js +0 -937
  110. package/dist/chunk-6SA3QQES.js +0 -61
  111. package/dist/chunk-7BF6TK55.js +0 -1097
  112. package/dist/chunk-7TQKR4PP.js +0 -294
  113. package/dist/chunk-7V26P53V.js +0 -712
  114. package/dist/chunk-AZ3ISID5.js +0 -298
  115. package/dist/chunk-B7SWRFUT.js +0 -332
  116. package/dist/chunk-BGN5ZMP4.js +0 -26
  117. package/dist/chunk-BTU3TJDS.js +0 -365
  118. package/dist/chunk-BW3WT46K.js +0 -937
  119. package/dist/chunk-C6KFWOFV.js +0 -616
  120. package/dist/chunk-CHF5OHIA.js +0 -61
  121. package/dist/chunk-CHJ27IGK.js +0 -26
  122. package/dist/chunk-CMBFNA7L.js +0 -27
  123. package/dist/chunk-DAHRH4ON.js +0 -331
  124. package/dist/chunk-DKOHBI74.js +0 -924
  125. package/dist/chunk-DTCOOBMX.js +0 -725
  126. package/dist/chunk-EBGIRKQY.js +0 -616
  127. package/dist/chunk-EUZND3CB.js +0 -27
  128. package/dist/chunk-EVCZO745.js +0 -365
  129. package/dist/chunk-FGOEVHY3.js +0 -60
  130. package/dist/chunk-G3BOQPVO.js +0 -365
  131. package/dist/chunk-GCOK2LC3.js +0 -282
  132. package/dist/chunk-HGMJFBC7.js +0 -654
  133. package/dist/chunk-K5ZUMYVS.js +0 -89
  134. package/dist/chunk-KQPDEVVS.js +0 -398
  135. package/dist/chunk-L6JRBDNS.js +0 -60
  136. package/dist/chunk-LA6KQEDU.js +0 -712
  137. package/dist/chunk-MDVXJWFN.js +0 -304
  138. package/dist/chunk-MEZVEBPN.js +0 -2008
  139. package/dist/chunk-MK4ERFYL.js +0 -2249
  140. package/dist/chunk-MLKGABMK.js +0 -9
  141. package/dist/chunk-MQ5GOYPH.js +0 -2249
  142. package/dist/chunk-N6IZB6KJ.js +0 -567
  143. package/dist/chunk-NEKUBFPT.js +0 -60
  144. package/dist/chunk-NHUC2QWH.js +0 -282
  145. package/dist/chunk-NMRUZALC.js +0 -1097
  146. package/dist/chunk-NYVAC6P5.js +0 -37
  147. package/dist/chunk-OF7UZIVB.js +0 -725
  148. package/dist/chunk-P6W3STU4.js +0 -2249
  149. package/dist/chunk-PBHF5WKN.js +0 -616
  150. package/dist/chunk-PTQJDMRT.js +0 -146
  151. package/dist/chunk-PZEGYCF5.js +0 -61
  152. package/dist/chunk-QBMDLBU2.js +0 -975
  153. package/dist/chunk-RQGQSLQK.js +0 -725
  154. package/dist/chunk-SDLZDHKP.js +0 -107
  155. package/dist/chunk-TNQWPPE6.js +0 -37
  156. package/dist/chunk-UHNL42EF.js +0 -2730
  157. package/dist/chunk-UNXCEF6S.js +0 -21
  158. package/dist/chunk-V2XTI523.js +0 -347
  159. package/dist/chunk-VAU366PN.js +0 -2241
  160. package/dist/chunk-VMVDTCXB.js +0 -712
  161. package/dist/chunk-VRW3FULF.js +0 -725
  162. package/dist/chunk-WADYRCO2.js +0 -304
  163. package/dist/chunk-WILQZRO4.js +0 -282
  164. package/dist/chunk-WUHJISPP.js +0 -298
  165. package/dist/chunk-XYU6TZOW.js +0 -182
  166. package/dist/chunk-Y6GP4QGG.js +0 -276
  167. package/dist/chunk-YECR7UIA.js +0 -347
  168. package/dist/chunk-YUTWTI4B.js +0 -654
  169. package/dist/chunk-Z65KYU7I.js +0 -26
  170. package/dist/chunk-Z6POF5YC.js +0 -975
  171. package/dist/chunk-ZBJP6WFL.js +0 -482
  172. package/dist/contracts-DDrwxvJ-.d.cts +0 -245
  173. package/dist/contracts-DDrwxvJ-.d.ts +0 -245
  174. package/dist/contracts-DOrhwbke.d.cts +0 -245
  175. package/dist/contracts-DOrhwbke.d.ts +0 -245
  176. package/dist/customElement-BKQfbSZQ.d.cts +0 -262
  177. package/dist/customElement-BKQfbSZQ.d.ts +0 -262
  178. package/dist/customElement-yz8uyk-0.d.cts +0 -308
  179. package/dist/customElement-yz8uyk-0.d.ts +0 -308
  180. package/dist/introspect-Cb0zgpi2.d.cts +0 -477
  181. package/dist/introspect-Y2xNXGSf.d.ts +0 -477
  182. package/dist/plugin-Bek4RhJY.d.cts +0 -43
  183. package/dist/plugin-Bek4RhJY.d.ts +0 -43
  184. package/dist/ssr-3RXHP5ES.js +0 -38
  185. package/dist/ssr-6GIMY5MX.js +0 -38
  186. package/dist/ssr-BA6sxxUd.d.cts +0 -135
  187. package/dist/ssr-BA6sxxUd.d.ts +0 -135
  188. package/dist/ssr-WKUPVSSK.js +0 -36
  189. package/dist/tagFactory-Dl8QCLga.d.cts +0 -23
  190. package/dist/tagFactory-Dl8QCLga.d.ts +0 -23
@@ -1,18 +1,22 @@
1
1
  import {
2
2
  bindAttribute
3
- } from "./chunk-PTQJDMRT.js";
3
+ } from "./chunk-DFPFITST.js";
4
4
  import {
5
5
  derived
6
- } from "./chunk-NEKUBFPT.js";
6
+ } from "./chunk-54EDRCEF.js";
7
+ import {
8
+ dispose,
9
+ registerDisposer
10
+ } from "./chunk-2UPRY23K.js";
7
11
  import {
8
12
  effect
9
- } from "./chunk-CHF5OHIA.js";
13
+ } from "./chunk-HB24TBAF.js";
10
14
  import {
11
15
  signal
12
- } from "./chunk-WZSPOOER.js";
16
+ } from "./chunk-CC65Y57T.js";
13
17
  import {
14
18
  track
15
- } from "./chunk-ZD6OAMTH.js";
19
+ } from "./chunk-VLPPXTYG.js";
16
20
 
17
21
  // src/ui/form.ts
18
22
  function required(message = "This field is required") {
@@ -90,7 +94,7 @@ function bindField(field, extras) {
90
94
  blur: () => field.touch()
91
95
  };
92
96
  const { on: extraOn, value: _ignoreValue, ...restExtras } = extras ?? {};
93
- const mergedOn = extraOn && typeof extraOn === "object" ? { ...fieldOn, ...extraOn } : fieldOn;
97
+ const mergedOn = extraOn && typeof extraOn === "object" ? { ...extraOn, ...fieldOn } : fieldOn;
94
98
  return {
95
99
  value: field.value,
96
100
  on: mergedOn,
@@ -115,9 +119,18 @@ function form(config) {
115
119
  }
116
120
  return null;
117
121
  });
122
+ const wrappedSet = (next) => {
123
+ setValue(next);
124
+ setManualErrors((prev) => {
125
+ if (!(name in prev) || prev[name] == null) return prev;
126
+ const copy = { ...prev };
127
+ copy[name] = null;
128
+ return copy;
129
+ });
130
+ };
118
131
  fieldMap[name] = {
119
132
  value,
120
- set: setValue,
133
+ set: wrappedSet,
121
134
  error,
122
135
  touched: isTouched,
123
136
  touch: () => setTouched(true),
@@ -161,14 +174,23 @@ function form(config) {
161
174
  }
162
175
  return result;
163
176
  });
177
+ const [submitting, setSubmitting] = signal(false);
164
178
  function handleSubmit(onSubmit) {
165
179
  return (e) => {
166
180
  if (e) e.preventDefault();
181
+ if (submitting()) return;
167
182
  for (const field of Object.values(fieldMap)) {
168
183
  field.touch();
169
184
  }
170
185
  if (isValid()) {
171
- onSubmit(values());
186
+ const result = onSubmit(values());
187
+ if (result && typeof result.then === "function") {
188
+ setSubmitting(true);
189
+ result.then(
190
+ () => setSubmitting(false),
191
+ () => setSubmitting(false)
192
+ );
193
+ }
172
194
  }
173
195
  };
174
196
  }
@@ -185,6 +207,7 @@ function form(config) {
185
207
  errors,
186
208
  isValid,
187
209
  isDirty,
210
+ submitting,
188
211
  touched: touchedState,
189
212
  values,
190
213
  handleSubmit,
@@ -210,9 +233,9 @@ function VirtualList(props) {
210
233
  content.style.right = "0";
211
234
  spacer.appendChild(content);
212
235
  container.appendChild(spacer);
213
- container.addEventListener("scroll", () => {
214
- setScrollTop(container.scrollTop);
215
- });
236
+ const onScroll = () => setScrollTop(container.scrollTop);
237
+ container.addEventListener("scroll", onScroll);
238
+ registerDisposer(container, () => container.removeEventListener("scroll", onScroll));
216
239
  const update = () => {
217
240
  const items = props.items();
218
241
  const totalHeight = items.length * props.itemHeight;
@@ -241,6 +264,7 @@ function intersection(options) {
241
264
  let observer = null;
242
265
  let currentElement = null;
243
266
  function observe(element) {
267
+ if (typeof IntersectionObserver === "undefined") return;
244
268
  unobserve();
245
269
  currentElement = element;
246
270
  observer = new IntersectionObserver((entries) => {
@@ -268,6 +292,11 @@ function intersection(options) {
268
292
  };
269
293
  }
270
294
  function lazyLoad(element, loader, options) {
295
+ if (typeof IntersectionObserver === "undefined") {
296
+ loader();
297
+ return () => {
298
+ };
299
+ }
271
300
  const observer = new IntersectionObserver((entries) => {
272
301
  for (const entry of entries) {
273
302
  if (entry.isIntersecting) {
@@ -320,20 +349,65 @@ function inputMask(options) {
320
349
  }
321
350
  return raw;
322
351
  }
352
+ function isSlot(c) {
353
+ return c === "9" || c === "A" || c === "*";
354
+ }
355
+ function buildStripRegex() {
356
+ const hasDigit = options.pattern.includes("9");
357
+ const hasLetter = options.pattern.includes("A");
358
+ const hasAny = options.pattern.includes("*");
359
+ if (hasAny) {
360
+ const literals = /* @__PURE__ */ new Set();
361
+ for (const c of options.pattern) {
362
+ if (!isSlot(c)) literals.add(c.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"));
363
+ }
364
+ return literals.size > 0 ? new RegExp(`[${Array.from(literals).join("")}]`, "g") : /(?!)/g;
365
+ }
366
+ if (hasDigit && hasLetter) return /[^a-zA-Z0-9]/g;
367
+ if (hasDigit) return /[^0-9]/g;
368
+ if (hasLetter) return /[^a-zA-Z]/g;
369
+ return /[^a-zA-Z0-9]/g;
370
+ }
371
+ const stripRegex = buildStripRegex();
372
+ const rawCharTest = options.pattern.includes("*") ? () => true : (c) => /[a-zA-Z0-9]/.test(c);
323
373
  function bind(input) {
324
- input.addEventListener("input", () => {
325
- const raw = input.value.replace(/[^a-zA-Z0-9]/g, "");
374
+ const onInput = () => {
375
+ const cursorBefore = input.selectionStart ?? input.value.length;
376
+ const oldValue = input.value;
377
+ const raw = oldValue.replace(stripRegex, "");
326
378
  const masked = applyMask(raw);
327
379
  setValue(masked);
328
380
  setRawValue(extractRaw(masked));
329
381
  input.value = masked;
330
- });
331
- input.addEventListener("focus", () => {
382
+ let rawBefore = 0;
383
+ for (let i = 0; i < cursorBefore && i < oldValue.length; i++) {
384
+ if (rawCharTest(oldValue[i])) rawBefore++;
385
+ }
386
+ let newCursor = 0;
387
+ let counted = 0;
388
+ for (; newCursor < masked.length; newCursor++) {
389
+ if (newCursor < options.pattern.length && isSlot(options.pattern[newCursor])) {
390
+ counted++;
391
+ if (counted >= rawBefore) {
392
+ newCursor++;
393
+ break;
394
+ }
395
+ }
396
+ }
397
+ input.setSelectionRange(newCursor, newCursor);
398
+ };
399
+ const onFocus = () => {
332
400
  if (!input.value) {
333
401
  const display = options.pattern.replace(/9/g, placeholder).replace(/A/g, placeholder).replace(/\*/g, placeholder);
334
402
  input.placeholder = display;
335
403
  }
336
- });
404
+ };
405
+ input.addEventListener("input", onInput);
406
+ input.addEventListener("focus", onFocus);
407
+ return () => {
408
+ input.removeEventListener("input", onInput);
409
+ input.removeEventListener("focus", onFocus);
410
+ };
337
411
  }
338
412
  return { value, rawValue, bind };
339
413
  }
@@ -375,8 +449,20 @@ function focus() {
375
449
  let currentElement = null;
376
450
  function bind(element) {
377
451
  currentElement = element;
378
- element.addEventListener("focus", () => setIsFocused(true));
379
- element.addEventListener("blur", () => setIsFocused(false));
452
+ const onFocus = () => setIsFocused(true);
453
+ const onBlur = () => setIsFocused(false);
454
+ element.addEventListener("focus", onFocus);
455
+ element.addEventListener("blur", onBlur);
456
+ let disposed = false;
457
+ const dispose2 = () => {
458
+ if (disposed) return;
459
+ disposed = true;
460
+ element.removeEventListener("focus", onFocus);
461
+ element.removeEventListener("blur", onBlur);
462
+ if (currentElement === element) currentElement = null;
463
+ };
464
+ registerDisposer(element, dispose2);
465
+ return dispose2;
380
466
  }
381
467
  function focus2() {
382
468
  currentElement?.focus();
@@ -391,12 +477,39 @@ function FocusTrap(nodes, options = {}) {
391
477
  container.setAttribute("data-sibu-focus-trap", "true");
392
478
  container.appendChild(nodes);
393
479
  const previouslyFocused = document.activeElement;
394
- container.addEventListener("keydown", (e) => {
480
+ const FOCUSABLE_SELECTOR = 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"]), [contenteditable]';
481
+ function isEffectivelyVisible(el) {
482
+ let node = el;
483
+ while (node) {
484
+ if (node.hasAttribute("inert")) return false;
485
+ if (node.getAttribute("aria-hidden") === "true") return false;
486
+ if (node.hidden) return false;
487
+ node = node.parentElement;
488
+ }
489
+ if (el.offsetParent === null && el.getClientRects().length === 0) return false;
490
+ return true;
491
+ }
492
+ function getFocusable() {
493
+ const raw = Array.from(container.querySelectorAll(FOCUSABLE_SELECTOR));
494
+ const out = [];
495
+ for (const el of raw) {
496
+ if (el.hasAttribute("disabled")) continue;
497
+ if (el.getAttribute("aria-hidden") === "true") continue;
498
+ if (el.hasAttribute("inert")) continue;
499
+ const ce = el.getAttribute("contenteditable");
500
+ if (ce !== null && ce === "false") continue;
501
+ if (!isEffectivelyVisible(el)) continue;
502
+ out.push(el);
503
+ }
504
+ return out;
505
+ }
506
+ const onTrapKeydown = (e) => {
395
507
  if (e.key !== "Tab") return;
396
- const focusable = container.querySelectorAll(
397
- 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
398
- );
399
- if (focusable.length === 0) return;
508
+ const focusable = getFocusable();
509
+ if (focusable.length === 0) {
510
+ e.preventDefault();
511
+ return;
512
+ }
400
513
  const first = focusable[0];
401
514
  const last = focusable[focusable.length - 1];
402
515
  if (e.shiftKey) {
@@ -410,33 +523,36 @@ function FocusTrap(nodes, options = {}) {
410
523
  first.focus();
411
524
  }
412
525
  }
413
- });
526
+ };
527
+ container.addEventListener("keydown", onTrapKeydown);
414
528
  if (options.autoFocus !== false) {
415
529
  queueMicrotask(() => {
416
- const first = container.querySelector(
417
- 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
418
- );
530
+ const first = getFocusable()[0];
419
531
  first?.focus();
420
532
  });
421
533
  }
534
+ let trapObserver = null;
535
+ function restoreFocusAndCleanup() {
536
+ if (options.restoreFocus !== false) previouslyFocused?.focus();
537
+ container.removeEventListener("keydown", onTrapKeydown);
538
+ if (trapObserver) {
539
+ trapObserver.disconnect();
540
+ trapObserver = null;
541
+ }
542
+ }
422
543
  if (options.restoreFocus !== false) {
423
- const observer = new MutationObserver((mutations) => {
424
- for (const mutation of mutations) {
425
- for (const removed of Array.from(mutation.removedNodes)) {
426
- if (removed === container || removed.contains(container)) {
427
- previouslyFocused?.focus();
428
- observer.disconnect();
429
- return;
430
- }
431
- }
544
+ trapObserver = new MutationObserver(() => {
545
+ if (!container.isConnected) {
546
+ restoreFocusAndCleanup();
432
547
  }
433
548
  });
434
549
  queueMicrotask(() => {
435
- if (container.parentNode) {
436
- observer.observe(container.parentNode, { childList: true });
550
+ if (container.isConnected) {
551
+ trapObserver.observe(container, { childList: true, subtree: true });
437
552
  }
438
553
  });
439
554
  }
555
+ registerDisposer(container, restoreFocusAndCleanup);
440
556
  return container;
441
557
  }
442
558
  function hotkey(combo, handler, options = {}) {
@@ -469,7 +585,16 @@ function hotkey(combo, handler, options = {}) {
469
585
  document.addEventListener("keydown", listener);
470
586
  return () => document.removeEventListener("keydown", listener);
471
587
  }
472
- function announce(message, priority = "polite") {
588
+ var announceQueues = {
589
+ polite: [],
590
+ assertive: []
591
+ };
592
+ var announceDraining = {
593
+ polite: false,
594
+ assertive: false
595
+ };
596
+ var ANNOUNCE_INTERVAL_MS = 150;
597
+ function ensureLiveRegion(priority) {
473
598
  let region = document.getElementById(`sibu-announce-${priority}`);
474
599
  if (!region) {
475
600
  region = document.createElement("div");
@@ -480,11 +605,33 @@ function announce(message, priority = "polite") {
480
605
  region.style.cssText = "position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; border: 0;";
481
606
  document.body.appendChild(region);
482
607
  }
608
+ return region;
609
+ }
610
+ function drainAnnounceQueue(priority) {
611
+ if (announceDraining[priority]) return;
612
+ const queue = announceQueues[priority];
613
+ if (queue.length === 0) return;
614
+ announceDraining[priority] = true;
615
+ const region = ensureLiveRegion(priority);
616
+ const next = queue.shift();
483
617
  region.textContent = "";
484
618
  requestAnimationFrame(() => {
485
- if (region) region.textContent = message;
619
+ if (!region.isConnected) {
620
+ announceDraining[priority] = false;
621
+ return;
622
+ }
623
+ region.textContent = next;
624
+ setTimeout(() => {
625
+ announceDraining[priority] = false;
626
+ drainAnnounceQueue(priority);
627
+ }, ANNOUNCE_INTERVAL_MS);
486
628
  });
487
629
  }
630
+ function announce(message, priority = "polite") {
631
+ if (typeof document === "undefined") return;
632
+ announceQueues[priority].push(message);
633
+ drainAnnounceQueue(priority);
634
+ }
488
635
 
489
636
  // src/ui/scopedStyle.ts
490
637
  var scopeCounter = 0;
@@ -522,6 +669,10 @@ function scopedStyle(css) {
522
669
  if (trimmed.startsWith("@") || trimmed.startsWith("from") || trimmed.startsWith("to") || /^\d+%$/.test(trimmed)) {
523
670
  return match;
524
671
  }
672
+ const pseudoIdx = trimmed.indexOf("::");
673
+ if (pseudoIdx >= 0) {
674
+ return `${trimmed.slice(0, pseudoIdx)}[${attr}]${trimmed.slice(pseudoIdx)}${delimiter}`;
675
+ }
525
676
  return `${trimmed}[${attr}]${delimiter}`;
526
677
  });
527
678
  if (typeof document !== "undefined") {
@@ -617,59 +768,91 @@ function bindData(el, key, getter) {
617
768
  }
618
769
 
619
770
  // src/ui/dialog.ts
771
+ var dialogStack = [];
772
+ var globalListenerAttached = false;
773
+ function __resetDialogStack() {
774
+ while (dialogStack.length > 0) dialogStack.pop();
775
+ if (typeof window !== "undefined" && globalListenerAttached) {
776
+ window.removeEventListener("keydown", handleGlobalKeydown);
777
+ globalListenerAttached = false;
778
+ }
779
+ }
780
+ function handleGlobalKeydown(event) {
781
+ if (event.key !== "Escape") return;
782
+ const top = dialogStack[dialogStack.length - 1];
783
+ if (top) top.close();
784
+ }
785
+ function ensureGlobalListener() {
786
+ if (typeof window === "undefined" || globalListenerAttached) return;
787
+ window.addEventListener("keydown", handleGlobalKeydown);
788
+ globalListenerAttached = true;
789
+ }
790
+ function removeGlobalListenerIfIdle() {
791
+ if (typeof window === "undefined") return;
792
+ if (!globalListenerAttached) return;
793
+ if (dialogStack.length > 0) return;
794
+ window.removeEventListener("keydown", handleGlobalKeydown);
795
+ globalListenerAttached = false;
796
+ }
620
797
  function dialog() {
621
798
  const [isOpen, setIsOpen] = signal(false);
622
- let listenerAttached = false;
623
- function handleKeydown(event) {
624
- if (event.key === "Escape") {
625
- close();
626
- }
799
+ const entry = { close: () => close() };
800
+ function pushOnStack() {
801
+ if (dialogStack.indexOf(entry) !== -1) return;
802
+ dialogStack.push(entry);
803
+ ensureGlobalListener();
804
+ }
805
+ function removeFromStack() {
806
+ const idx = dialogStack.indexOf(entry);
807
+ if (idx !== -1) dialogStack.splice(idx, 1);
808
+ removeGlobalListenerIfIdle();
627
809
  }
628
810
  function open() {
811
+ if (isOpen()) return;
629
812
  setIsOpen(true);
630
- if (typeof window !== "undefined" && !listenerAttached) {
631
- window.addEventListener("keydown", handleKeydown);
632
- listenerAttached = true;
633
- }
813
+ pushOnStack();
634
814
  }
635
815
  function close() {
636
- setIsOpen(false);
637
- if (typeof window !== "undefined" && listenerAttached) {
638
- window.removeEventListener("keydown", handleKeydown);
639
- listenerAttached = false;
816
+ if (!isOpen()) {
817
+ removeFromStack();
818
+ return;
640
819
  }
820
+ setIsOpen(false);
821
+ removeFromStack();
641
822
  }
642
823
  function toggle() {
643
- if (isOpen()) {
644
- close();
645
- } else {
646
- open();
647
- }
824
+ if (isOpen()) close();
825
+ else open();
826
+ }
827
+ function dispose2() {
828
+ removeFromStack();
829
+ setIsOpen(false);
648
830
  }
649
- return { open, close, isOpen, toggle };
831
+ return { open, close, isOpen, toggle, dispose: dispose2 };
650
832
  }
651
833
 
652
834
  // src/ui/toast.ts
653
- var toastCounter = 0;
654
835
  function toast(options) {
655
836
  const duration = options?.duration ?? 3e3;
656
837
  const maxToasts = options?.maxToasts ?? Infinity;
657
838
  const [toasts, setToasts] = signal([]);
658
839
  const timers = /* @__PURE__ */ new Map();
840
+ let toastCounter = 0;
659
841
  function show(message, type) {
660
842
  const id = `toast-${++toastCounter}`;
661
843
  const toast2 = { id, message, type };
844
+ const trimmedIds = [];
662
845
  setToasts((prev) => {
663
846
  const next = [...prev, toast2];
664
847
  if (next.length > maxToasts) {
665
848
  const removed = next.splice(0, next.length - maxToasts);
666
- for (const r of removed) {
667
- clearTimerForToast(r.id);
668
- }
849
+ for (const r of removed) trimmedIds.push(r.id);
669
850
  }
670
851
  return next;
671
852
  });
672
- if (duration > 0) {
853
+ for (const tid of trimmedIds) clearTimerForToast(tid);
854
+ const wasTrimmed = trimmedIds.indexOf(id) !== -1;
855
+ if (duration > 0 && !wasTrimmed) {
673
856
  const timer = setTimeout(() => {
674
857
  dismiss(id);
675
858
  }, duration);
@@ -755,14 +938,14 @@ function infiniteScroll(options) {
755
938
  },
756
939
  configurable: true
757
940
  });
758
- function dispose() {
941
+ function dispose2() {
759
942
  disposed = true;
760
943
  if (observer) {
761
944
  observer.disconnect();
762
945
  observer = null;
763
946
  }
764
947
  }
765
- return { sentinelRef: originalRef, loading, dispose };
948
+ return { sentinelRef: originalRef, loading, dispose: dispose2 };
766
949
  }
767
950
 
768
951
  // src/ui/pagination.ts
@@ -841,7 +1024,7 @@ function defineElement(name, component, options = {}) {
841
1024
  class SibuElement extends HTMLElement {
842
1025
  constructor() {
843
1026
  super();
844
- this._rendered = false;
1027
+ this._rendered = null;
845
1028
  if (options.shadow !== false) {
846
1029
  this._root = this.attachShadow({ mode: options.mode || "open" });
847
1030
  } else {
@@ -855,25 +1038,23 @@ function defineElement(name, component, options = {}) {
855
1038
  this._render();
856
1039
  }
857
1040
  disconnectedCallback() {
858
- if (this._root instanceof ShadowRoot) {
859
- this._root.innerHTML = "";
860
- }
861
- this._rendered = false;
1041
+ this._teardown();
862
1042
  }
863
1043
  attributeChangedCallback() {
864
1044
  if (this._rendered) {
865
1045
  this._render();
866
1046
  }
867
1047
  }
1048
+ _teardown() {
1049
+ if (this._rendered) {
1050
+ dispose(this._rendered);
1051
+ this._rendered = null;
1052
+ }
1053
+ this._root.replaceChildren();
1054
+ }
868
1055
  _render() {
1056
+ this._teardown();
869
1057
  const props = this._getProps();
870
- if (this._root instanceof ShadowRoot) {
871
- this._root.innerHTML = "";
872
- } else {
873
- while (this._root.firstChild) {
874
- this._root.removeChild(this._root.firstChild);
875
- }
876
- }
877
1058
  if (options.styles && this._root instanceof ShadowRoot) {
878
1059
  const styleEl = document.createElement("style");
879
1060
  styleEl.textContent = options.styles;
@@ -881,7 +1062,7 @@ function defineElement(name, component, options = {}) {
881
1062
  }
882
1063
  const el = component(props, this);
883
1064
  this._root.appendChild(el);
884
- this._rendered = true;
1065
+ this._rendered = el;
885
1066
  }
886
1067
  _getProps() {
887
1068
  const props = {};
@@ -946,6 +1127,7 @@ export {
946
1127
  bindAttrs,
947
1128
  bindBoolAttr,
948
1129
  bindData,
1130
+ __resetDialogStack,
949
1131
  dialog,
950
1132
  toast,
951
1133
  infiniteScroll,
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  SVG_NS,
3
3
  tagFactory
4
- } from "./chunk-MDVXJWFN.js";
4
+ } from "./chunk-KLRMB5ZS.js";
5
5
 
6
6
  // src/core/rendering/html.ts
7
7
  var html = tagFactory("html");