@optionfactory/fml 9.0.0-rc1 → 9.0.0-rc11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -13,7 +13,7 @@ An http client
13
13
  ## ful
14
14
  A no-build web components library based on ftl
15
15
 
16
- ful fields report their values through `ful-form`'s intercepted submit: a plain `<form>` carrying them submits none of their values. Expressions compare strictly (`==` is `===`, and there is no coercing spelling), and message placeholders are `\w+` names: `{amount}` yes, `{user-name}` no.
16
+ ful fields report their values through `ful-form`'s intercepted submit: a plain `<form>` carrying them submits none of their values. Expressions compare strictly (`==` is `===`, and there is no coercing spelling), and message placeholders are `\w+` names: `{amount}` yes, `{user-name}` no. Every field names its control in the markup, a `<label for>` pointing at it or `aria-labelledby` where the control is not something a `for` may target, so an accessibility audit and the browser's autofill read the label too and not assistive technology alone.
17
17
 
18
18
  ### Localization
19
19
 
@@ -73,7 +73,7 @@ ful.AsyncEvents.asyncOn(wizard, 'section:requested:dettagli', async (e) => {
73
73
  });
74
74
  ```
75
75
 
76
- The component awaits the union of the answers: the loading ring covers the wait, a failed delivery paints the section's problems and rejects the wizard's `move()`, and nobody listening is a plain activation at zero cost, so server-rendered sections never use the mechanism at all. `refresh()` re-fires explicitly (wizard by name or section, tabs by index or section; dialog and drawer argument-less), the drawer's `update()` keeping its own open-answer-deliver cycle.
76
+ The component awaits the union of the answers: the loading ring covers the wait, a failed delivery paints the section's problems and rejects the wizard's `move()`, and nobody listening is a plain activation at zero cost, so server-rendered sections never use the mechanism at all. `refresh()` re-fires explicitly (wizard by name or section, tabs by index or section; dialog and drawer argument-less), the `update()` of the drawer and the dialog keeping its own open-answer-deliver cycle.
77
77
 
78
78
  The palette is `light-dark()` based and follows the page's `color-scheme` alone: pin `color-scheme: light` to disable the dark side, declare `light dark` to follow the os, `dark` to force it; the library declares nothing itself, and it works per-subtree too. The theme's ink pairs hold WCAG AA in both schemes, kept honest by a suite that computes the ratios; wherever the accent is read instead of filled (ghost outlines, done steps, success toasts) use `--ful-accent-ink`, the accent tuned for text.
79
79
 
@@ -83,10 +83,9 @@ See `examples/ful/kitchen-sink.html` for a page showing every component in its n
83
83
 
84
84
  fml targets evergreen browsers from spring 2024. The hard floor is Chrome/Edge 123 (March 2024), Firefox 125 (April 2024) and Safari 17.5 (May 2024), set by the newest platform features the library leans on everywhere: `light-dark()`, the Popover API, `Promise.withResolvers`, `:has()` and native CSS nesting. Below the floor, script or styling fail outright.
85
85
 
86
- Two features degrade instead of breaking under newer floors of their own, and another carries its own fallback:
86
+ One feature degrades instead of breaking under a newer floor of its own, and another carries its own fallback:
87
87
 
88
88
  - the flash guard that keeps a field invisible until it has rendered is a custom state matched with `:state()`, Baseline since May 2024: Chrome/Edge 125, Firefox 126, Safari and iOS 17.4, so Safari is already below the hard floor and only Chrome 123-124 and Firefox 125 are affected. There the rule is dropped and a field shows for the moment between being parsed and being rendered
89
- - the fields' label and description wiring uses aria element reflection, Baseline since April 2025: an older browser keeps the visible layout intact while assistive technology silently loses the label and description associations
90
89
  - anchored popovers (the tooltip's note, the select's dropdown, the filter and boolean-value menus) need CSS anchor positioning: Chrome/Edge 129 (September 2024), Safari 26 (September 2025), Firefox 147 (January 2026; Firefox's support is partial against the full specification, but covers the subset fml uses, `anchor-name`, `position-anchor`, `position-area` (Baseline since January 2026), `anchor()` and `anchor-size()`). Below these a hand-placed fallback keeps the popovers anchored beside their invoker, and never runs where the css works
91
90
 
92
91
  On iOS every browser runs the system WebKit, so the floor reads in iOS versions: iOS 17.5 (May 2024) or newer, which every iPhone from the XS/XR (2018) onward can reach; the anchored popovers fall back to hand placing below iOS 26 (September 2025).
@@ -11,14 +11,28 @@
11
11
  "name": "Tooltip",
12
12
  "tagName": "ful-tooltip",
13
13
  "customElement": true,
14
- "description": "An info affordance: a themed icon button toggling a small popover with a short explanation, opened by click and by keyboard like a button, dismissed by Escape or an outside click. placement picks the side the note sits on: bottom (default), top, left or right.",
14
+ "description": "An info affordance: a themed icon button toggling a small popover with a short explanation, opened by click and by keyboard like a button, dismissed by Escape or an outside click. placement picks the side the note sits on: top (default), bottom, left or right.",
15
15
  "attributes": [
16
16
  {
17
17
  "name": "placement",
18
- "description": "Which side of the trigger the note opens on: bottom (the default), top, start or end. The library places the note itself on every platform, so the side is the same everywhere.",
18
+ "description": "Which side of the trigger the note opens on: top (the default), bottom, left or right. Above by default because a note opening downwards covers the control the marker explains. The library places the note itself on every platform, so the side is the same everywhere.",
19
19
  "type": {
20
20
  "text": "string"
21
21
  }
22
+ },
23
+ {
24
+ "name": "icon",
25
+ "description": "The ful-icon the marker shows, for a tooltip meaning something other than plain information. Defaults to the page's Tooltip.config.icon; a name the library does not paint is declared by the page as ful-icon[name='...'] { mask-image: ... }.",
26
+ "type": {
27
+ "text": "string"
28
+ }
29
+ },
30
+ {
31
+ "name": "describes",
32
+ "description": "For a tooltip standing in a field: the note joins the accessible description of that field's control, announced on reaching the field rather than only on opening the marker, and the marker leaves the tab order so a form of hinted fields costs no extra keystrokes to walk. The marker stays clickable, and keeps its tab stop wherever nothing took the note.",
33
+ "type": {
34
+ "text": "boolean"
35
+ }
22
36
  }
23
37
  ],
24
38
  "slots": [
@@ -34,7 +48,7 @@
34
48
  "name": "Dialog",
35
49
  "tagName": "ful-dialog",
36
50
  "customElement": true,
37
- "description": "A modal dialog on the native platform: header, body and buttons, the header attribute naming its heading, opened by any element carrying dialog-target set to its id, or programmatically through open()/ask(), which resolve with the data-result of the button that closed the dialog (null on Escape). Longer explanations belong here, shorter ones in ful-tooltip.",
51
+ "description": "A modal dialog on the native platform: header, body and buttons, the header attribute naming its heading, opened by any element carrying dialog-target set to its id, or programmatically through open()/ask(), which answer with how the dialog ended, or through update(callback), which opens it on a loading state and then shows either what the callback delivered or the problems of a rejection. Longer explanations belong here, shorter ones in ful-tooltip.",
38
52
  "attributes": [
39
53
  {
40
54
  "name": "header",
@@ -42,22 +56,40 @@
42
56
  "type": {
43
57
  "text": "string"
44
58
  }
59
+ },
60
+ {
61
+ "name": "requires-answer",
62
+ "description": "For the dialog that must be answered: the close button is not rendered and Escape is refused, so the only way out is a button carrying a result. Both, since a close button withheld while Escape still worked would be decoration rather than a rule.",
63
+ "type": {
64
+ "text": "boolean"
65
+ }
66
+ },
67
+ {
68
+ "name": "close-on-submit",
69
+ "description": "For the dialog whose answer is a form succeeding rather than a button being pressed: its own form submitting successfully closes it and answers with the response. Opt in, and its own form only, a ful-form the body holds directly, since a ful-table wraps its filters in a form of its own and searching one is not the dialog being answered. Such a dialog renders no acknowledge button of its own, the form's submit being its answer.",
70
+ "type": {
71
+ "text": "boolean"
72
+ }
45
73
  }
46
74
  ],
47
75
  "slots": [
76
+ {
77
+ "name": "header",
78
+ "description": "Content beside the title and before it: an icon, a badge, a status. It sits outside the heading rather than in it, the header attribute setting the heading through text."
79
+ },
48
80
  {
49
81
  "name": "",
50
82
  "description": "The dialog body."
51
83
  },
52
84
  {
53
85
  "name": "buttons",
54
- "description": "Buttons closing the dialog; each carries a data-result that ask() resolves with. Without this slot a localized acknowledge button is shown."
86
+ "description": "Buttons closing the dialog; each carries a data-result that the answer carries back. Without this slot a localized acknowledge button is shown. A dialog closing on its form renders none either way, so the slot is for buttons beside the form, not for suppressing one."
55
87
  }
56
88
  ],
57
89
  "events": [
58
90
  {
59
91
  "name": "close",
60
- "description": "Fired when the dialog closes, carrying the data-result of the button that closed it, or null on Escape; ask() and open() resolve with the same answer, and a dialog leaving the document while open answers its waiters with null.",
92
+ "description": "Fired when the dialog closes, carrying how it ended: dismissed tells a cancel from an answer, result the data-result of the button that closed it, response what a submit answered with. ask() and open() resolve with the same object, and a dialog leaving the document while open answers its waiters with a dismissal.",
61
93
  "type": {
62
94
  "text": "CustomEvent"
63
95
  }
@@ -91,9 +123,20 @@
91
123
  "type": {
92
124
  "text": "string"
93
125
  }
126
+ },
127
+ {
128
+ "name": "close-on-submit",
129
+ "description": "For the drawer whose ending is a form succeeding rather than its close button being pressed: the form its content holds directly submitting successfully closes it, the close event carrying the response. Opt in, and its own form only, since a ful-table wraps its filters in a form of its own and searching one is not the drawer finishing.",
130
+ "type": {
131
+ "text": "boolean"
132
+ }
94
133
  }
95
134
  ],
96
135
  "slots": [
136
+ {
137
+ "name": "header",
138
+ "description": "Content beside the title and before it: an icon, a badge, a status. It sits outside the heading rather than in it, update() setting the title through textContent and taking anything nested there with it."
139
+ },
97
140
  {
98
141
  "name": "",
99
142
  "description": "The drawer body, shown when update() never replaced it."
@@ -102,7 +145,7 @@
102
145
  "events": [
103
146
  {
104
147
  "name": "close",
105
- "description": "Fired when the drawer closes, by its close button or Escape.",
148
+ "description": "Fired when the drawer closes, carrying how it ended: dismissed tells the close button and Escape from a form that closed it under close-on-submit, and response what that submit answered with, a save answering with no body at all being a save all the same.",
106
149
  "type": {
107
150
  "text": "CustomEvent"
108
151
  }
@@ -296,6 +339,13 @@
296
339
  "type": {
297
340
  "text": "boolean"
298
341
  }
342
+ },
343
+ {
344
+ "name": "autocomplete",
345
+ "description": "The autofill token every field in the form takes unless it declares one of its own, copied onto the form element the host renders. A form cannot pass it down the way the platform would: each control carries form=\"\" so that the host is the only thing that submits, which leaves it with no form owner to inherit through, so the fields read it off the form instead.",
346
+ "type": {
347
+ "text": "string"
348
+ }
299
349
  }
300
350
  ],
301
351
  "slots": [],
@@ -398,11 +448,11 @@
398
448
  "name": "Input",
399
449
  "tagName": "ful-input",
400
450
  "customElement": true,
401
- "description": "A labelled text input. Use type to pick any native input type, or textarea; keep names the characters that survive a keystroke and reject the ones that do not, the caret staying among the survivors, and the uppercase and trim claims shape the value before it is read; v-type=number decodes the value to a number, blank staying null and a value that does not decode kept as it is, an undeclared type defaulting to number. Disabled follows native semantics: the attribute is the field's own claim, honored together with a disabled fieldset ancestry through :disabled, while the property reflects the claim only.",
451
+ "description": "A labelled text input. Use type to pick any native input type, the keypad types numeric and decimal, or textarea; keep names the characters that survive a keystroke and reject the ones that do not, the caret staying among the survivors, and the uppercase and trim claims shape the value before it is read; v-type=number decodes the value to a number, blank staying null and a value that does not decode kept as it is, an undeclared type defaulting to number. Disabled follows native semantics: the attribute is the field's own claim, honored together with a disabled fieldset ancestry through :disabled, while the property reflects the claim only.",
402
452
  "attributes": [
403
453
  {
404
454
  "name": "type",
405
- "description": "Any native input type, or the literal textarea to render one. Defaults to number under v-type=number, and a declared type wins.",
455
+ "description": "Any native input type, the literal textarea to render one, or numeric and decimal, which render a text input carrying that inputmode and filter what can be typed: numeric takes digits and a leading minus, decimal one separator as well, answering it as a dot whatever the keyboard offered. Neither enforces min, max or step, which the native number type still does. Defaults to number under v-type=number, and a declared type wins.",
406
456
  "type": {
407
457
  "text": "string"
408
458
  }
@@ -442,6 +492,13 @@
442
492
  "text": "boolean"
443
493
  }
444
494
  },
495
+ {
496
+ "name": "autocomplete",
497
+ "description": "The autofill token the browser classifies the control by, one of the names the html autocomplete grammar defines (name, given-name, street-address, postal-code, one-time-code, off, ...). It is set on the control itself because the control carries form=\"\", which leaves it without a form owner to inherit a form-wide setting from. Read once at the upgrade. A value outside the grammar reads as on, so an identifier of your own does not switch autofill off.",
498
+ "type": {
499
+ "text": "string"
500
+ }
501
+ },
445
502
  {
446
503
  "name": "disabled",
447
504
  "description": "The field's own disabled claim: the control is disabled and the field is left out of the submitted values, composed with a disabled fieldset ancestry. The property reflects the claim only, matches(':disabled') tells the effective state.",
@@ -515,7 +572,7 @@
515
572
  "attributes": [
516
573
  {
517
574
  "name": "type",
518
- "description": "Any native input type, or the literal textarea to render one. Defaults to number under v-type=number, and a declared type wins.",
575
+ "description": "Any native input type, the literal textarea to render one, or numeric and decimal, which render a text input carrying that inputmode and filter what can be typed: numeric takes digits and a leading minus, decimal one separator as well, answering it as a dot whatever the keyboard offered. Neither enforces min, max or step, which the native number type still does. Defaults to number under v-type=number, and a declared type wins.",
519
576
  "type": {
520
577
  "text": "string"
521
578
  }
@@ -555,6 +612,13 @@
555
612
  "text": "boolean"
556
613
  }
557
614
  },
615
+ {
616
+ "name": "autocomplete",
617
+ "description": "The autofill token the browser classifies the control by, one of the names the html autocomplete grammar defines (name, given-name, street-address, postal-code, one-time-code, off, ...). It is set on the control itself because the control carries form=\"\", which leaves it without a form owner to inherit a form-wide setting from. Read once at the upgrade. A value outside the grammar reads as on, so an identifier of your own does not switch autofill off.",
618
+ "type": {
619
+ "text": "string"
620
+ }
621
+ },
558
622
  {
559
623
  "name": "disabled",
560
624
  "description": "The field's own disabled claim: the control is disabled and the field is left out of the submitted values, composed with a disabled fieldset ancestry. The property reflects the claim only, matches(':disabled') tells the effective state.",
@@ -731,7 +795,7 @@
731
795
  "attributes": [
732
796
  {
733
797
  "name": "type",
734
- "description": "Any native input type, or the literal textarea to render one. Defaults to number under v-type=number, and a declared type wins.",
798
+ "description": "Any native input type, the literal textarea to render one, or numeric and decimal, which render a text input carrying that inputmode and filter what can be typed: numeric takes digits and a leading minus, decimal one separator as well, answering it as a dot whatever the keyboard offered. Neither enforces min, max or step, which the native number type still does. Defaults to number under v-type=number, and a declared type wins.",
735
799
  "type": {
736
800
  "text": "string"
737
801
  }
@@ -771,6 +835,13 @@
771
835
  "text": "boolean"
772
836
  }
773
837
  },
838
+ {
839
+ "name": "autocomplete",
840
+ "description": "The autofill token the browser classifies the control by, one of the names the html autocomplete grammar defines (name, given-name, street-address, postal-code, one-time-code, off, ...). It is set on the control itself because the control carries form=\"\", which leaves it without a form owner to inherit a form-wide setting from. Read once at the upgrade. A value outside the grammar reads as on, so an identifier of your own does not switch autofill off.",
841
+ "type": {
842
+ "text": "string"
843
+ }
844
+ },
774
845
  {
775
846
  "name": "disabled",
776
847
  "description": "The field's own disabled claim: the control is disabled and the field is left out of the submitted values, composed with a disabled fieldset ancestry. The property reflects the claim only, matches(':disabled') tells the effective state.",
@@ -865,7 +936,7 @@
865
936
  "attributes": [
866
937
  {
867
938
  "name": "type",
868
- "description": "Any native input type, or the literal textarea to render one. Defaults to number under v-type=number, and a declared type wins.",
939
+ "description": "Any native input type, the literal textarea to render one, or numeric and decimal, which render a text input carrying that inputmode and filter what can be typed: numeric takes digits and a leading minus, decimal one separator as well, answering it as a dot whatever the keyboard offered. Neither enforces min, max or step, which the native number type still does. Defaults to number under v-type=number, and a declared type wins.",
869
940
  "type": {
870
941
  "text": "string"
871
942
  }
@@ -905,6 +976,13 @@
905
976
  "text": "boolean"
906
977
  }
907
978
  },
979
+ {
980
+ "name": "autocomplete",
981
+ "description": "The autofill token the browser classifies the control by, one of the names the html autocomplete grammar defines (name, given-name, street-address, postal-code, one-time-code, off, ...). It is set on the control itself because the control carries form=\"\", which leaves it without a form owner to inherit a form-wide setting from. Read once at the upgrade. A value outside the grammar reads as on, so an identifier of your own does not switch autofill off.",
982
+ "type": {
983
+ "text": "string"
984
+ }
985
+ },
908
986
  {
909
987
  "name": "disabled",
910
988
  "description": "The field's own disabled claim: the control is disabled and the field is left out of the submitted values, composed with a disabled fieldset ancestry. The property reflects the claim only, matches(':disabled') tells the effective state.",
@@ -999,7 +1077,7 @@
999
1077
  "attributes": [
1000
1078
  {
1001
1079
  "name": "type",
1002
- "description": "Any native input type, or the literal textarea to render one. Defaults to number under v-type=number, and a declared type wins.",
1080
+ "description": "Any native input type, the literal textarea to render one, or numeric and decimal, which render a text input carrying that inputmode and filter what can be typed: numeric takes digits and a leading minus, decimal one separator as well, answering it as a dot whatever the keyboard offered. Neither enforces min, max or step, which the native number type still does. Defaults to number under v-type=number, and a declared type wins.",
1003
1081
  "type": {
1004
1082
  "text": "string"
1005
1083
  }
@@ -1039,6 +1117,13 @@
1039
1117
  "text": "boolean"
1040
1118
  }
1041
1119
  },
1120
+ {
1121
+ "name": "autocomplete",
1122
+ "description": "The autofill token the browser classifies the control by, one of the names the html autocomplete grammar defines (name, given-name, street-address, postal-code, one-time-code, off, ...). It is set on the control itself because the control carries form=\"\", which leaves it without a form owner to inherit a form-wide setting from. Read once at the upgrade. A value outside the grammar reads as on, so an identifier of your own does not switch autofill off.",
1123
+ "type": {
1124
+ "text": "string"
1125
+ }
1126
+ },
1042
1127
  {
1043
1128
  "name": "disabled",
1044
1129
  "description": "The field's own disabled claim: the control is disabled and the field is left out of the submitted values, composed with a disabled fieldset ancestry. The property reflects the claim only, matches(':disabled') tells the effective state.",
@@ -1236,12 +1321,31 @@
1236
1321
  "name": "caption",
1237
1322
  "description": "The caption of the table."
1238
1323
  },
1324
+ {
1325
+ "name": "empty",
1326
+ "description": "Shown in place of the rows when a load returns none, replacing the localized default message."
1327
+ },
1239
1328
  {
1240
1329
  "name": "footer",
1241
1330
  "description": "Content rendered in the table footer."
1242
1331
  }
1243
1332
  ],
1244
- "events": []
1333
+ "events": [
1334
+ {
1335
+ "name": "load:success",
1336
+ "description": "Fired once a load has settled, after the rows are in place and the busy state is gone, carrying the page response and the three requests that asked for it. Not fired for a load a newer one superseded, which owns nothing.",
1337
+ "type": {
1338
+ "text": "CustomEvent"
1339
+ }
1340
+ },
1341
+ {
1342
+ "name": "load:failure",
1343
+ "description": "Fired when a load rejects, after the error row is shown, carrying the exception and the three requests. Telling this from an empty result is what the aria-busy attribute alone cannot do.",
1344
+ "type": {
1345
+ "text": "CustomEvent"
1346
+ }
1347
+ }
1348
+ ]
1245
1349
  },
1246
1350
  {
1247
1351
  "kind": "class",
@@ -1325,7 +1429,7 @@
1325
1429
  "attributes": [
1326
1430
  {
1327
1431
  "name": "type",
1328
- "description": "Any native input type, or the literal textarea to render one. Defaults to number under v-type=number, and a declared type wins.",
1432
+ "description": "Any native input type, the literal textarea to render one, or numeric and decimal, which render a text input carrying that inputmode and filter what can be typed: numeric takes digits and a leading minus, decimal one separator as well, answering it as a dot whatever the keyboard offered. Neither enforces min, max or step, which the native number type still does. Defaults to number under v-type=number, and a declared type wins.",
1329
1433
  "type": {
1330
1434
  "text": "string"
1331
1435
  }
@@ -1365,6 +1469,13 @@
1365
1469
  "text": "boolean"
1366
1470
  }
1367
1471
  },
1472
+ {
1473
+ "name": "autocomplete",
1474
+ "description": "The autofill token the browser classifies the control by, one of the names the html autocomplete grammar defines (name, given-name, street-address, postal-code, one-time-code, off, ...). It is set on the control itself because the control carries form=\"\", which leaves it without a form owner to inherit a form-wide setting from. Read once at the upgrade. A value outside the grammar reads as on, so an identifier of your own does not switch autofill off.",
1475
+ "type": {
1476
+ "text": "string"
1477
+ }
1478
+ },
1368
1479
  {
1369
1480
  "name": "disabled",
1370
1481
  "description": "The field's own disabled claim: the control is disabled and the field is left out of the submitted values, composed with a disabled fieldset ancestry. The property reflects the claim only, matches(':disabled') tells the effective state.",
@@ -1445,7 +1556,7 @@
1445
1556
  "attributes": [
1446
1557
  {
1447
1558
  "name": "type",
1448
- "description": "Any native input type, or the literal textarea to render one. Defaults to number under v-type=number, and a declared type wins.",
1559
+ "description": "Any native input type, the literal textarea to render one, or numeric and decimal, which render a text input carrying that inputmode and filter what can be typed: numeric takes digits and a leading minus, decimal one separator as well, answering it as a dot whatever the keyboard offered. Neither enforces min, max or step, which the native number type still does. Defaults to number under v-type=number, and a declared type wins.",
1449
1560
  "type": {
1450
1561
  "text": "string"
1451
1562
  }
@@ -1485,6 +1596,13 @@
1485
1596
  "text": "boolean"
1486
1597
  }
1487
1598
  },
1599
+ {
1600
+ "name": "autocomplete",
1601
+ "description": "The autofill token the browser classifies the control by, one of the names the html autocomplete grammar defines (name, given-name, street-address, postal-code, one-time-code, off, ...). It is set on the control itself because the control carries form=\"\", which leaves it without a form owner to inherit a form-wide setting from. Read once at the upgrade. A value outside the grammar reads as on, so an identifier of your own does not switch autofill off.",
1602
+ "type": {
1603
+ "text": "string"
1604
+ }
1605
+ },
1488
1606
  {
1489
1607
  "name": "disabled",
1490
1608
  "description": "The field's own disabled claim: the control is disabled and the field is left out of the submitted values, composed with a disabled fieldset ancestry. The property reflects the claim only, matches(':disabled') tells the effective state.",
@@ -1565,7 +1683,7 @@
1565
1683
  "attributes": [
1566
1684
  {
1567
1685
  "name": "type",
1568
- "description": "Any native input type, or the literal textarea to render one. Defaults to number under v-type=number, and a declared type wins.",
1686
+ "description": "Any native input type, the literal textarea to render one, or numeric and decimal, which render a text input carrying that inputmode and filter what can be typed: numeric takes digits and a leading minus, decimal one separator as well, answering it as a dot whatever the keyboard offered. Neither enforces min, max or step, which the native number type still does. Defaults to number under v-type=number, and a declared type wins.",
1569
1687
  "type": {
1570
1688
  "text": "string"
1571
1689
  }
@@ -1605,6 +1723,13 @@
1605
1723
  "text": "boolean"
1606
1724
  }
1607
1725
  },
1726
+ {
1727
+ "name": "autocomplete",
1728
+ "description": "The autofill token the browser classifies the control by, one of the names the html autocomplete grammar defines (name, given-name, street-address, postal-code, one-time-code, off, ...). It is set on the control itself because the control carries form=\"\", which leaves it without a form owner to inherit a form-wide setting from. Read once at the upgrade. A value outside the grammar reads as on, so an identifier of your own does not switch autofill off.",
1729
+ "type": {
1730
+ "text": "string"
1731
+ }
1732
+ },
1608
1733
  {
1609
1734
  "name": "disabled",
1610
1735
  "description": "The field's own disabled claim: the control is disabled and the field is left out of the submitted values, composed with a disabled fieldset ancestry. The property reflects the claim only, matches(':disabled') tells the effective state.",
@@ -1752,11 +1877,11 @@
1752
1877
  "name": "TextFilter",
1753
1878
  "tagName": "ful-filter-text",
1754
1879
  "customElement": true,
1755
- "description": "A ful-table filter over text, with an operator such as CONTAINS or STARTS_WITH and a case sensitivity. Disabled follows native semantics: the attribute is the field's own claim, honored together with a disabled fieldset ancestry through :disabled, while the property reflects the claim only. A multiple ful-select used as a filter emits the bare value array the in-list annotations expect.",
1880
+ "description": "A ful-table filter over text, with an operator such as CONTAINS or STARTS_WITH and a case sensitivity. Disabled follows native semantics: the attribute is the field's own claim, honored together with a disabled fieldset ancestry through :disabled, while the property reflects the claim only.",
1756
1881
  "attributes": [
1757
1882
  {
1758
1883
  "name": "type",
1759
- "description": "Any native input type, or the literal textarea to render one. Defaults to number under v-type=number, and a declared type wins.",
1884
+ "description": "Any native input type, the literal textarea to render one, or numeric and decimal, which render a text input carrying that inputmode and filter what can be typed: numeric takes digits and a leading minus, decimal one separator as well, answering it as a dot whatever the keyboard offered. Neither enforces min, max or step, which the native number type still does. Defaults to number under v-type=number, and a declared type wins.",
1760
1885
  "type": {
1761
1886
  "text": "string"
1762
1887
  }
@@ -1796,6 +1921,13 @@
1796
1921
  "text": "boolean"
1797
1922
  }
1798
1923
  },
1924
+ {
1925
+ "name": "autocomplete",
1926
+ "description": "The autofill token the browser classifies the control by, one of the names the html autocomplete grammar defines (name, given-name, street-address, postal-code, one-time-code, off, ...). It is set on the control itself because the control carries form=\"\", which leaves it without a form owner to inherit a form-wide setting from. Read once at the upgrade. A value outside the grammar reads as on, so an identifier of your own does not switch autofill off.",
1927
+ "type": {
1928
+ "text": "string"
1929
+ }
1930
+ },
1799
1931
  {
1800
1932
  "name": "disabled",
1801
1933
  "description": "The field's own disabled claim: the control is disabled and the field is left out of the submitted values, composed with a disabled fieldset ancestry. The property reflects the claim only, matches(':disabled') tells the effective state.",
@@ -1874,6 +2006,179 @@
1874
2006
  }
1875
2007
  ]
1876
2008
  },
2009
+ {
2010
+ "kind": "class",
2011
+ "name": "InFilter",
2012
+ "tagName": "ful-filter-in",
2013
+ "customElement": true,
2014
+ "description": "A ful-table filter over set membership, answering for both of data-jpa's set annotations: InEnum and InList take the same array of values and no operator, differing only in what the server converts them to. The vocabulary comes from wherever a select's does, slotted options or a loader, and the selection is always multiple, an empty one contributing nothing rather than an empty array.",
2015
+ "attributes": [
2016
+ {
2017
+ "name": "name",
2018
+ "description": "The field name the surrounding form submits the selection under.",
2019
+ "type": {
2020
+ "text": "string"
2021
+ }
2022
+ },
2023
+ {
2024
+ "name": "loader",
2025
+ "description": "The registered component name the select loads its vocabulary through; loaders:select when absent.",
2026
+ "type": {
2027
+ "text": "string"
2028
+ }
2029
+ },
2030
+ {
2031
+ "name": "k-type",
2032
+ "description": "How a key coerces when it arrives from the response or from the value attribute: number, string or bool.",
2033
+ "type": {
2034
+ "text": "string"
2035
+ }
2036
+ },
2037
+ {
2038
+ "name": "src",
2039
+ "description": "The url the vocabulary is fetched from. Without it the slotted <option> elements are the whole vocabulary, held in memory.",
2040
+ "type": {
2041
+ "text": "string"
2042
+ }
2043
+ },
2044
+ {
2045
+ "name": "method",
2046
+ "description": "The method the vocabulary is fetched with; POST when absent.",
2047
+ "type": {
2048
+ "text": "string"
2049
+ }
2050
+ },
2051
+ {
2052
+ "name": "mode",
2053
+ "description": "chunked asks the endpoint per query instead of fetching the vocabulary once, for a list too large to hold.",
2054
+ "type": {
2055
+ "text": "string"
2056
+ }
2057
+ },
2058
+ {
2059
+ "name": "preload",
2060
+ "description": "Fetches the vocabulary while the element upgrades rather than on first use. The field paints immediately either way; the options arrive when they arrive.",
2061
+ "type": {
2062
+ "text": "boolean"
2063
+ }
2064
+ },
2065
+ {
2066
+ "name": "revision",
2067
+ "description": "Caches the fetched vocabulary in local storage under this revision; a load under a different one is a miss that evicts the entry. It is the cache's namespace, so a vocabulary that differs per user wants the principal in it. Left valueless, the revision the registry's 'revision' component answers is used, so a page states its build once instead of every element repeating it.",
2068
+ "type": {
2069
+ "text": "string"
2070
+ }
2071
+ },
2072
+ {
2073
+ "name": "k-expr",
2074
+ "description": "The expression yielding an option's key, evaluated against each row of the response. Set it together with l-expr to shape a response without a mapper component.",
2075
+ "type": {
2076
+ "text": "string"
2077
+ }
2078
+ },
2079
+ {
2080
+ "name": "l-expr",
2081
+ "description": "The expression yielding an option's label, evaluated against each row of the response.",
2082
+ "type": {
2083
+ "text": "string"
2084
+ }
2085
+ },
2086
+ {
2087
+ "name": "d-expr",
2088
+ "description": "The expression yielding the rows from the response body; the body itself when absent.",
2089
+ "type": {
2090
+ "text": "string"
2091
+ }
2092
+ },
2093
+ {
2094
+ "name": "m-expr",
2095
+ "description": "The expression yielding an option's metadata, evaluated against each row; the whole row when absent.",
2096
+ "type": {
2097
+ "text": "string"
2098
+ }
2099
+ },
2100
+ {
2101
+ "name": "response-mapper",
2102
+ "description": "The registered component name that turns a response into { key, label, metadata } entries. The k-expr/l-expr pair takes precedence; without either, the response is read as positional rows.",
2103
+ "type": {
2104
+ "text": "string"
2105
+ }
2106
+ },
2107
+ {
2108
+ "name": "disabled",
2109
+ "description": "The field's own disabled claim: the control is disabled and the field is left out of the submitted values, composed with a disabled fieldset ancestry. The property reflects the claim only, matches(':disabled') tells the effective state.",
2110
+ "type": {
2111
+ "text": "boolean"
2112
+ }
2113
+ },
2114
+ {
2115
+ "name": "readonly",
2116
+ "description": "Renders the control but does not let it be edited. A readonly field still contributes its value to the form.",
2117
+ "type": {
2118
+ "text": "boolean"
2119
+ }
2120
+ },
2121
+ {
2122
+ "name": "required",
2123
+ "description": "Marks the field as required for assistive technologies and shows the required marker on its label.",
2124
+ "type": {
2125
+ "text": "boolean"
2126
+ }
2127
+ },
2128
+ {
2129
+ "name": "value",
2130
+ "description": "Comma separated list of the selected keys. Empty contributes nothing, so the table drops the filter rather than sending an empty set.",
2131
+ "type": {
2132
+ "text": "string"
2133
+ }
2134
+ },
2135
+ {
2136
+ "name": "multiple",
2137
+ "description": "Always on for this element: a set of one is still a set, and a single selection would answer a bare key where the filter request wants an array.",
2138
+ "type": {
2139
+ "text": "boolean"
2140
+ }
2141
+ },
2142
+ {
2143
+ "name": "item-list",
2144
+ "description": "Shows the selection as a ful-item-list under the control as well, in addition to the field itself (the label for a single select, the ful-badge chips when multiple).",
2145
+ "type": {
2146
+ "text": "boolean"
2147
+ }
2148
+ }
2149
+ ],
2150
+ "slots": [
2151
+ {
2152
+ "name": "",
2153
+ "description": "The label of the field."
2154
+ },
2155
+ {
2156
+ "name": "info",
2157
+ "description": "Extra content rendered next to the label, for a hint or a help icon."
2158
+ },
2159
+ {
2160
+ "name": "before",
2161
+ "description": "An addon rendered inside the input group, before the control."
2162
+ },
2163
+ {
2164
+ "name": "after",
2165
+ "description": "An addon rendered inside the input group, after the control."
2166
+ },
2167
+ {
2168
+ "name": "dropdown",
2169
+ "description": "Replaces the template used to render the options."
2170
+ }
2171
+ ],
2172
+ "events": [
2173
+ {
2174
+ "name": "change",
2175
+ "description": "Fired with the selected keys, or undefined when nothing is picked.",
2176
+ "type": {
2177
+ "text": "CustomEvent"
2178
+ }
2179
+ }
2180
+ ]
2181
+ },
1877
2182
  {
1878
2183
  "kind": "class",
1879
2184
  "name": "Select",
@@ -1932,7 +2237,7 @@
1932
2237
  },
1933
2238
  {
1934
2239
  "name": "revision",
1935
- "description": "Caches the fetched vocabulary in local storage under this revision; a load under a different one is a miss that evicts the entry. It is the cache's namespace, so a vocabulary that differs per user wants the principal in it.",
2240
+ "description": "Caches the fetched vocabulary in local storage under this revision; a load under a different one is a miss that evicts the entry. It is the cache's namespace, so a vocabulary that differs per user wants the principal in it. Left valueless, the revision the registry's 'revision' component answers is used, so a page states its build once instead of every element repeating it.",
1936
2241
  "type": {
1937
2242
  "text": "string"
1938
2243
  }
@@ -2282,6 +2587,14 @@
2282
2587
  "module": "dist/fml.mjs"
2283
2588
  }
2284
2589
  },
2590
+ {
2591
+ "kind": "custom-element-definition",
2592
+ "name": "ful-filter-in",
2593
+ "declaration": {
2594
+ "name": "InFilter",
2595
+ "module": "dist/fml.mjs"
2596
+ }
2597
+ },
2285
2598
  {
2286
2599
  "kind": "custom-element-definition",
2287
2600
  "name": "ful-select",