sibujs 1.2.0 → 1.3.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.
- package/README.md +29 -25
- package/dist/browser.cjs +804 -2
- package/dist/browser.d.cts +591 -1
- package/dist/browser.d.ts +591 -1
- package/dist/browser.js +50 -8
- package/dist/build.cjs +654 -144
- package/dist/build.js +14 -12
- package/dist/cdn.global.js +188 -7
- package/dist/chunk-2BYQDGN3.js +742 -0
- package/dist/chunk-32DY64NT.js +282 -0
- package/dist/chunk-3AIRKM3B.js +1263 -0
- package/dist/chunk-3X2YG6YM.js +505 -0
- package/dist/chunk-5X6PP2UK.js +28 -0
- package/dist/chunk-77L6NL3X.js +1097 -0
- package/dist/chunk-BGN5ZMP4.js +26 -0
- package/dist/chunk-BTU3TJDS.js +365 -0
- package/dist/chunk-CHF5OHIA.js +61 -0
- package/dist/chunk-CMBFNA7L.js +27 -0
- package/dist/chunk-DAHRH4ON.js +331 -0
- package/dist/chunk-EBGIRKQY.js +616 -0
- package/dist/chunk-EUZND3CB.js +27 -0
- package/dist/chunk-F3FA4F32.js +292 -0
- package/dist/chunk-JAKHTMQU.js +1000 -0
- package/dist/chunk-JCI5M6U6.js +956 -0
- package/dist/chunk-KQPDEVVS.js +398 -0
- package/dist/chunk-NEKUBFPT.js +60 -0
- package/dist/chunk-NYVAC6P5.js +37 -0
- package/dist/chunk-PTQJDMRT.js +146 -0
- package/dist/chunk-QWZG56ET.js +2744 -0
- package/dist/chunk-TSOKIX5Z.js +654 -0
- package/dist/chunk-VRW3FULF.js +725 -0
- package/dist/chunk-WZSPOOER.js +84 -0
- package/dist/chunk-YT6HQ6AM.js +14 -0
- package/dist/chunk-ZD6OAMTH.js +277 -0
- package/dist/contracts-DDrwxvJ-.d.cts +245 -0
- package/dist/contracts-DDrwxvJ-.d.ts +245 -0
- package/dist/data.cjs +35 -2
- package/dist/data.d.cts +7 -0
- package/dist/data.d.ts +7 -0
- package/dist/data.js +9 -8
- package/dist/devtools.cjs +122 -0
- package/dist/devtools.d.cts +69 -461
- package/dist/devtools.d.ts +69 -461
- package/dist/devtools.js +127 -6
- package/dist/ecosystem.cjs +23 -6
- package/dist/ecosystem.d.cts +1 -1
- package/dist/ecosystem.d.ts +1 -1
- package/dist/ecosystem.js +10 -9
- package/dist/extras.cjs +1207 -65
- package/dist/extras.d.cts +5 -5
- package/dist/extras.d.ts +5 -5
- package/dist/extras.js +69 -24
- package/dist/index.cjs +663 -144
- package/dist/index.d.cts +397 -17
- package/dist/index.d.ts +397 -17
- package/dist/index.js +39 -17
- package/dist/introspect-BumjnBKr.d.cts +477 -0
- package/dist/introspect-CZrlcaYy.d.ts +477 -0
- package/dist/introspect-Cb0zgpi2.d.cts +477 -0
- package/dist/introspect-Y2xNXGSf.d.ts +477 -0
- package/dist/motion.js +4 -4
- package/dist/patterns.cjs +51 -2
- package/dist/patterns.d.cts +18 -8
- package/dist/patterns.d.ts +18 -8
- package/dist/patterns.js +7 -7
- package/dist/performance.js +4 -4
- package/dist/plugins.cjs +428 -81
- package/dist/plugins.d.cts +27 -4
- package/dist/plugins.d.ts +27 -4
- package/dist/plugins.js +156 -37
- package/dist/ssr-4PBXAOO3.js +40 -0
- package/dist/ssr-Do_SiVoL.d.cts +201 -0
- package/dist/ssr-Do_SiVoL.d.ts +201 -0
- package/dist/ssr.cjs +312 -60
- package/dist/ssr.d.cts +10 -1
- package/dist/ssr.d.ts +10 -1
- package/dist/ssr.js +13 -10
- package/dist/tagFactory-DaJ0YWX6.d.cts +47 -0
- package/dist/tagFactory-DaJ0YWX6.d.ts +47 -0
- package/dist/testing.cjs +233 -2
- package/dist/testing.d.cts +42 -1
- package/dist/testing.d.ts +42 -1
- package/dist/testing.js +129 -2
- package/dist/ui.cjs +374 -3
- package/dist/ui.d.cts +252 -2
- package/dist/ui.d.ts +252 -2
- package/dist/ui.js +328 -8
- package/dist/widgets.js +7 -7
- package/package.json +1 -1
package/dist/ui.d.cts
CHANGED
|
@@ -1,5 +1,153 @@
|
|
|
1
1
|
export { B as BoundFieldProps, C as CustomElementOptions, F as FieldConfig, a as FocusTrap, b as FormConfig, c as FormField, d as FormReturn, I as IntersectionResult, M as MaskOptions, T as Toast, e as ToastInstance, V as ValidatorFn, f as VirtualList, g as VirtualListProps, h as announce, i as aria, j as bindAttrs, k as bindBoolAttr, l as bindData, m as bindField, n as creditCardMask, o as custom, p as dateMask, q as defineElement, r as dialog, s as email, t as eventBus, u as focus, v as form, w as hotkey, x as infiniteScroll, y as inputMask, z as intersection, A as lazyLoad, D as matchesPattern, E as max, G as maxLength, H as min, J as minLength, K as pagination, L as phoneMask, N as removeScopedStyle, O as required, P as scopedStyle, Q as ssnMask, R as svgElement, S as timeMask, U as toast, W as withScopedStyle, X as zipMask } from './customElement-D2DJp_xn.cjs';
|
|
2
|
-
export { C as ComponentProps, P as PropDef, a as PropSchema, R as RenderProp, V as Validator, b as assertType, c as composable, d as compose, e as createGuard, f as createSlots, g as defineComponent, h as defineSlottedComponent, i as defineStrictComponent, v as validateProps, j as validators, w as withBoundary, k as withDefaults, l as withProps, m as withWrapper } from './contracts-
|
|
2
|
+
export { C as ComponentProps, P as PropDef, a as PropSchema, R as RenderProp, V as Validator, b as assertType, c as composable, d as compose, e as createGuard, f as createSlots, g as defineComponent, h as defineSlottedComponent, i as defineStrictComponent, v as validateProps, j as validators, w as withBoundary, k as withDefaults, l as withProps, m as withWrapper } from './contracts-DDrwxvJ-.cjs';
|
|
3
|
+
|
|
4
|
+
interface FormActionHandle<TArgs extends unknown[], TResult> {
|
|
5
|
+
/** Invoke the action. Rejections become `error()`, resolutions `result()`. */
|
|
6
|
+
run: (...args: TArgs) => Promise<void>;
|
|
7
|
+
/** True while the underlying promise is unresolved. */
|
|
8
|
+
pending: () => boolean;
|
|
9
|
+
/** Last caught error, or `null`. */
|
|
10
|
+
error: () => unknown;
|
|
11
|
+
/** Last resolved value, or `null`. */
|
|
12
|
+
result: () => TResult | null;
|
|
13
|
+
/** Clear result and error without affecting an in-flight call. */
|
|
14
|
+
reset: () => void;
|
|
15
|
+
/**
|
|
16
|
+
* A ready-to-attach submit handler for `<form>` elements. Calls
|
|
17
|
+
* `e.preventDefault()`, builds a `FormData`, and passes it to the
|
|
18
|
+
* underlying action. Only available when `TArgs = [FormData]`.
|
|
19
|
+
*/
|
|
20
|
+
onSubmit: (e: Event) => void;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Wrap an async function into a reactive form-action handle.
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* ```ts
|
|
27
|
+
* const save = formAction(async (data: FormData) => {
|
|
28
|
+
* const res = await fetch("/api/save", { method: "POST", body: data });
|
|
29
|
+
* if (!res.ok) throw new Error("Save failed");
|
|
30
|
+
* return res.json();
|
|
31
|
+
* });
|
|
32
|
+
*
|
|
33
|
+
* form({
|
|
34
|
+
* on: { submit: save.onSubmit },
|
|
35
|
+
* nodes: [
|
|
36
|
+
* input({ name: "title" }),
|
|
37
|
+
* button({ disabled: save.pending, nodes: () => (save.pending() ? "Saving..." : "Save") }),
|
|
38
|
+
* when(() => save.error() != null, () => div({ class: "error", nodes: () => String(save.error()) })),
|
|
39
|
+
* ],
|
|
40
|
+
* });
|
|
41
|
+
* ```
|
|
42
|
+
*/
|
|
43
|
+
declare function formAction<TArgs extends unknown[], TResult>(fn: (...args: TArgs) => Promise<TResult>): FormActionHandle<TArgs, TResult>;
|
|
44
|
+
|
|
45
|
+
interface FocusManagerOptions {
|
|
46
|
+
/** CSS selector for focusable descendants. Default matches common form/link controls. */
|
|
47
|
+
selector?: string;
|
|
48
|
+
/** Wrap focus from last→first and first→last. Default `true`. */
|
|
49
|
+
loop?: boolean;
|
|
50
|
+
}
|
|
51
|
+
interface FocusManagerHandle {
|
|
52
|
+
/** Move focus to the first focusable descendant. */
|
|
53
|
+
focusFirst: () => void;
|
|
54
|
+
/** Move focus to the last focusable descendant. */
|
|
55
|
+
focusLast: () => void;
|
|
56
|
+
/** Move focus to the next focusable descendant relative to `document.activeElement`. */
|
|
57
|
+
focusNext: () => void;
|
|
58
|
+
/** Move focus to the previous focusable descendant relative to `document.activeElement`. */
|
|
59
|
+
focusPrev: () => void;
|
|
60
|
+
/** Return the currently focusable descendants, in order. */
|
|
61
|
+
items: () => HTMLElement[];
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Build a focus manager scoped to a container. The manager offers
|
|
65
|
+
* `focusFirst` / `focusLast` / `focusNext` / `focusPrev` helpers that
|
|
66
|
+
* walk the focusable descendants in DOM order. It is the building
|
|
67
|
+
* block for custom listboxes, toolbars, and menus.
|
|
68
|
+
*
|
|
69
|
+
* This is a DOM-read utility — the item list is fetched fresh on every
|
|
70
|
+
* call so dynamic content is handled automatically.
|
|
71
|
+
*/
|
|
72
|
+
declare function createFocusManager(container: HTMLElement, options?: FocusManagerOptions): FocusManagerHandle;
|
|
73
|
+
interface ListboxOptions {
|
|
74
|
+
/** Whether the listbox is multi-select. Default `false`. */
|
|
75
|
+
multiple?: boolean;
|
|
76
|
+
/** CSS selector for option elements. Default `[role="option"]`. */
|
|
77
|
+
optionSelector?: string;
|
|
78
|
+
/** Called when the user commits a selection. */
|
|
79
|
+
onSelect?: (value: string) => void;
|
|
80
|
+
}
|
|
81
|
+
interface ListboxHandle {
|
|
82
|
+
/** Reactive value: the currently-active (highlighted) option value. */
|
|
83
|
+
activeValue: () => string | null;
|
|
84
|
+
/** Reactive value: the currently-selected option value (single-select) or CSV (multiple). */
|
|
85
|
+
selectedValue: () => string | null;
|
|
86
|
+
/** Stable id that can be used as `aria-activedescendant` on the trigger. */
|
|
87
|
+
activeDescendantId: () => string | null;
|
|
88
|
+
/** Cleanup: removes listeners. */
|
|
89
|
+
dispose: () => void;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Build an ARIA listbox on top of an existing container element. The
|
|
93
|
+
* listbox wires:
|
|
94
|
+
* - `role="listbox"` + `aria-multiselectable` on the container
|
|
95
|
+
* - keyboard navigation (Arrow keys, Home, End, Enter, Space)
|
|
96
|
+
* - `aria-activedescendant` tracking
|
|
97
|
+
* - option highlight via `data-highlighted`
|
|
98
|
+
*
|
|
99
|
+
* Each option must expose a `data-value` attribute. Options get a
|
|
100
|
+
* stable `id` on mount so the container's `aria-activedescendant`
|
|
101
|
+
* can point at the active one.
|
|
102
|
+
*
|
|
103
|
+
* @example
|
|
104
|
+
* ```ts
|
|
105
|
+
* const container = ul({ nodes: [
|
|
106
|
+
* li({ role: "option", "data-value": "a", nodes: "Apple" }),
|
|
107
|
+
* li({ role: "option", "data-value": "b", nodes: "Banana" }),
|
|
108
|
+
* ]}) as HTMLElement;
|
|
109
|
+
*
|
|
110
|
+
* const lb = createListbox(container, { onSelect: v => console.log(v) });
|
|
111
|
+
* ```
|
|
112
|
+
*/
|
|
113
|
+
declare function createListbox(container: HTMLElement, options?: ListboxOptions): ListboxHandle;
|
|
114
|
+
interface DialogAriaOptions {
|
|
115
|
+
/** Labelled-by id — the dialog title's id. */
|
|
116
|
+
labelledBy?: string;
|
|
117
|
+
/** Described-by id — the dialog description's id. */
|
|
118
|
+
describedBy?: string;
|
|
119
|
+
/** Modal dialog (true) or alertdialog (false for "alert"). Default `true`. */
|
|
120
|
+
modal?: boolean;
|
|
121
|
+
/** Use `role="alertdialog"` instead of `role="dialog"`. */
|
|
122
|
+
alert?: boolean;
|
|
123
|
+
}
|
|
124
|
+
interface DialogAriaHandle {
|
|
125
|
+
/** Auto-generated id that should be put on the title element. */
|
|
126
|
+
titleId: string;
|
|
127
|
+
/** Auto-generated id that should be put on the description element. */
|
|
128
|
+
descriptionId: string;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Apply the ARIA attributes needed for an accessible dialog to an
|
|
132
|
+
* existing element. Returns stable ids that the caller can pass to the
|
|
133
|
+
* title and description children so `aria-labelledby` / `aria-describedby`
|
|
134
|
+
* resolve correctly.
|
|
135
|
+
*
|
|
136
|
+
* Does NOT handle focus trapping — use `FocusTrap` or `createFocusManager`
|
|
137
|
+
* for that. Does NOT handle escape-to-close — wire that in the caller.
|
|
138
|
+
* The primitive is intentionally tight: it only owns the ARIA surface.
|
|
139
|
+
*
|
|
140
|
+
* @example
|
|
141
|
+
* ```ts
|
|
142
|
+
* const dlg = document.createElement("div");
|
|
143
|
+
* const aria = createDialogAria(dlg, { alert: false });
|
|
144
|
+
* dlg.append(
|
|
145
|
+
* h2({ id: aria.titleId, nodes: "Delete?" }),
|
|
146
|
+
* p({ id: aria.descriptionId, nodes: "This cannot be undone." }),
|
|
147
|
+
* );
|
|
148
|
+
* ```
|
|
149
|
+
*/
|
|
150
|
+
declare function createDialogAria(element: HTMLElement, options?: DialogAriaOptions): DialogAriaHandle;
|
|
3
151
|
|
|
4
152
|
/**
|
|
5
153
|
* lazyEffect creates an effect that only activates when the target element
|
|
@@ -25,4 +173,106 @@ export { C as ComponentProps, P as PropDef, a as PropSchema, R as RenderProp, V
|
|
|
25
173
|
*/
|
|
26
174
|
declare function lazyEffect(element: HTMLElement, effectFn: () => void, options?: IntersectionObserverInit): () => void;
|
|
27
175
|
|
|
28
|
-
|
|
176
|
+
/**
|
|
177
|
+
* Declarative, disposable timers.
|
|
178
|
+
*
|
|
179
|
+
* The native `setInterval` / `setTimeout` are easy to leak if the owning
|
|
180
|
+
* component is destroyed before they fire. These helpers return a handle
|
|
181
|
+
* with a `stop()` function plus (for interval) `pause()` / `resume()`, and
|
|
182
|
+
* optionally integrate with the sibujs disposal lifecycle through the
|
|
183
|
+
* returned cleanup.
|
|
184
|
+
*
|
|
185
|
+
* Neither helper depends on any reactive context — they're pure JS with
|
|
186
|
+
* nicer ergonomics for UIs that need to start and stop timers safely.
|
|
187
|
+
*/
|
|
188
|
+
interface IntervalHandle {
|
|
189
|
+
/** Stop the interval. Safe to call multiple times. */
|
|
190
|
+
stop: () => void;
|
|
191
|
+
/** Pause (preserving remaining ticks until `resume()`). */
|
|
192
|
+
pause: () => void;
|
|
193
|
+
/** Resume a paused interval. */
|
|
194
|
+
resume: () => void;
|
|
195
|
+
/** Whether the interval is currently running. */
|
|
196
|
+
isRunning: () => boolean;
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Like `setInterval(fn, ms)` but returns a handle that can be stopped,
|
|
200
|
+
* paused, and resumed without leaking closures.
|
|
201
|
+
*
|
|
202
|
+
* @example
|
|
203
|
+
* ```ts
|
|
204
|
+
* const tick = interval(() => setCount(c => c + 1), 1000);
|
|
205
|
+
* // later
|
|
206
|
+
* tick.pause();
|
|
207
|
+
* tick.resume();
|
|
208
|
+
* tick.stop();
|
|
209
|
+
* ```
|
|
210
|
+
*/
|
|
211
|
+
declare function interval(fn: () => void, ms: number): IntervalHandle;
|
|
212
|
+
interface TimeoutHandle {
|
|
213
|
+
/** Cancel the pending timeout. No-op if already fired. */
|
|
214
|
+
cancel: () => void;
|
|
215
|
+
/** Whether the callback has run or been cancelled. */
|
|
216
|
+
isPending: () => boolean;
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Like `setTimeout(fn, ms)` but returns a handle with an explicit `cancel()`.
|
|
220
|
+
*
|
|
221
|
+
* @example
|
|
222
|
+
* ```ts
|
|
223
|
+
* const t = timeout(() => setVisible(false), 3000);
|
|
224
|
+
* // cancel on user interaction
|
|
225
|
+
* input({ on: { focus: () => t.cancel() } });
|
|
226
|
+
* ```
|
|
227
|
+
*/
|
|
228
|
+
declare function timeout(fn: () => void, ms: number): TimeoutHandle;
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* hover attaches reactive hover tracking to an element. Uses `pointerenter`
|
|
232
|
+
* and `pointerleave` so it works on touch devices where a sustained press
|
|
233
|
+
* triggers a hover state.
|
|
234
|
+
*
|
|
235
|
+
* @param target Element to track
|
|
236
|
+
* @returns `{ hovered, dispose }` — reactive boolean plus cleanup
|
|
237
|
+
*
|
|
238
|
+
* @example
|
|
239
|
+
* ```ts
|
|
240
|
+
* const el = div({ class: "card" });
|
|
241
|
+
* const h = hover(el);
|
|
242
|
+
* effect(() => { el.classList.toggle("lifted", h.hovered()); });
|
|
243
|
+
* ```
|
|
244
|
+
*/
|
|
245
|
+
declare function hover(target: HTMLElement): {
|
|
246
|
+
hovered: () => boolean;
|
|
247
|
+
dispose: () => void;
|
|
248
|
+
};
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* scrollLock stacks body scroll locks — useful when a modal / drawer / sheet
|
|
252
|
+
* stack is open and background scroll must be suppressed.
|
|
253
|
+
*
|
|
254
|
+
* Each `lock()` call increments an internal counter and applies
|
|
255
|
+
* `overflow: hidden` + preserves the scrollbar-width padding to prevent
|
|
256
|
+
* layout shift. Calling `unlock()` decrements; when the counter hits zero
|
|
257
|
+
* the previous body style is restored.
|
|
258
|
+
*
|
|
259
|
+
* Safe to call from multiple concurrent overlays — the last one to unlock
|
|
260
|
+
* releases the lock.
|
|
261
|
+
*
|
|
262
|
+
* @example
|
|
263
|
+
* ```ts
|
|
264
|
+
* const lock = scrollLock();
|
|
265
|
+
* lock.lock();
|
|
266
|
+
* // ... modal open
|
|
267
|
+
* lock.unlock();
|
|
268
|
+
* ```
|
|
269
|
+
*/
|
|
270
|
+
interface ScrollLockHandle {
|
|
271
|
+
/** Activate a lock. Idempotent per-handle if called twice. */
|
|
272
|
+
lock: () => void;
|
|
273
|
+
/** Release this handle's lock. Idempotent. */
|
|
274
|
+
unlock: () => void;
|
|
275
|
+
}
|
|
276
|
+
declare function scrollLock(): ScrollLockHandle;
|
|
277
|
+
|
|
278
|
+
export { type DialogAriaHandle, type DialogAriaOptions, type FocusManagerHandle, type FocusManagerOptions, type FormActionHandle, type IntervalHandle, type ListboxHandle, type ListboxOptions, type ScrollLockHandle, type TimeoutHandle, createDialogAria, createFocusManager, createListbox, formAction, hover, interval, lazyEffect, scrollLock, timeout };
|
package/dist/ui.d.ts
CHANGED
|
@@ -1,5 +1,153 @@
|
|
|
1
1
|
export { B as BoundFieldProps, C as CustomElementOptions, F as FieldConfig, a as FocusTrap, b as FormConfig, c as FormField, d as FormReturn, I as IntersectionResult, M as MaskOptions, T as Toast, e as ToastInstance, V as ValidatorFn, f as VirtualList, g as VirtualListProps, h as announce, i as aria, j as bindAttrs, k as bindBoolAttr, l as bindData, m as bindField, n as creditCardMask, o as custom, p as dateMask, q as defineElement, r as dialog, s as email, t as eventBus, u as focus, v as form, w as hotkey, x as infiniteScroll, y as inputMask, z as intersection, A as lazyLoad, D as matchesPattern, E as max, G as maxLength, H as min, J as minLength, K as pagination, L as phoneMask, N as removeScopedStyle, O as required, P as scopedStyle, Q as ssnMask, R as svgElement, S as timeMask, U as toast, W as withScopedStyle, X as zipMask } from './customElement-D2DJp_xn.js';
|
|
2
|
-
export { C as ComponentProps, P as PropDef, a as PropSchema, R as RenderProp, V as Validator, b as assertType, c as composable, d as compose, e as createGuard, f as createSlots, g as defineComponent, h as defineSlottedComponent, i as defineStrictComponent, v as validateProps, j as validators, w as withBoundary, k as withDefaults, l as withProps, m as withWrapper } from './contracts-
|
|
2
|
+
export { C as ComponentProps, P as PropDef, a as PropSchema, R as RenderProp, V as Validator, b as assertType, c as composable, d as compose, e as createGuard, f as createSlots, g as defineComponent, h as defineSlottedComponent, i as defineStrictComponent, v as validateProps, j as validators, w as withBoundary, k as withDefaults, l as withProps, m as withWrapper } from './contracts-DDrwxvJ-.js';
|
|
3
|
+
|
|
4
|
+
interface FormActionHandle<TArgs extends unknown[], TResult> {
|
|
5
|
+
/** Invoke the action. Rejections become `error()`, resolutions `result()`. */
|
|
6
|
+
run: (...args: TArgs) => Promise<void>;
|
|
7
|
+
/** True while the underlying promise is unresolved. */
|
|
8
|
+
pending: () => boolean;
|
|
9
|
+
/** Last caught error, or `null`. */
|
|
10
|
+
error: () => unknown;
|
|
11
|
+
/** Last resolved value, or `null`. */
|
|
12
|
+
result: () => TResult | null;
|
|
13
|
+
/** Clear result and error without affecting an in-flight call. */
|
|
14
|
+
reset: () => void;
|
|
15
|
+
/**
|
|
16
|
+
* A ready-to-attach submit handler for `<form>` elements. Calls
|
|
17
|
+
* `e.preventDefault()`, builds a `FormData`, and passes it to the
|
|
18
|
+
* underlying action. Only available when `TArgs = [FormData]`.
|
|
19
|
+
*/
|
|
20
|
+
onSubmit: (e: Event) => void;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Wrap an async function into a reactive form-action handle.
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* ```ts
|
|
27
|
+
* const save = formAction(async (data: FormData) => {
|
|
28
|
+
* const res = await fetch("/api/save", { method: "POST", body: data });
|
|
29
|
+
* if (!res.ok) throw new Error("Save failed");
|
|
30
|
+
* return res.json();
|
|
31
|
+
* });
|
|
32
|
+
*
|
|
33
|
+
* form({
|
|
34
|
+
* on: { submit: save.onSubmit },
|
|
35
|
+
* nodes: [
|
|
36
|
+
* input({ name: "title" }),
|
|
37
|
+
* button({ disabled: save.pending, nodes: () => (save.pending() ? "Saving..." : "Save") }),
|
|
38
|
+
* when(() => save.error() != null, () => div({ class: "error", nodes: () => String(save.error()) })),
|
|
39
|
+
* ],
|
|
40
|
+
* });
|
|
41
|
+
* ```
|
|
42
|
+
*/
|
|
43
|
+
declare function formAction<TArgs extends unknown[], TResult>(fn: (...args: TArgs) => Promise<TResult>): FormActionHandle<TArgs, TResult>;
|
|
44
|
+
|
|
45
|
+
interface FocusManagerOptions {
|
|
46
|
+
/** CSS selector for focusable descendants. Default matches common form/link controls. */
|
|
47
|
+
selector?: string;
|
|
48
|
+
/** Wrap focus from last→first and first→last. Default `true`. */
|
|
49
|
+
loop?: boolean;
|
|
50
|
+
}
|
|
51
|
+
interface FocusManagerHandle {
|
|
52
|
+
/** Move focus to the first focusable descendant. */
|
|
53
|
+
focusFirst: () => void;
|
|
54
|
+
/** Move focus to the last focusable descendant. */
|
|
55
|
+
focusLast: () => void;
|
|
56
|
+
/** Move focus to the next focusable descendant relative to `document.activeElement`. */
|
|
57
|
+
focusNext: () => void;
|
|
58
|
+
/** Move focus to the previous focusable descendant relative to `document.activeElement`. */
|
|
59
|
+
focusPrev: () => void;
|
|
60
|
+
/** Return the currently focusable descendants, in order. */
|
|
61
|
+
items: () => HTMLElement[];
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Build a focus manager scoped to a container. The manager offers
|
|
65
|
+
* `focusFirst` / `focusLast` / `focusNext` / `focusPrev` helpers that
|
|
66
|
+
* walk the focusable descendants in DOM order. It is the building
|
|
67
|
+
* block for custom listboxes, toolbars, and menus.
|
|
68
|
+
*
|
|
69
|
+
* This is a DOM-read utility — the item list is fetched fresh on every
|
|
70
|
+
* call so dynamic content is handled automatically.
|
|
71
|
+
*/
|
|
72
|
+
declare function createFocusManager(container: HTMLElement, options?: FocusManagerOptions): FocusManagerHandle;
|
|
73
|
+
interface ListboxOptions {
|
|
74
|
+
/** Whether the listbox is multi-select. Default `false`. */
|
|
75
|
+
multiple?: boolean;
|
|
76
|
+
/** CSS selector for option elements. Default `[role="option"]`. */
|
|
77
|
+
optionSelector?: string;
|
|
78
|
+
/** Called when the user commits a selection. */
|
|
79
|
+
onSelect?: (value: string) => void;
|
|
80
|
+
}
|
|
81
|
+
interface ListboxHandle {
|
|
82
|
+
/** Reactive value: the currently-active (highlighted) option value. */
|
|
83
|
+
activeValue: () => string | null;
|
|
84
|
+
/** Reactive value: the currently-selected option value (single-select) or CSV (multiple). */
|
|
85
|
+
selectedValue: () => string | null;
|
|
86
|
+
/** Stable id that can be used as `aria-activedescendant` on the trigger. */
|
|
87
|
+
activeDescendantId: () => string | null;
|
|
88
|
+
/** Cleanup: removes listeners. */
|
|
89
|
+
dispose: () => void;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Build an ARIA listbox on top of an existing container element. The
|
|
93
|
+
* listbox wires:
|
|
94
|
+
* - `role="listbox"` + `aria-multiselectable` on the container
|
|
95
|
+
* - keyboard navigation (Arrow keys, Home, End, Enter, Space)
|
|
96
|
+
* - `aria-activedescendant` tracking
|
|
97
|
+
* - option highlight via `data-highlighted`
|
|
98
|
+
*
|
|
99
|
+
* Each option must expose a `data-value` attribute. Options get a
|
|
100
|
+
* stable `id` on mount so the container's `aria-activedescendant`
|
|
101
|
+
* can point at the active one.
|
|
102
|
+
*
|
|
103
|
+
* @example
|
|
104
|
+
* ```ts
|
|
105
|
+
* const container = ul({ nodes: [
|
|
106
|
+
* li({ role: "option", "data-value": "a", nodes: "Apple" }),
|
|
107
|
+
* li({ role: "option", "data-value": "b", nodes: "Banana" }),
|
|
108
|
+
* ]}) as HTMLElement;
|
|
109
|
+
*
|
|
110
|
+
* const lb = createListbox(container, { onSelect: v => console.log(v) });
|
|
111
|
+
* ```
|
|
112
|
+
*/
|
|
113
|
+
declare function createListbox(container: HTMLElement, options?: ListboxOptions): ListboxHandle;
|
|
114
|
+
interface DialogAriaOptions {
|
|
115
|
+
/** Labelled-by id — the dialog title's id. */
|
|
116
|
+
labelledBy?: string;
|
|
117
|
+
/** Described-by id — the dialog description's id. */
|
|
118
|
+
describedBy?: string;
|
|
119
|
+
/** Modal dialog (true) or alertdialog (false for "alert"). Default `true`. */
|
|
120
|
+
modal?: boolean;
|
|
121
|
+
/** Use `role="alertdialog"` instead of `role="dialog"`. */
|
|
122
|
+
alert?: boolean;
|
|
123
|
+
}
|
|
124
|
+
interface DialogAriaHandle {
|
|
125
|
+
/** Auto-generated id that should be put on the title element. */
|
|
126
|
+
titleId: string;
|
|
127
|
+
/** Auto-generated id that should be put on the description element. */
|
|
128
|
+
descriptionId: string;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Apply the ARIA attributes needed for an accessible dialog to an
|
|
132
|
+
* existing element. Returns stable ids that the caller can pass to the
|
|
133
|
+
* title and description children so `aria-labelledby` / `aria-describedby`
|
|
134
|
+
* resolve correctly.
|
|
135
|
+
*
|
|
136
|
+
* Does NOT handle focus trapping — use `FocusTrap` or `createFocusManager`
|
|
137
|
+
* for that. Does NOT handle escape-to-close — wire that in the caller.
|
|
138
|
+
* The primitive is intentionally tight: it only owns the ARIA surface.
|
|
139
|
+
*
|
|
140
|
+
* @example
|
|
141
|
+
* ```ts
|
|
142
|
+
* const dlg = document.createElement("div");
|
|
143
|
+
* const aria = createDialogAria(dlg, { alert: false });
|
|
144
|
+
* dlg.append(
|
|
145
|
+
* h2({ id: aria.titleId, nodes: "Delete?" }),
|
|
146
|
+
* p({ id: aria.descriptionId, nodes: "This cannot be undone." }),
|
|
147
|
+
* );
|
|
148
|
+
* ```
|
|
149
|
+
*/
|
|
150
|
+
declare function createDialogAria(element: HTMLElement, options?: DialogAriaOptions): DialogAriaHandle;
|
|
3
151
|
|
|
4
152
|
/**
|
|
5
153
|
* lazyEffect creates an effect that only activates when the target element
|
|
@@ -25,4 +173,106 @@ export { C as ComponentProps, P as PropDef, a as PropSchema, R as RenderProp, V
|
|
|
25
173
|
*/
|
|
26
174
|
declare function lazyEffect(element: HTMLElement, effectFn: () => void, options?: IntersectionObserverInit): () => void;
|
|
27
175
|
|
|
28
|
-
|
|
176
|
+
/**
|
|
177
|
+
* Declarative, disposable timers.
|
|
178
|
+
*
|
|
179
|
+
* The native `setInterval` / `setTimeout` are easy to leak if the owning
|
|
180
|
+
* component is destroyed before they fire. These helpers return a handle
|
|
181
|
+
* with a `stop()` function plus (for interval) `pause()` / `resume()`, and
|
|
182
|
+
* optionally integrate with the sibujs disposal lifecycle through the
|
|
183
|
+
* returned cleanup.
|
|
184
|
+
*
|
|
185
|
+
* Neither helper depends on any reactive context — they're pure JS with
|
|
186
|
+
* nicer ergonomics for UIs that need to start and stop timers safely.
|
|
187
|
+
*/
|
|
188
|
+
interface IntervalHandle {
|
|
189
|
+
/** Stop the interval. Safe to call multiple times. */
|
|
190
|
+
stop: () => void;
|
|
191
|
+
/** Pause (preserving remaining ticks until `resume()`). */
|
|
192
|
+
pause: () => void;
|
|
193
|
+
/** Resume a paused interval. */
|
|
194
|
+
resume: () => void;
|
|
195
|
+
/** Whether the interval is currently running. */
|
|
196
|
+
isRunning: () => boolean;
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Like `setInterval(fn, ms)` but returns a handle that can be stopped,
|
|
200
|
+
* paused, and resumed without leaking closures.
|
|
201
|
+
*
|
|
202
|
+
* @example
|
|
203
|
+
* ```ts
|
|
204
|
+
* const tick = interval(() => setCount(c => c + 1), 1000);
|
|
205
|
+
* // later
|
|
206
|
+
* tick.pause();
|
|
207
|
+
* tick.resume();
|
|
208
|
+
* tick.stop();
|
|
209
|
+
* ```
|
|
210
|
+
*/
|
|
211
|
+
declare function interval(fn: () => void, ms: number): IntervalHandle;
|
|
212
|
+
interface TimeoutHandle {
|
|
213
|
+
/** Cancel the pending timeout. No-op if already fired. */
|
|
214
|
+
cancel: () => void;
|
|
215
|
+
/** Whether the callback has run or been cancelled. */
|
|
216
|
+
isPending: () => boolean;
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Like `setTimeout(fn, ms)` but returns a handle with an explicit `cancel()`.
|
|
220
|
+
*
|
|
221
|
+
* @example
|
|
222
|
+
* ```ts
|
|
223
|
+
* const t = timeout(() => setVisible(false), 3000);
|
|
224
|
+
* // cancel on user interaction
|
|
225
|
+
* input({ on: { focus: () => t.cancel() } });
|
|
226
|
+
* ```
|
|
227
|
+
*/
|
|
228
|
+
declare function timeout(fn: () => void, ms: number): TimeoutHandle;
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* hover attaches reactive hover tracking to an element. Uses `pointerenter`
|
|
232
|
+
* and `pointerleave` so it works on touch devices where a sustained press
|
|
233
|
+
* triggers a hover state.
|
|
234
|
+
*
|
|
235
|
+
* @param target Element to track
|
|
236
|
+
* @returns `{ hovered, dispose }` — reactive boolean plus cleanup
|
|
237
|
+
*
|
|
238
|
+
* @example
|
|
239
|
+
* ```ts
|
|
240
|
+
* const el = div({ class: "card" });
|
|
241
|
+
* const h = hover(el);
|
|
242
|
+
* effect(() => { el.classList.toggle("lifted", h.hovered()); });
|
|
243
|
+
* ```
|
|
244
|
+
*/
|
|
245
|
+
declare function hover(target: HTMLElement): {
|
|
246
|
+
hovered: () => boolean;
|
|
247
|
+
dispose: () => void;
|
|
248
|
+
};
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* scrollLock stacks body scroll locks — useful when a modal / drawer / sheet
|
|
252
|
+
* stack is open and background scroll must be suppressed.
|
|
253
|
+
*
|
|
254
|
+
* Each `lock()` call increments an internal counter and applies
|
|
255
|
+
* `overflow: hidden` + preserves the scrollbar-width padding to prevent
|
|
256
|
+
* layout shift. Calling `unlock()` decrements; when the counter hits zero
|
|
257
|
+
* the previous body style is restored.
|
|
258
|
+
*
|
|
259
|
+
* Safe to call from multiple concurrent overlays — the last one to unlock
|
|
260
|
+
* releases the lock.
|
|
261
|
+
*
|
|
262
|
+
* @example
|
|
263
|
+
* ```ts
|
|
264
|
+
* const lock = scrollLock();
|
|
265
|
+
* lock.lock();
|
|
266
|
+
* // ... modal open
|
|
267
|
+
* lock.unlock();
|
|
268
|
+
* ```
|
|
269
|
+
*/
|
|
270
|
+
interface ScrollLockHandle {
|
|
271
|
+
/** Activate a lock. Idempotent per-handle if called twice. */
|
|
272
|
+
lock: () => void;
|
|
273
|
+
/** Release this handle's lock. Idempotent. */
|
|
274
|
+
unlock: () => void;
|
|
275
|
+
}
|
|
276
|
+
declare function scrollLock(): ScrollLockHandle;
|
|
277
|
+
|
|
278
|
+
export { type DialogAriaHandle, type DialogAriaOptions, type FocusManagerHandle, type FocusManagerOptions, type FormActionHandle, type IntervalHandle, type ListboxHandle, type ListboxOptions, type ScrollLockHandle, type TimeoutHandle, createDialogAria, createFocusManager, createListbox, formAction, hover, interval, lazyEffect, scrollLock, timeout };
|