@aceshooting/lyra-ui 21.0.0 → 21.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +56 -0
- package/README.md +6 -3
- package/custom-elements.json +1 -1
- package/dist/ai/runtime.js +1 -1
- package/dist/ai/types.d.ts +13 -5
- package/dist/cli/lyra-rename-ledger.mjs +1 -0
- package/dist/cli/migrate-wa.mjs +33 -13
- package/dist/cli/migration-contract.json +1 -1
- package/dist/components/agent-tools/agent-trace/agent-trace.class.d.ts +5 -3
- package/dist/components/agent-tools/agent-trace/agent-trace.class.js +3 -2
- package/dist/components/agent-tools/tool-call-block/tool-call-block.class.d.ts +15 -3
- package/dist/components/agent-tools/tool-call-block/tool-call-block.class.js +3 -3
- package/dist/components/agent-tools/tool-call-chip/tool-call-chip.class.d.ts +24 -8
- package/dist/components/agent-tools/tool-call-chip/tool-call-chip.class.js +3 -3
- package/dist/components/agent-tools/tool-param-form/tool-param-form.class.js +4 -0
- package/dist/components/agent-tools/tool-result-dialog/tool-result-dialog.class.d.ts +9 -5
- package/dist/components/agent-tools/tool-result-dialog/tool-result-dialog.class.js +2 -2
- package/dist/components/agent-tools/tool-result-dialog/tool-result-dialog.styles.js +1 -1
- package/dist/components/agent-tools/tool-status.js +2 -2
- package/dist/components/agent-tools/tool-timeline/tool-timeline.class.d.ts +8 -0
- package/dist/components/agent-tools/tool-timeline/tool-timeline.class.js +3 -2
- package/dist/components/agent-tools/tool-timeline/tool-timeline.styles.js +1 -1
- package/dist/components/agent-tools/trace-tree/span.d.ts +2 -2
- package/dist/components/charts/chart/box-plot.class.d.ts +3 -1
- package/dist/components/charts/chart/box-plot.class.js +9 -9
- package/dist/components/charts/chart/chart-bidi.d.ts +109 -0
- package/dist/components/charts/chart/chart-bidi.js +1 -0
- package/dist/components/charts/chart/chart.class.d.ts +17 -2
- package/dist/components/charts/chart/chart.class.js +10 -8
- package/dist/components/charts/chart/histogram.class.js +1 -1
- package/dist/components/charts/chart/lite-chart.class.d.ts +24 -5
- package/dist/components/charts/chart/lite-chart.class.js +9 -9
- package/dist/components/charts/chart/lite-chart.styles.js +1 -1
- package/dist/components/conversation/code-block/shiki-dark-theme.d.ts +2 -2
- package/dist/components/conversation/code-block/shiki-dark-theme.js +1 -1
- package/dist/components/conversation/conversation-item/conversation-item.class.d.ts +18 -2
- package/dist/components/conversation/conversation-item/conversation-item.class.js +3 -3
- package/dist/components/conversation/conversation-item/conversation-item.styles.js +1 -1
- package/dist/components/conversation/markdown/markdown-base.class.js +1 -1
- package/dist/components/conversation/markdown/markdown-core.class.d.ts +3 -1
- package/dist/components/conversation/markdown/markdown.class.d.ts +3 -1
- package/dist/components/conversation/message-actions/message-actions.class.d.ts +7 -2
- package/dist/components/conversation/message-actions/message-actions.class.js +2 -2
- package/dist/components/conversation/message-parts/message-parts.class.d.ts +6 -2
- package/dist/components/conversation/message-parts/message-parts.class.js +5 -3
- package/dist/components/conversation/selection-toolbar/selection-toolbar.class.d.ts +3 -2
- package/dist/components/conversation/selection-toolbar/selection-toolbar.class.js +1 -1
- package/dist/components/conversation/streaming-text/streaming-text-base.class.d.ts +1 -5
- package/dist/components/conversation/streaming-text/streaming-text-base.class.js +2 -2
- package/dist/components/conversation/streaming-text/streaming-text-core.class.d.ts +7 -1
- package/dist/components/conversation/streaming-text/streaming-text-core.class.js +3 -4
- package/dist/components/conversation/streaming-text/streaming-text.class.d.ts +7 -1
- package/dist/components/conversation/streaming-text/streaming-text.class.js +3 -4
- package/dist/components/conversation/thread-list/thread-list.class.d.ts +13 -2
- package/dist/components/conversation/thread-list/thread-list.class.js +3 -3
- package/dist/components/data/data-grid/data-grid.class.js +1 -1
- package/dist/components/data/document-library/document-library.class.js +2 -0
- package/dist/components/data/flow-canvas/flow-canvas.class.js +1 -1
- package/dist/components/data/flow-minimap/flow-minimap.class.js +1 -1
- package/dist/components/data/flow-node/flow-node.class.d.ts +6 -2
- package/dist/components/data/flow-node/flow-node.class.js +6 -2
- package/dist/components/data/flow-node/flow-node.styles.js +1 -1
- package/dist/components/data/gauge/gauge.styles.js +1 -1
- package/dist/components/data/heatmap/heatmap.class.d.ts +3 -0
- package/dist/components/data/heatmap/heatmap.class.js +2 -2
- package/dist/components/data/heatmap/heatmap.styles.js +1 -1
- package/dist/components/data/sparkline/sparkline.class.d.ts +5 -3
- package/dist/components/data/stat/stat.class.d.ts +4 -3
- package/dist/components/data/table/table.class.d.ts +32 -5
- package/dist/components/data/table/table.class.js +3 -2
- package/dist/components/forms/button/button.class.d.ts +7 -1
- package/dist/components/forms/button/button.styles.js +1 -1
- package/dist/components/forms/date-picker/date-input.styles.js +1 -1
- package/dist/components/forms/input/input.styles.js +1 -1
- package/dist/components/forms/rubric-form/rubric-form.class.js +1 -0
- package/dist/components/layout/app-rail/app-rail.class.d.ts +50 -11
- package/dist/components/layout/app-rail/app-rail.class.js +2 -2
- package/dist/components/layout/command-palette/command-palette.class.js +1 -1
- package/dist/components/layout/menu/menu.class.d.ts +6 -5
- package/dist/components/layout/split-panel/split-panel.class.d.ts +2 -1
- package/dist/components/media/flag/language-map.d.ts +8 -0
- package/dist/components/media/flag/language-map.js +1 -1
- package/dist/components/media/map/map-loader.d.ts +7 -1
- package/dist/components/media/map/map.class.d.ts +111 -5
- package/dist/components/media/map/map.class.js +3 -3
- package/dist/components/media/media-card/media-card.class.d.ts +18 -5
- package/dist/components/media/media-card/media-card.class.js +1 -1
- package/dist/components/overlays/overlay/popover.class.d.ts +20 -0
- package/dist/components/overlays/overlay/popover.class.js +2 -2
- package/dist/components/retrieval/graph/graph.class.d.ts +3 -3
- package/dist/components/retrieval/graph/graph.class.js +1 -1
- package/dist/components/retrieval/knowledge-graph-explorer/knowledge-graph-explorer.class.d.ts +5 -3
- package/dist/components/retrieval/knowledge-graph-explorer/knowledge-graph-explorer.class.js +3 -2
- package/dist/components/retrieval/mind-map/mind-map.class.d.ts +3 -3
- package/dist/components/retrieval/mind-map/mind-map.class.js +1 -1
- package/dist/components/retrieval/retrieval-results/retrieval-results.class.d.ts +4 -1
- package/dist/components/retrieval/retrieval-results/retrieval-results.class.js +5 -1
- package/dist/components/retrieval/source-picker/source-picker.class.d.ts +4 -1
- package/dist/components/retrieval/source-picker/source-picker.class.js +5 -1
- package/dist/components/utility/icon/icon.class.d.ts +9 -3
- package/dist/components/utility/icon/icon.class.js +1 -1
- package/dist/components/utility/mutation-observer/mutation-observer.class.d.ts +14 -8
- package/dist/components/utility/mutation-observer/mutation-observer.class.js +1 -1
- package/dist/components/viewers/document-viewer/document-viewer.class.d.ts +6 -1
- package/dist/components/viewers/document-viewer/document-viewer.class.js +7 -1
- package/dist/components/viewers/document-viewer/registry.d.ts +12 -3
- package/dist/components/viewers/geojson-view/geojson-view.class.d.ts +13 -5
- package/dist/components/viewers/geojson-view/geojson-view.class.js +1 -1
- package/dist/components/viewers/geojson-view/geojson-viewer.class.d.ts +3 -2
- package/dist/custom-elements-jsx.d.ts +1 -1
- package/dist/events.d.ts +644 -1242
- package/dist/internal/anchored-overlay-runtime.d.ts +2 -2
- package/dist/internal/cell-range.d.ts +0 -6
- package/dist/internal/cell-range.js +1 -1
- package/dist/internal/css-token-length.d.ts +7 -0
- package/dist/internal/css-token-length.js +1 -0
- package/dist/internal/default-strings.generated.d.ts +1 -1
- package/dist/internal/default-strings.generated.js +1 -1
- package/dist/internal/deferred-focus-return.d.ts +7 -4
- package/dist/internal/deferred-focus-return.js +1 -1
- package/dist/internal/dev-mode-attribute-warning.d.ts +35 -0
- package/dist/internal/dev-mode-attribute-warning.js +1 -1
- package/dist/internal/localization-runtime.js +1 -1
- package/dist/internal/localization-types.d.ts +1 -1
- package/dist/internal/localization.js +2 -2
- package/dist/internal/package-metadata.d.ts +1 -1
- package/dist/internal/package-metadata.js +1 -1
- package/dist/internal/papaparse-loader.d.ts +1 -1
- package/dist/internal/papaparse-loader.js +1 -1
- package/dist/internal/positioner.d.ts +10 -0
- package/dist/internal/positioner.js +1 -1
- package/dist/internal/shared-unions.d.ts +2 -1
- package/dist/internal/slotted-overlay-controller.d.ts +20 -0
- package/dist/internal/slotted-overlay-controller.js +1 -0
- package/dist/internal/text-quote.d.ts +1 -1
- package/dist/internal/text-quote.js +1 -1
- package/dist/internal/theme-observation.d.ts +4 -0
- package/dist/internal/theme-observation.js +1 -0
- package/dist/internal/theme-watcher.js +1 -1
- package/dist/localization.d.ts +6 -1
- package/dist/localization.js +1 -1
- package/dist/lyra.d.ts +1 -1
- package/dist/lyra.js +1 -1
- package/dist/svelte.d.ts +1 -1
- package/dist/testing/lyra-tag-event-map.js +1 -1
- package/dist/translations/ar/agent-tools.js +1 -1
- package/dist/translations/cs/agent-tools.js +1 -1
- package/dist/translations/da/agent-tools.js +1 -1
- package/dist/translations/de/agent-tools.js +1 -1
- package/dist/translations/de-CH/agent-tools.js +1 -1
- package/dist/translations/es/agent-tools.js +1 -1
- package/dist/translations/fa/agent-tools.js +1 -1
- package/dist/translations/fi/agent-tools.js +1 -1
- package/dist/translations/fr/agent-tools.js +1 -1
- package/dist/translations/he/agent-tools.js +1 -1
- package/dist/translations/hi/agent-tools.js +1 -1
- package/dist/translations/hr/agent-tools.js +1 -1
- package/dist/translations/hu/agent-tools.js +1 -1
- package/dist/translations/id/agent-tools.js +1 -1
- package/dist/translations/it/agent-tools.js +1 -1
- package/dist/translations/ja/agent-tools.js +1 -1
- package/dist/translations/kk/agent-tools.js +1 -1
- package/dist/translations/ko/agent-tools.js +1 -1
- package/dist/translations/nb/agent-tools.js +1 -1
- package/dist/translations/nl/agent-tools.js +1 -1
- package/dist/translations/nn/agent-tools.js +1 -1
- package/dist/translations/pl/agent-tools.js +1 -1
- package/dist/translations/pt-BR/agent-tools.js +1 -1
- package/dist/translations/pt-PT/agent-tools.js +1 -1
- package/dist/translations/pt-PT/charts.js +1 -1
- package/dist/translations/pt-PT/conversation.js +1 -1
- package/dist/translations/pt-PT/data.js +1 -1
- package/dist/translations/pt-PT/forms.js +1 -1
- package/dist/translations/pt-PT/layout.js +1 -1
- package/dist/translations/pt-PT/media.js +1 -1
- package/dist/translations/pt-PT/retrieval.js +1 -1
- package/dist/translations/pt-PT/shared.js +1 -1
- package/dist/translations/pt-PT/utility.js +1 -1
- package/dist/translations/pt-PT/viewers.js +2 -2
- package/dist/translations/ro/agent-tools.js +1 -1
- package/dist/translations/ru/agent-tools.js +1 -1
- package/dist/translations/sl/agent-tools.js +1 -1
- package/dist/translations/sv/agent-tools.js +1 -1
- package/dist/translations/tr/agent-tools.js +1 -1
- package/dist/translations/uk/agent-tools.js +1 -1
- package/dist/translations/zh-CN/agent-tools.js +1 -1
- package/dist/translations/zh-TW/agent-tools.js +1 -1
- package/dist/utilities/index.d.ts +1 -1
- package/dist/utilities/index.js +1 -1
- package/dist/utilities/localization.d.ts +28 -10
- package/dist/vue.d.ts +1 -1
- package/llms/components/lr-accordion-item.md +2 -344
- package/llms/components/lr-accordion.md +2 -344
- package/llms/components/lr-app-rail-group.md +2 -721
- package/llms/components/lr-app-rail-item.md +2 -721
- package/llms/components/lr-app-rail.md +29 -4
- package/llms/components/lr-bar-chart.md +2 -87
- package/llms/components/lr-box-plot.md +1 -1
- package/llms/components/lr-breadcrumb-item.md +2 -67
- package/llms/components/lr-bubble-chart.md +2 -87
- package/llms/components/lr-button.md +7 -0
- package/llms/components/lr-chart.md +4 -5
- package/llms/components/lr-chip-group.md +2 -272
- package/llms/components/lr-conversation-item.md +20 -2
- package/llms/components/lr-date-input.md +2 -457
- package/llms/components/lr-date-picker.md +6 -2
- package/llms/components/lr-document-library.md +3 -3
- package/llms/components/lr-document-viewer.md +14 -5
- package/llms/components/lr-doughnut-chart.md +2 -87
- package/llms/components/lr-dropdown-item.md +2 -306
- package/llms/components/lr-dropdown.md +6 -0
- package/llms/components/lr-flag.md +9 -1
- package/llms/components/lr-flow-node.md +4 -1
- package/llms/components/lr-geojson-view.md +3 -90
- package/llms/components/lr-geojson-viewer.md +13 -5
- package/llms/components/lr-icon-button.md +4 -1
- package/llms/components/lr-icon.md +14 -6
- package/llms/components/lr-input.md +9 -2
- package/llms/components/lr-lite-chart.md +17 -9
- package/llms/components/lr-map.md +87 -2
- package/llms/components/lr-markdown-core.md +5 -4
- package/llms/components/lr-markdown.md +4 -2
- package/llms/components/lr-media-card.md +10 -5
- package/llms/components/lr-menu-item.md +2 -306
- package/llms/components/lr-menu.md +9 -6
- package/llms/components/lr-menubar-item.md +2 -125
- package/llms/components/lr-message-actions.md +4 -1
- package/llms/components/lr-message-parts.md +14 -5
- package/llms/components/lr-mutation-observer.md +12 -6
- package/llms/components/lr-navigation-menu-item.md +2 -237
- package/llms/components/lr-number-input.md +4 -0
- package/llms/components/lr-option.md +2 -712
- package/llms/components/lr-pie-chart.md +2 -87
- package/llms/components/lr-polar-area-chart.md +2 -87
- package/llms/components/lr-popover.md +10 -0
- package/llms/components/lr-prompt-input.md +5 -1
- package/llms/components/lr-radar-chart.md +2 -87
- package/llms/components/lr-reorder-item.md +2 -157
- package/llms/components/lr-result-field.md +2 -117
- package/llms/components/lr-retrieval-results.md +3 -1
- package/llms/components/lr-rubric-form.md +3 -2
- package/llms/components/lr-scatter-chart.md +2 -87
- package/llms/components/lr-selection-toolbar.md +4 -2
- package/llms/components/lr-source-card.md +2 -186
- package/llms/components/lr-source-picker.md +3 -1
- package/llms/components/lr-sparkline.md +9 -4
- package/llms/components/lr-split-panel.md +5 -3
- package/llms/components/lr-stat.md +4 -3
- package/llms/components/lr-streaming-text-core.md +7 -5
- package/llms/components/lr-streaming-text.md +9 -6
- package/llms/components/lr-swatch-picker.md +130 -0
- package/llms/components/lr-tag.md +2 -177
- package/llms/components/lr-thread-list.md +27 -4
- package/llms/components/lr-timeline-item.md +2 -120
- package/llms/components/lr-toast-item.md +2 -223
- package/llms/components/lr-tool-call-block.md +12 -3
- package/llms/components/lr-tool-call-chip.md +12 -6
- package/llms/components/lr-tool-param-form.md +3 -2
- package/llms/components/lr-tool-result-dialog.md +7 -3
- package/llms/components/lr-tool-result-view.md +4 -3
- package/llms/components/lr-tool-timeline.md +12 -5
- package/llms/components/lr-tree-item.md +2 -354
- package/llms/index.md +22 -4
- package/llms/migration.md +75 -1
- package/llms/peers.md +1 -1
- package/llms/shared.md +86 -39
- package/llms-full.txt +739 -163
- package/package.json +6 -6
- package/registrations.json +1 -1
- package/vscode-css-data.json +1 -1
- package/vscode-html-data.json +1 -1
- package/web-types.json +1 -1
|
@@ -9,716 +9,6 @@
|
|
|
9
9
|
- **Release history** [CHANGELOG.md](../../CHANGELOG.md); family-wide breaking-change summaries: [llms-full.txt](../../llms-full.txt)
|
|
10
10
|
- **Deprecations** none
|
|
11
11
|
- **Optional peers** none
|
|
12
|
-
- **Themeable via** 7 parts, 7 custom properties — see
|
|
13
|
-
- **Documented with** `lr-combobox
|
|
12
|
+
- **Themeable via** 7 parts, 7 custom properties — see `lr-combobox.md`
|
|
13
|
+
- **Documented with** `lr-combobox`: see [lr-combobox.md](./lr-combobox.md).
|
|
14
14
|
- **Library-wide behavior** (events, form association, `locale`/`strings`, tokens, TS types): `llms/shared.md`
|
|
15
|
-
|
|
16
|
-
---
|
|
17
|
-
|
|
18
|
-
## `lr-combobox` / `lr-option`
|
|
19
|
-
|
|
20
|
-
Filterable single/multi-select combining a text input with a listbox. Mirrors the core
|
|
21
|
-
`<wa-combobox>` API under the `lr-` prefix. **Form-associated** (hand-rolled internals, not the
|
|
22
|
-
shared `FormAssociated` mixin — see gotchas).
|
|
23
|
-
|
|
24
|
-
Consumer writes on mounted options immediately update the owning picker and its submission,
|
|
25
|
-
without changing the reset default or emitting picker input/change events. Owner synchronization
|
|
26
|
-
does not echo as a consumer selected write.
|
|
27
|
-
|
|
28
|
-
A mounted `option.selected` assignment updates the live picker value and form submission
|
|
29
|
-
immediately, including deselection and equal-value writes; it emits no user input/change event.
|
|
30
|
-
|
|
31
|
-
When `multiple` becomes false, the public value, live option flags, and popup `aria-selected` expose
|
|
32
|
-
one selected occurrence. The retained multiple-selection history returns if multiple is enabled
|
|
33
|
-
again without another selection write.
|
|
34
|
-
|
|
35
|
-
Composing keyboard events (`isComposing` or legacy key code 229) remain with filter editing; they do
|
|
36
|
-
not navigate, select/create a value, or dismiss the popup.
|
|
37
|
-
|
|
38
|
-
Source options with `inert`, including inherited inertness, are unavailable through the popup. Live
|
|
39
|
-
inert changes refresh row availability. Text, insertion, replacement, removal, slot
|
|
40
|
-
reassignment/removal, and relevant attributes of `start`/`end`/`prefix`/`suffix` adornments refresh
|
|
41
|
-
the corresponding cloned presentation; unchanged presentation retains its clone identity while
|
|
42
|
-
filtering.
|
|
43
|
-
|
|
44
|
-
Host `aria-describedby` resolves external descriptions onto the native combobox filter before local
|
|
45
|
-
error/hint guidance. References track missing targets, replacement, removal/reinsertion, reconnect,
|
|
46
|
-
and adoption. Removing `label`, `hint`, or `error-text` safely omits the content while leaving
|
|
47
|
-
removed string properties at their native `null` readback; explicit empty strings remain supplied
|
|
48
|
-
empty strings.
|
|
49
|
-
|
|
50
|
-
**First-interaction registration.** Where initial-route weight is stricter than a static combobox
|
|
51
|
-
registration allows, keep a labelled native `<input list>` as the working pre-JavaScript control
|
|
52
|
-
and import only the granular combobox registration on its first focus. Copy the native value after
|
|
53
|
-
the import resolves so typing that happens while the chunk is in flight is not lost, then transfer
|
|
54
|
-
focus explicitly — the browser does not replay the focus event after custom-element upgrade:
|
|
55
|
-
|
|
56
|
-
```html
|
|
57
|
-
<div id="country-fallback">
|
|
58
|
-
<label for="country-native">Country</label>
|
|
59
|
-
<input id="country-native" name="country" list="country-options">
|
|
60
|
-
<datalist id="country-options"><option value="France"></option></datalist>
|
|
61
|
-
</div>
|
|
62
|
-
<lr-combobox id="country-enhanced" name="country" label="Country" hidden>
|
|
63
|
-
<lr-option value="France">France</lr-option>
|
|
64
|
-
</lr-combobox>
|
|
65
|
-
<script type="module">
|
|
66
|
-
const fallback = document.querySelector("#country-fallback");
|
|
67
|
-
const input = document.querySelector("#country-native");
|
|
68
|
-
const combobox = document.querySelector("#country-enhanced");
|
|
69
|
-
let registration;
|
|
70
|
-
input.addEventListener("focus", async () => {
|
|
71
|
-
if (!combobox.hidden) return;
|
|
72
|
-
registration ??= import(
|
|
73
|
-
"@aceshooting/lyra-ui/components/forms/combobox/combobox.js"
|
|
74
|
-
).catch((error) => {
|
|
75
|
-
registration = undefined; // let a later interaction retry
|
|
76
|
-
throw error;
|
|
77
|
-
});
|
|
78
|
-
await registration;
|
|
79
|
-
await customElements.whenDefined("lr-combobox");
|
|
80
|
-
combobox.value = input.value;
|
|
81
|
-
fallback.hidden = true;
|
|
82
|
-
combobox.hidden = false;
|
|
83
|
-
combobox.focus();
|
|
84
|
-
});
|
|
85
|
-
</script>
|
|
86
|
-
```
|
|
87
|
-
|
|
88
|
-
Leave the native control in place if registration fails. This pattern preserves the initial shell;
|
|
89
|
-
the full form-label, option, overlay, and first-open positioning contracts arrive in deferred
|
|
90
|
-
chunks instead of being weakened in a separate partial combobox implementation.
|
|
91
|
-
|
|
92
|
-
An `lr-option` row remains bounded by its owning listbox: the default label ellipsizes and each
|
|
93
|
-
`start`/`end` (or `prefix`/`suffix`) adornment is capped at 40% of the row, where a long slotted text adornment truncates with an
|
|
94
|
-
ellipsis. Unbroken metadata
|
|
95
|
-
therefore cannot widen a 320px LTR or RTL picker.
|
|
96
|
-
|
|
97
|
-
**Adornments in the popup (fixed in 11.0.0).** Before 11.0.0 this paragraph described behavior the
|
|
98
|
-
code did not have: `lr-combobox` builds its popup from normalized row *data* rather than from the
|
|
99
|
-
light-DOM nodes, so a slotted `start`/`end`/`prefix`/`suffix` adornment had nowhere to land and
|
|
100
|
-
simply never rendered — the documented slots and their documented parts were both dead inside the
|
|
101
|
-
one component `lr-option` exists to feed. They now render. The nodes are **cloned** into the row
|
|
102
|
-
(`option-start` / `option-end` parts, inert and `aria-hidden`, so they never join the option's
|
|
103
|
-
accessible name), which means the author's own `<lr-option>` subtree is left exactly where they put
|
|
104
|
-
it rather than being moved into a shadow root as a side effect of opening a dropdown:
|
|
105
|
-
|
|
106
|
-
```html
|
|
107
|
-
<lr-combobox label="Country">
|
|
108
|
-
<lr-option value="fr"><lr-flag slot="start" country="fr"></lr-flag>France</lr-option>
|
|
109
|
-
<lr-option value="mt"><lr-flag slot="start" country="mt"></lr-flag>Malta</lr-option>
|
|
110
|
-
</lr-combobox>
|
|
111
|
-
```
|
|
112
|
-
|
|
113
|
-
An async `source` row can carry the same two fields (`start`, `end`) alongside its existing `icon`.
|
|
114
|
-
|
|
115
|
-
### `lr-combobox`
|
|
116
|
-
|
|
117
|
-
**Properties:**
|
|
118
|
-
|
|
119
|
-
- `multiple: boolean = false` (reflected)
|
|
120
|
-
- `size: LyraSize = 'm'` (reflected — the shared control ladder, so both `2xs`/`xs`/`s`/`m`/`l`/`xl`
|
|
121
|
-
and the `small`/`medium`/`large` spellings are accepted; also scales the "+N" overflow tag and
|
|
122
|
-
decorative expand icon; `size="s"` shares its outer control height with `lr-input`, `lr-select`,
|
|
123
|
-
and `lr-segmented` without part overrides)
|
|
124
|
-
- `pill: boolean = false` (reflected) — rounds the trigger row's corners to a full pill, mirroring
|
|
125
|
-
`lr-input`'s own `pill`. It changes the private radius default to `--lr-radius-pill`, so an
|
|
126
|
-
inherited or direct `--lr-combobox-radius` remains authoritative
|
|
127
|
-
- `placeholder: string = ''`
|
|
128
|
-
- `disabled: boolean = false` (reflected)
|
|
129
|
-
- `required: boolean = false` (reflected — enforced via `internals.setValidity()`; also reflected as
|
|
130
|
-
`aria-required` on `<input part="combobox-input">` immediately. That semantic input exposes
|
|
131
|
-
`aria-invalid="true"` whenever visible error chrome is present, or after interaction while
|
|
132
|
-
intrinsic/custom validity fails; it explicitly returns to `"false"` when neither applies)
|
|
133
|
-
- `name: string = ''`
|
|
134
|
-
- `label: string = ''`
|
|
135
|
-
- `hint: string = ''`
|
|
136
|
-
- `errorText: string = ''` (attribute `error-text` — static error copy shown below the hint;
|
|
137
|
-
overridden by slotted `error` content when provided)
|
|
138
|
-
- `open: boolean = false` (reflected)
|
|
139
|
-
- `allowCreate: boolean = false` (attribute `allow-create`) — a nonmatching query renders a
|
|
140
|
-
localized create row. Activating it emits cancelable `lr-create`; unless vetoed, the component
|
|
141
|
-
appends a real `<lr-option>` and selects it (also supported in `multiple` mode)
|
|
142
|
-
- `allowCustomValue: boolean = false` (attribute `allow-custom-value`) — single-select only;
|
|
143
|
-
commits arbitrary text on Enter without creating an option
|
|
144
|
-
- `showUnknownOption: boolean = false` (attribute `show-unknown-option`, reflected) — appends every
|
|
145
|
-
committed value that no option or async row claims to the end of the listbox as a synthetic,
|
|
146
|
-
badged, keyboard-reachable, re-selectable row. Off by default. The synthetic row is filtered by
|
|
147
|
-
the active query exactly like the `allow-create` row is, so a query it does not match neither
|
|
148
|
-
shows it nor suppresses the "no matches" copy; re-picking it re-commits the same value and
|
|
149
|
-
deliberately does **not** reclassify it as known — the badge and the row both survive, and the row
|
|
150
|
-
never appears in `selectedRows`
|
|
151
|
-
- `getUnknownLabel?: (value: string) => string` (attribute: false) — renders the label for a
|
|
152
|
-
committed value that matches no option or async row, everywhere it appears (trigger, `multiple`
|
|
153
|
-
tag, synthetic row). `getTag` cannot serve this case: it is handed a matched option and there is
|
|
154
|
-
none. A blank return falls back to the raw value. Not consulted while an async `source` fetch has
|
|
155
|
-
never yet resolved for this element — see "Unknown committed values" below; `loadingText` covers
|
|
156
|
-
that window instead, since the value is not yet known to be unmatched at all
|
|
157
|
-
- `loading: boolean = false` (reflected) — mirrors `lr-select`'s own `loading`: whether a
|
|
158
|
-
committed value's real label may still be pending because its `<lr-option>` catalog hasn't
|
|
159
|
-
arrived yet (e.g. mounted asynchronously with no `source` involved). While `true`, a committed
|
|
160
|
-
value that currently matches no option/row renders the localized `loadingText` placeholder in the
|
|
161
|
-
trigger label or the relevant `multiple` tag instead of the raw value, with no
|
|
162
|
-
`notInCatalog`/`[part='unknown-value']` badge, and the open listbox shows the same loading row a
|
|
163
|
-
`source` fetch in flight shows. Independent of `source`'s own async lifecycle — either condition
|
|
164
|
-
alone suppresses the unknown-value presentation. Never mutates `value`/`selectedOptions` itself,
|
|
165
|
-
and does not itself disable the trigger
|
|
166
|
-
- `appearance: 'accent' | 'filled' | 'outlined' | 'filled-outlined' | 'plain' = 'outlined'`
|
|
167
|
-
(reflected) — the library's shared field-surface vocabulary, matching `lr-select`'s trigger.
|
|
168
|
-
`outlined` (the default) is a bordered surface; `filled` swaps the border for a raised fill;
|
|
169
|
-
`filled-outlined` keeps both; `plain` drops both; `accent` paints the loud brand fill with
|
|
170
|
-
on-brand text (the placeholder, expand icon, adornments and `multiple` tags all ride that
|
|
171
|
-
on-brand color rather than the quiet-text tokens). An unsupported value, including a raw
|
|
172
|
-
attribute/property write outside this type, clamps to the `'outlined'` default
|
|
173
|
-
- `placement: 'top' | 'bottom' = 'bottom'` (reflected; flip/shift can still keep the listbox in view)
|
|
174
|
-
- `positioningStrategy: PlaceStrategy = 'fixed'` (attribute `positioning-strategy`, reflected) —
|
|
175
|
-
the CSS positioning scheme the listbox is laid out with, spelled the same as on `lr-select`,
|
|
176
|
-
`lr-popover`, `lr-dropdown`, `lr-tooltip` and `lr-color-picker`. `fixed` is this control's
|
|
177
|
-
default and what it has always rendered: it positions against the viewport and escapes most
|
|
178
|
-
clipping ancestors, which suits a typeahead list that usually sits inside a scrollable region;
|
|
179
|
-
under a transformed, filtered or contained ancestor the listbox is promoted into the browser top
|
|
180
|
-
layer where the native Popover API exists (otherwise, as before, that ancestor clips it).
|
|
181
|
-
`absolute` positions against the nearest containing block and scrolls with it. An unsupported
|
|
182
|
-
value resolves to the default. Like `placement`, a change takes effect the next time the listbox
|
|
183
|
-
opens. When the instance sets nothing, the cascading `--lr-positioning-strategy` custom property
|
|
184
|
-
(`absolute`/`fixed`, set on `:root`, a theme, or one clipping ancestor) is honoured ahead of the
|
|
185
|
-
default; an explicit instance value always wins. There is deliberately no `hoist` alias here:
|
|
186
|
-
unlike on `lr-select`, where it is Shoelace's established spelling, it would be a boolean
|
|
187
|
-
defaulting to `true`, so its attribute could only ever express the value the control already has
|
|
188
|
-
- `sync?: PlaceSync` (reflected) — copies the trigger's width, height, or both onto the listbox,
|
|
189
|
-
spelled the same as on `lr-dropdown`/`lr-popup`/`lr-popover`. Unset (the default), the listbox
|
|
190
|
-
sizes to its own content, clamped between `--lr-size-12rem` and `--lr-size-28rem`, exactly as
|
|
191
|
-
before. `sync="width"` drops that content-based clamp so a full-width trigger with short option
|
|
192
|
-
labels gets a listbox that aligns to its own edges instead of floating narrower in the middle.
|
|
193
|
-
A synced listbox is capped on `--lr-positioner-available-inline-size` **alone** — the space the
|
|
194
|
-
positioner actually measured beside the anchor, which still keeps an over-wide trigger from
|
|
195
|
-
pushing it off-screen. `--lr-popover-viewport-clamp` does **not** apply to a synced listbox — its
|
|
196
|
-
92vw default used to shorten the listbox against its own trigger in exactly the full-width case
|
|
197
|
-
`sync` exists for, and `lr-popup` never applied it either. It still applies with `sync` unset.
|
|
198
|
-
Like `placement`, a change takes effect the next time the listbox opens
|
|
199
|
-
- `clearable: boolean = false` (reflected) — displays the clear button while there is something to
|
|
200
|
-
clear on **either** axis this control owns: a committed selection, or _visible_ filter text. See
|
|
201
|
-
"the clear button covers two axes" below
|
|
202
|
-
- `withClear: boolean = false` (attribute `with-clear`) — Web Awesome's spelling of `clearable`;
|
|
203
|
-
either one enables the same clear button. Not deprecated: Web Awesome names this attribute
|
|
204
|
-
`with-clear` and Shoelace names it `clearable`, so honouring both is what keeps a mechanical tag
|
|
205
|
-
rename from silently dropping the control
|
|
206
|
-
- `withLabel: boolean = false` and `withHint: boolean = false` (attributes `with-label` and
|
|
207
|
-
`with-hint`) — SSR slot-presence hints
|
|
208
|
-
- `getTag: ((option: LyraOption, index: number) => unknown) | undefined` (attribute: false) —
|
|
209
|
-
replaces a built-in multiple-selection tag with consumer Lit/DOM/text output; strings render as
|
|
210
|
-
text, never as HTML
|
|
211
|
-
- `validators: LyraComboboxValidator[] = []` (attribute: false) — extra JavaScript validators run
|
|
212
|
-
after the intrinsic `required` constraint, the same contract `lr-date-input` implements. Each
|
|
213
|
-
entry may be a `(value, input) => void | boolean | string | ValidityStateFlags` function (`value`
|
|
214
|
-
is the live `string | string[]`), an object with `validate(value, input)` returning that same
|
|
215
|
-
vocabulary, or a Web Awesome-compatible object with `checkValidity(input)` returning
|
|
216
|
-
`{ isValid, message, invalidKeys }`, where `invalidKeys` names `ValidityState` flags; that object
|
|
217
|
-
may also expose `observedAttributes` and a string or callback `message`. Changing any listed host
|
|
218
|
-
attribute revalidates automatically. `isValid: true` (or `true`/`undefined` from a function)
|
|
219
|
-
passes; otherwise the listed flags are set (`customError` when the list maps to nothing) and the
|
|
220
|
-
returned message wins over the validator-level fallback. A throwing validator fails closed with
|
|
221
|
-
the localized generic message. `disabled` bars them exactly as it bars `required`
|
|
222
|
-
- `validationTarget: HTMLElement | undefined` — writable native-validity focus anchor. After the
|
|
223
|
-
first render it defaults to the internal filter input; assign another element to override it, or
|
|
224
|
-
assign `undefined` to restore that input. It is `undefined` before the input exists
|
|
225
|
-
- `autocomplete: string = 'off'`, `inputMode: string = ''` (attribute `inputmode`),
|
|
226
|
-
`enterKeyHint: string = ''` (attribute `enterkeyhint`), `spellcheck: boolean = false`,
|
|
227
|
-
`autocapitalize: string = ''`, and `autocorrect: boolean = true` (attribute values `on`/`off`) —
|
|
228
|
-
native editing-assistance properties forwarded to the internal filter input. Removing a
|
|
229
|
-
`spellcheck` attribute after an override restores this component's declared `false` default.
|
|
230
|
-
The lowercase
|
|
231
|
-
mapped IDLs `inputmode` and `enterkeyhint` delegate to the corresponding camel-case native
|
|
232
|
-
properties
|
|
233
|
-
- `inputValue: string` — the live filter input text; programmatic writes are event-silent
|
|
234
|
-
- `maxOptionsVisible: number = 3` (attribute `max-options-visible` — caps how many selected **tags**
|
|
235
|
-
show before collapsing to `+N`; nothing to do with the suggestion list, see the three-caps note
|
|
236
|
-
below)
|
|
237
|
-
- `visibleOptions?: number` (attribute `visible-options`, new in 11.0.0) — bounds the popup to about
|
|
238
|
-
this many suggestion rows, leaving the rest reachable by scrolling. Purely presentational: every
|
|
239
|
-
row is still rendered. Measured from where row N actually starts rather than computed from a
|
|
240
|
-
token, because a row's height varies with `sub` lines, adornments and group labels. Unset imposes
|
|
241
|
-
no bound of its own and the listbox keeps exactly its previous max-height behavior; zero,
|
|
242
|
-
negative, and non-finite values normalize to unset rather than collapsing the popup
|
|
243
|
-
|
|
244
|
-
**The three caps, which are easy to confuse.** `visibleOptions` caps how many suggestion rows are
|
|
245
|
-
*visible* (presentation; the rest scroll). `maxRender` caps how many suggestion rows are *rendered
|
|
246
|
-
at all* (performance; the rest do not exist and are summarized by `option-overflow`).
|
|
247
|
-
`maxOptionsVisible` caps how many *selected tags* show in multi-select and never touches the
|
|
248
|
-
suggestion list.
|
|
249
|
-
- `emptyText?: string` (attribute `empty-text`) — omission displays localized `noMatches` (`"No
|
|
250
|
-
matches"` in the built-in English locale); any supplied string, including `''`, renders verbatim
|
|
251
|
-
- `loadingText?: string` (attribute `loading-text`) — shown while a `source` fetch is in flight,
|
|
252
|
-
and in place of the raw value on the trigger/tag for a committed value the fetch has never yet
|
|
253
|
-
resolved (see "Unknown committed values" below); omission displays localized `loading`
|
|
254
|
-
(`"Loading…"` in English), while any supplied string, including `''`, renders verbatim
|
|
255
|
-
- `overflowText?: string` (attribute `overflow-text`) — shown when `maxRender` caps the rows;
|
|
256
|
-
omission displays localized `comboboxOverflow` (`"+{n} more — refine your search"` in English).
|
|
257
|
-
A supplied template wins verbatim over `.strings`, including when it equals that English
|
|
258
|
-
template or is empty; `{n}` is still replaced with the locale-formatted hidden count
|
|
259
|
-
- `filter: OptionFilter | null = null` (attribute: false — `(option, query) => boolean`; default
|
|
260
|
-
matches `label`/`searchText` case-insensitively; ignored while `source` is set)
|
|
261
|
-
- `source: ComboboxSource | null = null` (attribute: false — `(query: string, options: { signal:
|
|
262
|
-
AbortSignal; limit: number }) => Promise<readonly ComboboxSourceRow[] | { rows, total? }>`; when set, replaces the light-DOM `<lr-option>`
|
|
263
|
-
list with an async lookup, debounced by `sourceDelay` ms after each keystroke and re-run on
|
|
264
|
-
clear/pick. Forward `options.signal` to `fetch(url, { signal })` to cancel the request when a
|
|
265
|
-
newer query supersedes it or the element disconnects. `loadingText` is shown while a call is in
|
|
266
|
-
flight; a stale in-flight call that resolves after a newer one (or after disconnect) is dropped
|
|
267
|
-
via a monotonic token. The exported type requires the `options` parameter; an existing
|
|
268
|
-
one-parameter `(query) => …` function remains assignable under TypeScript's ordinary function
|
|
269
|
-
parameter compatibility, but consumers that need cancellation should accept and forward
|
|
270
|
-
`options.signal`; honor `options.limit` when practical and return `{ rows, total }` to report the
|
|
271
|
-
provider-side match count. The component independently clone-normalizes the result, retains at
|
|
272
|
-
most 2,000 rows/250,000 aggregate text units, skips malformed or hostile rows, and never trusts a
|
|
273
|
-
provider to enforce the ceiling. A current rejection clears stale rows and renders a localized disabled
|
|
274
|
-
listbox row; that visible row is not a shadow live region. The same localized message is appended
|
|
275
|
-
to `[data-lr-live-region="assertive"]` in the document for each fresh post-mount rejection,
|
|
276
|
-
including an identical retry, while raw caught error text stays out of the UI.)
|
|
277
|
-
- `sourceDelay: number = 200` (attribute `source-delay` — debounce in ms between the last keystroke
|
|
278
|
-
and the `source` call; `0` fires on every keystroke. Sanitized to a finite non-negative duration,
|
|
279
|
-
falling back to `200` for a non-finite value)
|
|
280
|
-
- `maxRender: number = 200` (attribute `max-render` — caps how many rows render at once, always
|
|
281
|
-
keeping the current selection visible even if it's outside the cap; the excess renders as one
|
|
282
|
-
`overflowText` row instead of being dropped silently. See "Large option lists" below for how to
|
|
283
|
-
size it, and when `source` is the better answer. Runtime writes are capped at 1,000)
|
|
284
|
-
- `sourceTotal: number` (read-only) — provider-side total for the latest accepted response
|
|
285
|
-
- `sourceTruncated: boolean` (read-only) — whether the response reported or contained more rows
|
|
286
|
-
than the bounded retained snapshot; the overflow row includes that hidden count
|
|
287
|
-
- `value: string | string[]` — a getter/setter: plain `string` in single mode, `string[]` in
|
|
288
|
-
`multiple` mode. Assigning `undefined`/`null` clears the selection; every string, including `''`,
|
|
289
|
-
is instead a candidate value resolved against the current local options/async rows — an
|
|
290
|
-
`<lr-option value="">` (or a matching async row) is legitimate and round-trips like any other
|
|
291
|
-
value. A value matching no current option/row still commits rather than being dropped or treated
|
|
292
|
-
as a clear — see "Unknown committed values" below
|
|
293
|
-
- `customError: string | null` (attribute `custom-error`) — reflected consumer validation message
|
|
294
|
-
- `selectedRows` (read: `ComboboxSourceRow[]`; write: `readonly ComboboxSourceRow[]`) — structured
|
|
295
|
-
rows for the current selection, including any opaque `data` payload supplied by an async source
|
|
296
|
-
row or a light-DOM `<lr-option data>`, reached by reference and never deep-cloned.
|
|
297
|
-
Reads return detached row snapshots. Writes select stable row values resolved against the
|
|
298
|
-
current or deferred source, dropping duplicate and detached values without emitting selection
|
|
299
|
-
events. Selected async rows remain available after the query changes or a later source result
|
|
300
|
-
no longer contains them
|
|
301
|
-
- `selectionStart`, `selectionEnd`, and `selectionDirection` — selection getters/setters forwarded
|
|
302
|
-
to the internal input
|
|
303
|
-
|
|
304
|
-
**Unknown committed values.** A committed value matching no current option/row (a stale value from
|
|
305
|
-
a removed option, or a programmatic assignment with a typo) still commits — the raw string stays
|
|
306
|
-
fully reachable through `value`/`selectedRows` — but renders a dashed/italic
|
|
307
|
-
`[part='unknown-value']` badge next to the closed single-select input, or on the relevant
|
|
308
|
-
`multiple`-mode tag, instead of an unexplained bare label, mirroring `<lr-model-select>`'s synthetic
|
|
309
|
-
"not in catalog" stale-value row — see `--lr-combobox-unknown-value-border-style`/`-color` below.
|
|
310
|
-
The badge is suppressed while an async `source` fetch has never yet resolved for this element —
|
|
311
|
-
from mount through the debounce delay and the in-flight call itself — and never shown for an
|
|
312
|
-
`allowCustomValue` commit, which is a sanctioned unmatched value, not a stale one. Over that same
|
|
313
|
-
unresolved window the raw value itself is also withheld: the trigger (and any `multiple`-mode tag
|
|
314
|
-
for the same value) shows the `loadingText` placeholder instead, so a value seeded before its async
|
|
315
|
-
catalogue has ever answered never flashes an unexplained, unbadged machine key. Once the fetch
|
|
316
|
-
settles — success or failure — the raw value returns, badged if it still matches nothing.
|
|
317
|
-
|
|
318
|
-
**Methods:** `focus(options?)`, `blur()`, `select()`, `setSelectionRange()`, and `setRangeText()`
|
|
319
|
-
forward to the internal input. `setRangeText()` synchronizes the filter query and visible options.
|
|
320
|
-
`show(): Promise<void>` and `hide(): Promise<void>` settle after `lr-after-show` and
|
|
321
|
-
`lr-after-hide`, respectively. `refresh(): void` re-runs the current `source` query without changing
|
|
322
|
-
the source's identity, its debounce controller, or its delay. Reassigning `source` is a provider
|
|
323
|
-
change and clears the fetched rows and pending-selection cache; `refresh()` does not. It queues for
|
|
324
|
-
the next open while the listbox is closed, and is a no-op without a `source`. `resetValidity()`
|
|
325
|
-
clears consumer custom validity and restores the current intrinsic constraints. `getForm()` returns the owning form, including an external owner
|
|
326
|
-
selected by the `form` attribute.
|
|
327
|
-
`setCustomValidity(message)` carries a rejection no client-side constraint can express ("that option
|
|
328
|
-
is no longer available"): a non-empty message raises `customError`, becomes `validationMessage`, and
|
|
329
|
-
blocks submission; `''` clears it and restores the control's own computed validity, so a `required`
|
|
330
|
-
combobox with nothing chosen goes back to `valueMissing` rather than to valid. The message survives
|
|
331
|
-
every selection change and a `form.reset()` — like a native control, only another
|
|
332
|
-
`setCustomValidity('')` or `resetValidity()` clears it — and is used verbatim, never localized.
|
|
333
|
-
|
|
334
|
-
**8.0 migration:** the former camel-case string property `autoCorrect` is not retained as a public
|
|
335
|
-
alias. Set the boolean `autocorrect` IDL, or use `autocorrect="on"` / `autocorrect="off"` in markup.
|
|
336
|
-
|
|
337
|
-
`ComboboxSourceRow = { readonly value: string; readonly label: string; readonly sub?: string; readonly icon?: unknown; readonly start?: unknown;
|
|
338
|
-
readonly end?: unknown; readonly badge?: string |
|
|
339
|
-
number; accessibleLabel?: string; data?: unknown; dotColor?: string; group?: string; disabled?:
|
|
340
|
-
boolean }` — the row shape used by the async `source` path. `start` and `end` (new in 11.0.0) are
|
|
341
|
-
the async counterparts of `<lr-option>`'s `start`/`end` adornment slots and render as the
|
|
342
|
-
`option-start` / `option-end` parts, inert and aria-hidden exactly like `icon`. A string or number `start`/`end` truncates with an ellipsis when it outgrows the part; template content should render an element at its top level to do the same. `icon` renders as a decorative leading
|
|
343
|
-
visual whose rendered subtree stays visible but is inert and hidden from assistive technology;
|
|
344
|
-
put independent actions outside it. `badge` renders as trailing metadata, `accessibleLabel` can
|
|
345
|
-
provide richer spoken text than the visible label, and `data` is retained without being rendered
|
|
346
|
-
for retrieval through `selectedRows`.
|
|
347
|
-
`dotColor` accepts a valid CSS `color`; invalid values, declaration-breaking input, and `url()`
|
|
348
|
-
render a transparent dot.
|
|
349
|
-
The light-DOM `<lr-option>` path normalizes its supported label/sub/dot/group/data fields to the
|
|
350
|
-
same internal row model — `<lr-option data>` is the light-DOM counterpart of an async row's own
|
|
351
|
-
`data` field, reached by reference through `selectedRows` exactly the same way.
|
|
352
|
-
|
|
353
|
-
When a local option is removed or becomes disabled, or an async response shrinks, an existing
|
|
354
|
-
keyboard-active row clamps to the nearest enabled survivor. If every row is disabled or removed,
|
|
355
|
-
`aria-activedescendant` clears; an untouched list with no active row remains untouched.
|
|
356
|
-
|
|
357
|
-
**Events:** typing in the filter exposes the original bubbling/composed, non-cancelable `InputEvent`
|
|
358
|
-
as exactly one host `input` event (no `value` detail) and does not fire `change`. An actual user
|
|
359
|
-
selection mutation — pointer or keyboard selection, multiple-value toggle, tag/Backspace removal, or
|
|
360
|
-
clear — emits exactly one bubbling/composed, non-cancelable `input` `CustomEvent`, immediately
|
|
361
|
-
followed by the same shape of `change`, then a prefixed `lr-change` alias. All three carry
|
|
362
|
-
`detail: { value; data: readonly unknown[] }` — `value` is the new committed selection (a string in
|
|
363
|
-
single mode, a `string[]` in `multiple` mode); `data` is index-aligned with `value`: `data[i]`
|
|
364
|
-
describes `value[i]` — the opaque `data` payload of a light-DOM `<lr-option data>` or an async
|
|
365
|
-
source row's own `data`, reached by reference and never deep-cloned — or `undefined` in that
|
|
366
|
-
value's own slot when it currently matches no live row/option (see "Unknown committed values"
|
|
367
|
-
above; unlike `selectedRows`, which drops that entry instead). `lr-change` mirrors `<lr-checkbox>`'s namespaced alias; subscribe to it when you
|
|
368
|
-
want a `lr-`-prefixed event, or to the native-style `input`/`change` for parity with a native
|
|
369
|
-
control. Re-picking the current single value and programmatic/default/reset/restore writes are
|
|
370
|
-
silent (including on `lr-change`). The clear button emits one `lr-clear` after its
|
|
371
|
-
`input`/`change`/`lr-change` triple.
|
|
372
|
-
`lr-activate` (`detail: { value: string }`, bubbling/composed, non-cancelable) fires on **every**
|
|
373
|
-
activation of an available listbox row — a click, or Enter on the active row — whether or not the
|
|
374
|
-
selection actually moved. Its `value` is the activated option's own value, **always a single
|
|
375
|
-
string**, even in `multiple` mode, where the `input`/`change`/`lr-change` triple carries the whole
|
|
376
|
-
`string[]` instead. It reports that the user picked a row and gates nothing. Use it for the
|
|
377
|
-
single-select repeat pick that `change`/`lr-change` deliberately stay silent for — "re-run that
|
|
378
|
-
filter" is a real intent — which is otherwise unobservable, because the rows live in this shadow
|
|
379
|
-
root, so a retargeted `click` names no option and a keyboard commit produces no click at all. When
|
|
380
|
-
an activation _does_ move the selection, `input`/`change`/`lr-change` are emitted first, so either
|
|
381
|
-
listener reads the settled selection. Not fired for typing, for a committed custom value matching no
|
|
382
|
-
row, for the clear button, or for a programmatic `value` assignment.
|
|
383
|
-
`lr-filter` (`detail: { value: string }`) reports the in-progress filter text on every user-driven
|
|
384
|
-
keystroke — the live as-you-typed search string, deliberately _not_ `value`, which is the committed
|
|
385
|
-
selection. It is the supported way to read that text; reaching into the shadow root for
|
|
386
|
-
`[part="combobox-input"]`'s value is not. Named `lr-filter` rather than `lr-input` precisely because
|
|
387
|
-
`lr-input`'s detail on `<lr-input>` is the committed value, and the two must not share a name while
|
|
388
|
-
carrying different strings. It fires for user edits only. Picking a row, `form.reset()`, dismissing
|
|
389
|
-
the listbox, and a programmatic `value` write blank the filter silently. `setRangeText()` silently
|
|
390
|
-
replaces the requested or selected native text range, synchronizes the resulting query and visible
|
|
391
|
-
options, and refreshes an async source when present; it preserves the committed selection.
|
|
392
|
-
Activating the clear button is a user edit: when a query existed it emits `lr-filter` with
|
|
393
|
-
`value: ''` before the clear transaction finishes.
|
|
394
|
-
`lr-show` and `lr-hide` report the start of listbox visibility transitions. `lr-show` is a
|
|
395
|
-
cancelable veto point; `lr-hide` is cancelable while connected, but the disconnect-driven close is
|
|
396
|
-
non-cancelable because an already-removed control cannot honour a veto. Vetoing a connected close
|
|
397
|
-
is atomic: the filter query, active option, async result rows, reflected `open` state, and overlay
|
|
398
|
-
ownership remain unchanged, so the host can defer dismissal without reconstructing the search.
|
|
399
|
-
`lr-after-show` and
|
|
400
|
-
`lr-after-hide` fire when the corresponding transition settles. `lr-create` carries
|
|
401
|
-
`detail: { inputValue }` and is also cancelable: preventing it suppresses the default append/select
|
|
402
|
-
action so the host can normalize and commit its own option.
|
|
403
|
-
The internal input's `focus` and `blur` are relayed exactly once from the host as owner-realm
|
|
404
|
-
native `FocusEvent`s. Both bubble, cross the shadow boundary, and preserve `relatedTarget`.
|
|
405
|
-
`lr-invalid` (no detail) is emitted once as a bubbling/composed, **cancelable** alias when native
|
|
406
|
-
validity fails — see "The validity alias is cancelable in 8.0.0" above.
|
|
407
|
-
`lr-source-error` is non-cancelable, `detail: { error, query }` carrying the raw rejection from an
|
|
408
|
-
async `source` call plus the exact query string that call was made with (the rejected call's own
|
|
409
|
-
query, not necessarily the live `query`/`inputValue`, which may have moved on — or been cleared by
|
|
410
|
-
closing the listbox — by the time the rejection settles). The rendered copy stays localized and
|
|
411
|
-
never shows the raw error.
|
|
412
|
-
`lr-retry` is cancelable; the built-in failed-load action calls `refresh()`, and `preventDefault()`
|
|
413
|
-
leaves the failure on screen. While the failure state is the only popup content, the popup swaps
|
|
414
|
-
`role="listbox"` for `role="dialog"` (the input gains the matching `aria-haspopup="dialog"` and
|
|
415
|
-
drops `aria-activedescendant`, and the popup carries the localized failure heading as its accessible
|
|
416
|
-
name). `dialog` is one of the four popup roles WAI-ARIA lets a `role="combobox"` own, so the still
|
|
417
|
-
expanded `aria-controls` target keeps a valid owner while holding a retry `button` that is not a
|
|
418
|
-
legal listbox child. A successful retry restores `role="listbox"`.
|
|
419
|
-
|
|
420
|
-
**The clear button covers two axes, and announces only the one that moved.** A combobox owns both a
|
|
421
|
-
committed selection and an in-progress filter query, so the button renders whenever either has
|
|
422
|
-
something to clear, and one press clears both:
|
|
423
|
-
|
|
424
|
-
- Clearing a selection emits `input`, then `change`, then `lr-change`, then `lr-clear` — and, if the
|
|
425
|
-
query was also non-empty, `lr-filter` with an empty `value`.
|
|
426
|
-
- A **query-only** clear (nothing selected, just typed text) emits `lr-filter` with an empty
|
|
427
|
-
`value` and deliberately **no** `change` and **no** `lr-clear`. There was no selection
|
|
428
|
-
transition to report, so announcing one would be a lie. Don't listen for `lr-clear` to detect
|
|
429
|
-
"the user emptied the field" — listen for `lr-filter` when you care about the query.
|
|
430
|
-
- The query half of the render gate is scoped to states where the query is actually _visible_: an
|
|
431
|
-
open listbox in single-select, or any time in `multiple` mode. A closed single-select shows the
|
|
432
|
-
selected label rather than the query, so a stale query alone never surfaces a button offering to
|
|
433
|
-
clear text the user cannot see.
|
|
434
|
-
|
|
435
|
-
**Large option lists: sizing `maxRender`, and why the listbox is not windowed.** Every row that
|
|
436
|
-
survives the filter and fits under `maxRender` is a real DOM element — the listbox renders its rows
|
|
437
|
-
in full rather than recycling a small window of them across a scroll. That is a deliberate
|
|
438
|
-
accessibility constraint, not an omission: the filter input carries `aria-activedescendant`, which
|
|
439
|
-
is an **IDREF**, and an IDREF only resolves within its own tree scope. Moving the rows into the
|
|
440
|
-
library's windowing primitive (`<lr-virtual-list>`) would place them one shadow root deeper than the
|
|
441
|
-
input that has to point at them, where neither the attribute nor its `ariaActiveDescendantElement`
|
|
442
|
-
element-reflection replacement can reach — element reflection resolves same-root or upward only. So
|
|
443
|
-
`<lr-virtual-list>` stays `role="list"`/`role="listitem"` and is the right tool for feeds and
|
|
444
|
-
viewers, not for a listbox whose active row must stay addressable.
|
|
445
|
-
|
|
446
|
-
What that means in practice:
|
|
447
|
-
|
|
448
|
-
- **Up to a few hundred rows, raise `maxRender` and move on.** A few hundred flex rows is an
|
|
449
|
-
unremarkable amount of DOM; a country, currency, or time-zone list (typically 200–450 entries)
|
|
450
|
-
renders comfortably with `max-render` set to cover it. The cap exists to bound pathological
|
|
451
|
-
cases, not to make lists of that size expensive.
|
|
452
|
-
- **Past roughly a thousand rows, reach for `source` instead of a larger cap.** An async source
|
|
453
|
-
narrows the candidate set before it ever becomes DOM, which is a categorically better trade than
|
|
454
|
-
rendering everything and asking the browser to lay it out. Pair it with `sourceDelay` to debounce.
|
|
455
|
-
- **Leave the overflow row doing its job.** When the cap does bite, the excess collapses into one
|
|
456
|
-
`overflowText` row (default `"+{n} more — refine your search"`) rather than disappearing
|
|
457
|
-
silently, and the current selection is always kept visible even when it falls outside the cap.
|
|
458
|
-
Suppressing that row by setting `maxRender` far above the real list size trades a useful "keep
|
|
459
|
-
typing" affordance for layout work no user asked for.
|
|
460
|
-
|
|
461
|
-
**Slots:** default (`<lr-option>` children), `label`, `hint`, `error` (overrides the `errorText`
|
|
462
|
-
attribute when provided), plus two adornment slots:
|
|
463
|
-
|
|
464
|
-
- `start` — content at the inline-start of the trigger row, before the selected-value tags and the
|
|
465
|
-
filter input. It is decorative chrome, **not** an option: only `<lr-option>` elements in the
|
|
466
|
-
default slot are ever collected into the option list.
|
|
467
|
-
- `end` — content after the filter input and the built-in clear action, and before the expand icon,
|
|
468
|
-
so consumer content never sits outboard of the dropdown chevron.
|
|
469
|
-
- `clear-icon` and `expand-icon` replace the corresponding built-in glyphs.
|
|
470
|
-
- `source-error` — replaces the built-in failed-`source` state, retry control included. Deliberately
|
|
471
|
-
named apart from the form-control `error` slot: they are different failures and a field has to be
|
|
472
|
-
able to show both.
|
|
473
|
-
|
|
474
|
-
**CSS parts:** `form-control`, `form-control-label`, `label`, `form-control-input`, `combobox`,
|
|
475
|
-
`start` and `end` (the two
|
|
476
|
-
adornment-slot wrappers, each `hidden` while nothing is slotted into it), `tags`, `tag`,
|
|
477
|
-
`tag-overflow` (the "+N" indicator, carried alongside `tag` past `max-options-visible`,
|
|
478
|
-
mirroring `lr-select`'s identical two-part overflow chip), `tag-label`, `tag__content`,
|
|
479
|
-
`tag__remove-button`, `tag__remove-button__base`, `combobox-input`,
|
|
480
|
-
`clear-button`, `unknown-value` (the dashed/italic badge shown next to the closed single-select
|
|
481
|
-
input, or on a `multiple`-mode tag, when the committed value matches no current option/row),
|
|
482
|
-
`expand-icon`, `listbox`,
|
|
483
|
-
`group-label` (the heading of an option group — rows sharing a `group` — named as on `lr-select` and
|
|
484
|
-
`lr-emoji-picker` so one rule styles every grouped list; it labels the `role="group"` wrapper here),
|
|
485
|
-
`option`,
|
|
486
|
-
`option-dot` (the leading status dot, when a row's `dotColor` is set), `option-icon` (the inert,
|
|
487
|
-
aria-hidden decorative leading visual for an async row), `option-start` and `option-end` (the inert,
|
|
488
|
-
aria-hidden adornments cloned from the source option's `start`/`prefix` and `end`/`suffix` slots, or
|
|
489
|
-
from an async row's `start`/`end`), `option-label`, `option-sub` (a row's
|
|
490
|
-
secondary line, when `sub` is set), `option-badge` (an async row's trailing metadata),
|
|
491
|
-
`option-overflow` (the "+N more" indicator from `maxRender`),
|
|
492
|
-
`source-error-row` (the listbox row holding the failed-`source` state), `source-error` (the shared
|
|
493
|
-
failed-load state itself, with `source-error-base`, `source-error-icon`, `source-error-heading`,
|
|
494
|
-
`source-error-description` and `source-error-actions` forwarded from the composed `<lr-empty>`),
|
|
495
|
-
`retry-button`, `error`, `hint`
|
|
496
|
-
|
|
497
|
-
**TypeScript:** `LyraCombobox<Multiple extends boolean = boolean>` — `value`/`defaultValue` and the
|
|
498
|
-
`lr-change`/native `input` event detail `value` narrow to `string` when `Multiple` is `false` and
|
|
499
|
-
`string[]` (`readonly string[]` in a detail) when `true`. Types only; the runtime and the mirrored
|
|
500
|
-
surface are unchanged, and an untyped `<lr-combobox>` keeps `string | string[]`. Combobox has no
|
|
501
|
-
dedicated `lr-input` custom event (unlike `lr-select`); the exported `LyraComboboxChangeEvent`/
|
|
502
|
-
`LyraComboboxInputEvent` aliases type `lr-change` and the native `input` listener respectively.
|
|
503
|
-
|
|
504
|
-
**The required marker.** `required` with a non-empty `label` paints the library's shared marker on
|
|
505
|
-
`[part="form-control-label"]` — the one `::after` rule described above, not a copy of it, so
|
|
506
|
-
`--lr-form-control-required-content`, `--lr-form-control-required-color` and
|
|
507
|
-
`--lr-form-control-required-offset` retune or suppress it here exactly as they do on `lr-input`.
|
|
508
|
-
With no label text the part is hidden and no glyph is painted.
|
|
509
|
-
|
|
510
|
-
**Themeable custom properties:** `--lr-combobox-trigger-padding`,
|
|
511
|
-
`--lr-combobox-trigger-min-height`, `--lr-combobox-font-size`, `--lr-combobox-tag-padding`,
|
|
512
|
-
`--lr-combobox-tag-font-size`, `--tag-max-size` (default `var(--lr-size-5rem)`), `--show-duration`,
|
|
513
|
-
`--hide-duration`, and `--lr-combobox-expand-size` (the decorative icon box; each
|
|
514
|
-
standard size supplies an aligned default), plus shared tokens. `--lr-combobox-gap` (default
|
|
515
|
-
`--lr-space-xs`, the gap inside `[part='combobox']`) and `--lr-combobox-radius` (default
|
|
516
|
-
`--lr-radius`, its corner radius) are both retunable without a `::part(combobox)` rule but, unlike
|
|
517
|
-
the properties above, do not vary by `size` — the same `--lr-button-gap`/`-radius` pattern.
|
|
518
|
-
`--lr-combobox-tag-bg` (default `var(--lr-color-brand-quiet)`), `--lr-combobox-tag-color` (default
|
|
519
|
-
`var(--lr-color-text)`) and `--lr-combobox-tag-radius` (default `var(--lr-radius)`) retint a
|
|
520
|
-
selected tag's background, text color and corner radius without a `::part(tag)` rule; `-tag-bg` is
|
|
521
|
-
scoped independently of `--lr-combobox-option-active-bg`/`-option-badge-bg` below, which already
|
|
522
|
-
scope the same shared `--lr-color-brand-quiet` token for their own purposes.
|
|
523
|
-
|
|
524
|
-
The trigger row's own surface became public in 16.0.0, having been a private pair until then:
|
|
525
|
-
`--lr-combobox-fill` (default `var(--lr-color-surface)`) and `--lr-combobox-border-color` (default
|
|
526
|
-
`var(--lr-color-border)`). The `filled`/`filled-outlined` treatments default the fill to
|
|
527
|
-
`var(--lr-color-surface-raised)` and `filled` defaults the border to `transparent`; a value set
|
|
528
|
-
here wins over every treatment. `--lr-combobox-open-border-color` (default
|
|
529
|
-
`var(--lr-color-brand)`) recolors the row's edge while it holds focus, which was a hardcoded brand
|
|
530
|
-
border before. Read the name as the state the listbox opens in rather than as a synonym for `open`:
|
|
531
|
-
it is bound to `:focus-within`, so it also paints on a focused row whose listbox is closed — after
|
|
532
|
-
an Escape dismissal, say. It is named for symmetry with `lr-select`'s
|
|
533
|
-
`--lr-select-open-border-color`, which really is gated on `open`. `--lr-combobox-text-color`
|
|
534
|
-
(default `inherit`) recolors the trigger's own text the same way; `appearance="accent"` defaults it
|
|
535
|
-
instead to `var(--lr-color-on-brand)`, keeping placeholder, adornments and tag text readable on the
|
|
536
|
-
loud brand fill.
|
|
537
|
-
|
|
538
|
-
The shared field halo `--lr-form-control-focus-shadow` (default `none`) paints a `box-shadow`
|
|
539
|
-
while this control is focused — one name for every field-shaped control in the library, so a
|
|
540
|
-
halo is configured once instead of per component. It is additive: the focus outline and
|
|
541
|
-
border cue are the accessibility answer to focus and are never replaced by it.
|
|
542
|
-
|
|
543
|
-
`--lr-combobox-option-active-bg` (default `var(--lr-color-brand-quiet)`) recolors the background of
|
|
544
|
-
a hovered or keyboard-active `[part='option']` row — the same per-component indirection
|
|
545
|
-
`lr-select`'s identical `--lr-select-option-active-bg` uses, so a consumer can retheme just this
|
|
546
|
-
row state without hijacking the shared `--lr-color-brand-quiet` token library-wide.
|
|
547
|
-
|
|
548
|
-
The currently-**selected** row (`[part='option'][aria-selected='true']`) has its own matching set:
|
|
549
|
-
`--lr-combobox-option-selected-bg` (default `transparent`), `--lr-combobox-option-selected-border`
|
|
550
|
-
and `--lr-combobox-option-selected-color` (both default `var(--lr-color-brand)`), and
|
|
551
|
-
`--lr-combobox-option-selected-font-weight` (default `var(--lr-font-weight-semibold)`) — the same
|
|
552
|
-
four-token indirection `lr-select`/`lr-model-select` already provide for their own selected row.
|
|
553
|
-
Like the active-bg knob these are inline `var()` fallbacks, not declared on `:host`, so a consumer
|
|
554
|
-
can retheme the selected row without hijacking `--lr-color-brand` library-wide.
|
|
555
|
-
|
|
556
|
-
The listbox popup itself is a floating surface and paints from the **shared overlay-surface family**
|
|
557
|
-
(16.0.0), not from the page surface every card and input reads: `--lr-overlay-surface` (default
|
|
558
|
-
`var(--lr-color-surface-overlay)`), `--lr-overlay-border` (default `var(--lr-color-border)`),
|
|
559
|
-
`--lr-overlay-radius` (default `var(--lr-radius)`) and `--lr-overlay-shadow-anchored` (default
|
|
560
|
-
`var(--lr-shadow-m)`). None is declared on `:host`, so one declaration on `:root` — or on any
|
|
561
|
-
ancestor, to scope it — retints this popup together with every other floating surface, and none of
|
|
562
|
-
it touches the trigger row the popup drops from. The edge deliberately keeps the control tier, `var(--lr-color-border)`, rather than the
|
|
563
|
-
decorative `var(--lr-color-border-subtle)` floating panels default to: this popup belongs to the
|
|
564
|
-
form control it opens from and keeps that control's boundary contrast (see `<lr-popover>`).
|
|
565
|
-
|
|
566
|
-
`--lr-combobox-unknown-value-border-style` (default `dashed`) and
|
|
567
|
-
`--lr-combobox-unknown-value-border-color` (default `var(--lr-color-border)`) retheme the
|
|
568
|
-
`[part='unknown-value']` badge described above under "Unknown committed values".
|
|
569
|
-
`--lr-combobox-option-badge-bg` (default `var(--lr-color-brand-quiet)`) retints the
|
|
570
|
-
`[part='option-badge']` trailing metadata badge on an async row, and the "not in catalog" badge
|
|
571
|
-
`show-unknown-option` renders on the synthetic unmatched-value listbox row — the same
|
|
572
|
-
per-component indirection `lr-select`'s `--lr-select-option-badge-bg` uses.
|
|
573
|
-
|
|
574
|
-
`--lr-combobox-trigger-height` pins an **exact** input-container height (both floors and caps it),
|
|
575
|
-
for pixel-matching an `<lr-input>` or `<lr-select>` in the same toolbar row. It is **undeclared by
|
|
576
|
-
default**, leaving `--lr-combobox-trigger-min-height` as a floor only and the row free to grow —
|
|
577
|
-
see "exact-height hatches" under `lr-input` for why `auto` is not a way to opt back out. Intended
|
|
578
|
-
for a single-row combobox: in `multiple` mode, a tag row long enough to wrap overflows the pinned
|
|
579
|
-
box visibly (nothing is clipped or made unreachable), so leave it unset there.
|
|
580
|
-
|
|
581
|
-
**CSS custom states:** `blank`, `disabled`, `required`, `optional`, `valid`, `invalid`,
|
|
582
|
-
`user-valid`, and `user-invalid`.
|
|
583
|
-
|
|
584
|
-
**Optional peer deps:** none.
|
|
585
|
-
|
|
586
|
-
### `lr-option`
|
|
587
|
-
|
|
588
|
-
**Properties:**
|
|
589
|
-
|
|
590
|
-
- `value: string = ''`
|
|
591
|
-
- `disabled: boolean = false`
|
|
592
|
-
- `defaultSelected: boolean = false` (attribute `selected`; property writes do not reflect) — the
|
|
593
|
-
declarative and `form.reset()` default. Changing it after mount updates the parent's reset
|
|
594
|
-
baseline without overwriting a dirty live selection
|
|
595
|
-
- `selected: boolean = false` (property only) — live selectedness. The parent combobox/select
|
|
596
|
-
updates it as the current value changes; those live writes never rewrite the `selected`
|
|
597
|
-
attribute or `defaultSelected`
|
|
598
|
-
- `group: string = ''` (section header)
|
|
599
|
-
- `searchText: string = ''` (attribute `search-text` — extra text the filter matches beyond the
|
|
600
|
-
visible label)
|
|
601
|
-
- `sub: string = ''` (optional secondary line rendered under the label, e.g. a status/date summary)
|
|
602
|
-
- `dotColor: string = ''` (attribute `dot-color` — optional CSS color for a small leading status
|
|
603
|
-
dot; invalid values, declaration-breaking input, and `url()` render the dot transparently)
|
|
604
|
-
- `data?: unknown` (attribute: false) — opaque application payload, e.g. the backend record this
|
|
605
|
-
option represents. Never read or rendered by this component; retained by reference, never
|
|
606
|
-
deep-cloned, through the owning `lr-combobox`'s `selectedRows` and the owning `lr-select`'s
|
|
607
|
-
`selectedData`, and in both controls' `lr-input`/`lr-change`/`input`/`change` event details.
|
|
608
|
-
Assigning it notifies the owning picker with `lr-option-change`, like `sub`/`dotColor`/`group`
|
|
609
|
-
- `label: string` — settable WA-compatible plain-text label. A non-empty property/attribute wins;
|
|
610
|
-
otherwise it resolves to `defaultLabel`. Property writes stay property-only (no reflection)
|
|
611
|
-
- `defaultLabel: string` (read-only) — normalized accessibility-visible text generated from the
|
|
612
|
-
flattened default slot. Hidden subtrees are excluded, visible nested `aria-label` values replace
|
|
613
|
-
their descendants, and `start`/`end`/`prefix`/`suffix` adornments are excluded. Direct and
|
|
614
|
-
forwarding-slot mutations update the value and notify the owning picker
|
|
615
|
-
|
|
616
|
-
**Method:** `getTextLabel(): string` returns `defaultLabel`, preserving Shoelace's content-derived
|
|
617
|
-
plain-text contract even when a separate WA `label` override is present.
|
|
618
|
-
|
|
619
|
-
**Events:** `lr-option-change` — bubbles when the option's label or selectable data changes so
|
|
620
|
-
its parent `lr-combobox` or `lr-select` can refresh its normalized option rows. It is a private
|
|
621
|
-
child-to-parent refresh signal, not a picker event: the owning `lr-combobox`/`lr-select` consumes
|
|
622
|
-
it and stops it, so it never reaches a listener on the picker host (whose own contract is
|
|
623
|
-
`lr-change`/`lr-input`/`change`/`input`). Listen on the `<lr-option>` itself to observe it.
|
|
624
|
-
|
|
625
|
-
**Slots:** default (visible label), `start`/`end` (WA adornments), and `prefix`/`suffix` (Shoelace
|
|
626
|
-
aliases). `start` and `prefix` project into one leading wrapper; `end` and `suffix` project into one
|
|
627
|
-
trailing wrapper.
|
|
628
|
-
|
|
629
|
-
**CSS parts:** `base`, `checked-icon`, `label`, `start`/`prefix` (same node), and `end`/`suffix`
|
|
630
|
-
(same node). **CSS custom property:** `--current-text-color` (default `var(--lr-color-text)`) colors
|
|
631
|
-
the keyboard-current row. **CSS custom states:** `current` (the host is the roving-focus target),
|
|
632
|
-
`selected`, `disabled`, and `hover` (pointer presence, including drag sessions).
|
|
633
|
-
|
|
634
|
-
Own-anatomy state hooks are `--lr-option-hover-bg`, `--lr-option-active-bg`,
|
|
635
|
-
`--lr-option-current-bg`, `--lr-option-current-color`, `--lr-option-selected-font-weight`, and
|
|
636
|
-
`--lr-option-checked-icon-color`. Their defaults preserve brand-quiet hover/current paint, the
|
|
637
|
-
shared active mix, semibold selected text, and the brand checkmark. The current-color hook falls
|
|
638
|
-
back through upstream `--current-text-color`, so existing themes keep working.
|
|
639
|
-
|
|
640
|
-
The stock `lr-combobox` and `lr-select` intentionally treat each option as light-DOM data and
|
|
641
|
-
render normalized rows in their own shadow roots. Style those rows through the parent's `option`,
|
|
642
|
-
`option-label`, and related parts; the parts above style an option's own anatomy when it is rendered
|
|
643
|
-
by a custom owner.
|
|
644
|
-
|
|
645
|
-
```html
|
|
646
|
-
<lr-combobox id="cb" label="Country" placeholder="Search…" with-clear>
|
|
647
|
-
<lr-option value="fr">France</lr-option>
|
|
648
|
-
<lr-option value="de" search-text="deutschland">Germany</lr-option>
|
|
649
|
-
</lr-combobox>
|
|
650
|
-
<script type="module">
|
|
651
|
-
document
|
|
652
|
-
.getElementById("cb")
|
|
653
|
-
.addEventListener("change", (e) => console.log(e.target.value));
|
|
654
|
-
</script>
|
|
655
|
-
```
|
|
656
|
-
|
|
657
|
-
```html
|
|
658
|
-
<!-- Async data source instead of light-DOM <lr-option> children: -->
|
|
659
|
-
<lr-combobox id="cb2" label="Fruit (async)" with-clear></lr-combobox>
|
|
660
|
-
<script type="module">
|
|
661
|
-
document.getElementById("cb2").source = async (query) => {
|
|
662
|
-
const rows = await fetchFruit(query); // your own lookup
|
|
663
|
-
return rows.map((r) => ({
|
|
664
|
-
value: r.id,
|
|
665
|
-
label: r.name,
|
|
666
|
-
icon: renderFruitIcon(r), // decorative; hidden from assistive technology
|
|
667
|
-
badge: r.category,
|
|
668
|
-
accessibleLabel: `${r.name}, ${r.category}`,
|
|
669
|
-
data: r, // retained in cb2.selectedRows after selection
|
|
670
|
-
}));
|
|
671
|
-
};
|
|
672
|
-
</script>
|
|
673
|
-
```
|
|
674
|
-
|
|
675
|
-
Multi-select submits as **repeated `FormData` entries** (not a joined string) —
|
|
676
|
-
`new FormData(form).getAll(name)` behaves like a native multi-value control. An unnamed multi-select
|
|
677
|
-
(`multiple` with no `name`) contributes nothing to the form at all, matching a nameless native
|
|
678
|
-
`<select multiple>`, rather than falling back to a shared literal key that could collide with
|
|
679
|
-
another unnamed combobox in the same form.
|
|
680
|
-
Session-history/autofill state is stored as a name-independent JSON string array. A valid string
|
|
681
|
-
array restores the selection (single mode keeps its first entry); malformed or wrong-shape state
|
|
682
|
-
restores an empty selection. Restored state wins even when it arrives before the first option
|
|
683
|
-
collection, while `form.reset()` still returns to the declarative selected default. Restoration is
|
|
684
|
-
synchronous and fires no `input`/`change`/`lr-change` event.
|
|
685
|
-
|
|
686
|
-
**Known gotchas:**
|
|
687
|
-
|
|
688
|
-
- `with-clear` and `clearable` are equivalent and both are supported indefinitely; prefer
|
|
689
|
-
`clearable` in new code. `lr-input` and `lr-select` accept both spellings too.
|
|
690
|
-
- a host-level `aria-label` attribute on `<lr-combobox>` now takes priority over `label`/
|
|
691
|
-
`placeholder`/`"Combobox"` when resolving the accessible name on `[part="combobox-input"]` —
|
|
692
|
-
previously it was silently ignored. Matches the same fallback on `<lr-select>`.
|
|
693
|
-
- `aria-required` reflects `required` immediately; `aria-invalid`, by contrast, only reflects
|
|
694
|
-
**after the field has been touched** (first `blur`) — a `required` field with a validity error
|
|
695
|
-
doesn't look invalid to assistive tech before that, by design (avoids flashing invalid styling on
|
|
696
|
-
first render). Blurring the input (Tab away) now also closes an open listbox, the same as a
|
|
697
|
-
native `<select>`'s popup, not just a click outside or Escape.
|
|
698
|
-
- `dotColor`/`sub`/`group` are read from light-DOM `<lr-option>` children as before, but are also
|
|
699
|
-
first-class fields on `ComboboxSourceRow` for the async `source` path — an async lookup can drive
|
|
700
|
-
the same grouped/dot/sub-text rendering a static option list can.
|
|
701
|
-
- `icon`, `badge`, and `accessibleLabel` are async-source row features rather than `<lr-option>`
|
|
702
|
-
properties. Icons are decorative (`aria-hidden`); use `accessibleLabel` when the visible
|
|
703
|
-
label/sub/badge combination needs a fuller spoken name. `data`, by contrast, is a first-class
|
|
704
|
-
`<lr-option>` property too (the light-DOM counterpart of the async row's own `data`): it is
|
|
705
|
-
deliberately opaque, never rendered, and reachable through the read-only `selectedRows` getter
|
|
706
|
-
(combobox), the read-only `selectedData` getter (select), and both controls' `lr-input`/
|
|
707
|
-
`lr-change`/`input`/`change` event details.
|
|
708
|
-
- Full ARIA 1.2 combobox pattern (`role=combobox`, roving `aria-activedescendant`, real DOM focus
|
|
709
|
-
kept on the input) is implemented correctly — a genuine strength, safe to build on.
|
|
710
|
-
`<lr-option value="b" selected>` sets that option's `defaultSelected`, seeds the live selection,
|
|
711
|
-
and supplies the `form.reset()` baseline, mirroring native `<select><option selected>`. A
|
|
712
|
-
later-slotted option or post-mount `defaultSelected` change updates that baseline and updates the
|
|
713
|
-
live selection only while it is pristine. A user pick or direct `option.selected` write makes the
|
|
714
|
-
live selection dirty and never rewrites the declarative `selected` attribute/reset default.
|
|
715
|
-
- The floating listbox participates in Lyra's shared nonmodal overlay stack. Its computed
|
|
716
|
-
`--lr-overlay-stack-index`, Escape owner, outside-pointer dismissal, and focus handoff follow the
|
|
717
|
-
newest open Lyra overlay. Opening it above a color picker (or vice versa) therefore closes only
|
|
718
|
-
the visual top layer per Escape/pointer action rather than both popups.
|
|
719
|
-
|
|
720
|
-
**Additional API surface:**
|
|
721
|
-
|
|
722
|
-
- `part="tag-label"` — The selected-tag label. Kept on one line and truncated with an ellipsis at `--tag-max-size` (default `var(--lr-size-5rem)`), rather than wrapped. Note that `lr-select`'s tag of the same name defaults to `var(--lr-size-12rem)`, so the same `--tag-max-size` value yields a wider tag there.
|
|
723
|
-
|
|
724
|
-
---
|