@aceshooting/lyra-ui 2.0.0 → 2.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/README.md +87 -14
- package/custom-elements.json +11555 -5539
- package/dist/components/app-rail/app-rail-item.d.ts +30 -0
- package/dist/components/app-rail/app-rail-item.js +68 -0
- package/dist/components/app-rail/app-rail-item.styles.d.ts +1 -0
- package/dist/components/app-rail/app-rail-item.styles.js +65 -0
- package/dist/components/app-rail/app-rail.d.ts +21 -24
- package/dist/components/app-rail/app-rail.js +68 -132
- package/dist/components/app-rail/app-rail.styles.js +5 -2
- package/dist/components/attachment-chip/attachment-chip.d.ts +38 -11
- package/dist/components/attachment-chip/attachment-chip.js +73 -29
- package/dist/components/attachment-trigger/attachment-trigger.d.ts +13 -0
- package/dist/components/attachment-trigger/attachment-trigger.js +13 -1
- package/dist/components/attachment-trigger/attachment-trigger.styles.js +28 -6
- package/dist/components/chart/bar-chart.js +2 -18
- package/dist/components/chart/box-plot.d.ts +25 -1
- package/dist/components/chart/box-plot.js +136 -6
- package/dist/components/chart/box-plot.styles.js +13 -0
- package/dist/components/chart/bubble-chart.js +2 -18
- package/dist/components/chart/chart.d.ts +54 -10
- package/dist/components/chart/chart.js +182 -39
- package/dist/components/chart/doughnut-chart.js +2 -18
- package/dist/components/chart/histogram-bin.d.ts +5 -2
- package/dist/components/chart/histogram-bin.js +12 -4
- package/dist/components/chart/histogram.js +8 -16
- package/dist/components/chart/line-chart.js +2 -18
- package/dist/components/chart/lite-chart.d.ts +80 -3
- package/dist/components/chart/lite-chart.js +250 -53
- package/dist/components/chart/pie-chart.js +2 -18
- package/dist/components/chart/polar-area-chart.js +2 -18
- package/dist/components/chart/radar-chart.js +2 -18
- package/dist/components/chart/scatter-chart.js +2 -18
- package/dist/components/chat-composer/chat-composer.d.ts +42 -1
- package/dist/components/chat-composer/chat-composer.js +84 -3
- package/dist/components/chat-composer/chat-composer.styles.js +11 -1
- package/dist/components/chat-message/chat-message.d.ts +32 -4
- package/dist/components/chat-message/chat-message.js +57 -17
- package/dist/components/checkbox/checkbox.d.ts +66 -8
- package/dist/components/checkbox/checkbox.js +173 -50
- package/dist/components/checkbox/checkbox.styles.js +9 -2
- package/dist/components/chip/chip-group.js +8 -2
- package/dist/components/chip/chip.js +9 -2
- package/dist/components/citation-badge/citation-badge.js +21 -3
- package/dist/components/code-block/code-block.d.ts +38 -0
- package/dist/components/code-block/code-block.js +72 -7
- package/dist/components/code-block/code-loader.d.ts +44 -1
- package/dist/components/code-block/code-loader.js +63 -0
- package/dist/components/combobox/combobox.d.ts +46 -5
- package/dist/components/combobox/combobox.js +175 -35
- package/dist/components/combobox/combobox.styles.js +3 -3
- package/dist/components/context-meter/context-meter.js +17 -8
- package/dist/components/context-meter/context-meter.styles.js +0 -6
- package/dist/components/conversation-item/conversation-item.d.ts +13 -16
- package/dist/components/conversation-item/conversation-item.js +29 -20
- package/dist/components/date-picker/calendar-core.d.ts +24 -0
- package/dist/components/date-picker/calendar-core.js +51 -4
- package/dist/components/date-picker/date-input.d.ts +77 -10
- package/dist/components/date-picker/date-input.js +278 -73
- package/dist/components/date-picker/date-picker.d.ts +28 -4
- package/dist/components/date-picker/date-picker.js +95 -37
- package/dist/components/dialog/confirm.js +3 -6
- package/dist/components/dialog/dialog.d.ts +75 -31
- package/dist/components/dialog/dialog.js +147 -131
- package/dist/components/dialog/dialog.styles.js +46 -2
- package/dist/components/dock-panel/dock-panel.d.ts +8 -0
- package/dist/components/dock-panel/dock-panel.js +15 -1
- package/dist/components/dock-panel/dock-panel.styles.js +15 -1
- package/dist/components/document-preview/document-preview.d.ts +15 -7
- package/dist/components/document-preview/document-preview.js +29 -13
- package/dist/components/empty/empty.d.ts +1 -2
- package/dist/components/empty/empty.js +18 -12
- package/dist/components/empty/empty.styles.js +0 -3
- package/dist/components/export-button/export-button.d.ts +12 -0
- package/dist/components/export-button/export-button.js +33 -5
- package/dist/components/export-button/export-button.styles.js +5 -1
- package/dist/components/file-input/accept.js +2 -0
- package/dist/components/file-input/file-input.d.ts +10 -1
- package/dist/components/file-input/file-input.js +36 -5
- package/dist/components/flag/flag.d.ts +1 -1
- package/dist/components/flag/flag.js +15 -1
- package/dist/components/flag/flag.styles.js +1 -1
- package/dist/components/flag/language-map.d.ts +4 -1
- package/dist/components/flag/language-map.js +6 -3
- package/dist/components/gauge/gauge.d.ts +10 -1
- package/dist/components/gauge/gauge.js +28 -9
- package/dist/components/generation-status/generation-status.d.ts +13 -5
- package/dist/components/generation-status/generation-status.js +59 -5
- package/dist/components/graph/graph.d.ts +37 -3
- package/dist/components/graph/graph.js +132 -20
- package/dist/components/heatmap/calendar-grid.d.ts +15 -6
- package/dist/components/heatmap/calendar-grid.js +17 -6
- package/dist/components/heatmap/heatmap-scale.d.ts +5 -1
- package/dist/components/heatmap/heatmap-scale.js +8 -2
- package/dist/components/heatmap/heatmap.d.ts +166 -25
- package/dist/components/heatmap/heatmap.js +372 -89
- package/dist/components/json-viewer/json-viewer.d.ts +22 -5
- package/dist/components/json-viewer/json-viewer.js +115 -37
- package/dist/components/json-viewer/json-viewer.styles.js +2 -1
- package/dist/components/kbd/kbd.js +24 -7
- package/dist/components/live-region/live-region.d.ts +10 -1
- package/dist/components/live-region/live-region.js +42 -6
- package/dist/components/map/map.d.ts +24 -1
- package/dist/components/map/map.js +89 -24
- package/dist/components/markdown/markdown-loader.d.ts +24 -0
- package/dist/components/markdown/markdown-loader.js +35 -1
- package/dist/components/markdown/markdown.d.ts +46 -8
- package/dist/components/markdown/markdown.js +136 -31
- package/dist/components/markdown/markdown.styles.js +7 -1
- package/dist/components/media-card/media-card.d.ts +25 -17
- package/dist/components/media-card/media-card.js +43 -65
- package/dist/components/mention-popover/mention-popover.d.ts +4 -1
- package/dist/components/mention-popover/mention-popover.js +23 -5
- package/dist/components/menu/menu-item.d.ts +32 -3
- package/dist/components/menu/menu-item.js +94 -7
- package/dist/components/menu/menu-item.styles.js +8 -0
- package/dist/components/menu/menu.d.ts +25 -1
- package/dist/components/menu/menu.js +99 -3
- package/dist/components/menu/menu.styles.js +3 -2
- package/dist/components/model-select/model-select.d.ts +48 -3
- package/dist/components/model-select/model-select.js +120 -22
- package/dist/components/model-select/model-select.styles.js +17 -3
- package/dist/components/model-settings-panel/model-settings-panel.d.ts +25 -1
- package/dist/components/model-settings-panel/model-settings-panel.js +65 -1
- package/dist/components/playback/playback.d.ts +6 -2
- package/dist/components/playback/playback.js +72 -29
- package/dist/components/playback/playback.styles.js +6 -1
- package/dist/components/responsive-panel/responsive-panel.d.ts +29 -16
- package/dist/components/responsive-panel/responsive-panel.js +130 -132
- package/dist/components/responsive-panel/responsive-panel.styles.js +1 -1
- package/dist/components/result-card/result-card.d.ts +8 -2
- package/dist/components/result-card/result-card.js +9 -3
- package/dist/components/result-card/result-card.styles.js +0 -1
- package/dist/components/select/select.d.ts +59 -20
- package/dist/components/select/select.js +125 -43
- package/dist/components/skeleton/skeleton.d.ts +3 -0
- package/dist/components/skeleton/skeleton.js +7 -1
- package/dist/components/skeleton/skeleton.styles.js +5 -4
- package/dist/components/slider/slider.d.ts +57 -5
- package/dist/components/slider/slider.js +195 -80
- package/dist/components/source-card/source-card.d.ts +17 -5
- package/dist/components/source-card/source-card.js +55 -8
- package/dist/components/source-card/source-card.styles.js +1 -1
- package/dist/components/source-list/source-list.js +9 -1
- package/dist/components/sparkline/sparkline.js +34 -10
- package/dist/components/split/split.d.ts +141 -14
- package/dist/components/split/split.js +357 -52
- package/dist/components/split/split.styles.js +18 -1
- package/dist/components/stat/stat.d.ts +10 -4
- package/dist/components/stat/stat.js +46 -11
- package/dist/components/stream-status/stream-status.d.ts +27 -11
- package/dist/components/stream-status/stream-status.js +95 -13
- package/dist/components/streaming-text/streaming-text.d.ts +9 -3
- package/dist/components/streaming-text/streaming-text.js +25 -10
- package/dist/components/streaming-text/streaming-text.styles.js +3 -2
- package/dist/components/switch/switch.d.ts +52 -7
- package/dist/components/switch/switch.js +111 -40
- package/dist/components/switch/switch.styles.js +1 -1
- package/dist/components/table/table.d.ts +18 -3
- package/dist/components/table/table.js +88 -20
- package/dist/components/tabs/tabs.d.ts +23 -0
- package/dist/components/tabs/tabs.js +76 -12
- package/dist/components/tabs/tabs.styles.js +11 -0
- package/dist/components/thinking-panel/thinking-panel.d.ts +3 -1
- package/dist/components/thinking-panel/thinking-panel.js +34 -9
- package/dist/components/thinking-panel/thinking-panel.styles.js +6 -0
- package/dist/components/time-range/time-range.d.ts +59 -2
- package/dist/components/time-range/time-range.js +163 -67
- package/dist/components/toast/toast-item.d.ts +12 -3
- package/dist/components/toast/toast-item.js +90 -53
- package/dist/components/toast/toast-item.styles.js +59 -28
- package/dist/components/toast/toast.js +17 -5
- package/dist/components/toast/toaster.js +10 -1
- package/dist/components/tool-approval-dialog/tool-approval-dialog.d.ts +11 -10
- package/dist/components/tool-approval-dialog/tool-approval-dialog.js +50 -124
- package/dist/components/tool-approval-dialog/tool-approval-dialog.styles.js +1 -1
- package/dist/components/tool-call-chip/tool-call-chip.d.ts +18 -2
- package/dist/components/tool-call-chip/tool-call-chip.js +86 -25
- package/dist/components/tool-param-form/tool-param-form.d.ts +89 -28
- package/dist/components/tool-param-form/tool-param-form.js +350 -106
- package/dist/components/tool-result-dialog/tool-result-dialog.d.ts +27 -7
- package/dist/components/tool-result-dialog/tool-result-dialog.js +80 -115
- package/dist/components/tool-result-dialog/tool-result-dialog.styles.js +1 -1
- package/dist/components/tool-result-view/registry.d.ts +0 -8
- package/dist/components/tool-result-view/registry.js +7 -2
- package/dist/components/tool-result-view/tool-result-view.d.ts +6 -3
- package/dist/components/tool-result-view/tool-result-view.js +18 -4
- package/dist/components/tool-select-dialog/tool-select-dialog.d.ts +11 -11
- package/dist/components/tool-select-dialog/tool-select-dialog.js +44 -133
- package/dist/components/tool-select-dialog/tool-select-dialog.styles.js +13 -4
- package/dist/components/tree/tree-node.d.ts +4 -1
- package/dist/components/tree/tree-node.js +4 -1
- package/dist/components/tree/tree.d.ts +17 -8
- package/dist/components/tree/tree.js +21 -20
- package/dist/components/typing-indicator/typing-indicator.d.ts +12 -2
- package/dist/components/typing-indicator/typing-indicator.js +16 -4
- package/dist/components/typing-indicator/typing-indicator.styles.js +2 -2
- package/dist/components/virtual-list/virtual-list.d.ts +56 -19
- package/dist/components/virtual-list/virtual-list.js +184 -38
- package/dist/components/virtual-list/virtual-list.styles.js +12 -0
- package/dist/components/widget/widget.d.ts +5 -4
- package/dist/components/widget/widget.js +54 -120
- package/dist/components/widget/widget.styles.js +8 -2
- package/dist/components/word-cloud/word-cloud-layout.d.ts +7 -0
- package/dist/components/word-cloud/word-cloud-layout.js +20 -6
- package/dist/components/word-cloud/word-cloud.d.ts +22 -2
- package/dist/components/word-cloud/word-cloud.js +45 -6
- package/dist/components/word-cloud/word-cloud.styles.js +13 -1
- package/dist/internal/anchored-validity.d.ts +1 -0
- package/dist/internal/anchored-validity.js +86 -0
- package/dist/internal/form-associated.d.ts +7 -0
- package/dist/internal/form-associated.js +43 -4
- package/dist/internal/numbers.d.ts +12 -0
- package/dist/internal/numbers.js +26 -0
- package/dist/internal/overlay-manager.d.ts +51 -0
- package/dist/internal/overlay-manager.js +523 -0
- package/dist/internal/positioner.js +27 -3
- package/dist/internal/safe-url.d.ts +12 -0
- package/dist/internal/safe-url.js +41 -0
- package/dist/internal/tokens.styles.js +17 -11
- package/dist/lyra.d.ts +5 -2
- package/dist/lyra.js +3 -1
- package/llms-full.txt +970 -426
- package/llms.txt +5 -4
- package/package.json +19 -2
|
@@ -51,7 +51,10 @@ import './menu-item.js';
|
|
|
51
51
|
* Home/End jump to the first/last non-disabled item. Enter/Space activate
|
|
52
52
|
* the focused item. Escape closes and returns focus to the trigger. Tab
|
|
53
53
|
* closes the menu without trapping focus (the browser's own default Tab
|
|
54
|
-
* behavior proceeds untouched).
|
|
54
|
+
* behavior proceeds untouched). A printable keypress runs type-ahead:
|
|
55
|
+
* roving focus jumps to the next non-disabled item whose text starts with
|
|
56
|
+
* the accumulated buffer, cycling from just after the active item (mirrors
|
|
57
|
+
* `<lyra-select>`'s identical listbox type-ahead).
|
|
55
58
|
* - A click outside both the trigger and the open popup closes it (mirrors
|
|
56
59
|
* `<lyra-select>`'s `onDocPointer` exactly) — this does *not* refocus the
|
|
57
60
|
* trigger, since the outside click itself already moved focus somewhere
|
|
@@ -112,6 +115,11 @@ export class LyraMenu extends LyraElement {
|
|
|
112
115
|
this._isFirstUpdate = true;
|
|
113
116
|
this.pendingFocus = 'first';
|
|
114
117
|
this.listId = nextId('menu-list');
|
|
118
|
+
// Standard menu type-ahead, mirroring lyra-select's identical listbox
|
|
119
|
+
// trio: printable keystrokes accumulate into this buffer and reset ~500ms
|
|
120
|
+
// after the last one, so "d" then "e" narrows to "de" instead of
|
|
121
|
+
// restarting the search on every keystroke.
|
|
122
|
+
this.typeAheadBuffer = '';
|
|
115
123
|
this.onDocPointer = (e) => {
|
|
116
124
|
if (!e.composedPath().includes(this))
|
|
117
125
|
this.hide();
|
|
@@ -161,9 +169,19 @@ export class LyraMenu extends LyraElement {
|
|
|
161
169
|
this.reposition();
|
|
162
170
|
};
|
|
163
171
|
this.onItemsSlotChange = (e) => {
|
|
172
|
+
this.itemStateObserver?.disconnect();
|
|
164
173
|
this.items = e.target
|
|
165
174
|
.assignedElements({ flatten: true })
|
|
166
175
|
.filter((el) => el instanceof LyraMenuItem);
|
|
176
|
+
if (typeof MutationObserver !== 'undefined') {
|
|
177
|
+
this.itemStateObserver = new MutationObserver(() => this.onItemStateChange());
|
|
178
|
+
for (const item of this.items) {
|
|
179
|
+
this.itemStateObserver.observe(item, {
|
|
180
|
+
attributes: true,
|
|
181
|
+
attributeFilter: ['disabled', 'hidden', 'aria-hidden'],
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
}
|
|
167
185
|
if (this.activeIndex >= this.items.length)
|
|
168
186
|
this.activeIndex = -1;
|
|
169
187
|
this.applyRovingTabIndex();
|
|
@@ -175,6 +193,24 @@ export class LyraMenu extends LyraElement {
|
|
|
175
193
|
if (this.open && this.activeIndex === -1)
|
|
176
194
|
this.focusRoving(this.pendingFocus);
|
|
177
195
|
};
|
|
196
|
+
/** Rehomes roving focus immediately when an active item becomes disabled or hidden. */
|
|
197
|
+
this.onItemStateChange = () => {
|
|
198
|
+
const navigable = this.items.filter((item) => this.isNavigable(item));
|
|
199
|
+
if (this.activeIndex >= 0 && !this.isNavigable(this.items[this.activeIndex])) {
|
|
200
|
+
const current = this.activeIndex;
|
|
201
|
+
const next = navigable.find((item) => this.items.indexOf(item) > current) ??
|
|
202
|
+
navigable.find((item) => this.items.indexOf(item) < current);
|
|
203
|
+
if (next) {
|
|
204
|
+
this.setActiveItem(next);
|
|
205
|
+
}
|
|
206
|
+
else {
|
|
207
|
+
this.activeIndex = -1;
|
|
208
|
+
this.applyRovingTabIndex();
|
|
209
|
+
}
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
this.applyRovingTabIndex();
|
|
213
|
+
};
|
|
178
214
|
this.onItemSelect = (e) => {
|
|
179
215
|
const item = e.target;
|
|
180
216
|
if (!(item instanceof LyraMenuItem))
|
|
@@ -182,8 +218,27 @@ export class LyraMenu extends LyraElement {
|
|
|
182
218
|
this.emit('lyra-menu-select', { value: item.value });
|
|
183
219
|
this.hide(true);
|
|
184
220
|
};
|
|
221
|
+
/** Resyncs `activeIndex` (and the roving `tabindex`) to wherever real DOM
|
|
222
|
+
* focus actually lands, for any path that doesn't go through
|
|
223
|
+
* `setActiveItem()` -- e.g. a real mousedown on an item, which focuses it
|
|
224
|
+
* even while `disabled` (`tabIndex="-1"` remains mouse-focusable per
|
|
225
|
+
* spec). Without this, `activeIndex` goes stale the moment focus moves any
|
|
226
|
+
* other way, and subsequent Arrow-key navigation computes its next item
|
|
227
|
+
* from that stale position instead of from where focus actually is. A
|
|
228
|
+
* no-op for `setActiveItem()`'s own `.focus()` call, since `activeIndex`
|
|
229
|
+
* there is already set to match before focus moves. */
|
|
230
|
+
this.onListFocusIn = (e) => {
|
|
231
|
+
const target = e.target;
|
|
232
|
+
if (!(target instanceof LyraMenuItem))
|
|
233
|
+
return;
|
|
234
|
+
const index = this.items.indexOf(target);
|
|
235
|
+
if (index === -1 || index === this.activeIndex)
|
|
236
|
+
return;
|
|
237
|
+
this.activeIndex = index;
|
|
238
|
+
this.applyRovingTabIndex();
|
|
239
|
+
};
|
|
185
240
|
this.onListKeyDown = (e) => {
|
|
186
|
-
const navigable = this.items.filter((i) =>
|
|
241
|
+
const navigable = this.items.filter((i) => this.isNavigable(i));
|
|
187
242
|
const current = this.activeIndex >= 0 ? this.items[this.activeIndex] : undefined;
|
|
188
243
|
const currentNavIndex = current ? navigable.indexOf(current) : -1;
|
|
189
244
|
switch (e.key) {
|
|
@@ -227,6 +282,9 @@ export class LyraMenu extends LyraElement {
|
|
|
227
282
|
this.hide();
|
|
228
283
|
break;
|
|
229
284
|
default:
|
|
285
|
+
if (e.key.length === 1 && !e.altKey && !e.ctrlKey && !e.metaKey) {
|
|
286
|
+
this.typeAhead(e.key);
|
|
287
|
+
}
|
|
230
288
|
return;
|
|
231
289
|
}
|
|
232
290
|
};
|
|
@@ -280,7 +338,16 @@ export class LyraMenu extends LyraElement {
|
|
|
280
338
|
super.disconnectedCallback();
|
|
281
339
|
this.cleanup?.();
|
|
282
340
|
this.cleanup = undefined;
|
|
341
|
+
clearTimeout(this.typeAheadTimer);
|
|
342
|
+
this.itemStateObserver?.disconnect();
|
|
343
|
+
this.itemStateObserver = undefined;
|
|
283
344
|
document.removeEventListener('pointerdown', this.onDocPointer);
|
|
345
|
+
// Reset so a reconnect (e.g. a drag-drop reparent) re-triggers
|
|
346
|
+
// `updated()`'s `open`-driven branch -- without this, `open` stays
|
|
347
|
+
// `true` across the disconnect/reconnect and `changed.has('open')` never
|
|
348
|
+
// fires again, leaving the menu rendered open with no positioning and
|
|
349
|
+
// no outside-click listener.
|
|
350
|
+
this.open = false;
|
|
284
351
|
}
|
|
285
352
|
show(focus = 'first') {
|
|
286
353
|
if (this.open)
|
|
@@ -310,6 +377,9 @@ export class LyraMenu extends LyraElement {
|
|
|
310
377
|
this.triggerEl.setAttribute('aria-expanded', this.open ? 'true' : 'false');
|
|
311
378
|
this.triggerEl.setAttribute('aria-controls', this.listId);
|
|
312
379
|
}
|
|
380
|
+
isNavigable(item) {
|
|
381
|
+
return !item.disabled && !item.hidden && item.getAttribute('aria-hidden') !== 'true';
|
|
382
|
+
}
|
|
313
383
|
/** Flips exactly one non-disabled item's `tabIndex` to `0` (the roving
|
|
314
384
|
* target) and every other item's to `-1` -- see `<lyra-menu-item>`'s doc
|
|
315
385
|
* for why this is the sole authority over that property. */
|
|
@@ -322,7 +392,7 @@ export class LyraMenu extends LyraElement {
|
|
|
322
392
|
* non-disabled item. A no-op when there are none -- focus then simply
|
|
323
393
|
* stays on the trigger (see onTriggerKeyDown's Escape safety net). */
|
|
324
394
|
focusRoving(which) {
|
|
325
|
-
const navigable = this.items.filter((i) =>
|
|
395
|
+
const navigable = this.items.filter((i) => this.isNavigable(i));
|
|
326
396
|
if (!navigable.length)
|
|
327
397
|
return;
|
|
328
398
|
const item = which === 'first' ? navigable[0] : navigable[navigable.length - 1];
|
|
@@ -333,6 +403,30 @@ export class LyraMenu extends LyraElement {
|
|
|
333
403
|
this.applyRovingTabIndex();
|
|
334
404
|
item.focus();
|
|
335
405
|
}
|
|
406
|
+
/** Standard WAI-ARIA APG menu-button type-ahead: moves the roving focus to
|
|
407
|
+
* the next non-disabled item whose text content starts with the
|
|
408
|
+
* accumulated buffer, cycling from just after the currently active item
|
|
409
|
+
* -- mirrors `<lyra-select>`'s identical listbox type-ahead. */
|
|
410
|
+
typeAhead(char) {
|
|
411
|
+
clearTimeout(this.typeAheadTimer);
|
|
412
|
+
this.typeAheadBuffer += char.toLowerCase();
|
|
413
|
+
this.typeAheadTimer = setTimeout(() => {
|
|
414
|
+
this.typeAheadBuffer = '';
|
|
415
|
+
}, 500);
|
|
416
|
+
const navigable = this.items.filter((i) => !i.disabled);
|
|
417
|
+
if (!navigable.length)
|
|
418
|
+
return;
|
|
419
|
+
const current = this.activeIndex >= 0 ? this.items[this.activeIndex] : undefined;
|
|
420
|
+
const currentIndex = current ? navigable.indexOf(current) : -1;
|
|
421
|
+
const n = navigable.length;
|
|
422
|
+
for (let step = 1; step <= n; step++) {
|
|
423
|
+
const candidate = navigable[(currentIndex + step + n) % n];
|
|
424
|
+
if ((candidate.textContent ?? '').trim().toLowerCase().startsWith(this.typeAheadBuffer)) {
|
|
425
|
+
this.setActiveItem(candidate);
|
|
426
|
+
return;
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
}
|
|
336
430
|
render() {
|
|
337
431
|
return html `
|
|
338
432
|
<div part="trigger" @click=${this.onTriggerClick} @keydown=${this.onTriggerKeyDown}>
|
|
@@ -345,7 +439,9 @@ export class LyraMenu extends LyraElement {
|
|
|
345
439
|
role="menu"
|
|
346
440
|
aria-label=${this.label}
|
|
347
441
|
@keydown=${this.onListKeyDown}
|
|
442
|
+
@focusin=${this.onListFocusIn}
|
|
348
443
|
@lyra-menu-item-select=${this.onItemSelect}
|
|
444
|
+
@lyra-menu-item-state-change=${this.onItemStateChange}
|
|
349
445
|
>
|
|
350
446
|
<slot @slotchange=${this.onItemsSlotChange}></slot>
|
|
351
447
|
</div>
|
|
@@ -18,7 +18,8 @@ export const styles = css `
|
|
|
18
18
|
z-index: 900;
|
|
19
19
|
box-sizing: border-box;
|
|
20
20
|
min-inline-size: 10rem;
|
|
21
|
-
max-inline-size: min(92vw, 20rem);
|
|
21
|
+
max-inline-size: min(92vw, 20rem, var(--lyra-positioner-available-inline-size, 100vw));
|
|
22
|
+
max-block-size: min(20rem, var(--lyra-positioner-available-block-size, 20rem));
|
|
22
23
|
background: var(--lyra-color-surface);
|
|
23
24
|
border: 1px solid var(--lyra-color-border);
|
|
24
25
|
border-radius: var(--lyra-radius);
|
|
@@ -50,7 +51,7 @@ export const styles = css `
|
|
|
50
51
|
[part='list'] {
|
|
51
52
|
display: flex;
|
|
52
53
|
flex-direction: column;
|
|
53
|
-
max-block-size: 20rem;
|
|
54
|
+
max-block-size: min(20rem, var(--lyra-positioner-available-block-size, 20rem));
|
|
54
55
|
overflow-y: auto;
|
|
55
56
|
padding: var(--lyra-space-xs);
|
|
56
57
|
outline: none;
|
|
@@ -30,6 +30,8 @@ export type LyraModelCatalog = string[] | LyraModelCatalogEntry[];
|
|
|
30
30
|
*
|
|
31
31
|
* @customElement lyra-model-select
|
|
32
32
|
* @event lyra-change - The selected/typed value changed. `detail: { value: string; inCatalog: boolean }`.
|
|
33
|
+
* @csspart form-control-label - The `<label>` element (only rendered — and only contributes to the
|
|
34
|
+
* accessible name — once `label` is non-empty).
|
|
33
35
|
* @csspart trigger - The trigger button (closed-dropdown mode's positioning anchor).
|
|
34
36
|
* @csspart combobox - The text-input container (free-text mode's positioning anchor).
|
|
35
37
|
* @csspart combobox-input - The free-text mode's text input.
|
|
@@ -44,9 +46,23 @@ export declare class LyraModelSelect extends LyraElement {
|
|
|
44
46
|
static formAssociated: boolean;
|
|
45
47
|
static styles: import("lit").CSSResultGroup[];
|
|
46
48
|
static properties: {
|
|
49
|
+
disabled: {
|
|
50
|
+
type: BooleanConstructor;
|
|
51
|
+
reflect: boolean;
|
|
52
|
+
noAccessor: boolean;
|
|
53
|
+
};
|
|
54
|
+
required: {
|
|
55
|
+
type: BooleanConstructor;
|
|
56
|
+
reflect: boolean;
|
|
57
|
+
noAccessor: boolean;
|
|
58
|
+
};
|
|
47
59
|
value: {
|
|
48
60
|
noAccessor: boolean;
|
|
49
61
|
};
|
|
62
|
+
name: {
|
|
63
|
+
reflect: boolean;
|
|
64
|
+
noAccessor: boolean;
|
|
65
|
+
};
|
|
50
66
|
};
|
|
51
67
|
/** Informational only — e.g. `'ollama'`. Rendered as a small leading badge for display grouping. */
|
|
52
68
|
provider: string;
|
|
@@ -54,29 +70,56 @@ export declare class LyraModelSelect extends LyraElement {
|
|
|
54
70
|
catalog?: LyraModelCatalog;
|
|
55
71
|
/** Let the user type/commit a value that isn't in `catalog`, even when `catalog` is non-empty. */
|
|
56
72
|
allowCustom: boolean;
|
|
73
|
+
/**
|
|
74
|
+
* Optional visible title above the control, mirroring `lyra-select`'s
|
|
75
|
+
* `label` exactly: rendered via a `part="form-control-label"` `<label>`
|
|
76
|
+
* paired with the control's id, and — once non-empty — it takes over as
|
|
77
|
+
* the accessible-name source (the `aria-label` fallback below is then only
|
|
78
|
+
* emitted when the host has an explicit `aria-label` override, same
|
|
79
|
+
* precedence as `lyra-select`). Leaving it empty (the default) keeps
|
|
80
|
+
* today's `aria-label || placeholder || 'Model'` chain untouched.
|
|
81
|
+
*/
|
|
82
|
+
label: string;
|
|
57
83
|
placeholder: string;
|
|
58
|
-
name: string;
|
|
59
|
-
disabled: boolean;
|
|
60
|
-
required: boolean;
|
|
61
84
|
open: boolean;
|
|
62
85
|
private activeIndex;
|
|
63
86
|
private query;
|
|
64
87
|
private touched;
|
|
65
88
|
private internals;
|
|
89
|
+
private validityController;
|
|
66
90
|
private listId;
|
|
67
91
|
private controlId;
|
|
68
92
|
private cleanup?;
|
|
69
93
|
private _value;
|
|
94
|
+
private _fieldsetDisabled;
|
|
95
|
+
private _name;
|
|
96
|
+
private _disabled;
|
|
97
|
+
private _required;
|
|
70
98
|
private _defaultValue;
|
|
71
99
|
constructor();
|
|
100
|
+
get form(): HTMLFormElement | null;
|
|
101
|
+
get labels(): NodeList;
|
|
102
|
+
get validity(): ValidityState;
|
|
103
|
+
get validationMessage(): string;
|
|
104
|
+
get willValidate(): boolean;
|
|
72
105
|
connectedCallback(): void;
|
|
73
106
|
disconnectedCallback(): void;
|
|
74
107
|
attributeChangedCallback(name: string, old: string | null, val: string | null): void;
|
|
75
108
|
/** The current model id (empty string when nothing is selected). */
|
|
76
109
|
get value(): string;
|
|
77
110
|
set value(next: string);
|
|
111
|
+
/** The form submission key, reflected synchronously for native form APIs. */
|
|
112
|
+
get name(): string;
|
|
113
|
+
set name(next: string);
|
|
114
|
+
get disabled(): boolean;
|
|
115
|
+
set disabled(next: boolean);
|
|
116
|
+
get required(): boolean;
|
|
117
|
+
set required(next: boolean);
|
|
118
|
+
/** Whether the control is disabled explicitly or by an ancestor fieldset. */
|
|
119
|
+
get effectiveDisabled(): boolean;
|
|
78
120
|
private updateValidity;
|
|
79
121
|
formResetCallback(): void;
|
|
122
|
+
formStateRestoreCallback(state: string | File | FormData | null, _mode?: 'restore' | 'autocomplete'): void;
|
|
80
123
|
formDisabledCallback(disabled: boolean): void;
|
|
81
124
|
checkValidity(): boolean;
|
|
82
125
|
reportValidity(): boolean;
|
|
@@ -114,6 +157,8 @@ export declare class LyraModelSelect extends LyraElement {
|
|
|
114
157
|
private onListboxClick;
|
|
115
158
|
private renderRows;
|
|
116
159
|
private renderListbox;
|
|
160
|
+
/** `part="form-control-label"` — see `label`'s doc comment for its precedence over `aria-label`. */
|
|
161
|
+
private renderLabel;
|
|
117
162
|
private renderClosed;
|
|
118
163
|
private renderFreeText;
|
|
119
164
|
render(): TemplateResult;
|
|
@@ -4,13 +4,14 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
4
4
|
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
5
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
6
|
};
|
|
7
|
-
import { html } from 'lit';
|
|
7
|
+
import { html, nothing } from 'lit';
|
|
8
8
|
import { property, state } from 'lit/decorators.js';
|
|
9
9
|
import { LyraElement } from '../../internal/lyra-element.js';
|
|
10
10
|
import { defineElement } from '../../internal/prefix.js';
|
|
11
11
|
import { place } from '../../internal/positioner.js';
|
|
12
12
|
import { nextId } from '../../internal/a11y.js';
|
|
13
13
|
import { chevronIcon } from '../../internal/icons.js';
|
|
14
|
+
import { AnchoredValidityController, VALIDITY_ANCHOR } from '../../internal/anchored-validity.js';
|
|
14
15
|
import { styles } from './model-select.styles.js';
|
|
15
16
|
/**
|
|
16
17
|
* `<lyra-model-select>` — a provider/model picker that renders as a closed
|
|
@@ -32,6 +33,8 @@ import { styles } from './model-select.styles.js';
|
|
|
32
33
|
*
|
|
33
34
|
* @customElement lyra-model-select
|
|
34
35
|
* @event lyra-change - The selected/typed value changed. `detail: { value: string; inCatalog: boolean }`.
|
|
36
|
+
* @csspart form-control-label - The `<label>` element (only rendered — and only contributes to the
|
|
37
|
+
* accessible name — once `label` is non-empty).
|
|
35
38
|
* @csspart trigger - The trigger button (closed-dropdown mode's positioning anchor).
|
|
36
39
|
* @csspart combobox - The text-input container (free-text mode's positioning anchor).
|
|
37
40
|
* @csspart combobox-input - The free-text mode's text input.
|
|
@@ -46,7 +49,10 @@ export class LyraModelSelect extends LyraElement {
|
|
|
46
49
|
static { this.formAssociated = true; }
|
|
47
50
|
static { this.styles = [LyraElement.styles, styles]; }
|
|
48
51
|
static { this.properties = {
|
|
52
|
+
disabled: { type: Boolean, reflect: true, noAccessor: true },
|
|
53
|
+
required: { type: Boolean, reflect: true, noAccessor: true },
|
|
49
54
|
value: { noAccessor: true },
|
|
55
|
+
name: { reflect: true, noAccessor: true },
|
|
50
56
|
}; }
|
|
51
57
|
constructor() {
|
|
52
58
|
super();
|
|
@@ -54,10 +60,17 @@ export class LyraModelSelect extends LyraElement {
|
|
|
54
60
|
this.provider = '';
|
|
55
61
|
/** Let the user type/commit a value that isn't in `catalog`, even when `catalog` is non-empty. */
|
|
56
62
|
this.allowCustom = false;
|
|
63
|
+
/**
|
|
64
|
+
* Optional visible title above the control, mirroring `lyra-select`'s
|
|
65
|
+
* `label` exactly: rendered via a `part="form-control-label"` `<label>`
|
|
66
|
+
* paired with the control's id, and — once non-empty — it takes over as
|
|
67
|
+
* the accessible-name source (the `aria-label` fallback below is then only
|
|
68
|
+
* emitted when the host has an explicit `aria-label` override, same
|
|
69
|
+
* precedence as `lyra-select`). Leaving it empty (the default) keeps
|
|
70
|
+
* today's `aria-label || placeholder || 'Model'` chain untouched.
|
|
71
|
+
*/
|
|
72
|
+
this.label = '';
|
|
57
73
|
this.placeholder = '';
|
|
58
|
-
this.name = '';
|
|
59
|
-
this.disabled = false;
|
|
60
|
-
this.required = false;
|
|
61
74
|
this.open = false;
|
|
62
75
|
this.activeIndex = -1;
|
|
63
76
|
// Free-text mode's live input text. Only meaningful while `open` — the
|
|
@@ -70,6 +83,10 @@ export class LyraModelSelect extends LyraElement {
|
|
|
70
83
|
this.listId = nextId('model-select-list');
|
|
71
84
|
this.controlId = nextId('model-select-control');
|
|
72
85
|
this._value = '';
|
|
86
|
+
this._fieldsetDisabled = false;
|
|
87
|
+
this._name = '';
|
|
88
|
+
this._disabled = false;
|
|
89
|
+
this._required = false;
|
|
73
90
|
// What `form.reset()` restores to — captured from the `value` *content
|
|
74
91
|
// attribute* only, mirroring native `<input>`/`FormAssociated`'s
|
|
75
92
|
// `_defaultValue` (see internal/form-associated.ts). There's no child
|
|
@@ -82,7 +99,7 @@ export class LyraModelSelect extends LyraElement {
|
|
|
82
99
|
};
|
|
83
100
|
// -- Closed-dropdown mode (trigger button) --------------------------------
|
|
84
101
|
this.onTriggerClick = () => {
|
|
85
|
-
if (this.
|
|
102
|
+
if (this.effectiveDisabled)
|
|
86
103
|
return;
|
|
87
104
|
this.open ? this.hide() : this.show();
|
|
88
105
|
};
|
|
@@ -139,7 +156,7 @@ export class LyraModelSelect extends LyraElement {
|
|
|
139
156
|
};
|
|
140
157
|
// -- Free-text mode (text input) ------------------------------------------
|
|
141
158
|
this.onComboMouseDown = (e) => {
|
|
142
|
-
if (this.
|
|
159
|
+
if (this.effectiveDisabled)
|
|
143
160
|
return;
|
|
144
161
|
e.preventDefault();
|
|
145
162
|
this.renderRoot.querySelector('[part="combobox-input"]')?.focus();
|
|
@@ -214,6 +231,8 @@ export class LyraModelSelect extends LyraElement {
|
|
|
214
231
|
e.preventDefault();
|
|
215
232
|
};
|
|
216
233
|
this.onListboxClick = (e) => {
|
|
234
|
+
if (this.effectiveDisabled)
|
|
235
|
+
return;
|
|
217
236
|
const optionEl = e.target.closest('[part="option"]');
|
|
218
237
|
const value = optionEl?.dataset.value;
|
|
219
238
|
if (value === undefined)
|
|
@@ -223,6 +242,30 @@ export class LyraModelSelect extends LyraElement {
|
|
|
223
242
|
this.selectEntry(entry);
|
|
224
243
|
};
|
|
225
244
|
this.internals = this.attachInternals();
|
|
245
|
+
this.validityController = new AnchoredValidityController(this, this.internals, () => this[VALIDITY_ANCHOR]());
|
|
246
|
+
// Native <input> always has a submission value ("") from construction —
|
|
247
|
+
// without this, a control whose `value` is never touched is entirely
|
|
248
|
+
// absent from FormData instead of present as "" (see form-associated.ts).
|
|
249
|
+
this.internals.setFormValue('');
|
|
250
|
+
}
|
|
251
|
+
get form() {
|
|
252
|
+
return this.internals.form;
|
|
253
|
+
}
|
|
254
|
+
get labels() {
|
|
255
|
+
return this.internals.labels;
|
|
256
|
+
}
|
|
257
|
+
get validity() {
|
|
258
|
+
return this.internals.validity;
|
|
259
|
+
}
|
|
260
|
+
get validationMessage() {
|
|
261
|
+
return this.internals.validationMessage;
|
|
262
|
+
}
|
|
263
|
+
get willValidate() {
|
|
264
|
+
return this.internals.willValidate;
|
|
265
|
+
}
|
|
266
|
+
/** @internal */
|
|
267
|
+
[VALIDITY_ANCHOR]() {
|
|
268
|
+
return this.renderRoot?.querySelector('[part="trigger"], [part="combobox-input"]') ?? null;
|
|
226
269
|
}
|
|
227
270
|
connectedCallback() {
|
|
228
271
|
super.connectedCallback();
|
|
@@ -231,7 +274,14 @@ export class LyraModelSelect extends LyraElement {
|
|
|
231
274
|
disconnectedCallback() {
|
|
232
275
|
super.disconnectedCallback();
|
|
233
276
|
this.cleanup?.();
|
|
277
|
+
this.cleanup = undefined;
|
|
234
278
|
document.removeEventListener('pointerdown', this.onDocPointer);
|
|
279
|
+
// Reset so a reconnect (e.g. a drag-drop reparent) re-triggers
|
|
280
|
+
// `updated()`'s `open`-driven branch -- without this, `open` stays
|
|
281
|
+
// `true` across the disconnect/reconnect and `changed.has('open')` never
|
|
282
|
+
// fires again, leaving the listbox rendered open with no positioning and
|
|
283
|
+
// no outside-click listener.
|
|
284
|
+
this.open = false;
|
|
235
285
|
}
|
|
236
286
|
attributeChangedCallback(name, old, val) {
|
|
237
287
|
super.attributeChangedCallback(name, old, val);
|
|
@@ -249,19 +299,65 @@ export class LyraModelSelect extends LyraElement {
|
|
|
249
299
|
this.updateValidity();
|
|
250
300
|
this.requestUpdate('value', old);
|
|
251
301
|
}
|
|
302
|
+
/** The form submission key, reflected synchronously for native form APIs. */
|
|
303
|
+
get name() {
|
|
304
|
+
return this._name;
|
|
305
|
+
}
|
|
306
|
+
set name(next) {
|
|
307
|
+
const old = this._name;
|
|
308
|
+
this._name = next ?? '';
|
|
309
|
+
if (this._name) {
|
|
310
|
+
this.setAttribute('name', this._name);
|
|
311
|
+
}
|
|
312
|
+
else {
|
|
313
|
+
this.removeAttribute('name');
|
|
314
|
+
}
|
|
315
|
+
this.requestUpdate('name', old);
|
|
316
|
+
}
|
|
317
|
+
get disabled() {
|
|
318
|
+
return this._disabled;
|
|
319
|
+
}
|
|
320
|
+
set disabled(next) {
|
|
321
|
+
const old = this._disabled;
|
|
322
|
+
this._disabled = Boolean(next);
|
|
323
|
+
this.toggleAttribute('disabled', this._disabled);
|
|
324
|
+
if (this._disabled)
|
|
325
|
+
this.hide();
|
|
326
|
+
this.requestUpdate('disabled', old);
|
|
327
|
+
}
|
|
328
|
+
get required() {
|
|
329
|
+
return this._required;
|
|
330
|
+
}
|
|
331
|
+
set required(next) {
|
|
332
|
+
const old = this._required;
|
|
333
|
+
this._required = Boolean(next);
|
|
334
|
+
this.toggleAttribute('required', this._required);
|
|
335
|
+
this.updateValidity();
|
|
336
|
+
this.requestUpdate('required', old);
|
|
337
|
+
}
|
|
338
|
+
/** Whether the control is disabled explicitly or by an ancestor fieldset. */
|
|
339
|
+
get effectiveDisabled() {
|
|
340
|
+
return this.disabled || this._fieldsetDisabled;
|
|
341
|
+
}
|
|
252
342
|
updateValidity() {
|
|
253
343
|
if (this.required && !this._value) {
|
|
254
|
-
this.
|
|
344
|
+
this.validityController.setValidity({ valueMissing: true }, 'Please choose a model.');
|
|
255
345
|
}
|
|
256
346
|
else {
|
|
257
|
-
this.
|
|
347
|
+
this.validityController.setValidity({});
|
|
258
348
|
}
|
|
259
349
|
}
|
|
260
350
|
formResetCallback() {
|
|
261
351
|
this.value = this._defaultValue;
|
|
262
352
|
}
|
|
353
|
+
formStateRestoreCallback(state, _mode) {
|
|
354
|
+
this.value = typeof state === 'string' ? state : '';
|
|
355
|
+
}
|
|
263
356
|
formDisabledCallback(disabled) {
|
|
264
|
-
this.
|
|
357
|
+
this._fieldsetDisabled = disabled;
|
|
358
|
+
if (disabled)
|
|
359
|
+
this.hide();
|
|
360
|
+
this.requestUpdate();
|
|
265
361
|
}
|
|
266
362
|
checkValidity() {
|
|
267
363
|
return this.internals.checkValidity();
|
|
@@ -307,7 +403,7 @@ export class LyraModelSelect extends LyraElement {
|
|
|
307
403
|
return this.effectiveEntries.find((e) => e.id === id)?.label ?? id;
|
|
308
404
|
}
|
|
309
405
|
show() {
|
|
310
|
-
if (this.open || this.
|
|
406
|
+
if (this.open || this.effectiveDisabled)
|
|
311
407
|
return;
|
|
312
408
|
this.open = true;
|
|
313
409
|
}
|
|
@@ -388,11 +484,17 @@ export class LyraModelSelect extends LyraElement {
|
|
|
388
484
|
</div>
|
|
389
485
|
`;
|
|
390
486
|
}
|
|
487
|
+
/** `part="form-control-label"` — see `label`'s doc comment for its precedence over `aria-label`. */
|
|
488
|
+
renderLabel() {
|
|
489
|
+
return html `<label part="form-control-label" for=${this.controlId} ?hidden=${!this.label}>${this.label}</label>`;
|
|
490
|
+
}
|
|
391
491
|
renderClosed() {
|
|
392
492
|
const rows = this.effectiveEntries;
|
|
393
493
|
const activeId = this.activeIndex >= 0 && rows[this.activeIndex] ? `${this.listId}-opt-${this.activeIndex}` : '';
|
|
394
494
|
const hasValue = this._value.length > 0;
|
|
495
|
+
const hasLabel = this.label.length > 0;
|
|
395
496
|
return html `
|
|
497
|
+
${this.renderLabel()}
|
|
396
498
|
<button
|
|
397
499
|
id=${this.controlId}
|
|
398
500
|
part="trigger"
|
|
@@ -402,10 +504,10 @@ export class LyraModelSelect extends LyraElement {
|
|
|
402
504
|
aria-expanded=${this.open ? 'true' : 'false'}
|
|
403
505
|
aria-controls=${this.listId}
|
|
404
506
|
aria-activedescendant=${activeId}
|
|
405
|
-
aria-label=${this.getAttribute('aria-label') || this.placeholder || 'Model'}
|
|
507
|
+
aria-label=${this.getAttribute('aria-label') || (hasLabel ? nothing : this.placeholder || 'Model')}
|
|
406
508
|
aria-required=${this.required ? 'true' : 'false'}
|
|
407
509
|
aria-invalid=${this.touched && !this.internals.validity.valid ? 'true' : 'false'}
|
|
408
|
-
?disabled=${this.
|
|
510
|
+
?disabled=${this.effectiveDisabled}
|
|
409
511
|
@click=${this.onTriggerClick}
|
|
410
512
|
@keydown=${this.onTriggerKeyDown}
|
|
411
513
|
@blur=${this.onTriggerBlur}
|
|
@@ -422,14 +524,16 @@ export class LyraModelSelect extends LyraElement {
|
|
|
422
524
|
renderFreeText() {
|
|
423
525
|
const rows = this.filteredEntries;
|
|
424
526
|
const activeId = this.activeIndex >= 0 && rows[this.activeIndex] ? `${this.listId}-opt-${this.activeIndex}` : '';
|
|
527
|
+
const hasLabel = this.label.length > 0;
|
|
425
528
|
return html `
|
|
529
|
+
${this.renderLabel()}
|
|
426
530
|
<div part="combobox" @mousedown=${this.onComboMouseDown}>
|
|
427
531
|
${this.provider ? html `<span part="provider-badge">${this.provider}</span>` : ''}
|
|
428
532
|
<input
|
|
429
533
|
id=${this.controlId}
|
|
430
534
|
part="combobox-input"
|
|
431
535
|
role="combobox"
|
|
432
|
-
aria-label=${this.getAttribute('aria-label') || this.placeholder || 'Model'}
|
|
536
|
+
aria-label=${this.getAttribute('aria-label') || (hasLabel ? nothing : this.placeholder || 'Model')}
|
|
433
537
|
aria-expanded=${this.open ? 'true' : 'false'}
|
|
434
538
|
aria-controls=${this.listId}
|
|
435
539
|
aria-activedescendant=${activeId}
|
|
@@ -439,7 +543,7 @@ export class LyraModelSelect extends LyraElement {
|
|
|
439
543
|
autocomplete="off"
|
|
440
544
|
.value=${this.open ? this.query : this.labelFor(this._value)}
|
|
441
545
|
placeholder=${this.placeholder}
|
|
442
|
-
?disabled=${this.
|
|
546
|
+
?disabled=${this.effectiveDisabled}
|
|
443
547
|
@input=${this.onInput}
|
|
444
548
|
@keydown=${this.onInputKeyDown}
|
|
445
549
|
@focus=${this.onInputFocus}
|
|
@@ -465,16 +569,10 @@ __decorate([
|
|
|
465
569
|
], LyraModelSelect.prototype, "allowCustom", void 0);
|
|
466
570
|
__decorate([
|
|
467
571
|
property()
|
|
468
|
-
], LyraModelSelect.prototype, "
|
|
572
|
+
], LyraModelSelect.prototype, "label", void 0);
|
|
469
573
|
__decorate([
|
|
470
574
|
property()
|
|
471
|
-
], LyraModelSelect.prototype, "
|
|
472
|
-
__decorate([
|
|
473
|
-
property({ type: Boolean, reflect: true })
|
|
474
|
-
], LyraModelSelect.prototype, "disabled", void 0);
|
|
475
|
-
__decorate([
|
|
476
|
-
property({ type: Boolean, reflect: true })
|
|
477
|
-
], LyraModelSelect.prototype, "required", void 0);
|
|
575
|
+
], LyraModelSelect.prototype, "placeholder", void 0);
|
|
478
576
|
__decorate([
|
|
479
577
|
property({ type: Boolean, reflect: true })
|
|
480
578
|
], LyraModelSelect.prototype, "open", void 0);
|
|
@@ -6,10 +6,24 @@ export const styles = css `
|
|
|
6
6
|
inline-size: 100%;
|
|
7
7
|
max-inline-size: 24rem;
|
|
8
8
|
}
|
|
9
|
-
:host(
|
|
9
|
+
:host(:disabled) {
|
|
10
10
|
cursor: not-allowed;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
+
[part='form-control-label'] {
|
|
14
|
+
display: block;
|
|
15
|
+
margin-block-end: var(--lyra-space-xs);
|
|
16
|
+
font-size: 0.875rem;
|
|
17
|
+
font-weight: 600;
|
|
18
|
+
}
|
|
19
|
+
/* :empty never matches here -- the part always renders a literal label
|
|
20
|
+
element regardless of label content -- so real emptiness is tracked via
|
|
21
|
+
the label property length and reflected through the hidden attribute
|
|
22
|
+
instead (same fix as lyra-select's own form-control-label). */
|
|
23
|
+
[part='form-control-label'][hidden] {
|
|
24
|
+
display: none;
|
|
25
|
+
}
|
|
26
|
+
|
|
13
27
|
[part='trigger'],
|
|
14
28
|
[part='combobox'] {
|
|
15
29
|
display: flex;
|
|
@@ -40,8 +54,8 @@ export const styles = css `
|
|
|
40
54
|
:host([open]) [part='trigger'] {
|
|
41
55
|
border-color: var(--lyra-color-brand);
|
|
42
56
|
}
|
|
43
|
-
:host(
|
|
44
|
-
:host(
|
|
57
|
+
:host(:disabled) [part='trigger'],
|
|
58
|
+
:host(:disabled) [part='combobox'] {
|
|
45
59
|
opacity: var(--lyra-opacity-disabled);
|
|
46
60
|
cursor: not-allowed;
|
|
47
61
|
}
|