@optionfactory/fml 9.0.0-rc15 → 9.0.0-rc18

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 (43) hide show
  1. package/README.md +16 -88
  2. package/dist/client-errors.iife.js +17 -26
  3. package/dist/client-errors.iife.js.map +1 -1
  4. package/dist/client-errors.iife.min.js.map +1 -1
  5. package/dist/custom-elements.json +61 -33
  6. package/dist/fml.css +21 -18
  7. package/dist/fml.css.map +1 -1
  8. package/dist/fml.iife.js +3504 -2055
  9. package/dist/fml.iife.js.map +1 -1
  10. package/dist/fml.iife.min.js +1 -1
  11. package/dist/fml.iife.min.js.map +1 -1
  12. package/dist/ftl.d.mts +395 -224
  13. package/dist/ftl.iife.js +400 -330
  14. package/dist/ftl.iife.js.map +1 -1
  15. package/dist/ftl.iife.min.js +1 -1
  16. package/dist/ftl.iife.min.js.map +1 -1
  17. package/dist/ftl.min.mjs +1 -1
  18. package/dist/ftl.min.mjs.map +1 -1
  19. package/dist/ftl.mjs +400 -330
  20. package/dist/ftl.mjs.map +1 -1
  21. package/dist/ful.css +21 -18
  22. package/dist/ful.css.map +1 -1
  23. package/dist/ful.d.mts +2610 -687
  24. package/dist/ful.iife.js +2960 -1552
  25. package/dist/ful.iife.js.map +1 -1
  26. package/dist/ful.iife.min.js +1 -1
  27. package/dist/ful.iife.min.js.map +1 -1
  28. package/dist/ful.min.mjs +1 -1
  29. package/dist/ful.min.mjs.map +1 -1
  30. package/dist/ful.mjs +2960 -1552
  31. package/dist/ful.mjs.map +1 -1
  32. package/dist/httpc.d.mts +139 -121
  33. package/dist/httpc.iife.js +137 -166
  34. package/dist/httpc.iife.js.map +1 -1
  35. package/dist/httpc.iife.min.js +1 -1
  36. package/dist/httpc.iife.min.js.map +1 -1
  37. package/dist/httpc.min.mjs +1 -1
  38. package/dist/httpc.min.mjs.map +1 -1
  39. package/dist/httpc.mjs +137 -166
  40. package/dist/httpc.mjs.map +1 -1
  41. package/dist/vscode.html-custom-data.json +27 -19
  42. package/dist/web-types.json +62 -38
  43. package/package.json +1 -1
package/dist/ful.d.mts CHANGED
@@ -1,25 +1,55 @@
1
1
  import { ParsedElement } from './ftl.mjs';
2
- declare const LocalStorage: {
3
- save: (k: any, v: any) => void;
4
- load: (k: any) => any;
5
- remove: (k: any) => void;
6
- pop: (k: any) => any;
7
- };
8
- declare const SessionStorage: {
9
- save: (k: any, v: any) => void;
10
- load: (k: any) => any;
11
- remove: (k: any) => void;
12
- pop: (k: any) => any;
13
- };
14
- declare const VersionedLocalStorage: {
15
- save(key: any, revision: any, data: any): void;
16
- load(key: any, revision: any): any;
2
+ import { HttpClient } from './httpc.mjs';
3
+ export type JsonStorage = {
4
+ /**
5
+ * stores `JSON.stringify(v)`
6
+ * under `k`, throwing what the encoding or the storage throws: an unreachable
7
+ * storage, a full quota, a value json cannot encode
8
+ */
9
+ save: (k: string, v: any) => void;
10
+ /**
11
+ * the value stored under `k`, decoded;
12
+ * undefined when there is none, when the storage cannot be reached, or when the
13
+ * stored text is not json, in which case the entry is also removed
14
+ */
15
+ load: (k: string) => any;
16
+ /**
17
+ * removes the entry under `k`
18
+ */
19
+ remove: (k: string) => void;
20
+ /**
21
+ * what `load` answers for `k`, removing the
22
+ * entry
23
+ */
24
+ pop: (k: string) => any;
17
25
  };
18
- declare const VersionedSessionStorage: {
19
- save(key: any, revision: any, data: any): void;
20
- load(key: any, revision: any): any;
26
+ export type VersionedJsonStorage = {
27
+ /**
28
+ * stores
29
+ * `data` with `revision` under `key`, throwing what `JsonStorage.save` throws
30
+ */
31
+ save: (key: string, revision: string, data: any) => void;
32
+ /**
33
+ * the data stored under
34
+ * `key` when it was saved with this exact revision; otherwise undefined, and the
35
+ * entry is removed, whether it holds another revision, holds no revision at
36
+ * all, or is missing
37
+ */
38
+ load: (key: string, revision: string) => any;
21
39
  };
40
+ /** `localStorage`, holding json. */
41
+ declare const LocalStorage: JsonStorage;
42
+ /** `sessionStorage`, holding json. */
43
+ declare const SessionStorage: JsonStorage;
44
+ /** `localStorage`, holding json beside a revision. */
45
+ declare const VersionedLocalStorage: VersionedJsonStorage;
46
+ /** `sessionStorage`, holding json beside a revision. */
47
+ declare const VersionedSessionStorage: VersionedJsonStorage;
22
48
  export type AsyncExtension = {
49
+ /**
50
+ * one per `asyncOn` listener the event
51
+ * reached, in the order they ran
52
+ */
23
53
  promises: Promise<any>[];
24
54
  };
25
55
  export type AsyncEvent = Event & {
@@ -27,7 +57,8 @@ export type AsyncEvent = Event & {
27
57
  };
28
58
  /**
29
59
  * @typedef {Object} AsyncExtension
30
- * @property {Promise<any>[]} promises
60
+ * @property {Promise<any>[]} promises one per `asyncOn` listener the event
61
+ * reached, in the order they ran
31
62
  * @typedef {Event & { async?: AsyncExtension }} AsyncEvent
32
63
  */
33
64
  /**
@@ -38,35 +69,57 @@ export type AsyncEvent = Event & {
38
69
  */
39
70
  declare class AsyncEvents {
40
71
  /**
41
- * Dispatches an event and handles asynchronous resolution based on the execution mode.
42
- * @param {HTMLElement} el - The target element dispatching the event.
43
- * @param {AsyncEvent} evt - The event instance.
44
- * @param {{mode?: 'broadcast' | 'pipeline' | 'delegate'}} [options] - Configuration options (defaults to 'broadcast').
45
- * @returns {Promise<any>} Resolves with an array of values for broadcasts, a single value for pipelines/delegates, or undefined.
72
+ * Dispatches `evt` on `el` synchronously, then waits for the answers of the
73
+ * `asyncOn` listeners it reached, on `el` or, for a bubbling event, on its
74
+ * ancestors. Listeners added with a plain `addEventListener` run but are not
75
+ * awaited. In `broadcast` mode, the default, it resolves with every answer
76
+ * in the order the listeners ran, an empty array when none did. In
77
+ * `pipeline` mode it accepts at most one listener and resolves with its
78
+ * answer, or undefined when none ran. In `delegate` mode it requires exactly
79
+ * one listener and resolves with its answer.
80
+ * @param {HTMLElement} el the element to dispatch on
81
+ * @param {AsyncEvent} evt the event to dispatch
82
+ * @param {{mode?: 'broadcast' | 'pipeline' | 'delegate'}} [options]
83
+ * @returns {Promise<any>} the answer or answers as the mode describes
84
+ * @throws what the first answer to fail threw or rejected with; or an
85
+ * Error naming the event and the mode when `pipeline` reached more than one
86
+ * listener or `delegate` did not reach exactly one, in which case the
87
+ * listeners have already run and their outcome is discarded. Both arrive as
88
+ * the rejection of the returned promise.
46
89
  */
47
90
  static fireAsync(el: HTMLElement, evt: AsyncEvent, options?: {
48
91
  mode?: 'broadcast' | 'pipeline' | 'delegate';
49
92
  }): Promise<any>;
50
93
  /**
51
- * Registers an asynchronous event listener wrapper.
52
- * @param {HTMLElement} el - The target element.
53
- * @param {string} type - The event name/type.
54
- * @param {Function} fn - The async listener middleware function returning the execution result.
55
- * @param {AddEventListenerOptions} [options] - Native addEventListener options.
56
- * @returns {EventListener} The underlying proxy listener function needed for cleanup via asyncOff.
94
+ * Adds a listener whose answer `fireAsync` can await. When the event
95
+ * reaches it, the listener attaches a promise to `evt.async.promises`,
96
+ * creating the list when it is the first, then calls `fn` with the event;
97
+ * the promise settles with what `fn` returns or throws, awaited when it is a
98
+ * promise. The event is dispatched the usual way, so an event fired with a
99
+ * plain `dispatchEvent` runs `fn` too, and nobody awaits its answer.
100
+ * @param {HTMLElement} el the element to listen on
101
+ * @param {string} type the event type
102
+ * @param {(evt: any) => any} fn called with the event; its result is the answer
103
+ * @param {AddEventListenerOptions} [options] passed to `addEventListener`
104
+ * @returns {EventListener} the listener actually added, which `asyncOff` takes
57
105
  */
58
- static asyncOn(el: HTMLElement, type: string, fn: Function, options?: AddEventListenerOptions): EventListener;
106
+ static asyncOn(el: HTMLElement, type: string, fn: (evt: any) => any, options?: AddEventListenerOptions): EventListener;
59
107
  /**
60
- * Unregisters an asynchronous event listener proxy.
61
- * @param {HTMLElement} el - The target element.
62
- * @param {string} type - The event name/type.
63
- * @param {EventListener} listener - The proxy listener instance previously returned by asyncOn.
64
- * @param {EventListenerOptions} [options] - Native removeEventListener options.
108
+ * Removes a listener added by `asyncOn`.
109
+ * @param {HTMLElement} el the element it listens on
110
+ * @param {string} type the event type it was added for
111
+ * @param {EventListener} listener what `asyncOn` returned, not the `fn` given to it
112
+ * @param {EventListenerOptions} [options] passed to `removeEventListener`
113
+ * @returns {void}
65
114
  */
66
115
  static asyncOff(el: HTMLElement, type: string, listener: EventListener, options?: EventListenerOptions): void;
67
116
  /**
68
- * Mixes the asynchronous execution engine extensions into target class prototypes.
69
- * @param {...Function} classes - The target class constructors to decorate.
117
+ * Assigns `fireAsync(evt, options)`, `asyncOn(type, fn, options)` and
118
+ * `asyncOff(type, listener, options)` onto each class's prototype, each
119
+ * calling the static of the same name with the instance as the element.
120
+ * Members of those names already on the prototype are overwritten.
121
+ * @param {...Function} classes the classes to extend
122
+ * @returns {void}
70
123
  */
71
124
  static mixInto(...classes: Function[]): void;
72
125
  }
@@ -74,6 +127,8 @@ export type Claim = {
74
127
  readonly stale: boolean;
75
128
  };
76
129
  /**
130
+ * A hold on one generation of a `Claims`: `stale` turns true once a later
131
+ * `take()` or `invalidate()` on it starts a newer generation.
77
132
  * @typedef {{ readonly stale: boolean }} Claim
78
133
  */
79
134
  /**
@@ -99,85 +154,95 @@ declare class Claims {
99
154
  * @returns {Claim}
100
155
  */
101
156
  hold(): Claim;
102
- /** Supersedes every claim without holding a new one. */
157
+ /**
158
+ * Supersedes every claim without holding a new one.
159
+ * @returns {void}
160
+ */
103
161
  invalidate(): void;
104
162
  }
105
163
  export type Describable = {
106
164
  describedBy(el: HTMLElement): boolean;
107
165
  };
108
166
  /**
109
- * The protocol by which content standing inside a field becomes part of the
110
- * accessible description of that field's control.
111
- *
112
- * A field owns its control's `aria-describedby`: it is the only thing that
113
- * knows which element the description belongs on, and it already writes the
114
- * entry for its own error region. Content the author slotted into the field
115
- * cannot write that attribute itself without becoming a second owner of it, and
116
- * it cannot be wired by the field either, because a slotted custom element
117
- * renders after the field has mounted and has nothing to point at when the
118
- * field looks.
119
- *
120
- * So the content asks, once it has something to offer. `describable(el)`
121
- * answers the nearest ancestor that accepts a description, and the caller hands
122
- * its element to that ancestor's `describedBy`, which answers whether it was
123
- * taken. Nothing here names a field or a tooltip: the relation is expressed as
124
- * a capability, so the two ends need not import each other, which matters
125
- * because the library's own arrow runs from the forms to the disclosures.
126
- *
127
- * The lookup lives here rather than at its one call site so the protocol has a
128
- * name, a place to be documented and a single definition to change.
129
- */
130
- /**
167
+ * Something that adds elements to the accessible description of the control it
168
+ * owns, such as a field: `describedBy(el)` adds `el` to the control's
169
+ * `aria-describedby` and answers whether it did.
131
170
  * @typedef {{ describedBy(el: HTMLElement): boolean }} Describable
132
171
  */
133
172
  /**
134
- * The nearest ancestor of `el` that accepts elements into the description of
135
- * whatever it considers its control, or null when nothing in the ancestry does.
173
+ * The nearest ancestor of `el` that has a `describedBy` method. Content
174
+ * slotted inside a field does not write the control's `aria-describedby`
175
+ * itself: once it has something to offer, it hands the element carrying the
176
+ * words to the `describedBy` of the ancestor this answers.
136
177
  * @param {Element} el
137
- * @returns {(Element & Describable) | null}
178
+ * @returns {(Element & Describable) | null} null when no ancestor has one
138
179
  */
139
180
  declare const describable: (el: Element) => (Element & Describable) | null;
140
181
  /**
141
- * Sleeping, debouncing and throttling. Debounce and throttle both return the
142
- * wrapped function together with a cancel function.
182
+ * Sleeping, debouncing and throttling. `debounce` and `throttle` both return
183
+ * the wrapped function together with an abort function, which cancels the call
184
+ * pending and leaves the wrapped function working.
143
185
  */
144
186
  declare class Timing {
145
- /** Resolves after the given milliseconds. @param {number} ms */
146
- static sleep(ms: number): Promise<any>;
147
187
  /**
148
- * Executes only after a period of inactivity (pause in events).
149
- * Respond to the "end" of a series of events.
188
+ * @param {number} ms
189
+ * @returns {Promise<void>} resolving after `ms` milliseconds
190
+ */
191
+ static sleep(ms: number): Promise<void>;
192
+ /**
193
+ * Collapses a burst of calls into one call of `func`, a burst ending once
194
+ * `timeoutMs` pass without a call. By default `func` is called at the end
195
+ * of the burst with the arguments of its last call. With `immediate` it is
196
+ * called at the first call of a burst, with that call's arguments, and not
197
+ * at the end. Without `immediate`, a call the wrapped function receives
198
+ * from inside `func` starts a new burst.
150
199
  * @param {number} timeoutMs
151
- * @param {function} func
152
- * @param {{ immediate?: boolean }} [options] - immediate fires on the leading edge instead of the trailing one
153
- * @returns {[function, function]}
200
+ * @param {(...args: any[]) => any} func
201
+ * @param {{ immediate?: boolean }} [options]
202
+ * @returns {[(...args: any[]) => void, () => void]} the wrapped function
203
+ * and the abort function
154
204
  */
155
- static debounce(timeoutMs: number, func: Function, options?: {
205
+ static debounce(timeoutMs: number, func: (...args: any[]) => any, options?: {
156
206
  immediate?: boolean;
157
- }): [Function, Function];
207
+ }): [(...args: any[]) => void, () => void];
158
208
  /**
159
- * Executes at most once per specified time interval, regardless of ongoing events.
209
+ * Calls `func` at most once per `timeoutMs`, always with the arguments of
210
+ * the latest call. With `leading`, on by default, a call arriving once the
211
+ * interval since the previous call of `func` has passed calls it at once;
212
+ * without it, the first call only starts the interval. With `trailing`, on
213
+ * by default, a call arriving inside the interval schedules `func` for the
214
+ * interval's end; without it, such a call is dropped.
160
215
  * @param {number} timeoutMs
161
- * @param {function} func
162
- * @param {{ leading?: boolean, trailing?: boolean }} [options] - which edges of the interval call, both by default
163
- * @returns {[function, function]}
216
+ * @param {(...args: any[]) => any} func
217
+ * @param {{ leading?: boolean, trailing?: boolean }} [options]
218
+ * @returns {[(...args: any[]) => void, () => void]} the wrapped function
219
+ * and the abort function
164
220
  */
165
- static throttle(timeoutMs: number, func: Function, options?: {
221
+ static throttle(timeoutMs: number, func: (...args: any[]) => any, options?: {
166
222
  leading?: boolean;
167
223
  trailing?: boolean;
168
- }): [Function, Function];
224
+ }): [(...args: any[]) => void, () => void];
169
225
  }
170
- /** Field wiring: extracting and filling values, pinning problems to the fields they name. */
226
+ /**
227
+ * Moves values between a form's named controls and a nested object, and pins
228
+ * problems onto the controls they name. Works on any native form, not only
229
+ * the one inside a `ful-form`. Every method reads the controls through
230
+ * `form.elements`, so a control in the subtree that belongs to another form
231
+ * (a nested form, a `form` attribute pointing elsewhere) is never read,
232
+ * written or marked.
233
+ */
171
234
  declare class Bindings {
172
235
  #private;
173
236
  /**
174
- * Flattens a nested object into dotted keys, stopping wherever `stops` names
175
- * a key: a field named `address` takes the whole object, while one named
176
- * `address.city` takes the leaf.
237
+ * Flattens a nested object into dotted keys, array indexes becoming numeric
238
+ * segments (`a.0.b`). It does not descend below a key that `stops` names, so
239
+ * with a field named `address` the whole object is kept under `address`,
240
+ * while with one named `address.city` the leaf is kept under `address.city`.
241
+ * A null value is kept as a leaf.
177
242
  * @param {{ [x: string]: any; }} obj
178
- * @param {string} prefix
179
- * @param {Set<String>} stops - the names the form actually has fields for
180
- * @return {{ [x: string]: any; }}
243
+ * @param {string} prefix prepended to every key, `''` at the root
244
+ * @param {Set<string>} stops the dotted keys to keep whole
245
+ * @returns {{ [x: string]: any; }} a flat object keyed by dotted path
181
246
  */
182
247
  static flatten(obj: {
183
248
  [x: string]: any;
@@ -186,19 +251,37 @@ declare class Bindings {
186
251
  };
187
252
  /**
188
253
  * Writes a value into an object at a dotted path, creating the intermediate
189
- * objects and arrays the path implies. A numeric segment makes an array.
190
- * @param {any} result
191
- * @param {string} path - a field name, `a.b` or `a[0].b`
254
+ * objects and arrays the path implies: a numeric segment makes its container
255
+ * an array. Brackets are not parsed, so `a[0]` is a plain key.
256
+ *
257
+ * An undefined value declares the path without filling it: an entry already
258
+ * there is kept, a missing one is set to null. A scalar or a null left on the
259
+ * path by a shorter overlapping name (`a` written before `a.b`) is replaced
260
+ * by a container, so the longer name wins whichever order they arrive in.
261
+ * @param {any} result the object or array to write into
262
+ * @param {string} path a dotted name such as `a.b` or `a.0.b`
192
263
  * @param {any} value
264
+ * @returns {any} the root written into, to be used in place of `result`: a
265
+ * new array replaces it when the first segment is numeric and `result` is
266
+ * not an array
267
+ * @throws {Error} when a segment is `__proto__`, `prototype` or `constructor`,
268
+ * before anything is written
193
269
  */
194
270
  static providePath(result: any, path: string, value: any): any;
195
271
  /**
196
- * Reads one control's value the way its kind demands: an unchecked radio
197
- * answers undefined so it contributes nothing, a checkbox answers its
198
- * checked state, a multiple select answers its selected values, and a blank
199
- * native control answers null rather than an empty string.
272
+ * Reads one control's value by its kind. The kind is read from the `type`
273
+ * attribute and the tag, so a ful element answers its own `value`.
274
+ *
275
+ * - a radio answers its `value` when checked and undefined otherwise
276
+ * - a checkbox answers its `checked` state
277
+ * - `data-ful-bind-type="boolean"` decodes the value: a radio answers
278
+ * whether its value is `'true'`, any other control answers null when
279
+ * blank and whether its value is `'true'` otherwise
280
+ * - a multiple select answers the array of its selected values
281
+ * - a native input, select or textarea answers null when blank
282
+ * - anything else answers its `value` unchanged
200
283
  * @param {Element & {dataset?: any} & {checked?: boolean} & {value?: any}} el
201
- * @returns {any} the value, or undefined where the control contributes none
284
+ * @returns {any} the value; undefined for an unchecked radio
202
285
  */
203
286
  static extract(el: Element & {
204
287
  dataset?: any;
@@ -207,13 +290,32 @@ declare class Bindings {
207
290
  } & {
208
291
  value?: any;
209
292
  }): any;
210
- static extractFrom(form: any, submitter: any): {};
211
293
  /**
212
- * Writes a value into one control, the inverse of `extract`: a radio is
213
- * checked when its own value matches, a checkbox takes the value as its
214
- * checked state, and a multiple select selects the options the list names.
294
+ * Reads the form's named controls into a nested object, the dotted names
295
+ * deciding its shape (see `providePath`) and each value read by `extract`.
296
+ * Controls are read in document order, so of two overlapping names the
297
+ * later one decides. A control is skipped when it has no `name`, when it
298
+ * matches `:disabled` (its own `disabled` or a disabled fieldset's), or when
299
+ * it is a submit, reset or plain button. The submitter is the exception to
300
+ * both of the last two: it contributes its name and value even while
301
+ * disabled.
302
+ * @param {HTMLFormElement} form
303
+ * @param {HTMLElement} [submitter] the button that submitted, when there is one
304
+ * @returns {any} the nested values; an array when the first segment of a name is numeric
305
+ * @throws {Error} when a name has a segment `providePath` refuses
306
+ */
307
+ static extractFrom(form: HTMLFormElement, submitter?: HTMLElement): any;
308
+ /**
309
+ * Writes a value into one control, the inverse of `extract`.
310
+ *
311
+ * - a radio is checked when `raw` is not null and its `value` attribute
312
+ * equals `String(raw)`, so a number or a boolean matches its text
313
+ * - a checkbox takes `raw` as its `checked` state
314
+ * - a multiple select selects the options whose value is in `raw`, compared
315
+ * as text: an array selects each entry, a scalar selects one, null selects none
316
+ * - anything else takes `raw` as its `value`
215
317
  * @param {Element & {dataset?: any} & {checked?: boolean} & {value?: any}} el
216
- * @param {any} raw the value as it arrived, coerced per control kind
318
+ * @param {any} raw
217
319
  */
218
320
  static mutate(el: Element & {
219
321
  dataset?: any;
@@ -222,8 +324,52 @@ declare class Bindings {
222
324
  } & {
223
325
  value?: any;
224
326
  }, raw: any): void;
225
- static mutateIn(form: any, values: any): void;
226
- static errors(form: any, es: any, scrollOnError: any): void;
327
+ /**
328
+ * Writes a nested object onto the form's named controls: the object is
329
+ * flattened with the control names as stops (see `flatten`), and each key
330
+ * is written by `mutate` into every control carrying exactly that name. A
331
+ * key naming no control is ignored, and a control the object does not name
332
+ * keeps its value.
333
+ * @param {HTMLFormElement} form
334
+ * @param {{ [x: string]: any; }} values
335
+ */
336
+ static mutateIn(form: HTMLFormElement, values: {
337
+ [x: string]: any;
338
+ }): void;
339
+ /**
340
+ * Shows problems on the form, replacing whatever the previous call showed.
341
+ *
342
+ * Every named control's custom validity is first cleared. A problem whose
343
+ * `type` is `FIELD_ERROR` or `INVALID_FORMAT` and whose `context` is not
344
+ * empty is pinned to a control: brackets in the context are turned into
345
+ * dots (`users[0].name` becomes `users.0.name`), and the longest leading
346
+ * part of it that names a control wins, so an exact field takes the problem
347
+ * alone and a composite field named for a prefix catches the problems of its
348
+ * subtree. Each control of that name gets `setCustomValidity(reason, rest)`,
349
+ * `rest` being the remainder of the path (`''` on an exact match) that a
350
+ * composite can use to route the problem to an inner control.
351
+ *
352
+ * Every other problem, and a field problem naming no control, is shown in
353
+ * each `ful-errors` banner of the form: the banner gets `role="alert"`, is
354
+ * revealed and holds the reasons one per line. With no such problem the
355
+ * banner is emptied and hidden.
356
+ *
357
+ * Each `ful-field-error` of the form gets `aria-live="off"` when
358
+ * `scrollOnError` is set, since focusing the field announces its error, and
359
+ * `aria-live="polite"` otherwise. With `scrollOnError` and at least one
360
+ * problem, the topmost `:invalid` control is focused.
361
+ *
362
+ * A banner or `ful-field-error` belongs to the nearest enclosing form, so
363
+ * those of a nested form are left alone.
364
+ * @param {HTMLFormElement} form
365
+ * @param {{ type: string; context?: string | null; reason: string; }[]} es the problems, as an httpc `Failure` carries them
366
+ * @param {boolean} scrollOnError
367
+ */
368
+ static errors(form: HTMLFormElement, es: {
369
+ type: string;
370
+ context?: string | null;
371
+ reason: string;
372
+ }[], scrollOnError: boolean): void;
227
373
  }
228
374
  /**
229
375
  * The base of every form-associated ful field: a form-associated custom element
@@ -232,212 +378,304 @@ declare class Bindings {
232
378
  *
233
379
  * A subclass owns its template, its value semantics and its change events. It
234
380
  * implements `_build(conf)`, which builds its dom and returns the pieces the
235
- * base drives: the control, the error region, the label, and the optional
236
- * `claims`, `announces`, `freeze` and `also`. The base does the wiring,
237
- * the mounting and the application of the declared state. Nothing in the base
238
- * is there to be called from a subclass's build.
381
+ * base drives (see `_build`), and the `value` pair. The base wires the pieces,
382
+ * mounts the fragment and applies the declared state. Nothing in the base is
383
+ * there to be called from a subclass's build.
239
384
  *
240
- * The pieces are the contract: the claim setters, the validity protocol and
241
- * the aria wiring all act on them, so a field with no native control returns a
242
- * focusable piece of its own chrome as the control. The getters and `focus()`
243
- * are the only members that tolerate a not-yet-rendered element, where page
244
- * code may read a claim or ask for the focus before the upgrade; the
245
- * properties go live only after the render, as ParsedElement documents. The
246
- * base references no ful vocabulary, only what its subclasses return to it.
385
+ * The claim setters, the validity protocol and the aria wiring all act on the
386
+ * pieces, so a field with no native control returns a focusable piece of its
387
+ * own chrome as the control. The claim getters and `focus()` are the only
388
+ * members that tolerate a not yet rendered element; the other members need the
389
+ * pieces and so work only after the render.
390
+ *
391
+ * The inner controls carry `form=""`, so the host is the only element the form
392
+ * sees. Enter pressed in an inner `<input>` of any type but `file`, `button`,
393
+ * `submit`, `reset` and `image` submits the field's form through
394
+ * `_requestSubmit()`, as Enter in a native input would. It does not when a
395
+ * listener inside the field already called `preventDefault()` on the keydown,
396
+ * while an input method is composing, or when the input is still associated
397
+ * with the form itself and so submits on its own.
247
398
  */
248
399
  declare class Field extends ParsedElement {
249
400
  #private;
250
401
  static formAssociated: boolean;
251
402
  /**
252
- * The claim attributes and the value are observed here so every field,
253
- * including the custom ones, keeps them live after the upgrade: the
254
- * attribute is a third way to author a claim, beside the markup and the
255
- * property,
256
- * exactly as a native input's. The value defaults to the string mapper and
257
- * every field with its own vocabulary overrides it (`value:bool`,
258
- * `value:csv`, `value:json`).
403
+ * The claim attributes and the value, observed so that every field,
404
+ * including a custom one that never lists them, keeps them live after the
405
+ * upgrade. A subclass's own `static observed` adds to this list. The value
406
+ * is read with the string mapper; a field with another vocabulary redeclares
407
+ * it with its own (`value:bool`, `value:csv`, `value:json`).
408
+ * @type {string[]}
259
409
  */
260
410
  static observed: string[];
261
- /** the role the element internals carry, 'presentation' unless the control is its own */
411
+ /**
412
+ * The role set on the element internals at construction. A field whose host
413
+ * is the widget itself overrides it, as `RadioGroup` does with `'radiogroup'`.
414
+ * @type {string}
415
+ */
262
416
  static ROLE: string;
417
+ /** Sets the internals' role from the subclass's `static ROLE`. */
263
418
  constructor();
264
419
  /**
265
- * Adds an element to the accessible description of the field's control and
266
- * answers whether the field took it.
267
- *
268
- * A field takes one whenever it is offered, before its own render as
269
- * readily as after: content slotted into a field is a custom element of its
270
- * own and may upgrade on either side of the field it stands in, which
271
- * happens in both directions in practice, a tooltip beating an async select
272
- * to its render while losing to a plain input. A description handed over
273
- * early waits here and is written the moment the field has somewhere to
274
- * write it, so the caller never has to know the order.
420
+ * Adds an element to the accessible description of the field and answers
421
+ * whether the field took it.
275
422
  *
276
- * The reference lands on the element handed over rather than on a wrapper
277
- * around it: a hidden element is included in a description only where it is
278
- * named directly, and content that reaches the description through a
279
- * wrapper is skipped while it is hidden. A popover closed until someone
280
- * opens it is exactly that, so the caller passes the popover itself.
423
+ * The field takes a description before its own render as readily as after:
424
+ * one handed over early is written as soon as the field has rendered, so the
425
+ * caller does not need to know which of the two upgraded first. The element
426
+ * is given an id (`ful-described-*`) when it has none, is added once however
427
+ * often it is offered, and is referenced from the `aria-describedby`
428
+ * attribute of the `described` piece (the control by default). The
429
+ * descriptions come in the order they were offered, the error region always
430
+ * last.
281
431
  *
282
- * An attribute rather than `ariaDescribedByElements`: the property reflects
283
- * to nothing, so the description would live in the accessibility tree alone
284
- * and vanish entirely on a browser without aria element reflection.
432
+ * Pass the element that carries the words rather than a wrapper around it:
433
+ * a hidden element is part of a description only where it is referenced
434
+ * directly, so a popover that is closed until opened must be passed itself.
285
435
  *
286
436
  * This is the field's half of the description protocol; `describable` in
287
437
  * `ful/descriptions.mjs` is the half the content uses to find the field.
288
- * @param {HTMLElement} el
289
- * @returns {boolean}
438
+ * @param {HTMLElement | null | undefined} el
439
+ * @returns {boolean} false only when `el` is missing
290
440
  */
291
- describedBy(el: HTMLElement): boolean;
292
- focus(options: any): void;
293
- /**
294
- * Clears or reports one validation problem: the text lands on the field's
295
- * live region and the state on the element internals, driving `:invalid`
296
- * styling. Validation is the server's: the submit travels regardless, and
297
- * the problems come back pinned here. The error mapping pins on the most
298
- * specific field name a problem's context reaches, handing over the
299
- * remaining path ('' on an exact match): the base ignores it, a composite
300
- * field owning a whole subtree overrides to route the problem to the inner
301
- * control it names.
302
- * @param {string} [error]
303
- * @param {string} [context] the path below this field's name, '' when exact
441
+ describedBy(el: HTMLElement | null | undefined): boolean;
442
+ /**
443
+ * Moves the focus to the `control` piece. Asked before the render, the
444
+ * request is kept (the last one wins) and applied once the control is
445
+ * mounted.
446
+ * @param {FocusOptions} [options]
447
+ */
448
+ focus(options?: FocusOptions): void;
449
+ /**
450
+ * Clears or reports one validation problem. A message sets a custom error on
451
+ * the element internals, which makes the host match `:invalid`, renders the
452
+ * message into the `error` piece and sets `aria-invalid="true"` on the
453
+ * `announces` piece, or on the `control` when the field announces nowhere.
454
+ * An empty or missing message clears all three.
455
+ *
456
+ * Validation is the server's: the submit is sent regardless, and the form's
457
+ * error mapping calls this with the problems that come back. It calls the
458
+ * field with the most specific name a problem's context reaches and passes
459
+ * the rest of the path as `context`. The base ignores `context`; a composite
460
+ * field owning a whole subtree overrides this to route the message to the
461
+ * inner control the path names.
462
+ *
463
+ * Needs the render: before it there is no error region to write to.
464
+ * @param {string} [error] the message, empty or missing to clear
465
+ * @param {string} [context] the path below this field's name, '' on an exact match
304
466
  */
305
467
  setCustomValidity(error?: string, context?: string): void;
306
- /** The form the platform associated the field with, null outside one: a native control's own `form`. */
468
+ /**
469
+ * The form the platform associated the field with, as a native control's
470
+ * own `form`.
471
+ * @returns {HTMLFormElement | null} null outside a form
472
+ */
307
473
  get form(): HTMLFormElement | null;
308
- /** Submits the associated form through its first submitter, as Enter on a native control would. */
474
+ /**
475
+ * Submits the associated form with `requestSubmit`, so constraint validation
476
+ * and the `submit` event run as for a user's submit. The submitter is the
477
+ * first enabled `button` or `input` of type `submit` in the form whose own
478
+ * `form` is that form; with none the form is submitted without one. Does
479
+ * nothing outside a form.
480
+ */
309
481
  _requestSubmit(): void;
310
482
  /**
311
- * Dispatches the field's change event: bubbling, not cancelable, the value
312
- * in the detail. Every field announces through this one method, and the detail
313
- * always carries the field's own `value`, so a listener can rely on
314
- * `el.value === evt.detail.value` whatever the field is. A field with more to
315
- * say adds keys beside it; none can replace it.
316
- * @param {Record<string, any>} [extras]
483
+ * Dispatches the field's `change` event on the host: bubbling, not
484
+ * cancelable, with `{ value: this.value, ...extras }` as the detail. Call it
485
+ * for a change the user made. Every field in the library announces through this method and
486
+ * passes no `value` key in `extras`, so a listener can rely on
487
+ * `el.value === evt.detail.value`. The extras are spread after `value`, so a
488
+ * `value` key among them would replace it.
489
+ * @param {Record<string, any>} [extras] further detail keys beside `value`
317
490
  */
318
491
  _notifyChange(extras?: Record<string, any>): void;
319
492
  /**
320
- * Whether the field's chrome should answer a gesture. Badges, dropzones,
321
- * menus and labels are not form controls, so their handlers must ask the
322
- * effective state: matches(':disabled') covers the fieldset ancestry the
323
- * disabled property deliberately does not reflect, readonly the field's
324
- * own claim.
493
+ * Whether the field's own chrome should act on a gesture. Badges, dropzones,
494
+ * menus and labels are not form controls, so the platform does not disable
495
+ * them: every handler on them asks this first. False while the host matches
496
+ * `:disabled`, which covers a disabled `<fieldset>` ancestry as well as the
497
+ * claim, or while the readonly claim holds.
498
+ * @returns {boolean}
325
499
  */
326
500
  _interactive(): boolean;
327
501
  /**
328
- * The field's value: every concrete field owns its semantics and overrides
329
- * this pair. The base pair exists so the form integration (the reset
330
- * protocol among others) has a member to write through; a custom field
331
- * forgetting its own keeps the base's inert one.
502
+ * The field's value, `null` when empty. Every concrete field overrides the
503
+ * pair with its own semantics; the form extraction, `values =` assignment,
504
+ * the `change` detail and `formResetCallback` all go through it. The base
505
+ * pair reads `undefined` and ignores writes.
332
506
  * @type {any}
333
507
  */
334
508
  get value(): any;
335
509
  set value(v: any);
336
510
  /**
337
- * A reset restores the field's declared value, as a native control's reset
338
- * restores its markup default: the `value` attribute goes back through the
339
- * element's own mapper and value setter, so every field resets through its
340
- * own semantics. A field whose value is not attribute backed overrides this.
511
+ * Restores the declared value on a form reset, as a native control restores
512
+ * its markup default: the current `value` attribute is decoded with the
513
+ * element's own mapper and written through the `value` setter. A field whose
514
+ * value is not attribute backed overrides this.
341
515
  */
342
516
  formResetCallback(): void;
343
517
  /**
344
- * The disabled protocol follows the semantics of a native form control:
518
+ * The field's own disabled claim, which follows a native form control:
345
519
  *
346
- * - the `disabled` attribute on the host is the field's own claim, and nothing
347
- * but its author ever writes or removes it, in markup or through the
348
- * property. The framework never claims on the form's behalf, so there is
349
- * nothing to unclaim and nothing to lose: a field declared disabled inside
350
- * a disabled `<fieldset>` stays disabled when the fieldset comes back,
351
- * exactly like a native input keeps its attribute.
352
- * - the effective state is the claim OR a disabled fieldset ancestry, which
353
- * the platform maintains on its own: `:disabled` matches both, a disabled
354
- * field is left out of the submitted values, and the inner native controls
355
- * are reached by the ancestry as descendants of the fieldset.
356
- * - the property reflects the claim only, like a native input's: a field
357
- * disabled by its ancestry reads `false` while `matches(':disabled')`
358
- * tells the effective state. Un-claiming inside a disabled fieldset
359
- * cannot enable the field.
360
- * - the inner controls mirror the claim and nothing else: the ancestry state
361
- * is never written anywhere, so it can never go stale, and the browser
362
- * composes the two on its own when it disables and re-enables a fieldset's
363
- * descendants. Subclass setters call super for the claim, then reach their
364
- * own controls, which mirror the claim like a native input's would.
365
- *
366
- * Because of this, formDisabledCallback carries nothing the framework needs
367
- * to apply, and the protocol does not define it.
520
+ * - the `disabled` attribute on the host is the claim. Only the author writes
521
+ * or removes it, in markup or through this property; the library never
522
+ * claims on a form's or fieldset's behalf. A field declared disabled inside
523
+ * a disabled `<fieldset>` stays disabled when the fieldset is re-enabled.
524
+ * - the effective state is the claim or a disabled `<fieldset>` ancestry:
525
+ * `:disabled` matches both, and a disabled field is left out of the
526
+ * submitted values.
527
+ * - the property reads the claim only: a field disabled by its ancestry reads
528
+ * `false` while `matches(':disabled')` is true. Removing the claim inside a
529
+ * disabled fieldset does not enable the field.
530
+ * - writing it sets or removes the `disabled` attribute on the `claims`
531
+ * piece (the control by default) and on every `also` control. The
532
+ * ancestry is never written anywhere: the browser disables those controls
533
+ * as descendants of the fieldset. A subclass setter calls super, then
534
+ * reaches any further controls of its own.
535
+ * @type {boolean}
368
536
  */
369
537
  get disabled(): boolean;
370
538
  set disabled(d: boolean);
371
539
  /**
372
- * A field is readonly through its control's native readOnly when it has one:
373
- * the control stays focusable and its text selectable, only editing is off.
374
- * Fields whose chrome must freeze too (popovers, buttons, label clicks) name
375
- * a `freeze` piece instead, whose gestures the base refuses while the claim
376
- * holds; the claim reflects on the host either way.
540
+ * The readonly claim, read from the host attribute. Writing it sets
541
+ * `readOnly` on the `claims` piece (the control by default) and on every
542
+ * `also` control, sets `aria-readonly` on the `announces` piece, and
543
+ * reflects the attribute. A text control stays focusable and selectable
544
+ * with only editing off. A field whose control has no usable native
545
+ * `readOnly` names a `freeze` piece, whose clicks are cancelled while the
546
+ * claim holds.
547
+ * @type {boolean}
377
548
  */
378
549
  get readonly(): boolean;
379
550
  set readonly(v: boolean);
380
551
  /**
381
- * A field is required through aria: the claim reflects on the host, the
382
- * announcement lives on the adopted control.
552
+ * The required claim, read from the host attribute. Writing it sets
553
+ * `aria-required` on the `announces` piece and reflects the attribute; no
554
+ * native `required` is set, since validation is the server's.
555
+ * @type {boolean}
383
556
  */
384
557
  get required(): boolean;
385
558
  set required(d: boolean);
386
559
  /**
387
- * The field's render is the base's: the subclass builds its dom in `_build`
388
- * and hands back what it built, the base wiring the pieces, mounting the
389
- * fragment and applying the declared state. Nothing in the base is there to
390
- * be called from a subclass's build. `_build` may be async (a select
391
- * awaiting its prefetch); a field that builds synchronously stays so.
560
+ * Calls `_build(conf)` and wires the pieces it answers. When `_build`
561
+ * returns a promise the pieces are wired once it settles; a field that
562
+ * builds synchronously renders synchronously.
563
+ * @param {{ slots: Record<string, DocumentFragment> | undefined }} conf
564
+ * @returns {void | Promise<void>}
392
565
  */
393
- render(conf: any): Promise<void> | undefined;
566
+ render(conf: {
567
+ slots: Record<string, DocumentFragment> | undefined;
568
+ }): void | Promise<void>;
394
569
  /**
395
- * Builds the field's dom and answers the pieces the base drives. The one
396
- * method a concrete field implements beside its value pair, and the only
397
- * place its dom is created; the base does the wiring and the mounting.
398
- *
399
- * - `fragment` is mounted on the host
400
- * - `control` is the focusable target: focus, the aria and, by default, all
401
- * three claims reach it
402
- * - `error` is the field's live region
403
- * - `label`, when given, names the control and focuses it on click
404
- * - `described` moves the description off the control and onto another
405
- * element, the host where no single control can carry it: the error
406
- * region and anything `describedBy` is later handed both land there
407
- * - `claims` moves the three claims onto a wrapper the field disables as a
408
- * whole, leaving focus and aria on the control
409
- * - `announces` is the element whose role carries `aria-readonly` and
410
- * `aria-required`, the host where the widget role lives there; `null` for a
411
- * field whose control has no role that accepts them
412
- * - `freeze` is for a field with no usable native readOnly: the readonly
413
- * claim refuses the gestures inside it, leaving it focusable and readable
414
- * - `also` are further controls mirroring disabled and readOnly beside the
415
- * first
570
+ * Builds the field's dom and answers the pieces the base drives, or a
571
+ * promise of them. The one method a concrete field implements beside its
572
+ * value pair, and the only place its dom is created. A subclass extending
573
+ * another field's build spreads the pieces it answered and overrides the
574
+ * keys it owns.
416
575
  *
417
- * A subclass extending another field's build spreads the pieces it answered
418
- * and overrides the keys it owns.
419
- * @param {{slots: any}} conf
420
- * @returns {any}
576
+ * - `fragment` (required) replaces the host's children.
577
+ * - `control` (required) is the focus target of `focus()`. By default it also
578
+ * carries the description, `aria-invalid`, `aria-readonly`,
579
+ * `aria-required`, and the mirrored `disabled` and `readOnly`.
580
+ * - `error` (required) is the live region `setCustomValidity` writes into.
581
+ * It is given an id (`ful-field-error-*`) when it has none and is always
582
+ * the last entry of the description.
583
+ * - `label` names the control. For a labelable control (`button`, `input`
584
+ * other than hidden, `meter`, `output`, `progress`, `select`, `textarea`)
585
+ * the control is given an id when it has none and the label's `for`
586
+ * points at it, so a click on the label does what it does in a plain
587
+ * form. For any other control the label is given an id, the control's
588
+ * `aria-labelledby` points at it, and a click on the label calls `focus()`.
589
+ * - `described` carries `aria-describedby` in place of the control: the
590
+ * host, for a field with no single control to describe.
591
+ * - `claims` receives the mirrored `disabled` and `readOnly` in place of the
592
+ * control, for a wrapper such as a `<fieldset>` the field disables as a
593
+ * whole. Focus and the aria stay on the control.
594
+ * - `announces` carries `aria-readonly`, `aria-required` and `aria-invalid`
595
+ * in place of the control: the host, where the widget role lives there.
596
+ * `null` for a field whose control has no role that accepts
597
+ * `aria-readonly` and `aria-required`; `aria-invalid` then goes on the
598
+ * control.
599
+ * - `freeze` is for a field with no usable native `readOnly`: a capturing
600
+ * click listener on it cancels the default action of every click inside
601
+ * it while the readonly claim holds. It stays focusable and in the
602
+ * accessibility tree.
603
+ * - `also` are further controls that mirror `disabled` and `readOnly`
604
+ * beside the claim target.
605
+ * @param {{ slots: Record<string, DocumentFragment> | undefined }} conf
606
+ * @returns {any} the pieces, or a promise of them
607
+ * @throws {Error} when the subclass does not implement it
421
608
  */
422
609
  _build(conf: {
423
- slots: any;
610
+ slots: Record<string, DocumentFragment> | undefined;
424
611
  }): any;
425
612
  }
426
- /** Submits a form's values as json to a url, mapping the request and the response through the configured mappers. */
613
+ /** Sends a form's request as a json body to a url, mapping the request and the response through the configured mappers. */
427
614
  declare class RemoteJsonFormLoader {
428
615
  #private;
429
- constructor(http: any, url: any, method: any, requestMapper: any, responseMapper: any);
430
- prepare(values: any, form: any): any;
431
- submit(request: any, form: any): Promise<any>;
432
- transform(response: any, form: any): any;
616
+ /**
617
+ * @param {{ request(method: string, url: string): { json(body: any): { fetch(): Promise<any> } } }} http the `http-client` component
618
+ * @param {string} url
619
+ * @param {string} method
620
+ * @param {(value: any, form: Form) => any} requestMapper
621
+ * @param {(value: any, form: Form) => any} responseMapper
622
+ */
623
+ constructor(http: {
624
+ request(method: string, url: string): {
625
+ json(body: any): {
626
+ fetch(): Promise<any>;
627
+ };
628
+ };
629
+ }, url: string, method: string, requestMapper: (value: any, form: Form) => any, responseMapper: (value: any, form: Form) => any);
630
+ /**
631
+ * @param {any} values
632
+ * @param {Form} form
633
+ * @returns {any} what the request mapper returns
634
+ */
635
+ prepare(values: any, form: Form): any;
636
+ /**
637
+ * @param {any} request
638
+ * @param {Form} form
639
+ * @returns {Promise<any>} what the client's `fetch()` resolves to: with the
640
+ * library's client, the `Response` of a 2xx status, rejecting with an
641
+ * `HttpClientError` (a `Failure`) otherwise
642
+ */
643
+ submit(request: any, form: Form): Promise<any>;
644
+ /**
645
+ * @param {any} response
646
+ * @param {Form} form
647
+ * @returns {any} what the response mapper returns
648
+ */
649
+ transform(response: any, form: Form): any;
433
650
  }
434
651
  /** Submits a form without a request: the request mapper produces the result the response mapper then reads, for a form handled entirely on the page. */
435
652
  declare class LocalFormLoader {
436
653
  #private;
437
- constructor(requestMapper: any, responseMapper: any);
438
- prepare(values: any, form: any): Promise<any>;
439
- submit(request: any, form: any, response: any): Promise<any>;
440
- transform(response: any, form: any): Promise<any>;
654
+ /**
655
+ * @param {(value: any, form: Form) => any} requestMapper
656
+ * @param {(value: any, form: Form) => any} responseMapper
657
+ */
658
+ constructor(requestMapper: (value: any, form: Form) => any, responseMapper: (value: any, form: Form) => any);
659
+ /**
660
+ * @param {any} values
661
+ * @param {Form} form
662
+ * @returns {Promise<any>} what the request mapper returns or resolves to
663
+ */
664
+ prepare(values: any, form: Form): Promise<any>;
665
+ /**
666
+ * Sends nothing.
667
+ * @param {any} request
668
+ * @param {Form} form
669
+ * @param {any} response what a `submit:requested` listener answered
670
+ * @returns {Promise<any>} `response`, unchanged
671
+ */
672
+ submit(request: any, form: Form, response: any): Promise<any>;
673
+ /**
674
+ * @param {any} response
675
+ * @param {Form} form
676
+ * @returns {Promise<any>} what the response mapper returns or resolves to
677
+ */
678
+ transform(response: any, form: Form): Promise<any>;
441
679
  }
442
680
  /**
443
681
  * Builds the form's loader from its attributes: a local one when no action is
@@ -450,121 +688,395 @@ declare class LocalFormLoader {
450
688
  * - `submit(request, form, response)` performs it and returns the response. The
451
689
  * third argument is whatever a `submit:requested` listener already answered,
452
690
  * which is how a loader with nothing to send returns it unchanged
453
- * - `transform(response, form)` turns that response into the detail of the
454
- * `submit:success` event
691
+ * - `transform(response, form)` turns that response into the `response` of the
692
+ * `submit:success` detail
455
693
  *
456
694
  * A rejection from any of the three is reported as a `submit:failure`.
457
695
  */
458
696
  declare class FormLoader {
459
- static create(el: any, conf: any): LocalFormLoader | RemoteJsonFormLoader;
697
+ /**
698
+ * The `request-mapper` and `response-mapper` attributes name registry
699
+ * components called as `(value, form)`; each defaults to the identity. A
700
+ * remote loader sends through the `http-client` component, with the
701
+ * `method` attribute or `POST`.
702
+ * @param {Form} el the form submitting
703
+ * @param {unknown} [conf] not read
704
+ * @returns {LocalFormLoader | RemoteJsonFormLoader}
705
+ */
706
+ static create(el: Form, conf?: unknown): LocalFormLoader | RemoteJsonFormLoader;
460
707
  }
461
708
  /**
462
- * Wraps its fields in a native form, extracts their values on submit and hands
463
- * them to a loader (loaders:form, or the action url as a json post),
464
- * announcing failures through the errors setter.
709
+ * Moves its children into a native `<form novalidate>`, extracts their values
710
+ * on submit and hands them to a loader, reporting problems through `errors`.
711
+ * Being `novalidate`, the form submits whatever the validity of its fields:
712
+ * the browser does not block a submit on a field still marked invalid.
713
+ *
714
+ * Attributes on the host starting with `form-` are forwarded, without the
715
+ * prefix, to the native form. `autocomplete` is copied onto the native form as
716
+ * well and stays on the host, the fields reading it from either.
717
+ * `clear-invalid-on-change` clears the custom validity of a field of this form
718
+ * when a `change` event bubbles from it; the field's form is read from its
719
+ * `form` property, or from `internals.form` for a form-associated element
720
+ * that has none. `scroll-on-error` is passed to `Bindings.errors` as
721
+ * `scrollOnError`.
722
+ *
723
+ * A native submit, by a button or by Enter in a field, is stopped and runs
724
+ * `submit` instead.
465
725
  */
466
726
  declare class Form extends ParsedElement {
467
727
  #private;
728
+ /** Read once at the upgrade: changing one afterwards has no effect. */
468
729
  static attributes: string[];
730
+ /** The native form holding the fields, created by the render. */
469
731
  form: any;
732
+ /** Builds the native form and moves the element's children into it. */
470
733
  render(): void;
471
734
  /**
472
- * Submits once: a submit while one is in flight is dropped before the
473
- * values are even extracted, so nothing fires and nothing travels; the
474
- * settled exchange re-arms the form. A write must not double behind a
475
- * second Enter or a programmatic call racing the first.
476
- * @param {HTMLElement} [submitter]
477
- * @returns
735
+ * Runs the submit pipeline, with the spinner up for its whole length:
736
+ *
737
+ * 1. builds the loader: the component the `loader` attribute names
738
+ * (`loaders:form` by default), called as `create(form)`
739
+ * 2. extracts the values with `Bindings.extractFrom` and has the loader
740
+ * `prepare` them into the request
741
+ * 3. fires a cancelable `submit` with `{ submitter, values, request }`.
742
+ * Cancelling it ends the submit there, reporting nothing and leaving the
743
+ * problems shown; a listener may replace `values` or `request` on the detail
744
+ * 4. clears the problems shown
745
+ * 5. fires `submit:requested` through `AsyncEvents.fireAsync` in `pipeline`
746
+ * mode: at most one async listener, whose answer is handed to the
747
+ * loader's `submit` as its third argument. A listener may replace
748
+ * `request` on the detail
749
+ * 6. has the loader `submit` the request and `transform` the response
750
+ * 7. fires `submit:success` with `{ submitter, values, request, response }`,
751
+ * `response` being the transformed one
752
+ *
753
+ * Anything thrown from step 1 on, a missing loader or a throwing mapper
754
+ * included, fires `submit:failure` with `{ submitter, values, request,
755
+ * exception }` instead; the returned promise does not reject. A `Failure`
756
+ * has its problems shown through `errors`, anything else is logged with
757
+ * `console.warn`. The `values` of both outcome events are the extracted
758
+ * ones, not a replacement a `submit` listener put on its detail.
759
+ *
760
+ * A call while a submit is in flight returns at once, doing nothing.
761
+ * @param {HTMLElement} [submitter] the button that submitted, if any; it is
762
+ * the only button whose name and value are submitted
763
+ * @returns {Promise<void>} settling once the submit has finished, whatever its outcome
478
764
  */
479
765
  submit(submitter?: HTMLElement): Promise<void>;
480
- /** The native reset, routing every field through its own value semantics. */
766
+ /** Resets the native form, each field returning to its default value. */
481
767
  reset(): void;
482
- /** Shows the spinners and holds the submit buttons off, overlapping spins sharing one claim. */
483
- spinner(spin: any): void;
484
- /** The values of the fields the form contains, extracted and filled back through Bindings. */
485
- set values(vs: {});
486
- get values(): {};
487
- /** Pins problems to the fields they name, the banner taking the nameless ones. */
488
- set errors(es: any);
768
+ /**
769
+ * Marks the form busy or releases it. Calls nest: only the first
770
+ * `spinner(true)` and the matching last `spinner(false)` act, so a caller's
771
+ * own spin may wrap a submit.
772
+ *
773
+ * Busy, the host carries `aria-busy="true"` and each `ful-spinner` of this
774
+ * form is revealed; one without text of its own is given `role="status"`
775
+ * (unless it has a role) and a visually hidden localized `spinner.loading`
776
+ * label. Each submit and reset button of this form is held off with
777
+ * `aria-disabled="true"`, keeping its focus, and a click on it is refused
778
+ * ahead of any listener on the button. Holding the buttons off does not
779
+ * stop a programmatic `submit`.
780
+ *
781
+ * Released, the spinners are hidden and lose the label they were given, and
782
+ * each held button gets back the `aria-disabled` it had. A button that
783
+ * joined the form while it was busy keeps its own state.
784
+ * @param {boolean} spin
785
+ */
786
+ spinner(spin: boolean): void;
787
+ /**
788
+ * Writes a nested object onto the fields of this form through
789
+ * `Bindings.mutateIn`: fields the object does not name keep their values.
790
+ * @param {{ [x: string]: any; }} vs
791
+ */
792
+ set values(vs: {
793
+ [x: string]: any;
794
+ });
795
+ /**
796
+ * The fields of this form read into a nested object by
797
+ * `Bindings.extractFrom`, with no submitter, so no button contributes.
798
+ * @returns {any}
799
+ */
800
+ get values(): any;
801
+ /**
802
+ * Shows problems through `Bindings.errors`, replacing the ones shown
803
+ * before: each is pinned to the field it names, and the rest go to the
804
+ * form's `ful-errors` banners. An empty array clears them.
805
+ * @param {{ type: string; context?: string | null; reason: string; }[]} es
806
+ */
807
+ set errors(es: {
808
+ type: string;
809
+ context?: string | null;
810
+ reason: string;
811
+ }[]);
489
812
  }
490
- /** A labelled text input over any native type or textarea; the temporal inputs are its subclasses. */
813
+ /**
814
+ * A labelled text input over any native input type, or a `<textarea>` under
815
+ * `type="textarea"`; the temporal inputs are its subclasses. The label is the
816
+ * default slot, `info` follows it, and `before` and `after` are rendered as
817
+ * affixes around the control. Every `input-*` attribute is forwarded onto the
818
+ * control, after everything the field sets there, so it has the last word.
819
+ *
820
+ * `type`, `v-type`, `keep`, `reject`, `uppercase`, `trim`, `unsigned` and
821
+ * `autocomplete` are configuration: read once at the upgrade, so a later write
822
+ * to them changes nothing. `placeholder` stays live.
823
+ *
824
+ * - `type="numeric"` and `type="decimal"` render a text input carrying that
825
+ * `inputmode`, so the native `min`, `max` and `step` do not apply. Typing is
826
+ * filtered to digits and a leading minus; a decimal also keeps its first `.`
827
+ * or `,` and drops any later one; `unsigned` drops the minus.
828
+ * - `keep` and `reject` are regular expressions filtering what is typed:
829
+ * `keep` leaves every match, concatenated, and `reject` removes every match.
830
+ * Either replaces the type's own filter, and `keep` wins over `reject` with a
831
+ * warning. A pattern that does not compile is warned about once and filters
832
+ * nothing. The caret stays next to the same character when text before it
833
+ * is removed.
834
+ * - `autocomplete` is put on the control. Without one the field takes the
835
+ * `autocomplete` attribute of the nearest enclosing `<form>`, which the
836
+ * platform does not pass on because the control carries `form=""`.
837
+ * - `uppercase`, `trim` and `v-type` shape the value as it is read; see `value`.
838
+ * - The control's native `change` is stopped and the field dispatches its own
839
+ * once the user commits a new value, as a text input does. A date, time,
840
+ * datetime-local, month or week control fires its native `change` while its
841
+ * segments are typed, as soon as they make a valid value; for those the field
842
+ * reports when the control is left or Enter is pressed, at once for a value
843
+ * picked from the browser's picker, and never a half-typed value.
844
+ *
845
+ * A subclass template may render more than one control, as the compare filters
846
+ * render the two bounds of a range: the first is `_input`, the one the field is
847
+ * named by, and every one of them gets the inputmode, `autocomplete`, the
848
+ * `input-` passthrough, the keystroke filter, the placeholder and the `change`
849
+ * reporting. The passthrough leaves `input-id` to the first alone, so no
850
+ * two controls share an id.
851
+ */
491
852
  declare class Input extends Field {
853
+ #private;
854
+ /** @type {string[]} */
492
855
  static observed: string[];
856
+ /** @type {string[]} */
493
857
  static attributes: string[];
494
858
  static slots: boolean;
495
859
  static template: string;
860
+ /**
861
+ * The inner control, set by `_build`: the `<input>`, or the `<textarea>`
862
+ * under `type="textarea"`. A subclass reads and writes it for the
863
+ * attributes it adds. Typed `any` so that a subclass can treat it as the
864
+ * one of the two it renders.
865
+ * @type {any}
866
+ */
496
867
  _input: any;
497
- _type(): any;
868
+ /**
869
+ * The type the field renders, which a subclass overrides to wrap another
870
+ * native type. The base answers the declared `type`, else `number` under
871
+ * `v-type="number"`, else `text`. It is also what selects the keystroke
872
+ * filter of `numeric` and `decimal` and the separator handling of `value`.
873
+ * @returns {string}
874
+ */
875
+ _type(): string;
876
+ /**
877
+ * @param {{ slots: Record<string, DocumentFragment> | undefined }} conf
878
+ * @returns {{fragment: DocumentFragment, control: any, error: Element | null, label: HTMLLabelElement | null}}
879
+ */
498
880
  _build({ slots }: {
499
- slots: any;
881
+ slots: Record<string, DocumentFragment> | undefined;
500
882
  }): {
501
- fragment: any;
883
+ fragment: DocumentFragment;
502
884
  control: any;
503
- error: any;
504
- label: any;
885
+ error: Element | null;
886
+ label: HTMLLabelElement | null;
505
887
  };
888
+ /**
889
+ * The control's text, shaped in this order: upper cased under `uppercase`,
890
+ * trimmed under `trim`, `null` when that leaves it empty, every `,` read as
891
+ * `.` for `type="decimal"`, and decoded to a number under
892
+ * `v-type="number"`, where text that does not decode is answered as it is.
893
+ * Writing `null`, `undefined` or `''` empties the control; anything else is
894
+ * written as its string. Writing the text the control already holds leaves
895
+ * it untouched, so a date or time control keeps the segments being typed.
896
+ * Answers a `string`, a `number` or `null`; typed `any` because subclasses
897
+ * answer other values.
898
+ * @type {any}
899
+ */
506
900
  get value(): any;
507
901
  set value(value: any);
508
- get placeholder(): any;
509
- set placeholder(d: any);
902
+ /**
903
+ * The control's placeholder, `null` when there is none. The control always
904
+ * carries one, a single space standing in for none, so `:placeholder-shown`
905
+ * matches an empty control and a floating label can rely on it; the blank
906
+ * one is neither answered here nor reflected onto the host.
907
+ * @type {string | null}
908
+ */
909
+ get placeholder(): string | null;
910
+ set placeholder(d: string | null);
510
911
  }
511
- /** Formats the yyyy-mm-dd date in its content in the page's locale, or the one its locale attribute names. */
912
+ /**
913
+ * Formats the yyyy-mm-dd date in its content as a numeric date, in the locale
914
+ * its `locale` attribute names, else the page's, else the platform default.
915
+ * Blank content, or content that does not name a date, renders the `default`
916
+ * attribute, or nothing without one.
917
+ */
512
918
  declare class LocalDate extends ParsedElement {
513
919
  static attributes: string[];
514
920
  render(): void;
515
921
  }
516
- /** Formats the ISO instant in its content in the page's locale and timezone. */
922
+ /**
923
+ * Formats the ISO instant in its content as a numeric date and 24 hour time
924
+ * with seconds, in the page's timezone and in the locale chosen as ful-local-date
925
+ * chooses it. Blank content, or content that does not name an instant, renders
926
+ * the `default` attribute, or nothing without one. A date-only value is read as
927
+ * local midnight of that day.
928
+ */
517
929
  declare class Instant extends ParsedElement {
518
930
  #private;
519
931
  static attributes: string[];
520
932
  render(): void;
521
- static isoToLocal(iso: any): string;
522
- static localToIso(local: any): string | null;
933
+ /**
934
+ * Converts an ISO instant to the `yyyy-mm-ddThh:mm:ss.sss` wall clock time
935
+ * a datetime-local input takes, in the page's timezone. A date-only value is
936
+ * read as local midnight of the day it names, not as UTC midnight.
937
+ * @param {string} iso
938
+ * @returns {string} with NaN fields for a value that does not parse
939
+ */
940
+ static isoToLocal(iso: string): string;
941
+ /**
942
+ * Converts a datetime-local wall clock time in the page's timezone to a UTC
943
+ * ISO instant. A date-only value is read as local midnight of that day.
944
+ * @param {string} local
945
+ * @returns {string|null} null for a value that does not parse
946
+ */
947
+ static localToIso(local: string): string | null;
523
948
  }
524
- /** A date input whose bounds accept a date, now, or an offset such as +1d. */
949
+ /**
950
+ * A date input whose `min` and `max` accept a yyyy-mm-dd date, `now` for
951
+ * today, or an offset from today such as `+1d`, `-2m` or `+1y` (days, months,
952
+ * years). A month offset landing past the end of the target month takes its
953
+ * last day. Anything else is passed to the input unchanged. The `step`
954
+ * attribute is applied before `min` and `max`.
955
+ */
525
956
  declare class InputLocalDate extends Input {
526
957
  #private;
527
958
  static observed: string[];
528
959
  _type(): string;
529
- get min(): any;
530
- set min(v: any);
531
- get max(): any;
532
- set max(v: any);
533
- get step(): any;
534
- set step(v: any);
960
+ /**
961
+ * The resolved lower bound, a yyyy-mm-dd date or whatever the input was given.
962
+ * @returns {string|null} null when unset
963
+ */
964
+ get min(): string | null;
965
+ /** @param {string|null} v a date, `now` or an offset; null or blank removes the bound */
966
+ set min(v: string | null);
967
+ /**
968
+ * The resolved upper bound, a yyyy-mm-dd date or whatever the input was given.
969
+ * @returns {string|null} null when unset
970
+ */
971
+ get max(): string | null;
972
+ /** @param {string|null} v a date, `now` or an offset; null or blank removes the bound */
973
+ set max(v: string | null);
974
+ /**
975
+ * The input's native step, in days on a date input and in seconds on a time input.
976
+ * @returns {string|null} null when unset
977
+ */
978
+ get step(): string | null;
979
+ /** @param {string|null} v */
980
+ set step(v: string | null);
535
981
  }
536
- /** A time input whose bounds accept a time, now, or an hour or minute offset, snapped to the step grid. */
982
+ /**
983
+ * A time input whose `min` and `max` accept a hh:mm time, `now`, or an offset
984
+ * from now such as `+2h` or `-30m` (hours, minutes: `m` is minutes here, not
985
+ * months), wrapping around midnight. A bound resolved from `now` or an offset
986
+ * is floored to the `step` grid (60 seconds when unset), so that the values
987
+ * on the grid stay valid, and carries seconds only when the step is not whole
988
+ * minutes. A literal bound, and anything else, is passed to the input unchanged.
989
+ */
537
990
  declare class InputLocalTime extends InputLocalDate {
538
991
  #private;
539
992
  _type(): string;
540
- get min(): any;
541
- set min(v: any);
542
- get max(): any;
543
- set max(v: any);
993
+ /**
994
+ * The resolved lower bound, a time or whatever the input was given.
995
+ * @returns {string|null} null when unset
996
+ */
997
+ get min(): string | null;
998
+ /** @param {string|null} v a time, `now` or an offset; null or blank removes the bound */
999
+ set min(v: string | null);
1000
+ /**
1001
+ * The resolved upper bound, a time or whatever the input was given.
1002
+ * @returns {string|null} null when unset
1003
+ */
1004
+ get max(): string | null;
1005
+ /** @param {string|null} v a time, `now` or an offset; null or blank removes the bound */
1006
+ set max(v: string | null);
544
1007
  }
545
- /** A datetime input whose value is read and written as an ISO instant. */
1008
+ /**
1009
+ * A datetime-local input whose value and bounds are read and written as UTC
1010
+ * ISO instants, shown as wall clock time in the page's timezone. A date-only
1011
+ * value is read as local midnight of that day.
1012
+ */
546
1013
  declare class InputInstant extends Input {
547
1014
  static observed: string[];
548
1015
  _type(): string;
1016
+ /**
1017
+ * The entered time as a UTC ISO instant.
1018
+ * @returns {string|null} null when empty or when the input holds text that does not parse
1019
+ */
549
1020
  get value(): string | null;
550
- set value(v: string | null);
1021
+ /** @param {string|null|undefined} v an ISO instant; a falsy value empties the input */
1022
+ set value(v: string | null | undefined);
1023
+ /**
1024
+ * The lower bound as a UTC ISO instant.
1025
+ * @returns {string|null} null when unset
1026
+ */
551
1027
  get min(): string | null;
1028
+ /** @param {string|null} v an ISO instant; a falsy value removes the bound */
552
1029
  set min(v: string | null);
1030
+ /**
1031
+ * The upper bound as a UTC ISO instant.
1032
+ * @returns {string|null} null when unset
1033
+ */
553
1034
  get max(): string | null;
1035
+ /** @param {string|null} v an ISO instant; a falsy value removes the bound */
554
1036
  set max(v: string | null);
555
- get step(): any;
556
- set step(v: any);
1037
+ /**
1038
+ * The input's native step, in seconds.
1039
+ * @returns {string|null} null when unset
1040
+ */
1041
+ get step(): string | null;
1042
+ /** @param {string|null} v */
1043
+ set step(v: string | null);
557
1044
  }
558
- /** A file input with an optional dropzone and item list, enforcing the size and count limits it declares. */
1045
+ /**
1046
+ * A file input with an optional dropzone and item list, enforcing the type,
1047
+ * size and count limits it declares.
1048
+ *
1049
+ * The `dropzone` and `item-list` attributes show the dropzone and the list of
1050
+ * chosen files. A `dropzone` slot replaces the default dropzone's content, and
1051
+ * an `items` slot holding a template replaces the stock item, rendered with the
1052
+ * same `files` overlay so that it reads the File objects it lists. Clicking the
1053
+ * dropzone opens the picker, and the host carries a `dragover` attribute while
1054
+ * a drag hovers it. A drop selects the files it carries, ignoring entries that
1055
+ * are not files; a drop with no file, or with several on a field that is not
1056
+ * `multiple`, changes nothing.
1057
+ *
1058
+ * Every selection, picked, dropped or assigned, goes through the constraints
1059
+ * in order: `accept` and `max-file-size` drop the files they refuse,
1060
+ * `max-total-size` and `max-files` clear the whole selection. Each violation
1061
+ * shows a warning. The change event fires for a pick, a drop and a removal
1062
+ * through an item's button; the `files`, `file` and `value` setters are silent,
1063
+ * like a native input's.
1064
+ *
1065
+ * While disabled or readonly, the picker, the dropzone and the item removals
1066
+ * do nothing, and a drop still does not reach the browser's default handling.
1067
+ */
559
1068
  declare class InputFile extends Input {
560
1069
  #private;
561
- /** how long a warning stands before the field retires it, matching the css fade */
1070
+ /**
1071
+ * Milliseconds a warning stays before the field removes it, whether or not
1072
+ * its css animation ran; read each time a warning is shown.
1073
+ * @type {number}
1074
+ */
562
1075
  static WARNING_TIMEOUT: number;
563
1076
  /**
564
- * A FileList holding exactly these files. The platform gives no way to
565
- * build one but through a DataTransfer, and every place that narrows a
566
- * selection rebuilt it by hand: five loops and three empty ones.
1077
+ * Builds a FileList holding exactly these files, the form `files` takes.
567
1078
  * @param {Iterable<File>} [files]
1079
+ * @returns {FileList}
568
1080
  */
569
1081
  static list(files?: Iterable<File>): FileList;
570
1082
  static observed: string[];
@@ -575,75 +1087,185 @@ declare class InputFile extends Input {
575
1087
  warning: string;
576
1088
  };
577
1089
  _build(conf: any): {
578
- fragment: any;
1090
+ fragment: DocumentFragment;
579
1091
  control: any;
580
- error: any;
581
- label: any;
1092
+ error: Element | null;
1093
+ label: HTMLLabelElement | null;
582
1094
  freeze: any;
583
1095
  };
584
- warning(key: any, args: any): void;
585
- get accept(): any;
586
- set accept(vs: any);
587
- get multiple(): any;
588
- set multiple(v: any);
589
- get files(): any;
590
- set files(vs: any);
591
- get file(): any;
592
- set file(v: any);
593
- get value(): any;
1096
+ /**
1097
+ * Shows a warning in the field's polite live region: the localized message
1098
+ * for an l10n key and its arguments. It is removed when its animation ends,
1099
+ * or after `WARNING_TIMEOUT` milliseconds at the latest. The warnings are
1100
+ * cleared whenever the selection changes.
1101
+ * @param {string} key
1102
+ * @param {Record<string, any>} [args]
1103
+ */
1104
+ warning(key: string, args?: Record<string, any>): void;
1105
+ /**
1106
+ * The accepted types, also set on the native input for its picker. A token
1107
+ * is a dot-prefixed extension matched against the end of the file name, a
1108
+ * mime type matched against the file's type with any parameters ignored, or
1109
+ * a family such as `image/*`; case is ignored, and any other token matches
1110
+ * nothing. A file matching no token is dropped with a warning; an empty list
1111
+ * accepts every file. Enforced from the next selection on.
1112
+ * @returns {string[]}
1113
+ */
1114
+ get accept(): string[];
1115
+ /** @param {string[]} vs */
1116
+ set accept(vs: string[]);
1117
+ /**
1118
+ * Whether the field takes several files; a single-file field ignores a drop of several.
1119
+ * @returns {boolean}
1120
+ */
1121
+ get multiple(): boolean;
1122
+ /** @param {boolean} v */
1123
+ set multiple(v: boolean);
1124
+ /**
1125
+ * The selected files, after the constraints.
1126
+ * @returns {FileList}
1127
+ */
1128
+ get files(): FileList;
1129
+ /**
1130
+ * Replaces the selection and runs the constraints over it, refreshing the
1131
+ * warnings and the item list, without firing change.
1132
+ * @param {FileList} vs
1133
+ */
1134
+ set files(vs: FileList);
1135
+ /**
1136
+ * The first selected file.
1137
+ * @returns {File|null}
1138
+ */
1139
+ get file(): File | null;
1140
+ /**
1141
+ * Replaces the selection with this one file, or empties it, as `files` does.
1142
+ * @param {File|null|undefined} v
1143
+ */
1144
+ set file(v: File | null | undefined);
1145
+ /**
1146
+ * The names of the selected files: an array when `multiple`, otherwise the
1147
+ * one name or null.
1148
+ * @returns {string[]|string|null}
1149
+ */
1150
+ get value(): string[] | string | null;
1151
+ /**
1152
+ * Empties the selection for a falsy value and ignores any other, since a
1153
+ * file name cannot select a file.
1154
+ * @param {any} v
1155
+ */
594
1156
  set value(v: any);
1157
+ /** Empties the selection, as a native file input's reset does, whatever the `value` attribute says. */
595
1158
  formResetCallback(): void;
596
- get totalsize(): any;
597
- get maxFiles(): any;
598
- set maxFiles(v: any);
599
- get maxFileSize(): any;
600
- set maxFileSize(v: any);
601
- get maxTotalSize(): any;
602
- set maxTotalSize(v: any);
603
- get itemList(): any;
604
- set itemList(v: any);
605
- get dropzone(): any;
606
- set dropzone(v: any);
1159
+ /**
1160
+ * The size of the selected files together, in bytes.
1161
+ * @returns {number}
1162
+ */
1163
+ get totalsize(): number;
1164
+ /**
1165
+ * The most files a selection may hold; a larger selection is cleared
1166
+ * entirely with a warning. Enforced from the next selection on.
1167
+ * @returns {number|null} null for no limit
1168
+ */
1169
+ get maxFiles(): number | null;
1170
+ /** @param {number|null} v */
1171
+ set maxFiles(v: number | null);
1172
+ /**
1173
+ * The largest size one file may have, in bytes; a larger file is dropped
1174
+ * from the selection with a warning. Enforced from the next selection on.
1175
+ * @returns {number|null} null for no limit
1176
+ */
1177
+ get maxFileSize(): number | null;
1178
+ /** @param {number|null} v */
1179
+ set maxFileSize(v: number | null);
1180
+ /**
1181
+ * The largest size the selected files may have together, in bytes; a
1182
+ * larger selection is cleared entirely with a warning. Enforced from the
1183
+ * next selection on.
1184
+ * @returns {number|null} null for no limit
1185
+ */
1186
+ get maxTotalSize(): number | null;
1187
+ /** @param {number|null} v */
1188
+ set maxTotalSize(v: number | null);
1189
+ /**
1190
+ * Whether the chosen files are listed, each with a button removing it; the
1191
+ * stylesheet shows the list from the reflected `item-list` attribute.
1192
+ * @returns {boolean}
1193
+ */
1194
+ get itemList(): boolean;
1195
+ /** @param {boolean} v */
1196
+ set itemList(v: boolean);
1197
+ /**
1198
+ * Whether the dropzone is shown; the stylesheet shows it from the reflected
1199
+ * `dropzone` attribute.
1200
+ * @returns {boolean}
1201
+ */
1202
+ get dropzone(): boolean;
1203
+ /** @param {boolean} v */
1204
+ set dropzone(v: boolean);
607
1205
  }
608
1206
  /**
609
- * CSS anchor positioning for a popover and the invoker it belongs to, with the
610
- * hand-placed fallback for the platforms that do not have it.
1207
+ * Places a popover beside the element or the rectangle it belongs to.
1208
+ *
1209
+ * `wire` pairs a popover with one invoker for its lifetime and relies on css
1210
+ * anchor positioning, placing the popover in script only where the platform
1211
+ * lacks it or the caller asks for it. `show` places a popover once, beside
1212
+ * whatever anchor the call hands over.
611
1213
  */
612
1214
  declare class Anchors {
613
1215
  /**
614
- * Anchors a popover to its invoker.
1216
+ * Anchors a popover to its invoker for the popover's lifetime.
615
1217
  *
616
- * The invoker is given an `anchor-name` and the popover a `position-anchor`
617
- * pointing at it, which is what a stylesheet needs to place the popover
1218
+ * On every platform the invoker is given an inline `anchor-name` and the
1219
+ * popover a `position-anchor` naming it, `--` followed by a unique id
1220
+ * built from `prefix`. That is what a stylesheet needs to place the popover
618
1221
  * itself: the library's own menus say `top: anchor(bottom); left:
619
1222
  * anchor(left)`. **Writing that css is the caller's half of this.** Without
620
1223
  * it the popover lands wherever the user agent puts a popover, which is not
621
1224
  * beside the invoker.
622
1225
  *
623
- * Where the platform has no anchor positioning the popover is placed here
624
- * instead, beside the invoker whenever it opens, clamped into the viewport,
625
- * following it on scroll and resize, and cleaned up on close. That placement
626
- * draws the geometry the css above describes, so the two agree.
1226
+ * Where the platform has no css anchor positioning, or under `handPlace`,
1227
+ * the popover is placed in script instead: beside the invoker whenever it
1228
+ * opens, clamped 8px inside the viewport, placed again on every scroll and
1229
+ * resize while open, and its inline placement removed on close. The gap to
1230
+ * the invoker is read from the popover's css margins. From `beforetoggle`
1231
+ * to the placing, in a microtask before the `toggle` event, the popover
1232
+ * carries an inline `visibility: hidden`, so a stylesheet setting
1233
+ * `visibility` on it competes with that. A popover removed from the
1234
+ * document while open, or whose invoker was removed, stops being placed.
1235
+ *
1236
+ * The script placement puts a plain popover below the invoker,
1237
+ * start-aligned. A popover matching `ful-note`, `.ful-note` or
1238
+ * `[placement]` reads its `placement` attribute instead: `top`, `right`,
1239
+ * `left`, or `bottom` by default, centred on the invoker along the other
1240
+ * axis. Such a popover is also given `--ful-note-callout-inline` and
1241
+ * `--ful-note-callout-block`: the invoker's centre, in pixels, measured
1242
+ * from the popover's padding box, which stays on the invoker when the
1243
+ * viewport pushes the popover off it.
1244
+ *
1245
+ * Wiring the same pair twice adds its listeners twice.
627
1246
  *
628
1247
  * @param {HTMLElement} invoker the element the popover belongs to
629
1248
  * @param {HTMLElement} popover the `[popover]` element to place
630
1249
  * @param {object} [options]
631
- * @param {string} [options.prefix] prefixes the generated anchor name and id,
632
- * so the dom says which component a name belongs to
633
- * @param {boolean} [options.invoke] points the invoker's `popovertarget` at
634
- * the popover, giving toggle and light dismiss with no script of your own
635
- * @param {boolean} [options.expanded] keeps the invoker's `aria-expanded` in
636
- * step with the popover
637
- * @param {boolean} [options.stretch] widens the popover to at least its
638
- * invoker's width, capped by its own `max-width`, which is what a combobox
639
- * dropdown wants; it flips above the invoker where the viewport leaves no
640
- * room below
641
- * @param {boolean} [options.handPlace] places here on every platform rather
642
- * than only as a fallback, which a popover asks for when it needs to know
643
- * where its invoker ended up: the tooltip's note points a callout at it, and
644
- * a pseudo-element cannot read an anchor outside its own containing block.
645
- * Such a popover declares no anchor placement in css, there being none to
646
- * agree with
1250
+ * @param {string} [options.prefix] prefixes the generated anchor name and
1251
+ * id, `ful-anchor` by default, so the dom says which component a name
1252
+ * belongs to
1253
+ * @param {boolean} [options.invoke] makes the invoker toggle the popover,
1254
+ * which light dismiss then closes with no script of your own. The popover
1255
+ * is given the generated id when it has none. A `button` or `input`
1256
+ * invoker gets a `popovertarget` naming it; any other element toggles it
1257
+ * on click and on the Enter and Space keys
1258
+ * @param {boolean} [options.expanded] sets the invoker's `aria-expanded` to
1259
+ * `false` at once and keeps it in step with the popover's `toggle` events
1260
+ * @param {boolean} [options.stretch] in the script placement, sizes the
1261
+ * popover to at least its invoker's width, capped by its own `max-width`
1262
+ * and the viewport, and flips it above the invoker where the viewport
1263
+ * leaves no room below, which is what a combobox dropdown wants. The
1264
+ * `placement` attribute is not read
1265
+ * @param {boolean} [options.handPlace] places in script on every platform
1266
+ * rather than only as a fallback, for a popover that needs to know where
1267
+ * its invoker ended up, such as a note pointing a callout at it. Such a
1268
+ * popover declares no anchor placement in css
647
1269
  */
648
1270
  static wire(invoker: HTMLElement, popover: HTMLElement, { prefix, invoke, expanded, stretch, handPlace }?: {
649
1271
  prefix?: string;
@@ -653,29 +1275,33 @@ declare class Anchors {
653
1275
  handPlace?: boolean;
654
1276
  }): void;
655
1277
  /**
656
- * Shows and places a popover beside an anchor, once: the popover shared by
657
- * many invokers, each open handing over its own anchor, and an anchor that
658
- * is a rectangle rather than an element, one read out of an iframe being
659
- * the case a paired invoker could not cover. No pairing, no reflow
660
- * following, no close wiring: closing is the caller's, or the platform's
661
- * for a light-dismissing popover.
1278
+ * Shows a popover and places it beside an anchor, once. It serves a
1279
+ * popover shared by many invokers, each open handing over its own anchor,
1280
+ * and an anchor that is a rectangle rather than an element, such as one
1281
+ * read out of an iframe. Nothing is paired and nothing follows a later
1282
+ * scroll or resize; closing is the caller's, or the platform's for a
1283
+ * light-dismissing popover.
662
1284
  *
663
- * The placement is the menus' own geometry, below the anchor and
664
- * start-aligned, clamped into the viewport; `flip` moves it above the
665
- * anchor where the viewport leaves no room below, as the css
666
- * `position-try-fallbacks` the paired popovers declare. A popover not
667
- * already open is shown and placed before the call returns, so the first
668
- * position anybody sees is the right one and the caller can move the focus
669
- * into it on the next line.
1285
+ * The popover goes below the anchor, start-aligned, clamped 8px inside the
1286
+ * viewport. `flip` moves it above the anchor where there is no room below
1287
+ * and there is room above. The placement is written as inline `top`, `left`,
1288
+ * `right`, `bottom` and `margin`, which stay after the popover closes and
1289
+ * are overwritten by the next call.
1290
+ *
1291
+ * A popover not already open is shown with `showPopover()` and placed
1292
+ * before the call returns, so the first position anybody sees is the right
1293
+ * one and the caller can move the focus into it on the next line. One
1294
+ * already open is placed without being shown again.
670
1295
  *
671
1296
  * @param {HTMLElement} popover the `[popover]` element to show, or an
672
1297
  * already-shown element to place
673
- * @param {Element|DOMRect} anchor the element, or the rectangle, to place
674
- * beside
1298
+ * @param {Element|DOMRect} anchor the element, or the rectangle in
1299
+ * viewport coordinates, to place beside
675
1300
  * @param {object} [options]
676
1301
  * @param {boolean} [options.flip] place above the anchor where no room is
677
- * left below
678
- * @param {number} [options.gap] the distance from the anchor, in pixels
1302
+ * left below, `false` by default
1303
+ * @param {number} [options.gap] the distance from the anchor, in pixels,
1304
+ * `0` by default
679
1305
  * @returns {HTMLElement} the popover
680
1306
  */
681
1307
  static show(popover: HTMLElement, anchor: Element | DOMRect, { flip, gap }?: {
@@ -683,73 +1309,150 @@ declare class Anchors {
683
1309
  gap?: number;
684
1310
  }): HTMLElement;
685
1311
  }
1312
+ export type SelectEntry = {
1313
+ key: any;
1314
+ label: string;
1315
+ metadata: any;
1316
+ };
1317
+ export type SelectHttpClient = {
1318
+ request(method: string, url: string): any;
1319
+ };
686
1320
  /**
687
1321
  * Fetches a select's whole vocabulary from a url and serves every later read
688
1322
  * from it. Concurrent callers share one request, the options may be cached in
689
- * local storage under a revision, and reconfiguring the url discards both.
1323
+ * local storage under a revision, and invalidating discards both.
690
1324
  */
691
1325
  declare class RemoteLoader {
692
1326
  #private;
1327
+ /**
1328
+ * @param {{ http: SelectHttpClient, url: string, method: string, responseMapper: (response: any) => SelectEntry[], prefetch: boolean, revision: string|null }} conf
1329
+ * a null revision never reads nor writes local storage
1330
+ */
693
1331
  constructor({ http, url, method, responseMapper, prefetch, revision }: {
694
- http: any;
695
- method: any;
696
- prefetch: any;
697
- responseMapper: any;
698
- revision: any;
699
- url: any;
1332
+ http: SelectHttpClient;
1333
+ url: string;
1334
+ method: string;
1335
+ responseMapper: (response: any) => SelectEntry[];
1336
+ prefetch: boolean;
1337
+ revision: string | null;
700
1338
  });
1339
+ /**
1340
+ * Fetches the vocabulary now when the loader was built with prefetch, and
1341
+ * does nothing otherwise.
1342
+ * @returns {Promise<void>}
1343
+ * @throws when the fetch fails, or when an invalidation supersedes it
1344
+ */
701
1345
  prefetch(): Promise<void>;
702
- exact(...keys: any[]): Promise<any>;
703
- load(needle: any): Promise<any>;
704
1346
  /**
705
- * Drops the cached vocabulary so the next question refetches it. Any fetch
706
- * still in flight is detached: its outcome belongs to the configuration that
707
- * started it and must neither be served nor stored for the new one.
1347
+ * The entries whose key loosely equals one of the keys, in vocabulary order.
1348
+ * @param {...any} keys
1349
+ * @returns {Promise<SelectEntry[]>}
1350
+ * @throws when the fetch fails, or when an invalidation supersedes it
1351
+ */
1352
+ exact(...keys: any[]): Promise<SelectEntry[]>;
1353
+ /**
1354
+ * The entries whose label contains the needle, ignoring case; every entry
1355
+ * for a nullish or empty needle.
1356
+ * @param {string|null} [needle]
1357
+ * @returns {Promise<SelectEntry[]>}
1358
+ * @throws when the fetch fails, or when an invalidation supersedes it
1359
+ */
1360
+ load(needle?: string | null): Promise<SelectEntry[]>;
1361
+ /**
1362
+ * Drops the vocabulary held in memory so the next question refetches it,
1363
+ * which may still be answered from local storage under the same revision.
1364
+ * A fetch still in flight is detached: its outcome is neither served nor
1365
+ * stored, and the callers waiting on it reject.
1366
+ * @returns {Promise<void>}
708
1367
  */
709
1368
  invalidate(): Promise<void>;
710
- reconfigureUrl(url: any): Promise<void>;
711
1369
  }
712
1370
  /** Asks the endpoint per query instead of fetching the vocabulary once, for a list too large to hold in memory. */
713
1371
  declare class PartialRemoteLoader {
714
1372
  #private;
1373
+ /**
1374
+ * @param {{ http: SelectHttpClient, url: string, method: string, responseMapper: (response: any) => SelectEntry[] }} conf
1375
+ */
715
1376
  constructor({ http, url, method, responseMapper }: {
716
- http: any;
717
- method: any;
718
- responseMapper: any;
719
- url: any;
1377
+ http: SelectHttpClient;
1378
+ url: string;
1379
+ method: string;
1380
+ responseMapper: (response: any) => SelectEntry[];
720
1381
  });
721
1382
  /**
722
- * Nothing is held between queries, so there is no cache to drop: the method
723
- * exists so a caller can invalidate any loader without knowing which it has.
1383
+ * Does nothing, since nothing is held between queries.
1384
+ * @returns {Promise<void>}
724
1385
  */
725
1386
  invalidate(): Promise<void>;
726
- reconfigureUrl(url: any): Promise<void>;
727
- exact(...keys: any[]): Promise<any>;
728
- load(needle: any): Promise<any>;
1387
+ /**
1388
+ * Asks the endpoint for the entries of the keys, sent as repeated `k`
1389
+ * parameters.
1390
+ * @param {...any} keys
1391
+ * @returns {Promise<SelectEntry[]>}
1392
+ * @throws when the request fails
1393
+ */
1394
+ exact(...keys: any[]): Promise<SelectEntry[]>;
1395
+ /**
1396
+ * Asks the endpoint for the entries matching the needle, sent as the `s`
1397
+ * parameter; the filtering is the endpoint's.
1398
+ * @param {string|null} [needle]
1399
+ * @returns {Promise<SelectEntry[]>}
1400
+ * @throws when the request fails
1401
+ */
1402
+ load(needle?: string | null): Promise<SelectEntry[]>;
729
1403
  }
730
1404
  /** Serves a select's options from an array held in memory, which is what the slotted `<option>` elements become. */
731
1405
  declare class InMemoryLoader {
732
1406
  #private;
733
- constructor(data: any);
734
- update(data: any): void;
735
- /** The vocabulary is the data itself: update replaces it, so there is nothing to drop. */
1407
+ /**
1408
+ * @param {SelectEntry[]} data
1409
+ */
1410
+ constructor(data: SelectEntry[]);
1411
+ /**
1412
+ * Replaces the vocabulary every later question is answered from.
1413
+ * @param {SelectEntry[]} data
1414
+ */
1415
+ update(data: SelectEntry[]): void;
1416
+ /**
1417
+ * Does nothing: the vocabulary is the data itself, which only update replaces.
1418
+ * @returns {Promise<void>}
1419
+ */
736
1420
  invalidate(): Promise<void>;
737
- exact(...keys: any[]): any;
738
- load(needle: any): any;
1421
+ /**
1422
+ * The entries whose key loosely equals one of the keys, in vocabulary order.
1423
+ * @param {...any} keys
1424
+ * @returns {SelectEntry[]}
1425
+ */
1426
+ exact(...keys: any[]): SelectEntry[];
1427
+ /**
1428
+ * The entries whose label contains the needle, ignoring case; every entry
1429
+ * for a nullish or empty needle.
1430
+ * @param {string|null} [needle]
1431
+ * @returns {SelectEntry[]}
1432
+ */
1433
+ load(needle?: string | null): SelectEntry[];
739
1434
  }
740
1435
  /**
741
1436
  * Builds the select's loader from its attributes: the slotted options in
742
- * memory, or a remote or chunked loader over src.
1437
+ * memory, or a remote or chunked loader over src. It is registered as the
1438
+ * `loaders:select` component.
743
1439
  *
744
- * A component registered under the `loader` attribute replaces this one and
745
- * must implement the same three methods, each answering `{ key, label,
746
- * metadata }` entries:
1440
+ * A component named by the select's `loader` attribute replaces this one: its
1441
+ * `create(el, conf)` answers an object with these methods, each answering
1442
+ * `{ key, label, metadata }` entries, directly or through a promise:
747
1443
  *
748
- * - `prefetch()` warms the vocabulary if it can, and resolves either way
749
1444
  * - `load(needle)` answers the entries matching the typed text, all of them
750
- * when the needle is nullish, which is the empty search the list opens with
1445
+ * when the needle is empty, which is the search the list opens with
751
1446
  * - `exact(...keys)` answers the entries for those keys, used to label a value
752
- * assigned without going through the list
1447
+ * assigned without going through the list; a key it leaves out is dropped
1448
+ * from the selection
1449
+ * - `prefetch()`, optional, warms the vocabulary; the select calls it at the
1450
+ * upgrade without waiting, and awaits it in `reload()`
1451
+ * - `invalidate()`, optional, drops whatever the loader holds, called by
1452
+ * `reload()`
1453
+ *
1454
+ * An entry whose `metadata.disabled` is truthy is shown and cannot be picked,
1455
+ * with `metadata.reason`, when present, as the row's title.
753
1456
  */
754
1457
  declare class SelectLoader {
755
1458
  #private;
@@ -759,132 +1462,504 @@ declare class SelectLoader {
759
1462
  * `mode: 'chunked'`, per query. A test, or a caller holding its own
760
1463
  * configuration, builds a loader this way; `create` is the same thing with
761
1464
  * an element's attributes parsed first.
762
- * @param {{ data?: any[], http?: any, url?: string, method?: string, mode?: string, prefetch?: boolean, revision?: string|null, responseMapper?: any }} conf
1465
+ *
1466
+ * A remote loader fetches once and filters in memory, caching the response
1467
+ * in local storage under `method@url` when a revision is given; a chunked
1468
+ * one sends every search as `s` and every key lookup as `k`. The response
1469
+ * mapper turns the parsed response body into entries.
1470
+ * @param {{ data?: SelectEntry[], url?: undefined, http?: undefined, method?: undefined, mode?: undefined, prefetch?: undefined, revision?: undefined, responseMapper?: undefined }
1471
+ * | { data?: undefined, url: string, http: SelectHttpClient, method?: string, mode?: string, prefetch?: boolean, revision?: string|null, responseMapper: (response: any) => SelectEntry[] }} conf
1472
+ * `method` defaults to POST
1473
+ * @returns {InMemoryLoader|PartialRemoteLoader|RemoteLoader}
763
1474
  */
764
1475
  static from({ data, http, url, method, mode, prefetch, revision, responseMapper }: {
765
- data?: any[];
766
- http?: any;
767
- url?: string;
1476
+ data?: SelectEntry[];
1477
+ url?: undefined;
1478
+ http?: undefined;
1479
+ method?: undefined;
1480
+ mode?: undefined;
1481
+ prefetch?: undefined;
1482
+ revision?: undefined;
1483
+ responseMapper?: undefined;
1484
+ } | {
1485
+ data?: undefined;
1486
+ url: string;
1487
+ http: SelectHttpClient;
768
1488
  method?: string;
769
1489
  mode?: string;
770
1490
  prefetch?: boolean;
771
1491
  revision?: string | null;
772
- responseMapper?: any;
1492
+ responseMapper: (response: any) => SelectEntry[];
1493
+ }): InMemoryLoader | PartialRemoteLoader | RemoteLoader;
1494
+ /**
1495
+ * Builds the loader an element's attributes describe. Without `src` the
1496
+ * vocabulary is the `<option>` elements of `conf.options`, each keyed by its
1497
+ * `value` attribute or else its trimmed text. With `src` the url is
1498
+ * requested through the `http-client` component with `method` (POST by
1499
+ * default): once on first use, or at the upgrade under `preload`, or per
1500
+ * query under `mode="chunked"`. A `revision` caches the whole response in local storage; a
1501
+ * valueless one takes the `revision` component, calling it when it is a
1502
+ * function, and warns and does not cache when none is registered.
1503
+ *
1504
+ * The response becomes entries through `k-expr` and `l-expr` evaluated on
1505
+ * each row of `d-expr` (the response itself by default), with `m-expr` as
1506
+ * the metadata (the row by default); else through the `response-mapper`
1507
+ * component; else each row is read as a `[key, label, metadata]` array.
1508
+ * @param {ParsedElement} el the element declaring the loader attributes
1509
+ * @param {{ options?: DocumentFragment }} conf
1510
+ * @returns {InMemoryLoader|PartialRemoteLoader|RemoteLoader}
1511
+ */
1512
+ static create(el: ParsedElement, conf: {
1513
+ options?: DocumentFragment;
773
1514
  }): InMemoryLoader | PartialRemoteLoader | RemoteLoader;
774
- static create(el: any, conf: any): InMemoryLoader | PartialRemoteLoader | RemoteLoader;
775
1515
  }
776
- /** The options popup of a select: listbox semantics, one loading claim per show, a localized empty state. */
1516
+ /**
1517
+ * The options popup of a select, a `<ful-dropdown>` the owner shows as a
1518
+ * popover: a spinner while the loader runs, a localized empty state, and a
1519
+ * `role="listbox"` menu of `role="option"` rows.
1520
+ *
1521
+ * The owner drives it and listens to it. A pick dispatches a bubbling
1522
+ * `change` whose detail is `{ index, entry }`, the row's index as a string
1523
+ * and the entry it was rendered from. The active row is announced by a
1524
+ * non-bubbling `activechange` whose detail is `{ id }`, the row's id or null
1525
+ * when none is active, for the owner to set `aria-activedescendant`. A
1526
+ * pressed row does not take the focus from the owner's control.
1527
+ */
777
1528
  declare class Dropdown extends ParsedElement {
778
1529
  #private;
1530
+ /**
1531
+ * `listbox` is the id given to the menu, so an owner can set
1532
+ * `aria-controls` before this element upgrades; a generated id otherwise.
1533
+ */
779
1534
  static attributes: string[];
1535
+ /** The default slot, when not blank, is the row template in place of the `options` template. */
780
1536
  static slots: boolean;
781
1537
  static template: string;
1538
+ /**
1539
+ * `options` renders one row per entry, each entry carrying its `index`
1540
+ * beside its own keys. A replacement must keep one `<li>` per entry, in
1541
+ * order, with the index as its `value`: picking reads the entry back from it.
1542
+ */
782
1543
  static templates: {
783
1544
  options: string;
784
1545
  };
1546
+ /**
1547
+ * @param {{ slots: Record<string, DocumentFragment> }} conf
1548
+ */
785
1549
  render({ slots }: {
786
- slots: any;
1550
+ slots: Record<string, DocumentFragment>;
787
1551
  }): void;
1552
+ /**
1553
+ * Picks the highlighted row, or the first enabled one when none is: hides
1554
+ * the dropdown and dispatches `change`. Does nothing when no row is enabled.
1555
+ */
788
1556
  acceptSelection(): void;
789
- update(values: any, keys?: any[]): void;
1557
+ /**
1558
+ * Renders the entries as rows and highlights the first one whose key is
1559
+ * picked, else the row the template marked `selected`, else the first
1560
+ * enabled row. A row is marked `picked` and
1561
+ * `aria-selected="true"` when its key loosely equals one of the keys, and
1562
+ * `aria-disabled="true"` when its `metadata.disabled` is truthy, titled with
1563
+ * `metadata.reason`. No entries shows the empty state instead of the menu.
1564
+ * @param {SelectEntry[]} values
1565
+ * @param {any[]} [keys] the keys currently selected
1566
+ * @throws {Error} when values is undefined
1567
+ */
1568
+ update(values: SelectEntry[], keys?: any[]): void;
1569
+ /**
1570
+ * Closes the popover, if open, and announces that no row is active. A
1571
+ * `show` still waiting on its loader is superseded: its outcome is neither
1572
+ * rendered nor thrown.
1573
+ */
790
1574
  hide(): void;
1575
+ /**
1576
+ * Whether the popover is open.
1577
+ * @returns {boolean}
1578
+ */
791
1579
  get shown(): boolean;
792
- show(loader: any, keys?: any[]): Promise<void>;
793
- moveOrShow(forward: any, loader: any, keys?: any[]): Promise<void>;
794
- jump(first: any): void;
795
- page(forward: any): void;
1580
+ /**
1581
+ * Opens the popover with the spinner showing, then renders what the loader
1582
+ * answers as `update` does. A show overtaken by a later `show` or by `hide`
1583
+ * before its loader settles leaves the dropdown alone and resolves, even
1584
+ * when its loader rejected.
1585
+ * @param {() => SelectEntry[]|Promise<SelectEntry[]>} loader
1586
+ * @param {any[]} [keys] the keys currently selected
1587
+ * @returns {Promise<void>}
1588
+ * @throws what the loader threw, after hiding the dropdown, when this show
1589
+ * is still the current one
1590
+ */
1591
+ show(loader: () => SelectEntry[] | Promise<SelectEntry[]>, keys?: any[]): Promise<void>;
1592
+ /**
1593
+ * Moves the highlight to the next or previous enabled row when the dropdown
1594
+ * is shown, staying put at the edge, and shows it otherwise.
1595
+ * @param {boolean} forward
1596
+ * @param {() => SelectEntry[]|Promise<SelectEntry[]>} loader used only to show
1597
+ * @param {any[]} [keys] the keys currently selected, used only to show
1598
+ * @returns {Promise<void>}
1599
+ * @throws what `show` throws
1600
+ */
1601
+ moveOrShow(forward: boolean, loader: () => SelectEntry[] | Promise<SelectEntry[]>, keys?: any[]): Promise<void>;
1602
+ /**
1603
+ * Highlights the first enabled row, or the last one.
1604
+ * @param {boolean} first
1605
+ */
1606
+ jump(first: boolean): void;
1607
+ /**
1608
+ * Moves the highlight by as many enabled rows as the menu shows at once,
1609
+ * one when that cannot be measured, stopping at the last enabled row.
1610
+ * @param {boolean} forward
1611
+ */
1612
+ page(forward: boolean): void;
796
1613
  }
797
- /** A combobox acting like a select over a loader's vocabulary, single or multiple. */
1614
+ /**
1615
+ * A `<ful-select>`: a combobox over a loader's vocabulary of
1616
+ * `{ key, label, metadata }` entries, holding one key or, under `multiple`,
1617
+ * several. Typing filters the list, opening it from rest offers the whole
1618
+ * vocabulary, and the keyboard follows the combobox pattern: arrows move and
1619
+ * open (Alt+ArrowDown opens, Alt+ArrowUp closes), Home, End, PageUp and
1620
+ * PageDown move in the open list, Enter accepts the highlighted entry or,
1621
+ * with the list closed, submits the form, Escape reverts the edit, and Tab
1622
+ * or leaving the field commits it. Emptying the text of a single select and
1623
+ * leaving the field clears it. Backspace with the caret at the start removes
1624
+ * the last entry, and ArrowLeft there moves the focus to the badges of a
1625
+ * multiple select, where Enter, Space, Backspace or Delete remove one.
1626
+ *
1627
+ * Slots: the default one is the label, `info` sits beside it, `before` and
1628
+ * `after` are affixes around the control, `options` holds the `<option>`
1629
+ * elements of an in-memory vocabulary, `dropdown` replaces the dropdown's
1630
+ * row template and `items` the `items` template.
1631
+ *
1632
+ * Every change of the selection made through the element dispatches a
1633
+ * bubbling `change` whose detail carries `value` and `entry`, as the getters
1634
+ * answer them; assigning `value` dispatches none.
1635
+ */
798
1636
  declare class Select extends Field {
799
1637
  #private;
1638
+ /**
1639
+ * Read once at the upgrade, so a later write changes nothing: `name`,
1640
+ * `loader` (the component building the loader, `loaders:select` by
1641
+ * default), `k-type` (`number` or `boolean` coerce the keys, strings
1642
+ * otherwise), and the loader configuration `SelectLoader.create` reads,
1643
+ * but for `src` and `method`, which stay live.
1644
+ */
800
1645
  static attributes: string[];
1646
+ /**
1647
+ * Beside the field's claims: `multiple`, `item-list`, `value` as a comma
1648
+ * separated list of keys, trimmed, whether or not the select is multiple,
1649
+ * and `src` and `method`.
1650
+ */
801
1651
  static observed: string[];
802
- unmarshal(attr: any, str: any): any;
1652
+ /**
1653
+ * Reads a present but empty `value` attribute of a single select as the
1654
+ * empty key, which an `<option value="">` can carry; a multiple select reads
1655
+ * it as no keys.
1656
+ * @param {string} attr
1657
+ * @param {string|null} str
1658
+ * @returns {any}
1659
+ * @throws when the class declares no such attribute
1660
+ */
1661
+ unmarshal(attr: string, str: string | null): any;
803
1662
  static slots: boolean;
804
1663
  static template: string;
1664
+ /**
1665
+ * `items` renders the selection below the control, shown under `item-list`,
1666
+ * over `entries`, the selected entries in order. A replacement must keep one
1667
+ * `<ful-item>` per entry, in order, each with a `<button>` that removes it.
1668
+ */
805
1669
  static templates: {
806
1670
  items: string;
807
1671
  };
1672
+ /**
1673
+ * @param {{ slots: Record<string, DocumentFragment> }} conf
1674
+ * @returns {{ fragment: DocumentFragment, control: HTMLInputElement, error: Element|null, label: Element|null }}
1675
+ */
808
1676
  _build({ slots }: {
809
- slots: any;
1677
+ slots: Record<string, DocumentFragment>;
810
1678
  }): {
811
- fragment: any;
812
- control: any;
813
- error: any;
814
- label: any;
1679
+ fragment: DocumentFragment;
1680
+ control: HTMLInputElement;
1681
+ error: Element | null;
1682
+ label: Element | null;
815
1683
  };
816
- /** Hands the loader to the callback, for runtime reconfigurations. */
817
- withLoader(fn: any): Promise<any>;
818
- /**
819
- * Drops whatever the loader is holding and asks it about the current selection
820
- * again, which is what a select whose vocabulary depends on another control
821
- * needs when that control changes. A key the loader no longer knows is dropped
822
- * from the selection, so a value invalidated by the change does not survive it,
823
- * and one it still knows keeps its place with a fresh label.
1684
+ /**
1685
+ * Hands the loader to the callback, for runtime changes such as
1686
+ * `update(entries)` on an in-memory loader.
1687
+ * @template T
1688
+ * @param {(loader: any) => T|Promise<T>} fn
1689
+ * @returns {Promise<T>} what the callback answers
1690
+ */
1691
+ withLoader<T>(fn: (loader: any) => T | Promise<T>): Promise<T>;
1692
+ /**
1693
+ * Drops whatever the loader is holding, awaits its prefetch (which fetches
1694
+ * again when the select declares `preload`), and asks it about the current
1695
+ * selection again, which is what a select whose vocabulary depends on
1696
+ * another control needs when that control changes. A key the loader no
1697
+ * longer knows is dropped from the selection, so a value invalidated by the
1698
+ * change does not survive it, and one it still knows keeps its place with a
1699
+ * fresh label. The badges and items follow, and no `change` is dispatched.
824
1700
  *
825
- * Pass a url first where the vocabulary lives at a different address:
1701
+ * Where the vocabulary lives at a different address, write `src` or call
1702
+ * `reconfigure`, which build the loader for the new url and reload.
1703
+ * @returns {Promise<void>}
1704
+ * @throws what the loader's invalidate, prefetch or key lookup throws
1705
+ */
1706
+ reload(): Promise<void>;
1707
+ /**
1708
+ * The url the loader asks for the vocabulary, reflected as the `src`
1709
+ * attribute.
1710
+ * @returns {string|null}
1711
+ */
1712
+ get src(): string | null;
1713
+ /**
1714
+ * Points the select at another vocabulary: the dropdown closes, the loader
1715
+ * is built again through its component's `create`, as at the render, and
1716
+ * the select reloads as `reload()` does, so a selected key the new
1717
+ * vocabulary does not know is dropped and one it knows is labelled again,
1718
+ * with no `change` dispatched. A write that does not change the url does
1719
+ * nothing.
826
1720
  *
827
- * citta.addEventListener('change', async () => {
828
- * await cap.withLoader((l) => l.reconfigureUrl(`/api/cap?citta=${citta.value}`));
829
- * await cap.reload();
1721
+ * The reload is not awaited: a failed fetch or lookup surfaces as an
1722
+ * unhandled rejection.
1723
+ * @param {string|null|undefined} value null or undefined removes the url,
1724
+ * which the default loader answers with the slotted options
1725
+ */
1726
+ set src(value: string | null | undefined);
1727
+ /**
1728
+ * The http method the default loader sends, reflected as the `method`
1729
+ * attribute; `POST` when absent.
1730
+ * @returns {string|null}
1731
+ */
1732
+ get method(): string | null;
1733
+ /**
1734
+ * Changes the http method the way `src` changes the url: the loader is
1735
+ * built again and the select reloads.
1736
+ * @param {string|null|undefined} value
1737
+ */
1738
+ set method(value: string | null | undefined);
1739
+ /**
1740
+ * Writes `src` and `method` together and reloads once: the loader is built
1741
+ * again for the pair when either changed, as a single write of either does,
1742
+ * and the select then reloads as `reload()` does, whether or not they
1743
+ * changed.
1744
+ *
1745
+ * citta.addEventListener('change', () => {
1746
+ * cap.reconfigure({ src: `/api/cap?citta=${citta.value}` }, { notify: true });
830
1747
  * });
1748
+ * @param {{ src?: string|null, method?: string|null }} attributes a name
1749
+ * left out keeps its value, null removes the attribute
1750
+ * @param {{ notify?: boolean }} [options] `notify` dispatches `change` when
1751
+ * the reload dropped a selected key, so a form or a table filtered by the
1752
+ * select sees the value it lost; nothing is dispatched otherwise
1753
+ * @returns {Promise<void>} settling once the reload has
1754
+ * @throws what the loader's invalidate, prefetch or key lookup throws
1755
+ */
1756
+ reconfigure(attributes: {
1757
+ src?: string | null;
1758
+ method?: string | null;
1759
+ }, { notify }?: {
1760
+ notify?: boolean;
1761
+ }): Promise<void>;
1762
+ /**
1763
+ * The text typed to search the vocabulary: what the dropdown was last
1764
+ * asked to match. `''` while the field is not being searched, its control
1765
+ * then showing the selection's label rather than a query.
1766
+ * @returns {string}
1767
+ */
1768
+ get query(): string;
1769
+ /**
1770
+ * Selects the keys, coerced by `k-type`: a key, an array of keys, or null or
1771
+ * undefined for none. The empty string is a key like any other. The keys
1772
+ * are applied at once, each labeled by itself until the loader's `exact`
1773
+ * answers; a key it does not answer is then dropped, a key removed in the
1774
+ * meantime stays removed, and a later assignment wins over the late answer.
1775
+ * A failed lookup keeps the keys and surfaces as an unhandled rejection.
1776
+ * No `change` is dispatched. A key containing a
1777
+ * comma, which the value attribute cannot carry, is kept and warned about
1778
+ * once per element.
1779
+ * @param {any} vs
831
1780
  */
832
- reload(): Promise<void>;
833
1781
  set value(vs: any);
1782
+ /**
1783
+ * The selected keys, in selection order, for a multiple select; the one
1784
+ * key, or null, for a single one.
1785
+ * @returns {any}
1786
+ */
834
1787
  get value(): any;
835
- /** The selection as {key, label, metadata} entries, the change detail's vocabulary: the only one for a single select, every one when multiple. */
836
- get entry(): any;
837
- get multiple(): any;
838
- set multiple(v: any);
839
- get itemList(): any;
840
- set itemList(v: any);
1788
+ /**
1789
+ * The selection as entries, the ones in the `change` detail: an array for
1790
+ * a multiple select, the one entry or null for a single one. An entry whose
1791
+ * label has not been resolved yet carries its key as the label.
1792
+ * @returns {SelectEntry|SelectEntry[]|null}
1793
+ */
1794
+ get entry(): SelectEntry | SelectEntry[] | null;
1795
+ /**
1796
+ * Whether the select holds several keys: `value` and `entry` answer arrays,
1797
+ * the input stays empty and the selection shows as removable badges in the
1798
+ * control.
1799
+ * @returns {boolean}
1800
+ */
1801
+ get multiple(): boolean;
1802
+ /** @param {boolean} v */
1803
+ set multiple(v: boolean);
1804
+ /**
1805
+ * Whether the selection shows as the `items` list below the control instead
1806
+ * of as badges in it.
1807
+ * @returns {boolean}
1808
+ */
1809
+ get itemList(): boolean;
1810
+ /** @param {boolean} v */
1811
+ set itemList(v: boolean);
841
1812
  }
842
- /** A group of radios declared as ful-radio children, a fieldset carrying the group semantics. */
1813
+ /**
1814
+ * A group of radios declared as `ful-radio` children. Each `ful-radio` becomes
1815
+ * a radio input carrying its attributes, labelled by its content; every
1816
+ * `input-*` attribute on the group is forwarded onto each radio as well. The
1817
+ * default slot is the `<legend>`, and `header` and `footer` slots frame the
1818
+ * list. The radios share a name of their own (derived from `name`, or minted
1819
+ * when there is none) and carry `form=""`, so the group is the only element
1820
+ * the form sees. A radio's native `change` is stopped and republished as the
1821
+ * group's own.
1822
+ *
1823
+ * The host carries the `radiogroup` role, `aria-readonly`, `aria-required`,
1824
+ * `aria-invalid` and the description. The disabled and readonly claims act on
1825
+ * the group's `<fieldset>`, and readonly refuses every click inside it.
1826
+ * `focus()` lands on the first radio.
1827
+ */
843
1828
  declare class RadioGroup extends Field {
844
1829
  #private;
1830
+ /** @type {string[]} */
845
1831
  static attributes: string[];
846
1832
  static slots: boolean;
1833
+ /** @type {string} */
847
1834
  static ROLE: string;
848
1835
  static template: string;
849
1836
  /**
850
1837
  * @param {{slots: any}} conf
851
- * @returns {any}
1838
+ * @returns {any} the pieces: the first radio as the control, the fieldset
1839
+ * as the `claims` and `freeze` pieces, and the host as the `described` and
1840
+ * `announces` pieces
852
1841
  */
853
1842
  _build({ slots }: {
854
1843
  slots: any;
855
1844
  }): any;
1845
+ /**
1846
+ * The value of the checked radio, `null` when none is. Under
1847
+ * `type="boolean"` it answers `true` for a radio whose value is `true` and
1848
+ * `false` for any other. Writing a value checks the radio carrying it, and
1849
+ * writing `null` or a value no radio carries clears the selection.
1850
+ * @type {string | boolean | null}
1851
+ */
856
1852
  get value(): string | boolean | null;
857
1853
  set value(value: string | boolean | null);
858
1854
  }
859
- /** A checkbox, or a switch under the type=switch claim. */
1855
+ /**
1856
+ * A checkbox, or a switch with the `switch` role under `type="switch"`. The
1857
+ * label is the default slot, pointed at the input with `for` and `id`, so a
1858
+ * click on it toggles the value. Every `input-*` attribute is forwarded onto
1859
+ * the input. The input's native `change` is stopped and republished as the
1860
+ * field's own. Readonly freezes the whole choice, label included: no click
1861
+ * inside it toggles the value while the claim holds.
1862
+ */
860
1863
  declare class Checkbox extends Field {
861
1864
  #private;
1865
+ /** @type {string[]} */
862
1866
  static attributes: string[];
1867
+ /** @type {string[]} */
863
1868
  static observed: string[];
864
1869
  static slots: boolean;
865
1870
  static template: string;
1871
+ /**
1872
+ * @param {{ slots: Record<string, DocumentFragment> | undefined }} conf
1873
+ * @returns {any} the pieces, the `ful-choice` container being the `freeze` piece
1874
+ */
866
1875
  _build({ slots }: {
867
- slots: any;
868
- }): {
869
- fragment: any;
870
- control: any;
871
- error: any;
872
- label: any;
873
- freeze: any;
874
- };
875
- get value(): any;
876
- set value(value: any);
1876
+ slots: Record<string, DocumentFragment> | undefined;
1877
+ }): any;
1878
+ /**
1879
+ * Whether the box is checked. The `value` attribute is read with the `bool`
1880
+ * mapper, where only the text `true` means checked.
1881
+ * @type {boolean}
1882
+ */
1883
+ get value(): boolean;
1884
+ set value(value: boolean);
877
1885
  }
878
- /** The sort control of a table header: focusable, keyboard-activated, walking asc, desc, unsorted. */
1886
+ export type TablePageRequest = {
1887
+ page: number;
1888
+ size: number;
1889
+ };
1890
+ export type TableSortRequest = {
1891
+ sorter: string;
1892
+ order: string;
1893
+ };
1894
+ export type TablePageResponse = {
1895
+ data: any[];
1896
+ size: number;
1897
+ };
1898
+ export type TableRowLoader = {
1899
+ load(pageRequest: TablePageRequest, sortRequest: TableSortRequest | null, filterRequest: Record<string, any>): Promise<TablePageResponse>;
1900
+ };
1901
+ /**
1902
+ * @typedef {{ page: number, size: number }} TablePageRequest the zero based
1903
+ * index of the page and the number of rows in a page
1904
+ */
1905
+ /**
1906
+ * @typedef {{ sorter: string, order: string }} TableSortRequest the sort key
1907
+ * of a column and its direction, `asc` or `desc`
1908
+ */
1909
+ /**
1910
+ * @typedef {{ data: any[], size: number }} TablePageResponse the rows of the
1911
+ * requested page and the total number of rows across every page
1912
+ */
1913
+ /**
1914
+ * @typedef {{ load(pageRequest: TablePageRequest, sortRequest: TableSortRequest|null, filterRequest: Record<string, any>): Promise<TablePageResponse> }} TableRowLoader
1915
+ */
1916
+ /**
1917
+ * The sort control of a table header, `ful-sorter`: a `role="button"` with
1918
+ * `tabindex="0"`, activated by a click or by Enter, NumpadEnter or Space.
1919
+ *
1920
+ * Each activation dispatches `sort:requested` (bubbling, not cancelable) with
1921
+ * `detail.value` set to `{ sorter, order }`: `sorter` is the `sorter`
1922
+ * attribute, read once at the upgrade, and `order` is the one after the
1923
+ * current `order` in the cycle `asc`, `desc`, `null`. The button does not
1924
+ * change its own `order`: whoever handles the event sets it, which `ful-table`
1925
+ * does once the sorted page has loaded.
1926
+ */
879
1927
  declare class SortButton extends ParsedElement {
880
1928
  #private;
881
1929
  static attributes: string[];
882
1930
  static observed: string[];
883
1931
  render(): void;
884
- get order(): any;
885
- set order(value: any);
1932
+ /**
1933
+ * The direction the column is sorted in, `null` when unsorted.
1934
+ * @returns {string|null}
1935
+ */
1936
+ get order(): string | null;
1937
+ /**
1938
+ * Reflects the direction to the `order` attribute and sets `aria-sort` on
1939
+ * the enclosing `<th>`, if any: `ascending` for `asc`, `descending` for any
1940
+ * other non-empty value, removed for an empty one.
1941
+ * @param {string|null} value an empty string or `null` means unsorted
1942
+ */
1943
+ set order(value: string | null);
886
1944
  }
887
- /** The pager: a window of page links around the current one, and the reload control. */
1945
+ /**
1946
+ * The pager, `ful-pagination`: a previous button, a window of page buttons
1947
+ * around the current page, a next button, a reload button and a line saying
1948
+ * which page of how many is shown.
1949
+ *
1950
+ * The window holds at most `pages` buttons (read once at the upgrade, default 5),
1951
+ * centred on the current page and shifted back on the last pages so it stays
1952
+ * full. The current page's button carries `aria-current="page"` and stays
1953
+ * enabled. Previous and next are disabled where there is no page to go to.
1954
+ *
1955
+ * A click on an enabled button other than the current page dispatches
1956
+ * `page:requested` (bubbling, not cancelable) with `detail.value` the zero based
1957
+ * index of the page asked for; the reload button asks for the current page.
1958
+ * The pager does not move itself: whoever handles the event calls `update`,
1959
+ * which `ful-table` does once the page has loaded.
1960
+ *
1961
+ * The icons are the `ful-icon` names in `Pagination.config`.
1962
+ */
888
1963
  declare class Pagination extends ParsedElement {
889
1964
  #private;
890
1965
  static observed: string[];
@@ -897,47 +1972,130 @@ declare class Pagination extends ParsedElement {
897
1972
  static template: string;
898
1973
  render(): void;
899
1974
  /**
900
- * Moves the pager to a page, a page count, or both, and repaints once. The
901
- * two are one state: writing them one at a time repainted the bar twice per
902
- * load, the first pass drawing the new page against the old count.
903
- * @param {{ current?: number|null, total?: number|null }} [state]
1975
+ * Moves the pager to a page, a page count, or both, reflects both to their
1976
+ * attributes and repaints the bar once. A key left out keeps its value; a
1977
+ * `null` one becomes 0. A count below one is shown as one empty page.
1978
+ *
1979
+ * The bar is replaced on every repaint. When the focus was inside it, it
1980
+ * moves to the equivalent control of the new bar: the button of the same
1981
+ * page, else the same enabled control, else the current page's button.
1982
+ * @param {{ current?: number|null, total?: number|null }} [state] `current`
1983
+ * is the zero based index of the page shown, `total` the number of pages
904
1984
  */
905
1985
  update({ current: toCurrent, total: toTotal }?: {
906
1986
  current?: number | null;
907
1987
  total?: number | null;
908
1988
  }): void;
1989
+ /**
1990
+ * The number of pages, as last written.
1991
+ * @returns {number}
1992
+ */
909
1993
  get total(): number;
910
- set total(value: number);
1994
+ /**
1995
+ * Same as `update({ total: value })`.
1996
+ * @param {number|null} value
1997
+ */
1998
+ set total(value: number | null);
1999
+ /**
2000
+ * The zero based index of the page shown.
2001
+ * @returns {number}
2002
+ */
911
2003
  get current(): number;
912
- set current(value: number);
2004
+ /**
2005
+ * Same as `update({ current: value })`.
2006
+ * @param {number|null} value
2007
+ */
2008
+ set current(value: number | null);
913
2009
  }
914
2010
  /** Reads the schema declaration into the header and row templates a table renders from. */
915
2011
  declare class TableSchemaParser {
916
- static parse(nodeOrFragment: any, template: any): {
917
- headersTemplate: any;
918
- rowsTemplate: any;
2012
+ /**
2013
+ * Builds a header `<tr>` and a row `<tr data-tpl-each="rows">` from the
2014
+ * `<schema>` child of the slot. The `<schema>` attributes are copied onto
2015
+ * both rows. Each `<column>` becomes a `<th>` and a `<td>` carrying the
2016
+ * column's attributes except `title`, `sorter` and `order`, so a
2017
+ * `data-tpl-*` on a column applies to both cells; the two templates carry
2018
+ * `inHeaders` and `inRows` in their scope, so a column can tell them apart,
2019
+ * as in `data-tpl-if="inRows"`. The header cell holds the `<title>` child
2020
+ * element of the column, else the text of its `title` attribute, wrapped
2021
+ * in a `ful-sorter` when the column declares `sorter` or `order`. The body
2022
+ * cell holds the column's remaining children.
2023
+ *
2024
+ * The column elements are consumed: their `<title>`, `title`, `sorter` and
2025
+ * `order` are removed and their children moved into the cells.
2026
+ * @param {DocumentFragment|Element|undefined} nodeOrFragment the `schema`
2027
+ * slot, undefined when the table has none
2028
+ * @param {ReturnType<ParsedElement['template']>} template the template the
2029
+ * header and row templates derive from, keeping its scope
2030
+ * @returns {{ headersTemplate: ReturnType<ParsedElement['template']>, rowsTemplate: ReturnType<ParsedElement['template']>, sort: { sorter: string|null, order: string|null } | null, length: number }}
2031
+ * `sort` is the initial sort, taken from the first column declaring both
2032
+ * `order` and `sorter` (a column with only `order` gets an arrow but no
2033
+ * initial sort), `null` when none does; `length` is the number of columns
2034
+ * @throws {Error} when the slot is missing or holds no `<schema>`
2035
+ */
2036
+ static parse(nodeOrFragment: DocumentFragment | Element | undefined, template: ReturnType<ParsedElement['template']>): {
2037
+ headersTemplate: ReturnType<ParsedElement['template']>;
2038
+ rowsTemplate: ReturnType<ParsedElement['template']>;
919
2039
  sort: {
920
2040
  sorter: string | null;
921
2041
  order: string | null;
922
- };
2042
+ } | null;
923
2043
  length: number;
924
2044
  };
925
2045
  }
926
2046
  /** Serves a table's rows from an array held in memory, applying the sort and the paging itself. */
927
2047
  declare class InMemoryTableLoader {
928
2048
  #private;
929
- constructor(data: any);
930
- load(pageRequest: any, sortRequest: any, filterRequest: any): Promise<{
931
- data: any;
932
- size: any;
933
- }>;
934
- update(data: any): void;
2049
+ /**
2050
+ * @param {any[]} data the rows
2051
+ */
2052
+ constructor(data: any[]);
2053
+ /**
2054
+ * Sorts a copy of the rows by the `sorter` property, rows missing it last
2055
+ * in either direction and rows with equal values in their original order,
2056
+ * then slices the requested page. The filters are ignored.
2057
+ * @param {TablePageRequest} pageRequest
2058
+ * @param {{ sorter?: string|null, order?: string|null } | null} sortRequest
2059
+ * `order` `desc` sorts descending, anything else ascending; no `sorter`
2060
+ * keeps the original order
2061
+ * @param {Record<string, any>} filterRequest not read
2062
+ * @returns {Promise<TablePageResponse>}
2063
+ */
2064
+ load(pageRequest: TablePageRequest, sortRequest: {
2065
+ sorter?: string | null;
2066
+ order?: string | null;
2067
+ } | null, filterRequest: Record<string, any>): Promise<TablePageResponse>;
2068
+ /**
2069
+ * Replaces the rows. The table shows them at its next load.
2070
+ * @param {any[]} data
2071
+ */
2072
+ update(data: any[]): void;
935
2073
  }
936
2074
  /** Requests one page of rows from a url, passing the page, the sort and the filters to the endpoint. */
937
2075
  declare class RemoteTableLoader {
938
2076
  #private;
939
- constructor(http: any, url: any, method: any, responseMapper?: (response: any) => any);
940
- load(pageRequest: any, sortRequest: any, filterRequest: any): Promise<any>;
2077
+ /**
2078
+ * @param {{ request(method: string, url: string): any }} http the `http-client` component
2079
+ * @param {string} url
2080
+ * @param {string} method
2081
+ * @param {(response: any) => TablePageResponse} [responseMapper] turns the
2082
+ * parsed json body into the page response; the identity when omitted
2083
+ */
2084
+ constructor(http: {
2085
+ request(method: string, url: string): any;
2086
+ }, url: string, method: string, responseMapper?: (response: any) => TablePageResponse);
2087
+ /**
2088
+ * Sends the query parameters `page`, `size`, `sort` as `sorter,order`
2089
+ * (left out when `sortRequest` is null) and `filters` as a json object of
2090
+ * the filters with a truthy value (left out when there is none), and
2091
+ * answers the json body through the response mapper.
2092
+ * @param {TablePageRequest} pageRequest
2093
+ * @param {TableSortRequest|null} sortRequest
2094
+ * @param {Record<string, any>} filterRequest
2095
+ * @returns {Promise<TablePageResponse>} rejecting with what the client's
2096
+ * `fetchJson()` rejects with
2097
+ */
2098
+ load(pageRequest: TablePageRequest, sortRequest: TableSortRequest | null, filterRequest: Record<string, any>): Promise<TablePageResponse>;
941
2099
  }
942
2100
  /**
943
2101
  * Builds the table's loader from its attributes: an in-memory one, or the
@@ -945,23 +2103,55 @@ declare class RemoteTableLoader {
945
2103
  *
946
2104
  * A component registered under the `loader` attribute replaces this one and
947
2105
  * must implement `load(pageRequest, sortRequest, filterRequest)`, answering
948
- * `{ data, page, size }` for the requested page. `pageRequest` carries the page
949
- * index and its size, `sortRequest` the column and direction, and
2106
+ * `{ data, size }`: the rows of the requested page and the total count of
2107
+ * rows. `pageRequest` carries the page index and its size, `sortRequest` the column and direction, and
950
2108
  * `filterRequest` the values of the filters in the slot.
951
2109
  */
952
2110
  declare class TableLoader {
953
- static create(el: any, conf: any): InMemoryTableLoader | RemoteTableLoader;
2111
+ /**
2112
+ * A table with a `src` attribute gets a `RemoteTableLoader` sending through
2113
+ * the `http-client` component with the `method` attribute or `GET`, and
2114
+ * mapping the response through the component named by `response-mapper`,
2115
+ * if any. A table without `src` gets an empty `InMemoryTableLoader`, which
2116
+ * `withLoader` reaches to give it rows.
2117
+ * @param {Element & { component(name: string): any }} el the table
2118
+ * @param {unknown} [conf] not read
2119
+ * @returns {InMemoryTableLoader | RemoteTableLoader}
2120
+ */
2121
+ static create(el: Element & {
2122
+ component(name: string): any;
2123
+ }, conf?: unknown): InMemoryTableLoader | RemoteTableLoader;
954
2124
  }
955
- /** A table loading its rows from a loader, with sorting, pagination and an optional filter form. */
2125
+ /**
2126
+ * A table, `ful-table`, loading its rows a page at a time from a loader, with
2127
+ * a header sorter per sortable column, a `ful-pagination` under it and an
2128
+ * optional filter form.
2129
+ *
2130
+ * The loader is the component named by `loader`, default `loaders:table`
2131
+ * (see `TableLoader`). The `schema` slot declares the columns (see
2132
+ * `TableSchemaParser.parse`); the `filters` slot is wrapped in a `ful-form`,
2133
+ * whose values are the filter request; the `caption`, `footer` and `empty`
2134
+ * slots go in the table's caption, its `<tfoot>` and the panel shown for a
2135
+ * load answering no rows. Attributes starting with `table-` are forwarded,
2136
+ * without the prefix, to the inner `<table>`.
2137
+ *
2138
+ * With `autoload` the first load starts at the upgrade, which does not wait
2139
+ * for it. Without it the table shows an initial panel until `load`,
2140
+ * `reload` or `resetWithFilter` is called.
2141
+ *
2142
+ * Inside the table a `page:requested` loads that page with the current size,
2143
+ * sort and filters; a `sort:requested` loads the current page with the new
2144
+ * sort (none for a `null` order) and, once that load is the one showing, sets
2145
+ * the order of the sorter that asked and clears the others; a `submit:success`
2146
+ * of the filter form loads the first page with the submitted request as the
2147
+ * filters. The loads these start, and the autoload, are not awaited by
2148
+ * anything: a failure shows the error panel and its rejection is unhandled.
2149
+ */
956
2150
  declare class Table extends ParsedElement {
957
2151
  #private;
2152
+ /** Read once at the upgrade. */
958
2153
  static attributes: string[];
959
- /**
960
- * The page size stays live: a rows-per-page control is a normal thing to
961
- * put next to a table, and the size is the one piece of the request an
962
- * author changes after the table is up. The rest of the request is the
963
- * table's own state, moved by the pager, the sorters and the filter form.
964
- */
2154
+ /** These stay live after the upgrade: see `pageSize`, `src` and `method`. */
965
2155
  static observed: string[];
966
2156
  static slots: boolean;
967
2157
  static config: {
@@ -972,26 +2162,224 @@ declare class Table extends ParsedElement {
972
2162
  static templates: {
973
2163
  row: string;
974
2164
  };
975
- /** How many rows a page asks the loader for: the size the next load will carry. */
2165
+ /**
2166
+ * How many rows a page asks the loader for: the size the next load will
2167
+ * carry. Ten before the render.
2168
+ * @returns {number}
2169
+ */
976
2170
  get pageSize(): number;
977
2171
  /**
978
- * Changes the page size and reloads from the first page, the current index
979
- * meaning nothing under a new size. A table that has not loaded yet only
980
- * records it: writing the size is not a request to start loading, which is
981
- * what `autoload` and `reload()` are for.
2172
+ * Changes the page size and reloads from the first page, keeping the sort
2173
+ * and the filters. A table that has not loaded yet only records it:
2174
+ * writing the size is not a request to start loading, which is what
2175
+ * `autoload` and `reload()` are for. A write that does not change the size
2176
+ * does nothing. A write made before the render is replaced by the declared
2177
+ * `page-size` when the table renders.
2178
+ *
2179
+ * The reload is not awaited: a failure shows the error panel and its
2180
+ * rejection is unhandled.
2181
+ * @param {number|null|undefined} value absent or null is the default of
2182
+ * ten, so removing the attribute restores it
2183
+ */
2184
+ set pageSize(value: number | null | undefined);
2185
+ /**
2186
+ * The url the loader asks for rows, reflected as the `src` attribute.
2187
+ * @returns {string|null}
2188
+ */
2189
+ get src(): string | null;
2190
+ /**
2191
+ * Points the table at another url: the loader is built again through its
2192
+ * component's `create`, as at the render, and the table reloads from the
2193
+ * first page, keeping the size, the sort and the filters. A table that has
2194
+ * not loaded yet only rebuilds its loader. A write that does not change the
2195
+ * url does nothing.
982
2196
  *
983
- * Absent or null is the default of ten, so removing the attribute restores
984
- * it rather than asking the loader for NaN rows.
2197
+ * The reload is not awaited: a failure shows the error panel and its
2198
+ * rejection is unhandled.
2199
+ * @param {string|null|undefined} value null or undefined removes the url,
2200
+ * which the default loader answers with an empty in-memory loader
2201
+ */
2202
+ set src(value: string | null | undefined);
2203
+ /**
2204
+ * The http method the default loader sends with, reflected as the `method`
2205
+ * attribute; `GET` when absent.
2206
+ * @returns {string|null}
2207
+ */
2208
+ get method(): string | null;
2209
+ /**
2210
+ * Changes the http method the way `src` changes the url: the loader is
2211
+ * built again and the table reloads from the first page.
2212
+ * @param {string|null|undefined} value
2213
+ */
2214
+ set method(value: string | null | undefined);
2215
+ /**
2216
+ * Writes `src` and `method` together and loads once: the loader is built
2217
+ * again for the pair when either changed, the page going back to the first
2218
+ * one as a single write of either does, and the table then reloads as
2219
+ * `reload()` does, whether or not they changed. Unlike a write of either,
2220
+ * it loads a table that has not loaded yet.
2221
+ * @param {{ src?: string|null, method?: string|null }} attributes a name
2222
+ * left out keeps its value, null removes the attribute
2223
+ * @returns {Promise<void>} as `load`
2224
+ */
2225
+ reconfigure(attributes: {
2226
+ src?: string | null;
2227
+ method?: string | null;
2228
+ }): Promise<void>;
2229
+ /**
2230
+ * @param {{ slots: Record<string, DocumentFragment> }} c
2231
+ * @returns {Promise<void>}
2232
+ * @throws {Error} when the `schema` slot is missing or holds no `<schema>`
985
2233
  */
986
- set pageSize(value: number);
987
2234
  render({ slots }: {
988
- slots: any;
2235
+ slots: Record<string, DocumentFragment>;
989
2236
  }): Promise<void>;
2237
+ /**
2238
+ * Loads the last request that loaded, or, before any has, the first page
2239
+ * with the declared size and sort and the values of the filters slot. A
2240
+ * size written through `pageSize` since then replaces the page with the
2241
+ * first one at that size.
2242
+ * @returns {Promise<void>} as `load`
2243
+ */
990
2244
  reload(): Promise<void>;
991
- load(pageRequest: any, sortRequest: any, filterRequest: any): Promise<void>;
992
- /** Hands the loader to the callback, for runtime reconfigurations. */
993
- withLoader(fn: any): Promise<any>;
994
- resetWithFilter(filterRequest: any): Promise<void>;
2245
+ /**
2246
+ * Asks the loader for a page and shows it.
2247
+ *
2248
+ * While the load runs the table carries `aria-busy="true"`, and the rows
2249
+ * already shown stay; the spinner panel shows only when there are none.
2250
+ * When the loader answers, the rows and the pager are replaced, the empty
2251
+ * panel shows for an answer with no rows (the `empty` slot rendered with
2252
+ * `schema`, `pageRequest`, `filterRequest` and `pageResponse` in scope),
2253
+ * the request becomes the one
2254
+ * `reload` repeats, `aria-busy` is removed and `load:success` is
2255
+ * dispatched (bubbling, not cancelable) with `detail` set to
2256
+ * `{ pageRequest, sortRequest, filterRequest, response }`. A page beyond
2257
+ * the last one the answer reports is not shown: the last page is loaded
2258
+ * instead, and the returned promise settles with that load.
2259
+ *
2260
+ * When the loader rejects, the rows are removed, the error panel shows the
2261
+ * `reason` of each of the error's `problems`, one per line, or the error as text,
2262
+ * `aria-busy` is removed, `load:failure` is dispatched (bubbling, not
2263
+ * cancelable) with `detail` set to
2264
+ * `{ pageRequest, sortRequest, filterRequest, exception }`, and the
2265
+ * returned promise rejects with the same error.
2266
+ *
2267
+ * A load started after this one supersedes it: when this one's answer or
2268
+ * failure arrives it changes nothing, dispatches nothing and resolves.
2269
+ * @param {TablePageRequest} pageRequest
2270
+ * @param {TableSortRequest|null} sortRequest
2271
+ * @param {Record<string, any>} filterRequest
2272
+ * @returns {Promise<void>}
2273
+ * @throws what the loader rejects with, unless a newer load superseded this one
2274
+ */
2275
+ load(pageRequest: TablePageRequest, sortRequest: TableSortRequest | null, filterRequest: Record<string, any>): Promise<void>;
2276
+ /**
2277
+ * Hands the loader to the callback, for runtime reconfigurations, such as
2278
+ * `update(rows)` on the in-memory loader. The loader exists from the
2279
+ * render on. Nothing is reloaded.
2280
+ * @template T
2281
+ * @param {(loader: any) => T | Promise<T>} fn
2282
+ * @returns {Promise<T>} what `fn` returns or resolves to
2283
+ */
2284
+ withLoader<T>(fn: (loader: any) => T | Promise<T>): Promise<T>;
2285
+ /**
2286
+ * Loads the first page with the given filters, keeping the size and the sort.
2287
+ * @param {Record<string, any>} filterRequest
2288
+ * @returns {Promise<void>} as `load`
2289
+ */
2290
+ resetWithFilter(filterRequest: Record<string, any>): Promise<void>;
2291
+ }
2292
+ /**
2293
+ * An invoker button paired with the `ul[popover][role=menu]` that follows it:
2294
+ * the chrome behind every filter's operator, sensitivity and boolean value.
2295
+ *
2296
+ * It fills the menu from a vocabulary, wires it the first time more than one
2297
+ * choice survives the whitelist, pins the button to a static glyph when a
2298
+ * single one does, takes the shared menu keyboard protocol, and keeps the
2299
+ * button's value, glyph and aria-label in step. A pick that changes the value
2300
+ * calls back; the host decides what that means.
2301
+ *
2302
+ * The button's `value` attribute is the store, as it is for a native control:
2303
+ * the menu protocol finds the current item by it, and nothing mirrors it.
2304
+ */
2305
+ declare class ChoiceButton {
2306
+ #private;
2307
+ /**
2308
+ * The declared choices narrowed to a vocabulary, in the declared order.
2309
+ * When none of them is in the vocabulary, or none is declared, the answer
2310
+ * is a copy of the whole vocabulary.
2311
+ * @param {string[] | null | undefined} declared
2312
+ * @param {string[]} vocabulary
2313
+ * @returns {string[]}
2314
+ */
2315
+ static narrow(declared: string[] | null | undefined, vocabulary: string[]): string[];
2316
+ /**
2317
+ * Takes the button's next sibling as its menu and listens for picks on it.
2318
+ * The menu is neither filled nor wired until `allowed` is first written.
2319
+ *
2320
+ * A click on an item, while `interactive()` answers true, sets the value,
2321
+ * hides the menu and, when the value changed, calls `onPick`.
2322
+ * @param {HTMLElement} button the invoker, whose next sibling is its menu
2323
+ * @param {{vocabulary: string[], glyphs?: Record<string,string>, labelFor?: (v: string) => string,
2324
+ * display?: ((v: string) => string)|null, interactive?: () => boolean, onPick?: (v: string) => void}} conf
2325
+ * `vocabulary` lists every choice; `glyphs` maps a choice to its compact
2326
+ * glyph; `labelFor` answers the localized word, used as the button's
2327
+ * `aria-label` and beside the glyph in the menu (the choice itself by
2328
+ * default); `display` answers the button's text (by default the glyph,
2329
+ * or the choice where it has none); `interactive` is asked before a
2330
+ * pick is taken; `onPick` receives a pick that changed the value
2331
+ */
2332
+ constructor(button: HTMLElement, { vocabulary, glyphs, labelFor, display, interactive, onPick }: {
2333
+ vocabulary: string[];
2334
+ glyphs?: Record<string, string>;
2335
+ labelFor?: (v: string) => string;
2336
+ display?: ((v: string) => string) | null;
2337
+ interactive?: () => boolean;
2338
+ onPick?: (v: string) => void;
2339
+ });
2340
+ /**
2341
+ * The choices the menu offers. Writing it narrows the declared set with
2342
+ * `narrow`, refills the menu, wires the popover and the menu keyboard the
2343
+ * first time more than one choice is allowed, and updates the button's
2344
+ * disabled state and popup attributes. When one choice is left the button
2345
+ * is pinned and the value is set to it.
2346
+ * @type {string[]}
2347
+ */
2348
+ get allowed(): string[];
2349
+ set allowed(declared: string[]);
2350
+ /**
2351
+ * The choice when the menu allows it, otherwise the first allowed one.
2352
+ * @param {string|null} choice
2353
+ * @returns {string}
2354
+ */
2355
+ preferring(choice: string | null): string;
2356
+ /**
2357
+ * Whether fewer than two choices are allowed. A pinned button is disabled,
2358
+ * opens no menu and shows the single choice as its value.
2359
+ * @returns {boolean}
2360
+ */
2361
+ get pinned(): boolean;
2362
+ /**
2363
+ * The host's fixed claim: hides the button with the `hidden` attribute
2364
+ * while `value` keeps answering.
2365
+ * @param {boolean} fixed
2366
+ */
2367
+ set fixed(fixed: boolean);
2368
+ /**
2369
+ * The current choice, stored as the button's `value` attribute, where the
2370
+ * menu keyboard also finds it. Writing it sets the button's text from
2371
+ * `display`, its `aria-label` from `labelFor`, and `aria-checked` on the
2372
+ * menu item carrying it. It does not call `onPick`.
2373
+ * @type {string | null}
2374
+ */
2375
+ get value(): string | null;
2376
+ set value(choice: string | null);
2377
+ /**
2378
+ * The host's disabled claim. The button is disabled while either the claim
2379
+ * or the pin holds, so lifting one does not enable it while the other holds.
2380
+ * @param {boolean} claimed
2381
+ */
2382
+ set claimed(claimed: boolean);
995
2383
  }
996
2384
  export type FilterCriterion = {
997
2385
  label: string | null;
@@ -1002,55 +2390,134 @@ export type FilterCriterion = {
1002
2390
  * The shared shape of every operator-and-operands filter: an operator menu, one
1003
2391
  * or two operands of the type the subclass declares, and a tuple that mirrors
1004
2392
  * the data-jpa compare annotations.
2393
+ *
2394
+ * The value is `[operator, operand]`, or `[operator, from, to]` for BETWEEN,
2395
+ * the only operator that shows the second operand. The `operators` attribute
2396
+ * whitelists the menu; the `operator` attribute fixes the operator for the
2397
+ * element's life instead, hiding its button, and every tuple then carries it.
2398
+ * Declaring both warns and the singular wins; a fixed name outside the
2399
+ * vocabulary warns and fixes the default operator.
2400
+ *
2401
+ * Both operands mirror the disabled and readonly claims, and readonly also
2402
+ * refuses the clicks that would open the operator menu. The change event fires
2403
+ * when either operand changes or a different operator is picked.
1005
2404
  */
1006
2405
  declare class CompareFilter extends Input {
1007
2406
  #private;
1008
2407
  static observed: string[];
1009
2408
  static attributes: string[];
1010
- static OPERATORS: string[];
1011
- static DEFAULT_OPERATOR: string;
1012
2409
  static template: string;
1013
2410
  _operator: any;
1014
2411
  _container: any;
1015
2412
  _value1: any;
1016
2413
  _value2: any;
1017
2414
  _build(conf: any): {
1018
- fragment: any;
2415
+ fragment: DocumentFragment;
1019
2416
  control: any;
1020
- error: any;
1021
- label: any;
2417
+ error: Element | null;
2418
+ label: HTMLLabelElement | null;
1022
2419
  freeze: any;
1023
2420
  also: any[];
1024
2421
  };
1025
2422
  _showDefaultOperator(): void;
2423
+ /**
2424
+ * Restores the declared `value` tuple, operator included, as a field reset
2425
+ * does. Without a `value` attribute it empties the operands and also shows
2426
+ * the default operator again, or the first whitelisted one where the
2427
+ * default is not allowed.
2428
+ */
1026
2429
  formResetCallback(): void;
2430
+ /**
2431
+ * The native input type both operands render with.
2432
+ * @returns {string}
2433
+ */
1027
2434
  _type(): string;
1028
- _serialize(v: any): any;
1029
- _deserialize(v: any): any;
2435
+ /**
2436
+ * Converts an operand from its input's value to the form the tuple carries.
2437
+ * @param {string} v
2438
+ * @returns {string|null}
2439
+ */
2440
+ _serialize(v: string): string | null;
2441
+ /**
2442
+ * Converts an operand from the tuple to the value its input shows.
2443
+ * @param {string} v
2444
+ * @returns {string}
2445
+ */
2446
+ _deserialize(v: string): string;
2447
+ /**
2448
+ * The operator shown on render and after a valueless reset, when the whitelist allows it.
2449
+ * @returns {string}
2450
+ */
1030
2451
  _defaultOperator(): string;
2452
+ /**
2453
+ * Every operator the filter knows, which `operators` narrows.
2454
+ * @returns {string[]}
2455
+ */
1031
2456
  _vocabulary(): string[];
1032
2457
  _declaredOperators: any;
1033
- get operators(): any;
1034
- set operators(declared: any);
2458
+ /**
2459
+ * The operators the menu offers, narrowed to the vocabulary: an empty list,
2460
+ * or one where no name is known, offers all of it. A single operator pins
2461
+ * the button, and an assigned tuple then carries that operator whatever it
2462
+ * names. An assignment made before the upgrade is held narrowed and
2463
+ * answered back until the render, when the `operators` attribute is applied
2464
+ * over it, an absent attribute meaning the whole vocabulary. Assignments are
2465
+ * ignored while the `operator` attribute fixes the operator.
2466
+ * @returns {string[]|undefined} undefined before the upgrade when nothing was assigned
2467
+ */
2468
+ get operators(): string[] | undefined;
2469
+ /** @param {string[]|null} declared */
2470
+ set operators(declared: string[] | null);
2471
+ /**
2472
+ * The filter's tuple, `[operator, operand]` or `[operator, from, to]` for
2473
+ * BETWEEN, each operand converted by `_serialize` (an ISO instant for the
2474
+ * instant filter). Null while any operand the operator uses is empty.
2475
+ * @returns {any[]|null}
2476
+ */
1035
2477
  get value(): any[] | null;
1036
- set value(v: any[] | null);
2478
+ /**
2479
+ * Shows a tuple: the operator it names, unless a pinned or fixed operator
2480
+ * replaces it, and its operands, an operand missing from a shorter tuple
2481
+ * leaving its input empty. Null or undefined empties both operands and
2482
+ * keeps the operator.
2483
+ * @param {any[]|null|undefined} v
2484
+ */
2485
+ set value(v: any[] | null | undefined);
1037
2486
  _tuple(): any[] | null;
1038
2487
  _applyTuple(v: any): void;
1039
2488
  _showOperator(operator: any): void;
1040
- /** only a BETWEEN carries a second operand */
2489
+ /** Shows the second operand only while the operator is BETWEEN. */
1041
2490
  _syncBetween(): void;
1042
2491
  /**
1043
- * The operands as shown rather than as serialized: a date filter describes
1044
- * what is in its control, not the iso string it sends.
2492
+ * The operator and the operands the operator uses, as their inputs show
2493
+ * them rather than as serialized: a date filter describes what is in its
2494
+ * control, not the ISO string it sends. Null while every one of those
2495
+ * operands is empty, so a BETWEEN with one bound answers that bound while
2496
+ * its value is still null.
1045
2497
  * @returns {FilterCriterion|null}
1046
2498
  */
1047
2499
  get criterion(): FilterCriterion | null;
2500
+ /**
2501
+ * The field's own disabled claim. Setting it disables both operands and
2502
+ * every menu button the filter composes; a button pinned to one choice
2503
+ * stays disabled when the claim is lifted.
2504
+ * @returns {boolean}
2505
+ */
1048
2506
  get disabled(): boolean;
2507
+ /** @param {boolean} d */
1049
2508
  set disabled(d: boolean);
1050
- /** every menu button the filter composes, so one claim reaches them all */
1051
- _choices(): any[];
2509
+ /**
2510
+ * The menu buttons the filter composes, which the disabled claim reaches. A
2511
+ * subclass adding a menu appends its own to the base's.
2512
+ * @returns {ChoiceButton[]}
2513
+ */
2514
+ _choices(): ChoiceButton[];
1052
2515
  }
1053
- /** The compare filter over ISO instants, defaulting to LTE. */
2516
+ /**
2517
+ * The compare filter over ISO instants, defaulting to LTE. The operands are
2518
+ * shown as local date and time in the page's timezone and carried in the tuple
2519
+ * as UTC ISO instants.
2520
+ */
1054
2521
  declare class InstantFilter extends CompareFilter {
1055
2522
  _defaultOperator(): string;
1056
2523
  _type(): string;
@@ -1065,9 +2532,18 @@ declare class LocalDateFilter extends CompareFilter {
1065
2532
  declare class NumberFilter extends CompareFilter {
1066
2533
  _type(): string;
1067
2534
  }
1068
- /** The compare filter over text, carrying a case sensitivity beside the operator. */
2535
+ /**
2536
+ * The compare filter over text, defaulting to CONTAINS and adding CONTAINS,
2537
+ * STARTS_WITH and ENDS_WITH to the compare operators.
2538
+ *
2539
+ * Its tuple carries a case sensitivity after the operator, `[operator,
2540
+ * sensitivity, operand]` or `[operator, sensitivity, from, to]`, the
2541
+ * sensitivity being IGNORE_CASE (the default) or CASE_SENSITIVE and switched
2542
+ * through a second menu. The `sensitivities` attribute whitelists that menu and
2543
+ * `sensitivity` fixes one mode, with the same rules and warnings as
2544
+ * `operators` and `operator`.
2545
+ */
1069
2546
  declare class TextFilter extends CompareFilter {
1070
- #private;
1071
2547
  static observed: string[];
1072
2548
  static attributes: string[];
1073
2549
  static template: string;
@@ -1075,27 +2551,71 @@ declare class TextFilter extends CompareFilter {
1075
2551
  _vocabulary(): string[];
1076
2552
  _sensitivityButton: any;
1077
2553
  _build(conf: any): {
1078
- fragment: any;
2554
+ fragment: DocumentFragment;
1079
2555
  control: any;
1080
- error: any;
1081
- label: any;
2556
+ error: Element | null;
2557
+ label: HTMLLabelElement | null;
1082
2558
  freeze: any;
1083
2559
  also: any[];
1084
2560
  };
1085
2561
  _choices(): any[];
1086
2562
  get _sensitivity(): any;
1087
2563
  _declaredSensitivities: any;
1088
- get sensitivities(): any;
1089
- set sensitivities(declared: any);
2564
+ /**
2565
+ * The sensitivities the menu offers, narrowed to IGNORE_CASE and
2566
+ * CASE_SENSITIVE: an empty list, or one where no name is known, offers both.
2567
+ * When the mode shown leaves the list, the first listed one is shown. Held
2568
+ * before the upgrade and ignored under a fixed `sensitivity`, as `operators` is.
2569
+ * @returns {string[]|undefined} undefined before the upgrade when nothing was assigned
2570
+ */
2571
+ get sensitivities(): string[] | undefined;
2572
+ /** @param {string[]|null} declared */
2573
+ set sensitivities(declared: string[] | null);
2574
+ /**
2575
+ * The tuple with the sensitivity after the operator, `[operator,
2576
+ * sensitivity, ...operands]`. Null while any operand the operator uses is empty.
2577
+ * @returns {any[]|null}
2578
+ */
1090
2579
  get value(): any[] | null;
1091
- set value(v: any[] | null);
2580
+ /**
2581
+ * Shows a tuple as the compare filter does, reading the sensitivity at
2582
+ * index 1. A sensitivity the whitelist does not allow leaves the current
2583
+ * one in place. Null or undefined empties the operands and keeps the
2584
+ * operator and the sensitivity.
2585
+ * @param {any[]|null|undefined} v
2586
+ */
2587
+ set value(v: any[] | null | undefined);
2588
+ /**
2589
+ * Restores the declared `value` tuple, sensitivity included. Without a
2590
+ * `value` attribute it also shows the default operator and IGNORE_CASE
2591
+ * again, or the first whitelisted mode where IGNORE_CASE is not allowed.
2592
+ */
1092
2593
  formResetCallback(): void;
1093
2594
  }
1094
- /** The boolean filter: an EQ or NEQ operator and an any/yes/no menu. */
2595
+ /**
2596
+ * The boolean filter: an EQ or NEQ operator menu and a value menu of any, yes
2597
+ * and no, the value button showing the localized word.
2598
+ *
2599
+ * The value is `[operator, 'true']` or `[operator, 'false']`, null while the
2600
+ * value menu is on any. The `operators` attribute whitelists the operator menu
2601
+ * as on the compare filters. The disabled claim reaches the value button and
2602
+ * both menus, and readonly refuses the clicks that would open them. The
2603
+ * required and readonly claims reflect on the host only: no `aria-required` or
2604
+ * `aria-readonly` is written, since the value button's role accepts neither.
2605
+ */
1095
2606
  declare class BooleanFilter extends Field {
1096
2607
  static observed: string[];
1097
2608
  static slots: boolean;
2609
+ /**
2610
+ * The operators the filter knows, which `operators` narrows.
2611
+ * @type {string[]}
2612
+ */
1098
2613
  static OPERATORS: string[];
2614
+ /**
2615
+ * The operator shown on render and after a valueless reset, or the first
2616
+ * whitelisted one where the whitelist excludes it.
2617
+ * @type {string}
2618
+ */
1099
2619
  static DEFAULT_OPERATOR: string;
1100
2620
  static template: string;
1101
2621
  _operator: any;
@@ -1104,64 +2624,128 @@ declare class BooleanFilter extends Field {
1104
2624
  _build({ slots }: {
1105
2625
  slots: any;
1106
2626
  }): {
1107
- fragment: any;
1108
- control: any;
1109
- error: any;
1110
- label: any;
2627
+ fragment: DocumentFragment;
2628
+ control: HTMLElement;
2629
+ error: Element | null;
2630
+ label: HTMLLabelElement | null;
1111
2631
  announces: null;
1112
2632
  freeze: any;
1113
2633
  };
1114
2634
  _declaredOperators: any;
1115
- get operators(): any;
1116
- set operators(declared: any);
2635
+ /**
2636
+ * The operators the menu offers, narrowed to EQ and NEQ: an empty list, or
2637
+ * one where no name is known, offers both, and a single one pins the
2638
+ * button. Held before the upgrade and replaced by the `operators` attribute
2639
+ * at the render, as on the compare filters.
2640
+ * @returns {string[]|undefined} undefined before the upgrade when nothing was assigned
2641
+ */
2642
+ get operators(): string[] | undefined;
2643
+ /** @param {string[]|null} declared */
2644
+ set operators(declared: string[] | null);
2645
+ /** @returns {string[]} */
1117
2646
  _vocabulary(): string[];
1118
- get value(): any[] | null;
1119
- set value(v: any[] | null);
2647
+ /**
2648
+ * `[operator, token]`, the token being 'true' or 'false'; null while the value menu is on any.
2649
+ * @returns {string[]|null}
2650
+ */
2651
+ get value(): string[] | null;
2652
+ /**
2653
+ * Shows a tuple: its operator, unless a pinned operator replaces it, and its
2654
+ * token, a missing token meaning any. Null or undefined sets the value menu
2655
+ * back to any and keeps the operator.
2656
+ * @param {any[]|null|undefined} v
2657
+ */
2658
+ set value(v: any[] | null | undefined);
2659
+ /**
2660
+ * Restores the declared `value` tuple. Without a `value` attribute it sets
2661
+ * the value menu back to any and also shows the default operator again.
2662
+ */
1120
2663
  formResetCallback(): void;
1121
- /** @returns {FilterCriterion|null} */
2664
+ /**
2665
+ * The operator and the localized word the value menu shows, not the token.
2666
+ * Null while the value menu is on any.
2667
+ * @returns {FilterCriterion|null}
2668
+ */
1122
2669
  get criterion(): FilterCriterion | null;
2670
+ /**
2671
+ * The field's own disabled claim. Setting it disables the value button and
2672
+ * both menu buttons; an operator button pinned to one choice stays disabled
2673
+ * when the claim is lifted.
2674
+ * @returns {boolean}
2675
+ */
1123
2676
  get disabled(): boolean;
2677
+ /** @param {boolean} d */
1124
2678
  set disabled(d: boolean);
1125
2679
  }
1126
2680
  /**
1127
2681
  * Set membership over a select's vocabulary, answering for both of data-jpa's
1128
2682
  * set annotations: InEnum and InList take the same bare array of values and no
1129
- * operator, differing only in what the server converts them to.
2683
+ * operator, differing only in what the server converts them to. A declared
2684
+ * `operator` attribute is sent in front of the keys instead, for a server field
2685
+ * that reads the set through a compare.
1130
2686
  */
1131
2687
  declare class InFilter extends Select {
1132
2688
  #private;
1133
2689
  static attributes: string[];
1134
- get value(): any;
2690
+ /**
2691
+ * The chosen keys as an array, a single choice included, led by the
2692
+ * declared `operator` when there is one. Null while nothing is chosen, so
2693
+ * that a table drops the filter instead of sending an empty set that
2694
+ * matches nothing.
2695
+ * @returns {any[]|null}
2696
+ */
2697
+ get value(): any[] | null;
2698
+ /**
2699
+ * Selects the keys of an array or a single bare key; a leading element equal
2700
+ * to the declared `operator` is dropped first, so the filter takes back what
2701
+ * it answered. A single select takes the first key. Null or undefined clears
2702
+ * the selection.
2703
+ * @param {any} v
2704
+ */
1135
2705
  set value(v: any);
1136
- /** @returns {FilterCriterion|null} */
2706
+ /**
2707
+ * The declared operator, or null, and the labels of the chosen entries. Null
2708
+ * while nothing is chosen.
2709
+ * @returns {FilterCriterion|null}
2710
+ */
1137
2711
  get criterion(): FilterCriterion | null;
1138
2712
  }
1139
2713
  /**
1140
- * An info icon button toggling a popover with a short explanation.
2714
+ * An icon button that toggles a native popover, a `ful-note`, holding the
2715
+ * default slot as a short explanation. The popover gives light dismiss and
2716
+ * Escape, and the button carries an `aria-expanded` kept in step with it and
2717
+ * a localized `aria-label`.
2718
+ *
2719
+ * The marker is the `ful-icon` that `Tooltip.config.icon` names for the whole
2720
+ * page, or the one the `icon` attribute names for a single tooltip. A name the
2721
+ * library does not paint is declared by the page as
2722
+ * `ful-icon[name='...'] { mask-image: ... }`.
1141
2723
  *
1142
- * The marker is the page's `config.icon`, and the `icon` attribute names a
1143
- * `ful-icon` for the tooltip that means something other than plain information:
1144
- * a caveat, a warning, a setting. A name the library does not paint is the
1145
- * page's own, declared as `ful-icon[name='...'] { mask-image: ... }`.
2724
+ * `placement` picks the side of the marker the note opens on: `top` (the
2725
+ * default), `bottom`, `left` or `right`. The note is placed in script on every
2726
+ * platform, centred on the marker, clamped into the viewport and following the
2727
+ * marker on scroll and resize, with a callout on the edge facing the marker.
1146
2728
  *
1147
- * `describes` is for the tooltip standing in a field: the note becomes part of
1148
- * the accessible description of that field's control, so it is announced on
1149
- * reaching the field rather than only on opening the marker, and the marker
1150
- * leaves the tab order, so a form of hinted fields costs no extra keystrokes to
1151
- * walk. The marker stays clickable, and stays a tab stop wherever the note was
1152
- * not taken, a tooltip claiming `describes` outside a field among them: the
1153
- * stop only goes where something else delivers the content.
2729
+ * `describes` offers the note to the nearest ancestor that takes a description
2730
+ * (see `describable`), a field's control in practice: when it is taken, the
2731
+ * note becomes part of that control's accessible description and the marker
2732
+ * leaves the tab order, staying clickable. When nothing takes it, the marker
2733
+ * keeps its tab stop and a warning is logged.
1154
2734
  */
1155
2735
  declare class Tooltip extends ParsedElement {
1156
2736
  #private;
1157
2737
  static slots: boolean;
1158
2738
  static attributes: string[];
2739
+ /** The page-wide defaults: `icon` is the `ful-icon` name of the marker. */
1159
2740
  static config: {
1160
2741
  icon: string;
1161
2742
  };
1162
2743
  static template: string;
2744
+ /**
2745
+ * @param {{ slots: Record<string, DocumentFragment> }} c
2746
+ */
1163
2747
  render({ slots }: {
1164
- slots: any;
2748
+ slots: Record<string, DocumentFragment>;
1165
2749
  }): void;
1166
2750
  }
1167
2751
  export type DialogOutcome = {
@@ -1170,87 +2754,162 @@ export type DialogOutcome = {
1170
2754
  response: any;
1171
2755
  };
1172
2756
  /**
1173
- * A modal dialog on the native platform, open()/ask() resolving with the
1174
- * closer's data-result.
2757
+ * How a dialog ended. `dismissed` is true for Escape, the close button, a
2758
+ * backdrop click, a `data-result=""` button, `close()` without a result and a
2759
+ * removal from the document; `result` is the `data-result` of the button, or
2760
+ * the argument of `close(result)`, that answered; `response` is what the form
2761
+ * answered with under `close-on-submit`. Whichever of `result` and `response`
2762
+ * did not answer is null, as both are on a dismissal.
2763
+ * @typedef {{ dismissed: boolean, result: string|null, response: any }} DialogOutcome
2764
+ */
2765
+ /**
2766
+ * A modal dialog on the native `<dialog>`, rendered with a header, a body and
2767
+ * a footer. `open()` and `ask()` show it and resolve with a `DialogOutcome`.
1175
2768
  *
1176
- * The header carries a close button, as the drawer's does: Escape dismisses a
1177
- * modal on its own, but nothing says so, and a dialog whose only exit is a key
1178
- * you have to know about leaves a pointer with nowhere to go. It answers the way
1179
- * Escape does, with null.
2769
+ * The `header` attribute is the title, rendered as an `h2` that names the
2770
+ * dialog through `aria-labelledby`; without it the dialog is unnamed. The
2771
+ * `header` slot is rendered in the header before the title, the default slot
2772
+ * is the body, and the `buttons` slot is rendered in the footer. Without a
2773
+ * `buttons` slot, and without `close-on-submit`, the footer holds a localized
2774
+ * acknowledge button answering `acknowledged`.
1180
2775
  *
1181
- * `requires-answer` is for the dialog that must be answered: the close button is not
1182
- * rendered and Escape is refused, so the only way out is a button that carries a
1183
- * result. It has to be both, a close button withheld while Escape still worked
1184
- * being decoration rather than a rule.
2776
+ * Any button inside the dialog carrying `data-result` closes it with that
2777
+ * result, an empty one being a dismissal. The header carries a close button,
2778
+ * and a press and release both on the backdrop close the dialog, each as a
2779
+ * dismissal, as Escape does.
1185
2780
  *
1186
- * The chrome is reachable by class as well as by tag, so a plain `<dialog
1187
- * class="ful-dialog">` written by a page gets the same look whatever its
1188
- * structure: the tag form matches a direct child, and `ful-dialog-header`,
1189
- * `ful-dialog-body` and `ful-dialog-footer` match at any depth, which is what a
1190
- * dialog whose content is wrapped in a form needs.
1191
- */
1192
- /**
1193
- * How a dialog ended: `dismissed` tells a cancel from an answer, `result` carries
1194
- * the `data-result` of the button that closed it and `response` what a submit
1195
- * answered with, the one that did not happen being null.
1196
- * @typedef {{ dismissed: boolean, result: string|null, response: any }} DialogOutcome
2781
+ * Before any of those three gestures closes it, the element dispatches a
2782
+ * cancelable, non-bubbling `dialog:dismiss` with `detail: { reason }`, the
2783
+ * reason being `button`, `backdrop` or `escape`; a listener calling
2784
+ * `preventDefault()` keeps it open, to guard unsaved edits. A `data-result`
2785
+ * button, `close-on-submit` and `close()` answer rather than dismiss, and
2786
+ * dispatch none.
2787
+ *
2788
+ * The platform lets a page refuse Escape once per user interaction: a second
2789
+ * Escape with no click or keystroke in between closes the dialog whatever the
2790
+ * page does, `requires-answer` included. That close is still announced by a
2791
+ * `dialog:dismiss`, which is then not cancelable.
2792
+ *
2793
+ * `requires-answer` withholds the close button and refuses Escape and the
2794
+ * backdrop, so the dialog is closed only by a button carrying `data-result`
2795
+ * or from code through `close()`.
2796
+ *
2797
+ * `close-on-submit` closes the dialog when a `ful-form` that is a direct child
2798
+ * of the body dispatches `submit:success`, answering with its
2799
+ * `detail.response`. A form deeper in the body, such as the filter form of a
2800
+ * `ful-table`, does not close it. The listener sits on the body, so a form
2801
+ * delivered by `update()` is covered as well.
2802
+ *
2803
+ * Every close dispatches a non-bubbling `close` event on the element carrying
2804
+ * the `DialogOutcome` as `detail`, before the pending `ask()` calls resolve.
2805
+ * An element anywhere in the page carrying `dialog-target` set to the
2806
+ * element's id opens it on click.
2807
+ *
2808
+ * The chrome is styled by class as well as by tag, so a plain `<dialog
2809
+ * class="ful-dialog">` written by a page gets the same look: the tag form
2810
+ * matches a direct child, and `ful-dialog-header`, `ful-dialog-body` and
2811
+ * `ful-dialog-footer` match at any depth, as a dialog whose content is wrapped
2812
+ * in a form needs.
1197
2813
  */
1198
2814
  declare class Dialog extends ParsedElement {
1199
2815
  #private;
1200
2816
  static attributes: string[];
1201
2817
  static slots: boolean;
1202
2818
  static template: string;
2819
+ /**
2820
+ * @param {{ slots: Record<string, DocumentFragment> }} c
2821
+ */
1203
2822
  render({ slots }: {
1204
- slots: any;
2823
+ slots: Record<string, DocumentFragment>;
1205
2824
  }): void;
2825
+ /**
2826
+ * Resolves every pending `ask()` and `open()` with the outcome so far,
2827
+ * a dismissal for a dialog still open, so that no caller waits on a dialog
2828
+ * that left the document. The native dialog is not closed and no `close`
2829
+ * event is dispatched.
2830
+ */
1206
2831
  disconnectedCallback(): void;
1207
- open(): Promise<any>;
1208
- ask(): Promise<any>;
1209
2832
  /**
1210
- * Opens the dialog and waits for the callback, as `ful-drawer`'s does: a
1211
- * resolved value paints the body (which is returned), a rejection paints the
1212
- * problems and travels to the caller, and an update superseded by a newer one
1213
- * paints nothing. The title is the `header` attribute, configuration like the
1214
- * rest of the dialog's chrome, so what update() owns is the body alone.
2833
+ * The same as `ask()`.
2834
+ * @returns {Promise<DialogOutcome>}
1215
2835
  */
1216
- update(cb: any): Promise<any>;
2836
+ open(): Promise<DialogOutcome>;
1217
2837
  /**
1218
- * Re-fires section:requested on the body, open or closed: the explicit
1219
- * request for a body that wants refreshing. A failed refresh paints its
1220
- * problems, nothing rejects: update() stays the rejecting call.
2838
+ * Shows the dialog as a modal and resolves with how it ends.
2839
+ *
2840
+ * The call that opens the dialog resets the outcome of the previous
2841
+ * opening, hides the error and loading sections, and dispatches
2842
+ * `section:requested` on the element (bubbling, with
2843
+ * `detail: { section, first, name: null, index: null }`, `section` being
2844
+ * the body and `first` true only on the first answered request), so a
2845
+ * listener can fill the body. A failed request is painted into the body
2846
+ * and does not reject. On a dialog already open nothing is shown or
2847
+ * requested, and the call waits for the same ending as the calls before it.
2848
+ * @returns {Promise<DialogOutcome>} resolving on close, or on removal from
2849
+ * the document with a dismissal
2850
+ */
2851
+ ask(): Promise<DialogOutcome>;
2852
+ /**
2853
+ * Empties and hides the body, shows the loading section, opens the dialog
2854
+ * if it is closed, and fills the body with what the callback resolves to.
2855
+ * A rejection is shown, as the problems' reasons, in the error section,
2856
+ * which has `role="alert"`, and the body is hidden. The title stays the
2857
+ * `header` attribute.
2858
+ *
2859
+ * No `section:requested` is dispatched, and no `ask()` waiter is added:
2860
+ * listen for the `close` event to learn how the dialog ended. When a newer
2861
+ * `update()` starts before the callback settles, this call paints nothing:
2862
+ * it resolves with the body regardless, and still rejects with the
2863
+ * callback's error.
2864
+ * @param {() => Node|string|Promise<Node|string>} cb producing the body content
2865
+ * @returns {Promise<Element>} the body section, in the document
2866
+ * @throws {any} what the callback threw or rejected with
2867
+ */
2868
+ update(cb: () => Node | string | Promise<Node | string>): Promise<Element>;
2869
+ /**
2870
+ * Dispatches `section:requested` for the body again, as `ask()` does,
2871
+ * whether the dialog is open or closed. A failed request is painted into
2872
+ * the body.
2873
+ * @returns {Promise<any[]|undefined>} the listeners' answers, undefined when
2874
+ * nobody answered or the request failed; it never rejects
1221
2875
  */
1222
2876
  refresh(): Promise<any[] | undefined>;
1223
- close(result: any): void;
1224
2877
  /**
1225
- * Asks a question in a dialog of the page's own, resolving with its outcome
1226
- * and removing the element afterwards: the element stays the author's when
1227
- * the question lives in the page, while a question asked from a button had
1228
- * to build, render, show, answer and clean up by hand.
2878
+ * Closes an open dialog, doing nothing on a closed one. A non-empty result
2879
+ * answers as the button carrying that `data-result` would; no result, or
2880
+ * an empty one, is a dismissal.
2881
+ * @param {string} [result]
2882
+ */
2883
+ close(result?: string): void;
2884
+ /**
2885
+ * Appends a new `ful-dialog` to `document.body`, asks it, and removes it
2886
+ * once it is answered or its render fails.
1229
2887
  *
1230
- * The body is text or a node: a string is never read as markup, an
1231
- * `innerHTML` sink being a thing the library does not carry. Each button is
1232
- * a `[result, label, className?]` tuple rendered as its answer, or a node
1233
- * the caller has built itself, which is how a button carries anything a
1234
- * tuple cannot say and how a footer gets a spacer.
1235
- * @param {string} header
2888
+ * A string body is set as text and never parsed as markup; a node is
2889
+ * appended as it is. Each button is either a `[result, label, className?]`
2890
+ * tuple, rendered as a `button` carrying `data-result`, or a node the
2891
+ * caller built, placed in the footer in the order given. With no buttons
2892
+ * the dialog shows its localized acknowledge button.
2893
+ * @param {string} header the title
1236
2894
  * @param {string|Node} body
1237
2895
  * @param {([string, string, string?]|Node)[]} [buttons]
1238
- * @param {{ className?: string }} [options] `className` dresses the dialog,
1239
- * the way a declared `ful-dialog` is given one to size it
1240
- * @returns {Promise<DialogOutcome>}
2896
+ * @param {{ className?: string }} [options] `className` is set as the class
2897
+ * of the `ful-dialog` element
2898
+ * @returns {Promise<DialogOutcome>} rejecting with what the render threw
1241
2899
  */
1242
2900
  static ask(header: string, body: string | Node, buttons?: ([string, string, string?] | Node)[], { className }?: {
1243
2901
  className?: string;
1244
2902
  }): Promise<DialogOutcome>;
1245
2903
  /**
1246
- * Asks a yes/no question, resolving with the confirm button's answer:
1247
- * the cancel button, the close button and Escape all answer false, a
1248
- * dismissal being the negative.
1249
- * @param {string} header
1250
- * @param {string|Node} body
1251
- * @param {{ confirm?: string, cancel?: string }} [labels] overriding the localized defaults
1252
- * @param {{ className?: string }} [options] as `ask` takes them
1253
- * @returns {Promise<boolean>}
2904
+ * Asks a yes/no question through `Dialog.ask`, with a cancel button and a
2905
+ * confirm button carrying the `ful-button` class.
2906
+ * @param {string} header the title
2907
+ * @param {string|Node} body as `Dialog.ask` takes it
2908
+ * @param {{ confirm?: string, cancel?: string }} [labels] the button labels,
2909
+ * `dialog.confirm` and `dialog.cancel` localized by default
2910
+ * @param {{ className?: string }} [options] as `Dialog.ask` takes them
2911
+ * @returns {Promise<boolean>} true only for the confirm button; the cancel
2912
+ * button, the close button and Escape answer false
1254
2913
  */
1255
2914
  static confirm(header: string, body: string | Node, labels?: {
1256
2915
  confirm?: string;
@@ -1260,187 +2919,451 @@ declare class Dialog extends ParsedElement {
1260
2919
  }): Promise<boolean>;
1261
2920
  }
1262
2921
  /**
1263
- * A side panel drawer on the native dialog platform, update() owning its
1264
- * open-deliver cycle.
2922
+ * A side panel on the native `<dialog>`, shown as a modal: a header holding
2923
+ * the title and a localized close button, then loading, error and content
2924
+ * sections, the default slot being the content.
2925
+ *
2926
+ * The `header` attribute is the initial title, rendered as an `h2` that names
2927
+ * the panel through `aria-labelledby`. The `header` slot is rendered in the
2928
+ * header before the title and outside it, so setting the title as text does
2929
+ * not remove it. `placement` is copied onto the native dialog: `end` (the
2930
+ * default, when absent) or `start`, following the writing direction.
1265
2931
  *
1266
- * The `header` slot is content beside the title, before it: an icon, a badge, a
1267
- * status. It sits outside the heading rather than in it because `update()` sets
1268
- * the title through `textContent`, which would take anything nested there with
1269
- * it.
2932
+ * The close button, Escape, and a press and release both on the backdrop all
2933
+ * go through `close()`, once a cancelable, non-bubbling `drawer:dismiss` with
2934
+ * `detail: { reason }` (`button`, `backdrop` or `escape`) has been dispatched
2935
+ * on the element and no listener prevented it: `preventDefault()` keeps the
2936
+ * drawer open, to guard unsaved edits. `close()` and `close-on-submit`
2937
+ * dispatch none. The platform lets a page refuse Escape once per user
2938
+ * interaction: a second Escape with no click or keystroke in between closes
2939
+ * the drawer at once, without the slide, and its `drawer:dismiss` is then not
2940
+ * cancelable. Every close dispatches a non-bubbling `close` event on
2941
+ * the element with `detail: { dismissed, response }`: `{ dismissed: false,
2942
+ * response }` when a form closed it under `close-on-submit`, and
2943
+ * `{ dismissed: true, response: null }` for any other close.
2944
+ *
2945
+ * `close-on-submit` closes the drawer when a `ful-form` that is a direct child
2946
+ * of the content section dispatches `submit:success`, carrying its
2947
+ * `detail.response`, which may be null. A form deeper in the content, such as
2948
+ * the filter form of a `ful-table`, does not close it. The listener sits on
2949
+ * the content section, so a form delivered by `update()` is covered as well.
2950
+ *
2951
+ * An element anywhere in the page carrying `dialog-target` set to the
2952
+ * element's id calls `open()` on click.
1270
2953
  */
1271
2954
  declare class Drawer extends ParsedElement {
1272
2955
  #private;
1273
2956
  static attributes: string[];
1274
2957
  static slots: boolean;
1275
2958
  static template: string;
2959
+ /**
2960
+ * @param {{ slots: Record<string, DocumentFragment> }} c
2961
+ */
1276
2962
  render({ slots }: {
1277
- slots: any;
2963
+ slots: Record<string, DocumentFragment>;
1278
2964
  }): void;
1279
- get header(): any;
1280
- set header(v: any);
1281
2965
  /**
1282
- * Opens the drawer under the given header and waits for the callback: a
1283
- * resolved value paints the content section (which is returned), a
1284
- * rejection paints the problems and travels to the caller, and an update
1285
- * superseded by a newer one paints nothing.
2966
+ * The title, read and written as the heading's text.
2967
+ * @type {string}
2968
+ */
2969
+ get header(): string;
2970
+ /** @param {string|null|undefined} v written as text, `''` for null or undefined */
2971
+ set header(v: string | null | undefined);
2972
+ /**
2973
+ * Sets the title, empties and hides the content section, shows the loading
2974
+ * section, opens the drawer if it is closed (putting back one that is
2975
+ * sliding out), and fills the content with what the callback resolves to.
2976
+ * A rejection is shown, as the problems' reasons, in the error section,
2977
+ * which has `role="alert"`, and the content is hidden.
2978
+ *
2979
+ * No `section:requested` is dispatched. When a newer `update()` starts
2980
+ * before the callback settles, this call paints nothing: it resolves with
2981
+ * the content section regardless, and still rejects with the callback's
2982
+ * error.
2983
+ * @param {string} header the title, set as text
2984
+ * @param {() => Node|string|Promise<Node|string>} cb producing the content
2985
+ * @returns {Promise<Element>} the content section, in the document
2986
+ * @throws {any} what the callback threw or rejected with
1286
2987
  */
1287
- update(header: any, cb: any): Promise<any>;
2988
+ update(header: string, cb: () => Node | string | Promise<Node | string>): Promise<Element>;
1288
2989
  /**
1289
- * Re-fires section:requested on the content, open or closed: the explicit
1290
- * request for a body that wants refreshing. A failed refresh paints its
1291
- * problems, nothing rejects: update() stays the rejecting call.
2990
+ * Dispatches `section:requested` for the content again, as `open()` does,
2991
+ * whether the drawer is open or closed. A failed request is painted into
2992
+ * the content section.
2993
+ * @returns {Promise<any[]|undefined>} the listeners' answers, undefined when
2994
+ * nobody answered or the request failed; it never rejects
1292
2995
  */
1293
2996
  refresh(): Promise<any[] | undefined>;
2997
+ /**
2998
+ * Shows the drawer as a modal. On a drawer already open nothing happens,
2999
+ * except that one sliding out stops and stays open.
3000
+ *
3001
+ * The call that opens the drawer resets the outcome of the previous
3002
+ * opening, hides the error and loading sections, and dispatches
3003
+ * `section:requested` on the element (bubbling, with
3004
+ * `detail: { section, first, name: null, index: null }`, `section` being
3005
+ * the content section and `first` true only on the first answered request),
3006
+ * so a listener can fill the content. A failed request is painted into the
3007
+ * content section.
3008
+ */
1294
3009
  open(): void;
3010
+ /**
3011
+ * Slides the drawer out and closes it when the animation ends, setting the
3012
+ * `closing` attribute on the native dialog meanwhile; the `close` event is
3013
+ * dispatched then, after this call has returned. Where the user prefers
3014
+ * reduced motion, or no animation runs, it closes at once. Does nothing on
3015
+ * a drawer that is closed or already sliding out.
3016
+ */
1295
3017
  close(): void;
1296
3018
  }
1297
3019
  /**
1298
- * A menu of commands on the native popover platform: the invoker named by
1299
- * `for` toggles it and light dismiss and Escape come from the browser, while
1300
- * the element adds the menu semantics and the keyboard protocol.
3020
+ * A menu of commands on the native popover platform, opened by the element
3021
+ * whose id `for` names. The menu is the popover itself: light dismiss and
3022
+ * Escape come from the browser, and it is anchored to its invoker through
3023
+ * `Anchors.wire`, placed in script where the platform has no css anchor
3024
+ * positioning.
3025
+ *
3026
+ * The items are the buttons and `a[href]` links written as its direct
3027
+ * children, kept as they are. Activation is the item's own click, which the
3028
+ * menu follows by hiding itself, so a page listens for the click where it
3029
+ * already listens. The menu fires no events of its own.
1301
3030
  *
1302
- * The items are the buttons and links written inside it, kept as they are and
1303
- * given their menu role and their place in the roving focus. Activation is the
1304
- * item's own click, which the element only follows by closing.
3031
+ * The keyboard is the ARIA menu protocol: the focus moves to the
3032
+ * first item as the menu opens, the arrows, Home and End walk the items,
3033
+ * typing searches their text, Enter and Space activate the focused one, and
3034
+ * Escape and a close from inside the menu hand the focus back to the invoker.
1305
3035
  */
1306
3036
  declare class Menu extends ParsedElement {
1307
3037
  #private;
3038
+ /** @type {string[]} */
1308
3039
  static attributes: string[];
3040
+ /**
3041
+ * Gives the menu the `menu` role and an empty `popover` attribute unless
3042
+ * it already has one, gives the invoker `aria-haspopup="menu"`, an
3043
+ * `aria-expanded` kept in step, and the toggle of the menu, and marks the
3044
+ * items. `for` is read here, once.
3045
+ * @returns {void}
3046
+ * @throws {Error} when `for` names no element in the document at the render
3047
+ */
1309
3048
  render(): void;
1310
3049
  /**
1311
- * The commands, in the order the arrows walk them: the menu's own buttons
1312
- * and links, minus the disabled and hidden ones, which keep their role and
1313
- * leave the walk.
3050
+ * The commands, in the order the arrows walk them: the menu's direct
3051
+ * `button` and `a[href]` children, minus those carrying a `disabled` or
3052
+ * `hidden` attribute, which keep their role and leave the walk. Reading it
3053
+ * also marks any child added since the render, giving it the `menuitem`
3054
+ * role, unless it has a role already, and a `tabIndex` of -1.
3055
+ * @type {HTMLElement[]}
1314
3056
  */
1315
3057
  get items(): HTMLElement[];
1316
3058
  }
1317
- /** A transient feedback region: each show() stacks a toast that retires on its own timer. */
3059
+ export type ToastOptions = {
3060
+ /**
3061
+ * the theme class and
3062
+ * the announcement, `info` when absent or unknown
3063
+ */
3064
+ severity?: 'info' | 'success' | 'warning' | 'error';
3065
+ /**
3066
+ * the milliseconds before the toast retires, the
3067
+ * region's own timeout when absent
3068
+ */
3069
+ timeout?: number;
3070
+ /**
3071
+ * a button labelled
3072
+ * with `label` as text, beside the message
3073
+ */
3074
+ action?: {
3075
+ label: any;
3076
+ onClick?: () => void;
3077
+ };
3078
+ };
3079
+ /**
3080
+ * @typedef {object} ToastOptions
3081
+ * @property {'info'|'success'|'warning'|'error'} [severity] the theme class and
3082
+ * the announcement, `info` when absent or unknown
3083
+ * @property {number} [timeout] the milliseconds before the toast retires, the
3084
+ * region's own timeout when absent
3085
+ * @property {{ label: any, onClick?: () => void }} [action] a button labelled
3086
+ * with `label` as text, beside the message
3087
+ */
3088
+ /**
3089
+ * A region of transient messages, with `role="region"` and a localized
3090
+ * `aria-label`. Each `show()` appends a `ful-toast` that retires on its own
3091
+ * timer, so concurrent toasts stack.
3092
+ *
3093
+ * The region is a manual popover, whatever `popover` the page declared, and
3094
+ * each `show()` shows it again, so it is on top of the top layer when a toast
3095
+ * arrives: above a modal dialog or drawer opened before it. While a modal is
3096
+ * open the page outside it is inert, the region included: its toasts show and
3097
+ * retire on their timers, and their buttons answer once the modal closes.
3098
+ *
3099
+ * `timeout` is the region's default timer in milliseconds, 5000 when absent
3100
+ * or zero.
3101
+ *
3102
+ * A `show-toast` event dispatched on the document, or bubbling to it, shows its
3103
+ * `detail` in every connected region that has rendered, `detail.message` being
3104
+ * the message and the other fields the `ToastOptions`. The document listener is
3105
+ * added once, so a second region shows each toast once, and an event with no
3106
+ * detail is ignored.
3107
+ */
1318
3108
  declare class Toasts extends ParsedElement {
1319
3109
  #private;
1320
3110
  static attributes: string[];
3111
+ /** Upgrades the region, and makes a re-attached one answer `show-toast` again. */
1321
3112
  connectedCallback(): void;
3113
+ /** Stops the region answering `show-toast` while it is out of the document. */
1322
3114
  disconnectedCallback(): void;
1323
3115
  render(): void;
1324
3116
  /**
1325
- * Appends a toast carrying the message (a Failure shows its problems'
1326
- * reasons, one per line), severity picking the theme and the announcement,
1327
- * the toast retiring through its own timer or its dismiss button.
3117
+ * Appends a `ful-toast` holding the message as text, a `Failure` showing
3118
+ * its problems' reasons one per line and anything else its string form
3119
+ * (`''` for null or undefined). The toast carries the severity as a class
3120
+ * and has `role="alert"` for `error` and `role="status"` otherwise.
1328
3121
  *
1329
- * The timer holds while the toast is hovered or holds the focus, so an
1330
- * actionable toast waits for its reader: `action` is `{ label, onClick }`,
1331
- * a button beside the message whose click answers and retires. The same
1332
- * options travel in the `show-toast` event's detail.
3122
+ * The toast retires when its timer runs out, when its localized dismiss
3123
+ * button is clicked, or when its action button is clicked, after
3124
+ * `action.onClick` runs. The timer pauses while the pointer is over the
3125
+ * toast or the focus is inside it, and resumes with the time that was left
3126
+ * once both have gone. A retiring toast holding the focus moves it to the
3127
+ * region, which has `tabindex="-1"` for this. The toast is removed once its
3128
+ * `ful-toast-out` animation ends, or at once where the user prefers reduced
3129
+ * motion or no animation runs.
3130
+ *
3131
+ * A connected region is shown again as a popover, which puts it above
3132
+ * whatever opened in the top layer since; the focus stays where it was.
1333
3133
  * @param {any} message
1334
- * @param {any} [options] severity, timeout and action
1335
- * @returns {HTMLElement}
3134
+ * @param {ToastOptions} [options]
3135
+ * @returns {HTMLElement} the toast, already in the region
1336
3136
  */
1337
- show(message: any, options?: any): HTMLElement;
3137
+ show(message: any, options?: ToastOptions): HTMLElement;
1338
3138
  }
1339
3139
  /**
1340
3140
  * A tab panel: one visible panel at a time, announced through the tab pattern
1341
- * (a tablist of tab buttons, each panel a tabpanel named by its tab). The tabs
1342
- * are declared as <tab> elements in the tabs slot, the panels as the slotless
1343
- * children, paired in order. Entering a panel fires the section:requested
1344
- * family on it (generic and #index, panels being nameless) and awaits the
1345
- * answers, so a panel can deliver itself asynchronously.
3141
+ * (a tablist of tab buttons, each panel a tabpanel labelled by its tab). The
3142
+ * tabs are declared as `<tab>` elements in the `tabs` slot, the panels as the
3143
+ * slotless children, paired in order. Each `<tab>` becomes a `button` with the
3144
+ * `tab` role carrying the tab's content, inside a `ful-tablist` with the
3145
+ * `tablist` role. A panel that already has an id keeps it and its tab points at
3146
+ * it; a panel without one gets a generated id. Every panel carries
3147
+ * `tabindex="0"`, so the visible one follows its tab in the tab order and the
3148
+ * hidden ones stay out of it.
3149
+ *
3150
+ * The tabs use a roving tabindex and automatic activation: a click activates
3151
+ * its tab, and on the tablist ArrowRight and ArrowLeft move to the next and
3152
+ * previous tab, wrapping around, while Home and End jump to the first and the
3153
+ * last, moving the focus with the activation.
3154
+ *
3155
+ * Entering a panel fires `section:requested` and `section:requested:#<index>`
3156
+ * on the host, bubbling, with detail `{ name: null, section, index, first }`,
3157
+ * and awaits the answers registered through `AsyncEvents.asyncOn`, so a panel
3158
+ * can deliver itself asynchronously: see `SectionRequests`.
1346
3159
  */
1347
3160
  declare class Tabs extends ParsedElement {
1348
3161
  #private;
1349
3162
  static slots: boolean;
1350
3163
  static observed: string[];
1351
3164
  static template: string;
3165
+ /**
3166
+ * Pairs the declared tabs with the panels in order. When the counts
3167
+ * differ, the unpaired tabs and panels are left in the dom as they are and
3168
+ * a warning is logged.
3169
+ * @param {{ slots: Record<string, DocumentFragment> }} conf
3170
+ * @returns {void}
3171
+ */
1352
3172
  render({ slots }: {
1353
- slots: any;
3173
+ slots: Record<string, DocumentFragment>;
1354
3174
  }): void;
3175
+ /**
3176
+ * The index of the visible panel, reflected to the `active` attribute.
3177
+ * Writing it clamps the value to the paired tabs, a value that is not a
3178
+ * number counting as 0, and selects that tab and shows its panel. When the
3179
+ * index changes on a rendered element it dispatches `tabs:change` on the
3180
+ * host (not bubbling) with detail `{ active, previous }`, then fires the
3181
+ * section requests for the entered panel. The first write, which applies
3182
+ * the attribute after the render, fires the requests without the event.
3183
+ * A failed delivery paints the panel's error and rejects nowhere.
3184
+ * @type {number}
3185
+ */
1355
3186
  get active(): number;
1356
3187
  /**
1357
- * Re-fires the section:requested family on the panel (by index or the
1358
- * panel element itself), whether active or not: the explicit request for a
1359
- * content that wants refreshing. A failed refresh paints its problems,
1360
- * nothing rejects: there is no caller to reject towards.
3188
+ * Fires the section requests again for a panel, whether it is visible or
3189
+ * not, with `first` false once an earlier request was answered.
3190
+ * @param {number | Element} ref the panel's index, or the panel itself
3191
+ * @returns {Promise<any[] | undefined> | undefined} a promise resolving to
3192
+ * the answers, or to undefined when nobody listened or the delivery
3193
+ * failed (its error painted in the panel); undefined, with a warning, when
3194
+ * `ref` names no panel
1361
3195
  */
1362
- refresh(ref: any): Promise<any[] | undefined> | undefined;
3196
+ refresh(ref: number | Element): Promise<any[] | undefined> | undefined;
1363
3197
  set active(v: number);
1364
3198
  }
1365
3199
  /**
1366
- * An accordion over native details/summary disclosures: the platform carries
1367
- * the semantics, the keyboard and the toggling, the chrome paints the group.
1368
- * With the exclusive claim the render assigns one shared name to every panel,
1369
- * which is the platform's own exclusive grouping: opening one closes the others.
3200
+ * An accordion over native `details`/`summary` disclosures: the browser
3201
+ * provides the semantics, the keyboard and the toggling, and the element adds
3202
+ * the group's styling. The `details` written as its children are rendered,
3203
+ * unchanged, inside a `ful-accordion-group`.
1370
3204
  */
1371
3205
  declare class Accordion extends ParsedElement {
1372
3206
  #private;
1373
3207
  static slots: boolean;
3208
+ /** @type {string[]} */
1374
3209
  static observed: string[];
1375
3210
  static template: string;
3211
+ /**
3212
+ * @param {{ slots: Record<string, DocumentFragment> | undefined }} c
3213
+ * @returns {void}
3214
+ */
1376
3215
  render({ slots }: {
1377
- slots: any;
3216
+ slots: Record<string, DocumentFragment> | undefined;
1378
3217
  }): void;
3218
+ /**
3219
+ * Whether opening one panel closes the others, reflected to the
3220
+ * `exclusive` attribute; only `true` turns it on. Setting it gives the
3221
+ * group's direct `details` children one shared, newly generated `name`,
3222
+ * the platform's exclusive grouping, or removes their `name` when off.
3223
+ * A `details` added to the group afterwards is not named until the next
3224
+ * write.
3225
+ * @type {boolean}
3226
+ */
1379
3227
  get exclusive(): boolean;
1380
3228
  set exclusive(v: boolean);
1381
3229
  }
1382
3230
  /**
1383
- * A wizard: a progress of steps over one-of-N sections, the homeinsurance
1384
- * layout distilled. The steps are declared as <step> elements in the steps
1385
- * slot, the sections as the slotless children, paired in order; each section
1386
- * may carry a data-step name, which is what move() answers to. The current
1387
- * step is the aria-current=step claim, carried in lockstep by the step and
1388
- * its section: the chrome (including which section is shown) follows the
1389
- * claim alone, so the markup state and the style can never disagree. The
1390
- * progress chrome shows the current step alone by default; the progress
1391
- * attribute picks another shape over the same claims (timeline, dots, none).
1392
- * Entering a section
1393
- * fires the section:requested family on it and awaits the answers, so a
1394
- * section can deliver itself asynchronously; move() resolves when the entered
1395
- * section is painted, and rejects when its delivery fails.
3231
+ * A wizard: a progress of steps over one-of-N sections. The steps are declared
3232
+ * as `<step>` elements in the `steps` slot, the sections as the slotless
3233
+ * children, paired in order; each section may carry a `data-step` name, which
3234
+ * is what `move()` and `refresh()` answer to. The steps become the items of an
3235
+ * `ol` inside `ful-steps`, labelled by the localized `wizard.progress`.
3236
+ *
3237
+ * The current step is the `aria-current="step"` claim, carried by the step and
3238
+ * its section together, and the stylesheet hides every `section` child that
3239
+ * does not carry it. A section that carries the claim in the markup when the
3240
+ * wizard renders stays current; otherwise the first section is. Every paired
3241
+ * section gets `tabindex="-1"`, so a move can focus it.
3242
+ *
3243
+ * Entering a section, the first one at the render included, fires
3244
+ * `section:requested`, `section:requested:#<index>` and, when the section has a
3245
+ * `data-step`, `section:requested:<name>` on the host, bubbling, with detail
3246
+ * `{ name, section, index, first }`, and awaits the answers registered through
3247
+ * `AsyncEvents.asyncOn`: see `SectionRequests`. A failure of the initial entry
3248
+ * paints the section's error and rejects nowhere.
1396
3249
  */
1397
3250
  declare class Wizard extends ParsedElement {
1398
3251
  #private;
1399
3252
  static slots: boolean;
1400
3253
  static observed: string[];
1401
3254
  static template: string;
3255
+ /**
3256
+ * Pairs the declared steps with the sections in order. When the counts
3257
+ * differ, the unpaired steps and sections are left in the dom as they are
3258
+ * and a warning is logged.
3259
+ * @param {{ slots: Record<string, DocumentFragment> }} conf
3260
+ * @returns {void}
3261
+ */
1402
3262
  render({ slots }: {
1403
- slots: any;
3263
+ slots: Record<string, DocumentFragment>;
1404
3264
  }): void;
3265
+ /**
3266
+ * The zero-based position of the current step.
3267
+ * @type {number}
3268
+ */
1405
3269
  get index(): number;
1406
- get step(): any;
1407
- get progress(): any;
1408
- set progress(v: any);
3270
+ /**
3271
+ * The `data-step` of the current section, or null when it has none or no
3272
+ * section is paired.
3273
+ * @type {string | null}
3274
+ */
3275
+ get step(): string | null;
3276
+ /**
3277
+ * The shape of the progress chrome, reflected to the `progress` attribute:
3278
+ * unset shows the current step alone, `timeline` every step as done,
3279
+ * current or future, `dots` one dot per step, `none` no progress at all.
3280
+ * The value only drives the stylesheet; the moves are the same in every
3281
+ * shape.
3282
+ * @type {string | null}
3283
+ */
3284
+ get progress(): string | null;
3285
+ set progress(v: string | null);
3286
+ /**
3287
+ * Moves to the following step; at the last step nothing happens.
3288
+ * @returns {Promise<any[] | undefined> | undefined} what `move()` answers
3289
+ * @throws what the entered section's delivery rejected with, as the rejection
3290
+ * of the returned promise
3291
+ */
1409
3292
  next(): Promise<any[] | undefined> | undefined;
3293
+ /**
3294
+ * Moves to the preceding step; at the first step nothing happens.
3295
+ * @returns {Promise<any[] | undefined> | undefined} what `move()` answers
3296
+ * @throws what the entered section's delivery rejected with, as the rejection
3297
+ * of the returned promise
3298
+ */
1410
3299
  prev(): Promise<any[] | undefined> | undefined;
1411
- move(ref: any): Promise<any[] | undefined> | undefined;
1412
3300
  /**
1413
- * Re-fires the section:requested family on the named section (or the
1414
- * section element itself), whether active or not: the explicit request for a
1415
- * content that wants refreshing. A failed refresh paints its problems,
1416
- * nothing rejects: move() stays the rejecting call.
3301
+ * Moves to the section carrying `data-step="<ref>"`. A move makes that step
3302
+ * and section current, moves the focus to the section, dispatches
3303
+ * `wizard:change` on the host (not bubbling) with detail `{ index, step }`,
3304
+ * then fires the section requests for it.
3305
+ * @param {string} ref the section's `data-step`
3306
+ * @returns {Promise<any[] | undefined> | undefined} a promise resolving to
3307
+ * the answers once they are delivered, or to undefined when nobody
3308
+ * listened; undefined when the section is already current, and undefined
3309
+ * with a warning when no section carries `ref`
3310
+ * @throws what the entered section's delivery rejected with, as the rejection
3311
+ * of the returned promise, after its error is painted in the section
1417
3312
  */
1418
- refresh(ref: any): Promise<any[] | undefined> | undefined;
3313
+ move(ref: string): Promise<any[] | undefined> | undefined;
3314
+ /**
3315
+ * Fires the section requests again for a section, whether it is current or
3316
+ * not, with `first` false once an earlier request was answered.
3317
+ * @param {string | Element} ref the section's `data-step`, or the section
3318
+ * itself
3319
+ * @returns {Promise<any[] | undefined> | undefined} a promise resolving to
3320
+ * the answers, or to undefined when nobody listened or the delivery failed
3321
+ * (its error painted in the section); undefined, with a warning, when `ref`
3322
+ * names no section
3323
+ */
3324
+ refresh(ref: string | Element): Promise<any[] | undefined> | undefined;
1419
3325
  }
1420
3326
  /**
1421
3327
  * Registers everything ful provides on a registry: the elements, the loader
1422
3328
  * components, an http client, and the translations for the configured
1423
- * language. A page calls `registry.plugin(new Plugin({…})).configure()` once.
3329
+ * language. A page calls `registry.plugin(new Plugin({…})).configure()` once;
3330
+ * `registry.plugin` calls this plugin's `configure`.
1424
3331
  */
1425
3332
  declare class Plugin {
1426
3333
  #private;
1427
3334
  /**
1428
- * @param {{ language?: string, translations?: Record<string, any>, httpClient?: any }} [options]
3335
+ * @param {{ language?: string, translations?: Record<string, any>, httpClient?: HttpClient }} [options]
1429
3336
  * `language` is fixed for the page: a full BCP-47 tag or a primary subtag,
1430
- * defaulting to the browser's language. `translations` is a flat
1431
- * active-language map applied over the built-in translations: reword built-in
3337
+ * defaulting to `navigator.language`, then to 'en'. Its primary subtag,
3338
+ * lowercased, picks the built-in translations (en, it, es, fr); the messages
3339
+ * are the english ones, overridden by that language's, overridden by
3340
+ * `translations`, so a language without built-in translations reads in
3341
+ * english. `translations` is a flat map of keys to messages: reword built-in
1432
3342
  * keys ('pagination.showing', …) or add your own ('checkout.total', …).
1433
3343
  * `httpClient` is the client every ful component fetches through, registered
1434
- * as the `http-client` component: where an unauthorized session goes is an
1435
- * application decision, so a page that does not want the default's redirect
1436
- * to '/' builds its own.
3344
+ * as the `http-client` component; without one, `configure` builds a client
3345
+ * that sends the csrf token and navigates to '/' on a 401 response.
1437
3346
  */
1438
3347
  constructor(options?: {
1439
3348
  language?: string;
1440
3349
  translations?: Record<string, any>;
1441
- httpClient?: any;
3350
+ httpClient?: HttpClient;
1442
3351
  });
1443
- configure(registry: any): void;
3352
+ /**
3353
+ * Defines on `registry` the `l10n` module, the `http-client` component,
3354
+ * every `ful-*` element, the `loaders:select`, `loaders:form` and
3355
+ * `loaders:table` components, and an overlay publishing `l10n` (the merged
3356
+ * messages) and `locale` (the `language` as given) to every template.
3357
+ * @param {{ defineModule(name: string, module: any): any, defineComponent(name: string, component: any): any, defineElement(tag: string, klass: CustomElementConstructor): any, defineOverlay(...data: any[]): any }} registry
3358
+ * the ftl `Registry`, each of those methods answering the registry itself
3359
+ * @returns {void}
3360
+ */
3361
+ configure(registry: {
3362
+ defineModule(name: string, module: any): any;
3363
+ defineComponent(name: string, component: any): any;
3364
+ defineElement(tag: string, klass: CustomElementConstructor): any;
3365
+ defineOverlay(...data: any[]): any;
3366
+ }): void;
1444
3367
  }
1445
3368
  export { Accordion, Anchors, AsyncEvents, Bindings, BooleanFilter, Checkbox, Claims, CompareFilter, Dialog, Drawer, Dropdown, Field, Form, FormLoader, InFilter, Input, InputFile, InputInstant, InputLocalDate, InputLocalTime, Instant, InstantFilter, LocalDate, LocalDateFilter, LocalStorage, Menu, NumberFilter, Pagination, Plugin, RadioGroup, Select, SelectLoader, SessionStorage, SortButton, Table, TableLoader, TableSchemaParser, Tabs, TextFilter, Timing, Toasts, Tooltip, VersionedLocalStorage, VersionedSessionStorage, Wizard, describable };
1446
3369