sibujs 4.4.0 → 4.6.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 (112) hide show
  1. package/README.md +11 -0
  2. package/dist/browser.cjs +339 -130
  3. package/dist/browser.d.cts +61 -16
  4. package/dist/browser.d.ts +61 -16
  5. package/dist/browser.js +8 -6
  6. package/dist/build.cjs +273 -82
  7. package/dist/build.js +15 -16
  8. package/dist/cdn.dev.global.js +12 -12
  9. package/dist/cdn.full.dev.global.js +11 -11
  10. package/dist/cdn.full.global.js +10 -10
  11. package/dist/cdn.global.js +12 -12
  12. package/dist/{chunk-IXKSNWV5.js → chunk-2INLLLMZ.js} +1 -1
  13. package/dist/chunk-3ISI6ACU.js +44 -0
  14. package/dist/{chunk-VCTAEPSB.js → chunk-47M47FOM.js} +11 -3
  15. package/dist/chunk-5HZXGZ6T.js +24 -0
  16. package/dist/{chunk-KKLW7YWL.js → chunk-5MT6SJ3P.js} +275 -144
  17. package/dist/{chunk-3BTTCZ5J.js → chunk-5Q4R7HCL.js} +42 -4
  18. package/dist/{chunk-ADM46X22.js → chunk-7GQCWFOE.js} +157 -61
  19. package/dist/{chunk-4Z3SPQ67.js → chunk-7NCARGJW.js} +16 -8
  20. package/dist/{chunk-5DXA2J44.js → chunk-7XHATCIH.js} +5 -2
  21. package/dist/{chunk-LQFGQNMV.js → chunk-AIF3Z2T7.js} +302 -39
  22. package/dist/{chunk-UCALUKB5.js → chunk-BAAG6ZTI.js} +98 -29
  23. package/dist/{chunk-NVNJH22U.js → chunk-D33YTSX5.js} +2 -2
  24. package/dist/{chunk-M2F7TZHH.js → chunk-DKGBBKOF.js} +136 -80
  25. package/dist/{chunk-NIOYEGBQ.js → chunk-HURREPU2.js} +27 -13
  26. package/dist/{chunk-HFCOH2GN.js → chunk-HYXDKS4N.js} +153 -110
  27. package/dist/chunk-J6FW5TV6.js +233 -0
  28. package/dist/{chunk-36S2YPP4.js → chunk-JSXPZCET.js} +1 -1
  29. package/dist/chunk-NYNYSPK7.js +318 -0
  30. package/dist/{chunk-HQSEH5F6.js → chunk-ORMZXBKQ.js} +108 -45
  31. package/dist/{chunk-LYVUX7NT.js → chunk-OXUY2A6L.js} +287 -130
  32. package/dist/{chunk-6GSIXTWX.js → chunk-PK6FK2G2.js} +187 -66
  33. package/dist/{chunk-W2EQ7X2L.js → chunk-QE4TTDU3.js} +47 -9
  34. package/dist/{chunk-XLO7SLIX.js → chunk-SXXVZMKZ.js} +262 -70
  35. package/dist/{chunk-R25EFXXC.js → chunk-TUCPL2HB.js} +3 -3
  36. package/dist/{chunk-RR7M3FHM.js → chunk-UZQ6ALFS.js} +3 -3
  37. package/dist/{chunk-PNIRUQ4C.js → chunk-VVWPJ543.js} +6 -8
  38. package/dist/{chunk-7GIHSAWB.js → chunk-XC4MEKGA.js} +3 -3
  39. package/dist/{chunk-SKPTERHP.js → chunk-XRRZKZYX.js} +55 -31
  40. package/dist/{contracts-DBdg9J_a.d.cts → contracts-DRIuclVT.d.cts} +10 -2
  41. package/dist/{contracts-DBdg9J_a.d.ts → contracts-DRIuclVT.d.ts} +10 -2
  42. package/dist/{customElement-CNZxEB9G.d.ts → customElement-MmInOW1U.d.cts} +59 -10
  43. package/dist/{customElement-CNZxEB9G.d.cts → customElement-MmInOW1U.d.ts} +59 -10
  44. package/dist/data.cjs +293 -125
  45. package/dist/data.d.cts +47 -9
  46. package/dist/data.d.ts +47 -9
  47. package/dist/data.js +12 -9
  48. package/dist/devtools.cjs +121 -58
  49. package/dist/devtools.js +7 -8
  50. package/dist/dispose-GEIG2KOF.js +28 -0
  51. package/dist/ecosystem.cjs +391 -112
  52. package/dist/ecosystem.d.cts +31 -7
  53. package/dist/ecosystem.d.ts +31 -7
  54. package/dist/ecosystem.js +12 -12
  55. package/dist/extras.cjs +2250 -879
  56. package/dist/extras.d.cts +10 -9
  57. package/dist/extras.d.ts +10 -9
  58. package/dist/extras.js +39 -28
  59. package/dist/index.cjs +273 -82
  60. package/dist/index.d.cts +44 -154
  61. package/dist/index.d.ts +44 -154
  62. package/dist/index.js +24 -27
  63. package/dist/motion.cjs +137 -42
  64. package/dist/motion.js +5 -5
  65. package/dist/patterns.cjs +397 -48
  66. package/dist/patterns.d.cts +32 -9
  67. package/dist/patterns.d.ts +32 -9
  68. package/dist/patterns.js +8 -8
  69. package/dist/performance.cjs +338 -217
  70. package/dist/performance.d.cts +2 -2
  71. package/dist/performance.d.ts +2 -2
  72. package/dist/performance.js +8 -9
  73. package/dist/plugin-DVgSnTfK.d.cts +112 -0
  74. package/dist/plugin-DVgSnTfK.d.ts +112 -0
  75. package/dist/plugins.cjs +587 -203
  76. package/dist/plugins.d.cts +3 -3
  77. package/dist/plugins.d.ts +3 -3
  78. package/dist/plugins.js +35 -23
  79. package/dist/signal-EotCj4hS.d.cts +110 -0
  80. package/dist/signal-EotCj4hS.d.ts +110 -0
  81. package/dist/{ssr-BiPRdZ6n.d.cts → ssr-Bli9XRW5.d.cts} +5 -0
  82. package/dist/{ssr-BiPRdZ6n.d.ts → ssr-Bli9XRW5.d.ts} +5 -0
  83. package/dist/{ssr-Y7XOEPEN.js → ssr-XOTUASDO.js} +4 -5
  84. package/dist/ssr.cjs +214 -73
  85. package/dist/ssr.d.cts +9 -3
  86. package/dist/ssr.d.ts +9 -3
  87. package/dist/ssr.js +11 -12
  88. package/dist/{startup-BMpaiMhP.d.ts → startup-BLfSeL15.d.cts} +73 -22
  89. package/dist/{startup-BMpaiMhP.d.cts → startup-BLfSeL15.d.ts} +73 -22
  90. package/dist/tagFactory-DFstCLQV.d.cts +117 -0
  91. package/dist/tagFactory-DkaNVUNV.d.ts +117 -0
  92. package/dist/testing.cjs +2481 -2153
  93. package/dist/testing.d.cts +56 -5
  94. package/dist/testing.d.ts +56 -5
  95. package/dist/testing.js +580 -307
  96. package/dist/ui.cjs +798 -328
  97. package/dist/ui.d.cts +40 -7
  98. package/dist/ui.d.ts +40 -7
  99. package/dist/ui.js +151 -56
  100. package/dist/widgets.cjs +319 -285
  101. package/dist/widgets.js +9 -10
  102. package/package.json +2 -2
  103. package/dist/chunk-2WLZ6757.js +0 -149
  104. package/dist/chunk-7NBDXVHS.js +0 -60
  105. package/dist/chunk-CCSJMTRN.js +0 -15
  106. package/dist/chunk-QKRPLZ2V.js +0 -108
  107. package/dist/chunk-WWV3SJ3L.js +0 -131
  108. package/dist/dispose-46BOMMQJ.js +0 -19
  109. package/dist/plugin-D30wlGW5.d.cts +0 -71
  110. package/dist/plugin-D30wlGW5.d.ts +0 -71
  111. package/dist/tagFactory-Bzupt4Pj.d.cts +0 -55
  112. package/dist/tagFactory-Bzupt4Pj.d.ts +0 -55
@@ -1,9 +1,13 @@
1
1
  import {
2
- registerDisposer
3
- } from "./chunk-QKRPLZ2V.js";
2
+ adoptThenable
3
+ } from "./chunk-5HZXGZ6T.js";
4
4
  import {
5
5
  signal
6
- } from "./chunk-NVNJH22U.js";
6
+ } from "./chunk-D33YTSX5.js";
7
+ import {
8
+ registerDisposer,
9
+ reportError
10
+ } from "./chunk-NYNYSPK7.js";
7
11
 
8
12
  // src/ui/transition.ts
9
13
  function transition(element, options = {}) {
@@ -32,6 +36,15 @@ function transition(element, options = {}) {
32
36
  resolvePrev();
33
37
  }
34
38
  }
39
+ function complete(callback, name, resolve) {
40
+ try {
41
+ callback?.();
42
+ } catch (error) {
43
+ reportError(error, { phase: "async", name, node: element });
44
+ } finally {
45
+ resolve();
46
+ }
47
+ }
35
48
  function enter() {
36
49
  return new Promise((resolve) => {
37
50
  cancelPending();
@@ -44,8 +57,7 @@ function transition(element, options = {}) {
44
57
  activeTimer = null;
45
58
  pendingResolve = null;
46
59
  if (enterClass) element.classList.remove(enterClass);
47
- onEnterDone?.();
48
- resolve();
60
+ complete(onEnterDone, "transition(onEnterDone)", resolve);
49
61
  };
50
62
  if (duration > 0) {
51
63
  pendingResolve = resolve;
@@ -66,8 +78,7 @@ function transition(element, options = {}) {
66
78
  activeTimer = null;
67
79
  pendingResolve = null;
68
80
  if (leaveClass) element.classList.remove(leaveClass);
69
- onLeaveDone?.();
70
- resolve();
81
+ complete(onLeaveDone, "transition(onLeaveDone)", resolve);
71
82
  };
72
83
  if (duration > 0) {
73
84
  pendingResolve = resolve;
@@ -274,18 +285,29 @@ async function sequence(steps) {
274
285
  function TransitionGroup(options) {
275
286
  const [elements, setElements] = signal([]);
276
287
  const positions = /* @__PURE__ */ new Map();
288
+ function runCallback(name, callback, el) {
289
+ const report = (error) => reportError(error, { phase: "async", name: `TransitionGroup.${name}`, node: el });
290
+ try {
291
+ adoptThenable(callback(el))?.then(void 0, report);
292
+ } catch (error) {
293
+ report(error);
294
+ }
295
+ }
277
296
  function add(el) {
278
297
  setElements((prev) => [...prev, el]);
279
- if (options.enter) {
280
- options.enter(el);
281
- }
298
+ if (options.enter) runCallback("enter", options.enter, el);
282
299
  }
283
300
  async function remove(el) {
284
- if (options.leave) {
285
- await options.leave(el);
301
+ try {
302
+ if (options.leave) {
303
+ await adoptThenable(options.leave(el));
304
+ }
305
+ } catch (error) {
306
+ reportError(error, { phase: "async", name: "TransitionGroup.leave", node: el });
307
+ } finally {
308
+ positions.delete(el);
309
+ setElements((prev) => prev.filter((e) => e !== el));
286
310
  }
287
- positions.delete(el);
288
- setElements((prev) => prev.filter((e) => e !== el));
289
311
  }
290
312
  function track(newElements) {
291
313
  const oldPositions = /* @__PURE__ */ new Map();
@@ -297,17 +319,13 @@ function TransitionGroup(options) {
297
319
  const currentSet = new Set(elements());
298
320
  for (const el of newElements) {
299
321
  if (!currentSet.has(el)) {
300
- if (options.enter) {
301
- options.enter(el);
302
- }
322
+ if (options.enter) runCallback("enter", options.enter, el);
303
323
  }
304
324
  }
305
325
  const newSet = new Set(newElements);
306
326
  for (const el of elements()) {
307
327
  if (!newSet.has(el)) {
308
- if (options.leave) {
309
- options.leave(el);
310
- }
328
+ if (options.leave) runCallback("leave", options.leave, el);
311
329
  }
312
330
  }
313
331
  setElements(newElements);
@@ -317,7 +335,7 @@ function TransitionGroup(options) {
317
335
  if (oldRect && typeof el.getBoundingClientRect === "function") {
318
336
  const newRect = el.getBoundingClientRect();
319
337
  if (oldRect.left !== newRect.left || oldRect.top !== newRect.top) {
320
- options.move(el);
338
+ runCallback("move", options.move, el);
321
339
  }
322
340
  }
323
341
  }
@@ -368,9 +386,13 @@ function springSignal(initial, options) {
368
386
  let target = initial;
369
387
  let rafId = null;
370
388
  let lastTime = 0;
389
+ let disposed = false;
390
+ const canAnimate = () => typeof requestAnimationFrame === "function";
371
391
  const REF_DT_MS = 1e3 / 60;
372
392
  const MAX_STEP_RATIO = 4;
373
393
  function tick(now) {
394
+ rafId = null;
395
+ if (disposed) return;
374
396
  if (lastTime === 0) lastTime = now;
375
397
  const rawDt = now - lastTime;
376
398
  lastTime = now;
@@ -383,23 +405,27 @@ function springSignal(initial, options) {
383
405
  if (Math.abs(current - target) < precision && Math.abs(velocity) < precision) {
384
406
  current = target;
385
407
  velocity = 0;
386
- rafId = null;
387
408
  lastTime = 0;
388
409
  setValue(current);
389
410
  return;
390
411
  }
391
412
  setValue(current);
413
+ if (disposed || rafId !== null) return;
392
414
  rafId = requestAnimationFrame(tick);
393
415
  }
416
+ function cancelFrame() {
417
+ if (rafId !== null) {
418
+ if (typeof cancelAnimationFrame === "function") cancelAnimationFrame(rafId);
419
+ rafId = null;
420
+ }
421
+ }
394
422
  function set(newTarget) {
423
+ if (disposed) return;
395
424
  target = newTarget;
396
- if (prefersReducedMotion()) {
425
+ if (!canAnimate() || prefersReducedMotion()) {
397
426
  current = newTarget;
398
427
  velocity = 0;
399
- if (rafId !== null) {
400
- cancelAnimationFrame(rafId);
401
- rafId = null;
402
- }
428
+ cancelFrame();
403
429
  lastTime = 0;
404
430
  setValue(current);
405
431
  return;
@@ -410,10 +436,8 @@ function springSignal(initial, options) {
410
436
  }
411
437
  }
412
438
  function dispose() {
413
- if (rafId !== null) {
414
- cancelAnimationFrame(rafId);
415
- rafId = null;
416
- }
439
+ disposed = true;
440
+ cancelFrame();
417
441
  lastTime = 0;
418
442
  }
419
443
  return [value, set, dispose];
@@ -32,7 +32,15 @@ declare function withWrapper<P>(WrappedComponent: Component<P>, wrapper: (compon
32
32
  * Button("Click"); // type="button", disabled=false automatically
33
33
  * ```
34
34
  */
35
- declare function withDefaults<P extends object>(component: Component<P>, defaults: Partial<P>): Component<Partial<P>>;
35
+ /**
36
+ * Props accepted by a {@link withDefaults} component: every key that has a
37
+ * default becomes optional; every other key keeps its original required or
38
+ * optional status.
39
+ */
40
+ type WithDefaultsProps<P, D> = Omit<P, keyof D> & Partial<Pick<P, Extract<keyof P, keyof D>>>;
41
+ declare function withDefaults<P extends object, const D extends Partial<P> = Partial<P>>(component: Component<P>, defaults: D & {
42
+ [K in Exclude<keyof D, keyof P>]: never;
43
+ }): Component<WithDefaultsProps<P, D>>;
36
44
  /**
37
45
  * Composes multiple HOC wrappers into a single wrapper.
38
46
  * Applied from right to left (like function composition).
@@ -257,4 +265,4 @@ declare function assertType<T>(value: unknown, validator: Validator<T>, label?:
257
265
  */
258
266
  declare function createGuard<T>(validator: Validator<T>): (value: unknown) => value is T;
259
267
 
260
- export { type ComponentProps as C, type PropDef as P, RenderProp as R, type Validator as V, type PropSchema as a, assertType as b, compose as c, createGuard as d, createSlots as e, defineComponent as f, defineSlottedComponent as g, defineStrictComponent as h, validators as i, withDefaults as j, withProps as k, withWrapper as l, validateProps as v, withBoundary as w };
268
+ export { type ComponentProps as C, type PropDef as P, RenderProp as R, type Validator as V, type WithDefaultsProps as W, type PropSchema as a, assertType as b, compose as c, createGuard as d, createSlots as e, defineComponent as f, defineSlottedComponent as g, defineStrictComponent as h, validators as i, withDefaults as j, withProps as k, withWrapper as l, validateProps as v, withBoundary as w };
@@ -32,7 +32,15 @@ declare function withWrapper<P>(WrappedComponent: Component<P>, wrapper: (compon
32
32
  * Button("Click"); // type="button", disabled=false automatically
33
33
  * ```
34
34
  */
35
- declare function withDefaults<P extends object>(component: Component<P>, defaults: Partial<P>): Component<Partial<P>>;
35
+ /**
36
+ * Props accepted by a {@link withDefaults} component: every key that has a
37
+ * default becomes optional; every other key keeps its original required or
38
+ * optional status.
39
+ */
40
+ type WithDefaultsProps<P, D> = Omit<P, keyof D> & Partial<Pick<P, Extract<keyof P, keyof D>>>;
41
+ declare function withDefaults<P extends object, const D extends Partial<P> = Partial<P>>(component: Component<P>, defaults: D & {
42
+ [K in Exclude<keyof D, keyof P>]: never;
43
+ }): Component<WithDefaultsProps<P, D>>;
36
44
  /**
37
45
  * Composes multiple HOC wrappers into a single wrapper.
38
46
  * Applied from right to left (like function composition).
@@ -257,4 +265,4 @@ declare function assertType<T>(value: unknown, validator: Validator<T>, label?:
257
265
  */
258
266
  declare function createGuard<T>(validator: Validator<T>): (value: unknown) => value is T;
259
267
 
260
- export { type ComponentProps as C, type PropDef as P, RenderProp as R, type Validator as V, type PropSchema as a, assertType as b, compose as c, createGuard as d, createSlots as e, defineComponent as f, defineSlottedComponent as g, defineStrictComponent as h, validators as i, withDefaults as j, withProps as k, withWrapper as l, validateProps as v, withBoundary as w };
268
+ export { type ComponentProps as C, type PropDef as P, RenderProp as R, type Validator as V, type WithDefaultsProps as W, type PropSchema as a, assertType as b, compose as c, createGuard as d, createSlots as e, defineComponent as f, defineSlottedComponent as g, defineStrictComponent as h, validators as i, withDefaults as j, withProps as k, withWrapper as l, validateProps as v, withBoundary as w };
@@ -28,6 +28,16 @@ interface FormReturn<T extends object> {
28
28
  handleSubmit: (onSubmit: (values: T) => void | Promise<void>) => (e?: Event) => void;
29
29
  reset: () => void;
30
30
  setError: (field: keyof T, message: string) => void;
31
+ /**
32
+ * Release the form's derived graph: every field `error` and the `errors`,
33
+ * `isValid`, `isDirty`, `touched` and `values` aggregates. Call it when the
34
+ * form's owner goes away, e.g. `onCleanup(f.dispose, formElement)`.
35
+ *
36
+ * Afterwards the derived accessors are inert — they return their last
37
+ * settled values and never recompute — while field `value()`/`set()` keep
38
+ * working as plain signals. Idempotent.
39
+ */
40
+ dispose: () => void;
31
41
  }
32
42
  /**
33
43
  * Require a non-empty value. Rejects `null`, `undefined`, `""` and `[]`.
@@ -148,6 +158,17 @@ interface VirtualListProps<T> {
148
158
  }
149
159
  /**
150
160
  * VirtualList renders only visible items for efficient large-list rendering.
161
+ *
162
+ * Scope — it virtualizes ONE axis:
163
+ * - vertical only, inside a container of fixed `containerHeight`;
164
+ * - every item has the same fixed `itemHeight`;
165
+ * - on each scroll or `items()` change the visible window is disposed and
166
+ * re-rendered through `renderItem` (no keyed reuse of item nodes);
167
+ * - no horizontal virtualization, frozen rows/columns or sticky headers.
168
+ *
169
+ * Two-axis content (spreadsheets, large grids with frozen headers) needs its
170
+ * own windowing, typically a keyed `each()` over the visible row indices with a
171
+ * nested keyed `each()` over the visible column indices.
151
172
  */
152
173
  declare function VirtualList<T>(props: VirtualListProps<T>): HTMLElement;
153
174
 
@@ -228,6 +249,12 @@ declare function FocusTrap(nodes: HTMLElement, options?: {
228
249
  * Supports two calling styles:
229
250
  * - String combo: hotkey("ctrl+shift+z", handler)
230
251
  * - Explicit flags: hotkey("z", handler, { ctrl: true, shift: true })
252
+ *
253
+ * Combo modifiers: `ctrl`/`control`, `shift`, `alt`/`option`,
254
+ * `meta`/`cmd`/`command`, and `mod` — Cmd on Apple platforms, Ctrl
255
+ * everywhere else. An unknown modifier throws, because dropping it would
256
+ * make the shortcut fire on the bare key. The `+` key is written as a
257
+ * trailing plus: `hotkey("+", h)`, `hotkey("ctrl++", h)`.
231
258
  */
232
259
  declare function hotkey(combo: string, handler: (e: KeyboardEvent) => void, options?: {
233
260
  ctrl?: boolean;
@@ -302,6 +329,12 @@ declare function bindAttrs(el: HTMLElement, attrs: Record<string, AttributeSourc
302
329
  * Reactively toggle a boolean attribute (like disabled, readonly, hidden).
303
330
  * When the value is truthy the attribute is present (set to ""),
304
331
  * when falsy the attribute is removed entirely.
332
+ *
333
+ * `aria-*` names are the exception, as in every attribute writer: ARIA states
334
+ * are "true"/"false" tokens, and a missing state means "not applicable" rather
335
+ * than `false`, so `bindBoolAttr(el, "aria-busy", false)` writes
336
+ * `aria-busy="false"`. This helper cannot remove an ARIA attribute; use
337
+ * `bindAttribute(el, "aria-busy", () => (busy() ? true : null))` for that.
305
338
  * Returns a teardown function to stop reactive tracking.
306
339
  *
307
340
  * The attribute NAME is policed too: `bindBoolAttr(el, "onclick", true)` would
@@ -403,14 +436,7 @@ declare function infiniteScroll(options: {
403
436
  dispose: () => void;
404
437
  };
405
438
 
406
- /**
407
- * pagination provides reactive pagination state and controls.
408
- */
409
- declare function pagination(options: {
410
- totalItems: () => number;
411
- pageSize?: number;
412
- initialPage?: number;
413
- }): {
439
+ interface PaginationResult {
414
440
  page: () => number;
415
441
  pageSize: () => number;
416
442
  totalPages: () => number;
@@ -419,11 +445,34 @@ declare function pagination(options: {
419
445
  goTo: (page: number) => void;
420
446
  startIndex: () => number;
421
447
  endIndex: () => number;
422
- };
448
+ /**
449
+ * Release the subscriptions to `totalItems`. Call it when the pagination is
450
+ * discarded before the state it reads. Afterwards the accessors return their
451
+ * last values and the controls no longer move the page. Idempotent.
452
+ */
453
+ dispose: () => void;
454
+ }
455
+ /**
456
+ * pagination provides reactive pagination state and controls.
457
+ *
458
+ * It subscribes to the caller's `totalItems` getter, which usually lives longer
459
+ * than the pagination itself (a store, a query result). Call `dispose()` when
460
+ * the pagination is no longer used, e.g. `onUnmount(pager.dispose, el)`.
461
+ */
462
+ declare function pagination(options: {
463
+ totalItems: () => number;
464
+ pageSize?: number;
465
+ initialPage?: number;
466
+ }): PaginationResult;
423
467
 
424
468
  /**
425
469
  * eventBus creates a typed publish/subscribe event system.
426
470
  * No reactive state needed -- pure event dispatching.
471
+ *
472
+ * `emit()` delivers to the handlers registered when it starts. A throwing
473
+ * handler is reported through the runtime error pipeline and the remaining
474
+ * handlers still run. Handlers added during delivery receive the next event;
475
+ * handlers removed (or cleared) during delivery are skipped for the rest of it.
427
476
  */
428
477
  declare function eventBus<T extends object>(): {
429
478
  on: <K extends keyof T>(event: K, handler: (data: T[K]) => void) => () => void;
@@ -458,4 +507,4 @@ declare function defineElement(name: string, component: (props: Record<string, u
458
507
  */
459
508
  declare function svgElement(tag: string, props?: Record<string, unknown>, ...nodes: (SVGElement | string)[]): SVGElement;
460
509
 
461
- export { type AttributeSource as A, type BoundFieldProps as B, type CustomElementOptions as C, intersection as D, lazyLoad as E, type FieldConfig as F, matchesPattern as G, max as H, type IntersectionResult as I, maxLength as J, min as K, minLength as L, type MaskOptions as M, pagination as N, phoneMask as O, removeScopedStyle as P, required as Q, scopedStyle as R, ssnMask as S, type Toast as T, svgElement as U, type ValidatorFn as V, timeMask as W, toast as X, withScopedStyle as Y, zipMask as Z, __resetDialogStack as _, type AttributeValue as a, FocusTrap as b, type FormConfig as c, type FormField as d, type FormReturn as e, type ToastInstance as f, VirtualList as g, type VirtualListProps as h, announce as i, aria as j, bindAttrs as k, bindBoolAttr as l, bindData as m, bindField as n, creditCardMask as o, custom as p, dateMask as q, defineElement as r, dialog as s, email as t, eventBus as u, focus as v, form as w, hotkey as x, infiniteScroll as y, inputMask as z };
510
+ export { zipMask as $, type AttributeSource as A, type BoundFieldProps as B, type CustomElementOptions as C, intersection as D, lazyLoad as E, type FieldConfig as F, matchesPattern as G, max as H, type IntersectionResult as I, maxLength as J, min as K, minLength as L, type MaskOptions as M, pagination as N, phoneMask as O, type PaginationResult as P, removeScopedStyle as Q, required as R, scopedStyle as S, type Toast as T, ssnMask as U, type ValidatorFn as V, svgElement as W, timeMask as X, toast as Y, withScopedStyle as Z, __resetDialogStack as _, type AttributeValue as a, FocusTrap as b, type FormConfig as c, type FormField as d, type FormReturn as e, type ToastInstance as f, VirtualList as g, type VirtualListProps as h, announce as i, aria as j, bindAttrs as k, bindBoolAttr as l, bindData as m, bindField as n, creditCardMask as o, custom as p, dateMask as q, defineElement as r, dialog as s, email as t, eventBus as u, focus as v, form as w, hotkey as x, infiniteScroll as y, inputMask as z };
@@ -28,6 +28,16 @@ interface FormReturn<T extends object> {
28
28
  handleSubmit: (onSubmit: (values: T) => void | Promise<void>) => (e?: Event) => void;
29
29
  reset: () => void;
30
30
  setError: (field: keyof T, message: string) => void;
31
+ /**
32
+ * Release the form's derived graph: every field `error` and the `errors`,
33
+ * `isValid`, `isDirty`, `touched` and `values` aggregates. Call it when the
34
+ * form's owner goes away, e.g. `onCleanup(f.dispose, formElement)`.
35
+ *
36
+ * Afterwards the derived accessors are inert — they return their last
37
+ * settled values and never recompute — while field `value()`/`set()` keep
38
+ * working as plain signals. Idempotent.
39
+ */
40
+ dispose: () => void;
31
41
  }
32
42
  /**
33
43
  * Require a non-empty value. Rejects `null`, `undefined`, `""` and `[]`.
@@ -148,6 +158,17 @@ interface VirtualListProps<T> {
148
158
  }
149
159
  /**
150
160
  * VirtualList renders only visible items for efficient large-list rendering.
161
+ *
162
+ * Scope — it virtualizes ONE axis:
163
+ * - vertical only, inside a container of fixed `containerHeight`;
164
+ * - every item has the same fixed `itemHeight`;
165
+ * - on each scroll or `items()` change the visible window is disposed and
166
+ * re-rendered through `renderItem` (no keyed reuse of item nodes);
167
+ * - no horizontal virtualization, frozen rows/columns or sticky headers.
168
+ *
169
+ * Two-axis content (spreadsheets, large grids with frozen headers) needs its
170
+ * own windowing, typically a keyed `each()` over the visible row indices with a
171
+ * nested keyed `each()` over the visible column indices.
151
172
  */
152
173
  declare function VirtualList<T>(props: VirtualListProps<T>): HTMLElement;
153
174
 
@@ -228,6 +249,12 @@ declare function FocusTrap(nodes: HTMLElement, options?: {
228
249
  * Supports two calling styles:
229
250
  * - String combo: hotkey("ctrl+shift+z", handler)
230
251
  * - Explicit flags: hotkey("z", handler, { ctrl: true, shift: true })
252
+ *
253
+ * Combo modifiers: `ctrl`/`control`, `shift`, `alt`/`option`,
254
+ * `meta`/`cmd`/`command`, and `mod` — Cmd on Apple platforms, Ctrl
255
+ * everywhere else. An unknown modifier throws, because dropping it would
256
+ * make the shortcut fire on the bare key. The `+` key is written as a
257
+ * trailing plus: `hotkey("+", h)`, `hotkey("ctrl++", h)`.
231
258
  */
232
259
  declare function hotkey(combo: string, handler: (e: KeyboardEvent) => void, options?: {
233
260
  ctrl?: boolean;
@@ -302,6 +329,12 @@ declare function bindAttrs(el: HTMLElement, attrs: Record<string, AttributeSourc
302
329
  * Reactively toggle a boolean attribute (like disabled, readonly, hidden).
303
330
  * When the value is truthy the attribute is present (set to ""),
304
331
  * when falsy the attribute is removed entirely.
332
+ *
333
+ * `aria-*` names are the exception, as in every attribute writer: ARIA states
334
+ * are "true"/"false" tokens, and a missing state means "not applicable" rather
335
+ * than `false`, so `bindBoolAttr(el, "aria-busy", false)` writes
336
+ * `aria-busy="false"`. This helper cannot remove an ARIA attribute; use
337
+ * `bindAttribute(el, "aria-busy", () => (busy() ? true : null))` for that.
305
338
  * Returns a teardown function to stop reactive tracking.
306
339
  *
307
340
  * The attribute NAME is policed too: `bindBoolAttr(el, "onclick", true)` would
@@ -403,14 +436,7 @@ declare function infiniteScroll(options: {
403
436
  dispose: () => void;
404
437
  };
405
438
 
406
- /**
407
- * pagination provides reactive pagination state and controls.
408
- */
409
- declare function pagination(options: {
410
- totalItems: () => number;
411
- pageSize?: number;
412
- initialPage?: number;
413
- }): {
439
+ interface PaginationResult {
414
440
  page: () => number;
415
441
  pageSize: () => number;
416
442
  totalPages: () => number;
@@ -419,11 +445,34 @@ declare function pagination(options: {
419
445
  goTo: (page: number) => void;
420
446
  startIndex: () => number;
421
447
  endIndex: () => number;
422
- };
448
+ /**
449
+ * Release the subscriptions to `totalItems`. Call it when the pagination is
450
+ * discarded before the state it reads. Afterwards the accessors return their
451
+ * last values and the controls no longer move the page. Idempotent.
452
+ */
453
+ dispose: () => void;
454
+ }
455
+ /**
456
+ * pagination provides reactive pagination state and controls.
457
+ *
458
+ * It subscribes to the caller's `totalItems` getter, which usually lives longer
459
+ * than the pagination itself (a store, a query result). Call `dispose()` when
460
+ * the pagination is no longer used, e.g. `onUnmount(pager.dispose, el)`.
461
+ */
462
+ declare function pagination(options: {
463
+ totalItems: () => number;
464
+ pageSize?: number;
465
+ initialPage?: number;
466
+ }): PaginationResult;
423
467
 
424
468
  /**
425
469
  * eventBus creates a typed publish/subscribe event system.
426
470
  * No reactive state needed -- pure event dispatching.
471
+ *
472
+ * `emit()` delivers to the handlers registered when it starts. A throwing
473
+ * handler is reported through the runtime error pipeline and the remaining
474
+ * handlers still run. Handlers added during delivery receive the next event;
475
+ * handlers removed (or cleared) during delivery are skipped for the rest of it.
427
476
  */
428
477
  declare function eventBus<T extends object>(): {
429
478
  on: <K extends keyof T>(event: K, handler: (data: T[K]) => void) => () => void;
@@ -458,4 +507,4 @@ declare function defineElement(name: string, component: (props: Record<string, u
458
507
  */
459
508
  declare function svgElement(tag: string, props?: Record<string, unknown>, ...nodes: (SVGElement | string)[]): SVGElement;
460
509
 
461
- export { type AttributeSource as A, type BoundFieldProps as B, type CustomElementOptions as C, intersection as D, lazyLoad as E, type FieldConfig as F, matchesPattern as G, max as H, type IntersectionResult as I, maxLength as J, min as K, minLength as L, type MaskOptions as M, pagination as N, phoneMask as O, removeScopedStyle as P, required as Q, scopedStyle as R, ssnMask as S, type Toast as T, svgElement as U, type ValidatorFn as V, timeMask as W, toast as X, withScopedStyle as Y, zipMask as Z, __resetDialogStack as _, type AttributeValue as a, FocusTrap as b, type FormConfig as c, type FormField as d, type FormReturn as e, type ToastInstance as f, VirtualList as g, type VirtualListProps as h, announce as i, aria as j, bindAttrs as k, bindBoolAttr as l, bindData as m, bindField as n, creditCardMask as o, custom as p, dateMask as q, defineElement as r, dialog as s, email as t, eventBus as u, focus as v, form as w, hotkey as x, infiniteScroll as y, inputMask as z };
510
+ export { zipMask as $, type AttributeSource as A, type BoundFieldProps as B, type CustomElementOptions as C, intersection as D, lazyLoad as E, type FieldConfig as F, matchesPattern as G, max as H, type IntersectionResult as I, maxLength as J, min as K, minLength as L, type MaskOptions as M, pagination as N, phoneMask as O, type PaginationResult as P, removeScopedStyle as Q, required as R, scopedStyle as S, type Toast as T, ssnMask as U, type ValidatorFn as V, svgElement as W, timeMask as X, toast as Y, withScopedStyle as Z, __resetDialogStack as _, type AttributeValue as a, FocusTrap as b, type FormConfig as c, type FormField as d, type FormReturn as e, type ToastInstance as f, VirtualList as g, type VirtualListProps as h, announce as i, aria as j, bindAttrs as k, bindBoolAttr as l, bindData as m, bindField as n, creditCardMask as o, custom as p, dateMask as q, defineElement as r, dialog as s, email as t, eventBus as u, focus as v, form as w, hotkey as x, infiniteScroll as y, inputMask as z };