@aceshooting/lyra-ui 1.2.0 → 2.0.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 +11 -0
- package/custom-elements.json +23341 -11202
- package/dist/components/app-rail/app-rail.d.ts +171 -0
- package/dist/components/app-rail/app-rail.js +483 -0
- package/dist/components/app-rail/app-rail.styles.d.ts +1 -0
- package/dist/components/app-rail/app-rail.styles.js +125 -0
- package/dist/components/attachment-chip/attachment-chip.d.ts +114 -0
- package/dist/components/attachment-chip/attachment-chip.js +312 -0
- package/dist/components/attachment-chip/attachment-chip.styles.d.ts +1 -0
- package/dist/components/attachment-chip/attachment-chip.styles.js +184 -0
- package/dist/components/attachment-trigger/attachment-trigger.d.ts +83 -0
- package/dist/components/attachment-trigger/attachment-trigger.js +258 -0
- package/dist/components/attachment-trigger/attachment-trigger.styles.d.ts +1 -0
- package/dist/components/attachment-trigger/attachment-trigger.styles.js +68 -0
- package/dist/components/chart/box-plot.js +11 -1
- package/dist/components/chart/chart-loader.d.ts +44 -13
- package/dist/components/chart/chart-loader.js +65 -18
- package/dist/components/chart/chart.d.ts +1 -2
- package/dist/components/chart/chart.js +67 -12
- package/dist/components/chart/histogram-bin.d.ts +4 -1
- package/dist/components/chart/histogram-bin.js +24 -14
- package/dist/components/chart/lite-chart.d.ts +54 -0
- package/dist/components/chart/lite-chart.js +140 -22
- package/dist/components/chart/lite-chart.styles.js +9 -0
- package/dist/components/chat-composer/chat-composer.d.ts +92 -0
- package/dist/components/chat-composer/chat-composer.js +285 -0
- package/dist/components/chat-composer/chat-composer.styles.d.ts +1 -0
- package/dist/components/chat-composer/chat-composer.styles.js +135 -0
- package/dist/components/chat-message/chat-message.d.ts +113 -0
- package/dist/components/chat-message/chat-message.js +291 -0
- package/dist/components/chat-message/chat-message.styles.d.ts +1 -0
- package/dist/components/chat-message/chat-message.styles.js +203 -0
- package/dist/components/checkbox/checkbox.d.ts +59 -0
- package/dist/components/checkbox/checkbox.js +230 -0
- package/dist/components/checkbox/checkbox.styles.d.ts +1 -0
- package/dist/components/checkbox/checkbox.styles.js +70 -0
- package/dist/components/chip/chip-group.d.ts +56 -0
- package/dist/components/chip/chip-group.js +124 -0
- package/dist/components/chip/chip-group.styles.d.ts +1 -0
- package/dist/components/chip/chip-group.styles.js +57 -0
- package/dist/components/chip/chip.d.ts +64 -0
- package/dist/components/chip/chip.js +115 -0
- package/dist/components/chip/chip.styles.d.ts +1 -0
- package/dist/components/chip/chip.styles.js +115 -0
- package/dist/components/citation-badge/citation-badge.d.ts +113 -0
- package/dist/components/citation-badge/citation-badge.js +300 -0
- package/dist/components/citation-badge/citation-badge.styles.d.ts +1 -0
- package/dist/components/citation-badge/citation-badge.styles.js +96 -0
- package/dist/components/code-block/code-block.d.ts +89 -0
- package/dist/components/code-block/code-block.js +317 -0
- package/dist/components/code-block/code-block.styles.d.ts +1 -0
- package/dist/components/code-block/code-block.styles.js +187 -0
- package/dist/components/code-block/code-loader.d.ts +44 -0
- package/dist/components/code-block/code-loader.js +79 -0
- package/dist/components/combobox/combobox.d.ts +19 -1
- package/dist/components/combobox/combobox.js +84 -14
- package/dist/components/combobox/option.d.ts +5 -1
- package/dist/components/combobox/option.js +35 -0
- package/dist/components/context-meter/context-meter.d.ts +53 -0
- package/dist/components/context-meter/context-meter.js +150 -0
- package/dist/components/context-meter/context-meter.styles.d.ts +1 -0
- package/dist/components/context-meter/context-meter.styles.js +120 -0
- package/dist/components/conversation-item/conversation-item.d.ts +128 -0
- package/dist/components/conversation-item/conversation-item.js +322 -0
- package/dist/components/conversation-item/conversation-item.styles.d.ts +1 -0
- package/dist/components/conversation-item/conversation-item.styles.js +155 -0
- package/dist/components/date-picker/date-input.d.ts +24 -0
- package/dist/components/date-picker/date-input.js +134 -22
- package/dist/components/date-picker/date-picker.d.ts +7 -0
- package/dist/components/date-picker/date-picker.js +85 -16
- package/dist/components/dialog/confirm.d.ts +40 -0
- package/dist/components/dialog/confirm.js +79 -0
- package/dist/components/dialog/dialog.d.ts +85 -0
- package/dist/components/dialog/dialog.js +272 -0
- package/dist/components/dialog/dialog.styles.d.ts +1 -0
- package/dist/components/dialog/dialog.styles.js +52 -0
- package/dist/components/dock-panel/dock-panel.d.ts +134 -0
- package/dist/components/dock-panel/dock-panel.js +345 -0
- package/dist/components/dock-panel/dock-panel.styles.d.ts +1 -0
- package/dist/components/dock-panel/dock-panel.styles.js +145 -0
- package/dist/components/document-preview/document-preview.d.ts +135 -0
- package/dist/components/document-preview/document-preview.js +372 -0
- package/dist/components/document-preview/document-preview.styles.d.ts +1 -0
- package/dist/components/document-preview/document-preview.styles.js +191 -0
- package/dist/components/export-button/csv.js +1 -1
- package/dist/components/export-button/export-button.js +2 -0
- package/dist/components/flag/flag.d.ts +29 -13
- package/dist/components/flag/flag.js +39 -13
- package/dist/components/gauge/gauge.js +19 -10
- package/dist/components/generation-status/generation-status.d.ts +109 -0
- package/dist/components/generation-status/generation-status.js +273 -0
- package/dist/components/generation-status/generation-status.styles.d.ts +1 -0
- package/dist/components/generation-status/generation-status.styles.js +84 -0
- package/dist/components/heatmap/calendar-grid.d.ts +4 -1
- package/dist/components/heatmap/calendar-grid.js +25 -12
- package/dist/components/heatmap/heatmap-scale.d.ts +6 -0
- package/dist/components/heatmap/heatmap-scale.js +7 -1
- package/dist/components/heatmap/heatmap.d.ts +74 -0
- package/dist/components/heatmap/heatmap.js +135 -23
- package/dist/components/json-viewer/json-viewer.d.ts +53 -0
- package/dist/components/json-viewer/json-viewer.js +283 -0
- package/dist/components/json-viewer/json-viewer.styles.d.ts +1 -0
- package/dist/components/json-viewer/json-viewer.styles.js +176 -0
- package/dist/components/kbd/kbd.d.ts +77 -0
- package/dist/components/kbd/kbd.js +214 -0
- package/dist/components/kbd/kbd.styles.d.ts +1 -0
- package/dist/components/kbd/kbd.styles.js +52 -0
- package/dist/components/live-region/live-region.d.ts +81 -0
- package/dist/components/live-region/live-region.js +153 -0
- package/dist/components/live-region/live-region.styles.d.ts +1 -0
- package/dist/components/live-region/live-region.styles.js +10 -0
- package/dist/components/map/map.d.ts +8 -6
- package/dist/components/map/map.js +44 -8
- package/dist/components/markdown/markdown-loader.d.ts +34 -0
- package/dist/components/markdown/markdown-loader.js +43 -0
- package/dist/components/markdown/markdown.d.ts +81 -0
- package/dist/components/markdown/markdown.js +284 -0
- package/dist/components/markdown/markdown.styles.d.ts +1 -0
- package/dist/components/markdown/markdown.styles.js +82 -0
- package/dist/components/media-card/media-card.d.ts +122 -0
- package/dist/components/media-card/media-card.js +295 -0
- package/dist/components/media-card/media-card.styles.d.ts +1 -0
- package/dist/components/media-card/media-card.styles.js +132 -0
- package/dist/components/mention-popover/mention-popover.d.ts +200 -0
- package/dist/components/mention-popover/mention-popover.js +475 -0
- package/dist/components/mention-popover/mention-popover.styles.d.ts +1 -0
- package/dist/components/mention-popover/mention-popover.styles.js +101 -0
- package/dist/components/menu/menu-item.d.ts +62 -0
- package/dist/components/menu/menu-item.js +119 -0
- package/dist/components/menu/menu-item.styles.d.ts +1 -0
- package/dist/components/menu/menu-item.styles.js +67 -0
- package/dist/components/menu/menu.d.ts +143 -0
- package/dist/components/menu/menu.js +365 -0
- package/dist/components/menu/menu.styles.d.ts +1 -0
- package/dist/components/menu/menu.styles.js +66 -0
- package/dist/components/model-select/model-select.d.ts +125 -0
- package/dist/components/model-select/model-select.js +490 -0
- package/dist/components/model-select/model-select.styles.d.ts +1 -0
- package/dist/components/model-select/model-select.styles.js +189 -0
- package/dist/components/model-settings-panel/model-settings-panel.d.ts +69 -0
- package/dist/components/model-settings-panel/model-settings-panel.js +145 -0
- package/dist/components/model-settings-panel/model-settings-panel.styles.d.ts +1 -0
- package/dist/components/model-settings-panel/model-settings-panel.styles.js +82 -0
- package/dist/components/playback/playback.d.ts +13 -1
- package/dist/components/playback/playback.js +47 -14
- package/dist/components/responsive-panel/responsive-panel.d.ts +150 -0
- package/dist/components/responsive-panel/responsive-panel.js +365 -0
- package/dist/components/responsive-panel/responsive-panel.styles.d.ts +1 -0
- package/dist/components/responsive-panel/responsive-panel.styles.js +103 -0
- package/dist/components/result-card/result-card.d.ts +44 -0
- package/dist/components/result-card/result-card.js +83 -0
- package/dist/components/result-card/result-card.styles.d.ts +1 -0
- package/dist/components/result-card/result-card.styles.js +50 -0
- package/dist/components/result-card/result-field.d.ts +36 -0
- package/dist/components/result-card/result-field.js +87 -0
- package/dist/components/result-card/result-field.styles.d.ts +1 -0
- package/dist/components/result-card/result-field.styles.js +32 -0
- package/dist/components/select/select.d.ts +43 -2
- package/dist/components/select/select.js +150 -17
- package/dist/components/slider/slider.d.ts +89 -0
- package/dist/components/slider/slider.js +352 -0
- package/dist/components/slider/slider.styles.d.ts +1 -0
- package/dist/components/slider/slider.styles.js +87 -0
- package/dist/components/source-card/source-card.d.ts +92 -0
- package/dist/components/source-card/source-card.js +148 -0
- package/dist/components/source-card/source-card.styles.d.ts +1 -0
- package/dist/components/source-card/source-card.styles.js +69 -0
- package/dist/components/source-list/source-list.d.ts +63 -0
- package/dist/components/source-list/source-list.js +122 -0
- package/dist/components/source-list/source-list.styles.d.ts +1 -0
- package/dist/components/source-list/source-list.styles.js +59 -0
- package/dist/components/sparkline/sparkline.js +27 -13
- package/dist/components/split/split.d.ts +93 -1
- package/dist/components/split/split.js +284 -15
- package/dist/components/split/split.styles.js +30 -0
- package/dist/components/stat/stat.d.ts +6 -1
- package/dist/components/stat/stat.js +8 -2
- package/dist/components/stream-status/stream-status.d.ts +110 -0
- package/dist/components/stream-status/stream-status.js +210 -0
- package/dist/components/stream-status/stream-status.styles.d.ts +1 -0
- package/dist/components/stream-status/stream-status.styles.js +103 -0
- package/dist/components/streaming-text/streaming-text.d.ts +98 -0
- package/dist/components/streaming-text/streaming-text.js +209 -0
- package/dist/components/streaming-text/streaming-text.styles.d.ts +1 -0
- package/dist/components/streaming-text/streaming-text.styles.js +62 -0
- package/dist/components/switch/switch.d.ts +58 -0
- package/dist/components/switch/switch.js +179 -0
- package/dist/components/switch/switch.styles.d.ts +1 -0
- package/dist/components/switch/switch.styles.js +75 -0
- package/dist/components/table/table.d.ts +73 -4
- package/dist/components/table/table.js +169 -20
- package/dist/components/table/table.styles.js +6 -1
- package/dist/components/tabs/tabs.d.ts +51 -0
- package/dist/components/tabs/tabs.js +190 -0
- package/dist/components/tabs/tabs.styles.d.ts +1 -0
- package/dist/components/tabs/tabs.styles.js +63 -0
- package/dist/components/thinking-panel/thinking-panel.d.ts +121 -0
- package/dist/components/thinking-panel/thinking-panel.js +235 -0
- package/dist/components/thinking-panel/thinking-panel.styles.d.ts +1 -0
- package/dist/components/thinking-panel/thinking-panel.styles.js +117 -0
- package/dist/components/toast/toast-item.d.ts +1 -0
- package/dist/components/toast/toast-item.js +31 -6
- package/dist/components/tool-approval-dialog/tool-approval-dialog.d.ts +130 -0
- package/dist/components/tool-approval-dialog/tool-approval-dialog.js +403 -0
- package/dist/components/tool-approval-dialog/tool-approval-dialog.styles.d.ts +1 -0
- package/dist/components/tool-approval-dialog/tool-approval-dialog.styles.js +160 -0
- package/dist/components/tool-call-chip/tool-call-chip.d.ts +89 -0
- package/dist/components/tool-call-chip/tool-call-chip.js +288 -0
- package/dist/components/tool-call-chip/tool-call-chip.styles.d.ts +1 -0
- package/dist/components/tool-call-chip/tool-call-chip.styles.js +203 -0
- package/dist/components/tool-param-form/tool-param-form.d.ts +145 -0
- package/dist/components/tool-param-form/tool-param-form.js +366 -0
- package/dist/components/tool-param-form/tool-param-form.styles.d.ts +1 -0
- package/dist/components/tool-param-form/tool-param-form.styles.js +75 -0
- package/dist/components/tool-result-dialog/tool-result-dialog.d.ts +102 -0
- package/dist/components/tool-result-dialog/tool-result-dialog.js +393 -0
- package/dist/components/tool-result-dialog/tool-result-dialog.styles.d.ts +1 -0
- package/dist/components/tool-result-dialog/tool-result-dialog.styles.js +176 -0
- package/dist/components/tool-result-view/registry.d.ts +81 -0
- package/dist/components/tool-result-view/registry.js +84 -0
- package/dist/components/tool-result-view/tool-result-view.d.ts +55 -0
- package/dist/components/tool-result-view/tool-result-view.js +138 -0
- package/dist/components/tool-result-view/tool-result-view.styles.d.ts +1 -0
- package/dist/components/tool-result-view/tool-result-view.styles.js +13 -0
- package/dist/components/tool-select-dialog/tool-select-dialog.d.ts +151 -0
- package/dist/components/tool-select-dialog/tool-select-dialog.js +439 -0
- package/dist/components/tool-select-dialog/tool-select-dialog.styles.d.ts +1 -0
- package/dist/components/tool-select-dialog/tool-select-dialog.styles.js +182 -0
- package/dist/components/tree/tree-node.d.ts +11 -2
- package/dist/components/tree/tree-node.js +16 -6
- package/dist/components/tree/tree.d.ts +8 -2
- package/dist/components/tree/tree.js +18 -11
- package/dist/components/typing-indicator/typing-indicator.d.ts +60 -0
- package/dist/components/typing-indicator/typing-indicator.js +96 -0
- package/dist/components/typing-indicator/typing-indicator.styles.d.ts +1 -0
- package/dist/components/typing-indicator/typing-indicator.styles.js +132 -0
- package/dist/components/virtual-list/virtual-list.d.ts +173 -0
- package/dist/components/virtual-list/virtual-list.js +461 -0
- package/dist/components/virtual-list/virtual-list.styles.d.ts +1 -0
- package/dist/components/virtual-list/virtual-list.styles.js +44 -0
- package/dist/components/word-cloud/word-cloud-layout.js +14 -1
- package/dist/components/word-cloud/word-cloud.d.ts +6 -0
- package/dist/components/word-cloud/word-cloud.js +25 -14
- package/dist/components/word-cloud/word-cloud.styles.js +3 -2
- package/dist/internal/announcer.d.ts +57 -0
- package/dist/internal/announcer.js +71 -0
- package/dist/internal/form-associated.d.ts +1 -0
- package/dist/internal/form-associated.js +55 -11
- package/dist/internal/group-by-recency.d.ts +50 -0
- package/dist/internal/group-by-recency.js +80 -0
- package/dist/internal/rtl.d.ts +23 -0
- package/dist/internal/rtl.js +28 -0
- package/dist/internal/scroll-lock.d.ts +10 -5
- package/dist/internal/scroll-lock.js +30 -14
- package/dist/internal/tokens.styles.js +6 -0
- package/dist/lyra.d.ts +130 -31
- package/dist/lyra.js +94 -26
- package/llms-full.txt +3836 -17
- package/llms.txt +16 -1
- package/package.json +23 -5
- package/dist/components/chart/bar-chart.stories.d.ts +0 -5
- package/dist/components/chart/bar-chart.stories.js +0 -20
- package/dist/components/chart/box-plot.stories.d.ts +0 -5
- package/dist/components/chart/box-plot.stories.js +0 -29
- package/dist/components/chart/bubble-chart.stories.d.ts +0 -5
- package/dist/components/chart/bubble-chart.stories.js +0 -26
- package/dist/components/chart/chart.stories.d.ts +0 -32
- package/dist/components/chart/chart.stories.js +0 -141
- package/dist/components/chart/doughnut-chart.stories.d.ts +0 -5
- package/dist/components/chart/doughnut-chart.stories.js +0 -26
- package/dist/components/chart/histogram.stories.d.ts +0 -5
- package/dist/components/chart/histogram.stories.js +0 -17
- package/dist/components/chart/line-chart.stories.d.ts +0 -22
- package/dist/components/chart/line-chart.stories.js +0 -99
- package/dist/components/chart/lite-chart.stories.d.ts +0 -14
- package/dist/components/chart/lite-chart.stories.js +0 -114
- package/dist/components/chart/pie-chart.stories.d.ts +0 -5
- package/dist/components/chart/pie-chart.stories.js +0 -26
- package/dist/components/chart/polar-area-chart.stories.d.ts +0 -5
- package/dist/components/chart/polar-area-chart.stories.js +0 -20
- package/dist/components/chart/radar-chart.stories.d.ts +0 -5
- package/dist/components/chart/radar-chart.stories.js +0 -20
- package/dist/components/chart/scatter-chart.stories.d.ts +0 -5
- package/dist/components/chart/scatter-chart.stories.js +0 -31
- package/dist/components/combobox/combobox.stories.d.ts +0 -24
- package/dist/components/combobox/combobox.stories.js +0 -102
- package/dist/components/date-picker/date-input.stories.d.ts +0 -10
- package/dist/components/date-picker/date-input.stories.js +0 -48
- package/dist/components/date-picker/date-picker.stories.d.ts +0 -10
- package/dist/components/date-picker/date-picker.stories.js +0 -25
- package/dist/components/empty/empty.stories.d.ts +0 -7
- package/dist/components/empty/empty.stories.js +0 -34
- package/dist/components/export-button/export-button.stories.d.ts +0 -7
- package/dist/components/export-button/export-button.stories.js +0 -37
- package/dist/components/file-input/file-input.stories.d.ts +0 -7
- package/dist/components/file-input/file-input.stories.js +0 -19
- package/dist/components/flag/flag.stories.d.ts +0 -6
- package/dist/components/flag/flag.stories.js +0 -31
- package/dist/components/gauge/gauge.stories.d.ts +0 -8
- package/dist/components/gauge/gauge.stories.js +0 -19
- package/dist/components/graph/graph.stories.d.ts +0 -8
- package/dist/components/graph/graph.stories.js +0 -68
- package/dist/components/heatmap/heatmap.stories.d.ts +0 -27
- package/dist/components/heatmap/heatmap.stories.js +0 -179
- package/dist/components/map/map.stories.d.ts +0 -15
- package/dist/components/map/map.stories.js +0 -135
- package/dist/components/playback/playback.stories.d.ts +0 -7
- package/dist/components/playback/playback.stories.js +0 -16
- package/dist/components/select/select.stories.d.ts +0 -21
- package/dist/components/select/select.stories.js +0 -91
- package/dist/components/skeleton/skeleton.stories.d.ts +0 -5
- package/dist/components/skeleton/skeleton.stories.js +0 -16
- package/dist/components/sparkline/sparkline.stories.d.ts +0 -11
- package/dist/components/sparkline/sparkline.stories.js +0 -39
- package/dist/components/split/split.stories.d.ts +0 -7
- package/dist/components/split/split.stories.js +0 -36
- package/dist/components/stat/stat.stories.d.ts +0 -9
- package/dist/components/stat/stat.stories.js +0 -81
- package/dist/components/table/table.stories.d.ts +0 -12
- package/dist/components/table/table.stories.js +0 -67
- package/dist/components/time-range/time-range.stories.d.ts +0 -8
- package/dist/components/time-range/time-range.stories.js +0 -26
- package/dist/components/toast/toast.stories.d.ts +0 -8
- package/dist/components/toast/toast.stories.js +0 -72
- package/dist/components/tree/tree.stories.d.ts +0 -9
- package/dist/components/tree/tree.stories.js +0 -52
- package/dist/components/widget/widget.stories.d.ts +0 -9
- package/dist/components/widget/widget.stories.js +0 -74
- package/dist/components/word-cloud/word-cloud.stories.d.ts +0 -10
- package/dist/components/word-cloud/word-cloud.stories.js +0 -55
|
@@ -0,0 +1,365 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
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
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
import { html } from 'lit';
|
|
8
|
+
import { property } from 'lit/decorators.js';
|
|
9
|
+
import { LyraElement } from '../../internal/lyra-element.js';
|
|
10
|
+
import { defineElement } from '../../internal/prefix.js';
|
|
11
|
+
import { place } from '../../internal/positioner.js';
|
|
12
|
+
import { rtlAwarePlacement } from '../../internal/rtl.js';
|
|
13
|
+
import { nextId } from '../../internal/a11y.js';
|
|
14
|
+
import { styles } from './menu.styles.js';
|
|
15
|
+
import { LyraMenuItem } from './menu-item.js';
|
|
16
|
+
import './menu-item.js';
|
|
17
|
+
/**
|
|
18
|
+
* `<lyra-menu>` — an anchored dropdown of `<lyra-menu-item>` actions, opened
|
|
19
|
+
* from a consumer-supplied trigger (typically an icon button). A close, drop-
|
|
20
|
+
* in-shaped replacement for reaching outside this library for a third-party
|
|
21
|
+
* dropdown to build a gear menu, an avatar menu, or a history row's overflow
|
|
22
|
+
* menu: click the trigger, a positioned menu appears, clicking an item both
|
|
23
|
+
* performs the action *and* closes the menu.
|
|
24
|
+
*
|
|
25
|
+
* **ARIA pattern — `role="menu"`/`role="menuitem"` with real roving DOM
|
|
26
|
+
* focus, not a listbox.** Two coherent, mutually-exclusive shapes were
|
|
27
|
+
* available here: (a) `role="listbox"`/`role="option"` with
|
|
28
|
+
* `aria-activedescendant`, the pattern `<lyra-select>`'s trigger-button +
|
|
29
|
+
* popup listbox uses, where DOM focus never leaves the trigger; or (b)
|
|
30
|
+
* `role="menu"`/`role="menuitem"` with real focus moving between actual
|
|
31
|
+
* focusable rows, the WAI-ARIA "menu button" pattern. This picks (b):
|
|
32
|
+
* `<lyra-menu-item>` rows are real, independently-focusable elements (see
|
|
33
|
+
* that class's own doc), which is the more natural fit for a menu
|
|
34
|
+
* specifically — unlike a listbox's rows, a menu's rows are conventionally
|
|
35
|
+
* button-/link-shaped, and every well-known native/OS menu (and this
|
|
36
|
+
* family's own `<lyra-tree>`/`<lyra-tree-node>` pair, which this component's
|
|
37
|
+
* roving-tabindex plumbing directly mirrors) already moves real focus rather
|
|
38
|
+
* than merely a virtual `aria-activedescendant` pointer. `role`/`tabIndex`
|
|
39
|
+
* are consistently the menu-button shape throughout — never mixed with
|
|
40
|
+
* listbox/option.
|
|
41
|
+
*
|
|
42
|
+
* Interaction contract (mirrors the WAI-ARIA APG "menu button" pattern):
|
|
43
|
+
* - Click the trigger (or Enter/Space on it, via the trigger's own native
|
|
44
|
+
* `click` activation) toggles the menu, moving focus to the first
|
|
45
|
+
* non-disabled item on open.
|
|
46
|
+
* - ArrowDown/ArrowUp on the trigger while closed also open it, focusing the
|
|
47
|
+
* first/last non-disabled item respectively.
|
|
48
|
+
* - Once open, ArrowDown/ArrowUp move the roving focus among non-disabled
|
|
49
|
+
* items (wrapping past either end — the recommended, and more common,
|
|
50
|
+
* menu-widget behavior, unlike `<lyra-select>`'s clamped listbox nav).
|
|
51
|
+
* Home/End jump to the first/last non-disabled item. Enter/Space activate
|
|
52
|
+
* the focused item. Escape closes and returns focus to the trigger. Tab
|
|
53
|
+
* closes the menu without trapping focus (the browser's own default Tab
|
|
54
|
+
* behavior proceeds untouched).
|
|
55
|
+
* - A click outside both the trigger and the open popup closes it (mirrors
|
|
56
|
+
* `<lyra-select>`'s `onDocPointer` exactly) — this does *not* refocus the
|
|
57
|
+
* trigger, since the outside click itself already moved focus somewhere
|
|
58
|
+
* the user chose; Escape and a committed selection *do* refocus the
|
|
59
|
+
* trigger, since those are dismissals with nowhere else for focus to go.
|
|
60
|
+
*
|
|
61
|
+
* The trigger element itself is read from the `trigger` slot's assigned
|
|
62
|
+
* element (first one, if several are assigned) and enhanced imperatively
|
|
63
|
+
* with `aria-haspopup="menu"`/`aria-expanded`/`aria-controls` — the same
|
|
64
|
+
* "reach into a consumer-owned light-DOM element to complete its a11y
|
|
65
|
+
* wiring" approach `<lyra-dialog>` documents for its own heading detection,
|
|
66
|
+
* necessary here because those attributes belong on the actual interactive
|
|
67
|
+
* trigger, which lives outside this component's own shadow root.
|
|
68
|
+
*
|
|
69
|
+
* The popup is always rendered (never `display:none`) so `.focus()` calls on
|
|
70
|
+
* its content work synchronously the instant it opens — visually hidden via
|
|
71
|
+
* `visibility`/`opacity` instead (identical to `<lyra-select>`'s own
|
|
72
|
+
* `[part="listbox"]`). `visibility` is an inherited CSS property that
|
|
73
|
+
* pierces the `<slot>` projection boundary, so every closed-state
|
|
74
|
+
* `<lyra-menu-item>` is automatically excluded from sequential (Tab-key)
|
|
75
|
+
* navigation with no separate JS bookkeeping.
|
|
76
|
+
*
|
|
77
|
+
* @customElement lyra-menu
|
|
78
|
+
* @slot trigger - The consumer's own trigger element (typically an icon
|
|
79
|
+
* button). Clicking it toggles the menu; it's positioned against via
|
|
80
|
+
* `internal/positioner.js`'s `place()`.
|
|
81
|
+
* @slot - `<lyra-menu-item>` elements, plus optionally plain `<hr>` dividers
|
|
82
|
+
* between groups (native `<hr>` already carries an implicit `separator`
|
|
83
|
+
* role, matching what `role="menu"` expects between item groups).
|
|
84
|
+
* @event lyra-show - The menu opened. Not fired for markup that renders
|
|
85
|
+
* `open` true from the start (mirrors `<lyra-select>`'s identical guard).
|
|
86
|
+
* @event lyra-hide - The menu closed. Same first-render guard as `lyra-show`.
|
|
87
|
+
* @event lyra-menu-select - A `<lyra-menu-item>` was activated. `detail: {
|
|
88
|
+
* value }` — the consolidated re-fire of that item's own
|
|
89
|
+
* `lyra-menu-item-select` (see `<lyra-menu-item>`'s doc for why listening
|
|
90
|
+
* here, rather than on every item, is the recommended approach). Always
|
|
91
|
+
* followed by the menu closing and focus returning to the trigger.
|
|
92
|
+
* @csspart trigger - The wrapper around the `trigger` slot (the positioning anchor).
|
|
93
|
+
* @csspart popup - The positioned floating panel.
|
|
94
|
+
* @csspart list - The `role="menu"` container wrapping the default slot.
|
|
95
|
+
*/
|
|
96
|
+
export class LyraMenu extends LyraElement {
|
|
97
|
+
constructor() {
|
|
98
|
+
super(...arguments);
|
|
99
|
+
/** Whether the menu is open. */
|
|
100
|
+
this.open = false;
|
|
101
|
+
/** Accessible name for the `role="menu"` popup — override with something
|
|
102
|
+
* specific (e.g. "Row actions") when a page has more than one menu. */
|
|
103
|
+
this.label = 'Menu';
|
|
104
|
+
// Plain instance fields, not @state() -- render()'s template never reads
|
|
105
|
+
// either (items render via the plain default <slot>; there is no
|
|
106
|
+
// activeIndex-driven markup), so reactively scheduling a re-render on
|
|
107
|
+
// every roving-focus move would only trigger Lit's "scheduled an update
|
|
108
|
+
// after an update completed" dev-mode warning for no visual benefit --
|
|
109
|
+
// both only drive imperative side effects (applyRovingTabIndex()/.focus()).
|
|
110
|
+
this.items = [];
|
|
111
|
+
this.activeIndex = -1;
|
|
112
|
+
this._isFirstUpdate = true;
|
|
113
|
+
this.pendingFocus = 'first';
|
|
114
|
+
this.listId = nextId('menu-list');
|
|
115
|
+
this.onDocPointer = (e) => {
|
|
116
|
+
if (!e.composedPath().includes(this))
|
|
117
|
+
this.hide();
|
|
118
|
+
};
|
|
119
|
+
this.onTriggerClick = () => {
|
|
120
|
+
this.open ? this.hide() : this.show();
|
|
121
|
+
};
|
|
122
|
+
this.onTriggerKeyDown = (e) => {
|
|
123
|
+
// A safety net for a menu with zero navigable items: focus never leaves
|
|
124
|
+
// the trigger in that edge case (see focusRoving()), so onListKeyDown's
|
|
125
|
+
// own Escape handling would otherwise never run.
|
|
126
|
+
if (e.key === 'Escape' && this.open) {
|
|
127
|
+
e.preventDefault();
|
|
128
|
+
this.hide(true);
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
if (this.open)
|
|
132
|
+
return;
|
|
133
|
+
switch (e.key) {
|
|
134
|
+
case 'ArrowDown':
|
|
135
|
+
e.preventDefault();
|
|
136
|
+
this.show('first');
|
|
137
|
+
break;
|
|
138
|
+
case 'ArrowUp':
|
|
139
|
+
e.preventDefault();
|
|
140
|
+
this.show('last');
|
|
141
|
+
break;
|
|
142
|
+
default:
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
this.onTriggerSlotChange = (e) => {
|
|
147
|
+
const assigned = e.target.assignedElements({ flatten: true });
|
|
148
|
+
const next = assigned[0];
|
|
149
|
+
if (next === this.triggerEl)
|
|
150
|
+
return;
|
|
151
|
+
if (this.triggerEl) {
|
|
152
|
+
this.triggerEl.removeAttribute('aria-haspopup');
|
|
153
|
+
this.triggerEl.removeAttribute('aria-expanded');
|
|
154
|
+
this.triggerEl.removeAttribute('aria-controls');
|
|
155
|
+
}
|
|
156
|
+
this.triggerEl = next;
|
|
157
|
+
this.syncTriggerA11y();
|
|
158
|
+
// Covers the "open from the start" race documented on reposition()'s
|
|
159
|
+
// call in updated() -- a no-op resubscribe once already positioned.
|
|
160
|
+
if (this.open)
|
|
161
|
+
this.reposition();
|
|
162
|
+
};
|
|
163
|
+
this.onItemsSlotChange = (e) => {
|
|
164
|
+
this.items = e.target
|
|
165
|
+
.assignedElements({ flatten: true })
|
|
166
|
+
.filter((el) => el instanceof LyraMenuItem);
|
|
167
|
+
if (this.activeIndex >= this.items.length)
|
|
168
|
+
this.activeIndex = -1;
|
|
169
|
+
this.applyRovingTabIndex();
|
|
170
|
+
// Covers the same "open from the start" race as onTriggerSlotChange's
|
|
171
|
+
// reposition() call -- activeIndex === -1 means nothing has claimed the
|
|
172
|
+
// roving focus yet (never true once a user has actually navigated),
|
|
173
|
+
// so this only ever fires for that initial catch-up, not on every later
|
|
174
|
+
// items mutation while open.
|
|
175
|
+
if (this.open && this.activeIndex === -1)
|
|
176
|
+
this.focusRoving(this.pendingFocus);
|
|
177
|
+
};
|
|
178
|
+
this.onItemSelect = (e) => {
|
|
179
|
+
const item = e.target;
|
|
180
|
+
if (!(item instanceof LyraMenuItem))
|
|
181
|
+
return;
|
|
182
|
+
this.emit('lyra-menu-select', { value: item.value });
|
|
183
|
+
this.hide(true);
|
|
184
|
+
};
|
|
185
|
+
this.onListKeyDown = (e) => {
|
|
186
|
+
const navigable = this.items.filter((i) => !i.disabled);
|
|
187
|
+
const current = this.activeIndex >= 0 ? this.items[this.activeIndex] : undefined;
|
|
188
|
+
const currentNavIndex = current ? navigable.indexOf(current) : -1;
|
|
189
|
+
switch (e.key) {
|
|
190
|
+
case 'ArrowDown':
|
|
191
|
+
e.preventDefault();
|
|
192
|
+
if (navigable.length) {
|
|
193
|
+
this.setActiveItem(navigable[(currentNavIndex + 1 + navigable.length) % navigable.length]);
|
|
194
|
+
}
|
|
195
|
+
break;
|
|
196
|
+
case 'ArrowUp':
|
|
197
|
+
e.preventDefault();
|
|
198
|
+
if (navigable.length) {
|
|
199
|
+
const prevIndex = currentNavIndex <= 0 ? navigable.length - 1 : currentNavIndex - 1;
|
|
200
|
+
this.setActiveItem(navigable[prevIndex]);
|
|
201
|
+
}
|
|
202
|
+
break;
|
|
203
|
+
case 'Home':
|
|
204
|
+
e.preventDefault();
|
|
205
|
+
if (navigable.length)
|
|
206
|
+
this.setActiveItem(navigable[0]);
|
|
207
|
+
break;
|
|
208
|
+
case 'End':
|
|
209
|
+
e.preventDefault();
|
|
210
|
+
if (navigable.length)
|
|
211
|
+
this.setActiveItem(navigable[navigable.length - 1]);
|
|
212
|
+
break;
|
|
213
|
+
case 'Enter':
|
|
214
|
+
case ' ':
|
|
215
|
+
// Mirrors lyra-tree calling current.select() from its own delegated
|
|
216
|
+
// keydown handler, rather than each row wiring its own keydown.
|
|
217
|
+
e.preventDefault();
|
|
218
|
+
current?.select();
|
|
219
|
+
break;
|
|
220
|
+
case 'Escape':
|
|
221
|
+
e.preventDefault();
|
|
222
|
+
this.hide(true);
|
|
223
|
+
break;
|
|
224
|
+
case 'Tab':
|
|
225
|
+
// No preventDefault -- the browser's own default Tab navigation is
|
|
226
|
+
// left to proceed untouched, only the (now-stale) open menu closes.
|
|
227
|
+
this.hide();
|
|
228
|
+
break;
|
|
229
|
+
default:
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
static { this.styles = [LyraElement.styles, styles]; }
|
|
235
|
+
willUpdate() {
|
|
236
|
+
this._isFirstUpdate = !this.hasUpdated;
|
|
237
|
+
}
|
|
238
|
+
updated(changed) {
|
|
239
|
+
if (changed.has('open')) {
|
|
240
|
+
this.cleanup?.();
|
|
241
|
+
this.cleanup = undefined;
|
|
242
|
+
// All open-driven side effects (positioning, the click-outside
|
|
243
|
+
// listener, the lyra-show/lyra-hide events, and moving focus into the
|
|
244
|
+
// menu) live here rather than in show()/hide() so they fire however
|
|
245
|
+
// `open` became true -- via show()/hide()'s own user-interaction
|
|
246
|
+
// paths, or a consumer/test setting `el.open` directly, which bypasses
|
|
247
|
+
// both. Mirrors lyra-select's identical updated()-centralized approach.
|
|
248
|
+
if (this.open) {
|
|
249
|
+
document.addEventListener('pointerdown', this.onDocPointer);
|
|
250
|
+
if (!this._isFirstUpdate)
|
|
251
|
+
this.emit('lyra-show');
|
|
252
|
+
// Both reposition() and focusRoving() no-op gracefully if triggerEl/
|
|
253
|
+
// items aren't populated yet -- for markup that renders `open` true
|
|
254
|
+
// from the start, the trigger/default slots' *own* slotchange events
|
|
255
|
+
// (queued as microtasks) can still be pending at this point, ahead of
|
|
256
|
+
// Lit's synchronous first update. onTriggerSlotChange/
|
|
257
|
+
// onItemsSlotChange below re-run these same two calls once that
|
|
258
|
+
// catches up, so this always resolves correctly either way.
|
|
259
|
+
this.reposition();
|
|
260
|
+
this.focusRoving(this.pendingFocus);
|
|
261
|
+
}
|
|
262
|
+
else {
|
|
263
|
+
document.removeEventListener('pointerdown', this.onDocPointer);
|
|
264
|
+
if (!this._isFirstUpdate)
|
|
265
|
+
this.emit('lyra-hide');
|
|
266
|
+
}
|
|
267
|
+
this.syncTriggerA11y();
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
reposition() {
|
|
271
|
+
this.cleanup?.();
|
|
272
|
+
this.cleanup = undefined;
|
|
273
|
+
const popup = this.renderRoot.querySelector('[part="popup"]');
|
|
274
|
+
if (this.triggerEl && popup) {
|
|
275
|
+
const placement = this.placement && rtlAwarePlacement(this.placement, this);
|
|
276
|
+
this.cleanup = place(this.triggerEl, popup, placement ? { placement } : {});
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
disconnectedCallback() {
|
|
280
|
+
super.disconnectedCallback();
|
|
281
|
+
this.cleanup?.();
|
|
282
|
+
this.cleanup = undefined;
|
|
283
|
+
document.removeEventListener('pointerdown', this.onDocPointer);
|
|
284
|
+
}
|
|
285
|
+
show(focus = 'first') {
|
|
286
|
+
if (this.open)
|
|
287
|
+
return;
|
|
288
|
+
this.pendingFocus = focus;
|
|
289
|
+
this.open = true;
|
|
290
|
+
}
|
|
291
|
+
/** `refocusTrigger` is only ever `true` for a dismissal with nowhere else
|
|
292
|
+
* for focus to land (Escape, a committed selection) -- see the class
|
|
293
|
+
* doc's interaction contract for why an outside click deliberately omits it. */
|
|
294
|
+
hide(refocusTrigger = false) {
|
|
295
|
+
if (!this.open)
|
|
296
|
+
return;
|
|
297
|
+
this.open = false;
|
|
298
|
+
this.activeIndex = -1;
|
|
299
|
+
this.applyRovingTabIndex();
|
|
300
|
+
if (refocusTrigger)
|
|
301
|
+
this.triggerEl?.focus();
|
|
302
|
+
}
|
|
303
|
+
/** `aria-haspopup`/`aria-expanded`/`aria-controls` belong on the actual
|
|
304
|
+
* interactive trigger, which is consumer-owned light-DOM content outside
|
|
305
|
+
* this component's own shadow root -- see the class doc. */
|
|
306
|
+
syncTriggerA11y() {
|
|
307
|
+
if (!this.triggerEl)
|
|
308
|
+
return;
|
|
309
|
+
this.triggerEl.setAttribute('aria-haspopup', 'menu');
|
|
310
|
+
this.triggerEl.setAttribute('aria-expanded', this.open ? 'true' : 'false');
|
|
311
|
+
this.triggerEl.setAttribute('aria-controls', this.listId);
|
|
312
|
+
}
|
|
313
|
+
/** Flips exactly one non-disabled item's `tabIndex` to `0` (the roving
|
|
314
|
+
* target) and every other item's to `-1` -- see `<lyra-menu-item>`'s doc
|
|
315
|
+
* for why this is the sole authority over that property. */
|
|
316
|
+
applyRovingTabIndex() {
|
|
317
|
+
this.items.forEach((item, i) => {
|
|
318
|
+
item.tabIndex = i === this.activeIndex ? 0 : -1;
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
/** Moves the roving focus (and real DOM focus) to the first/last
|
|
322
|
+
* non-disabled item. A no-op when there are none -- focus then simply
|
|
323
|
+
* stays on the trigger (see onTriggerKeyDown's Escape safety net). */
|
|
324
|
+
focusRoving(which) {
|
|
325
|
+
const navigable = this.items.filter((i) => !i.disabled);
|
|
326
|
+
if (!navigable.length)
|
|
327
|
+
return;
|
|
328
|
+
const item = which === 'first' ? navigable[0] : navigable[navigable.length - 1];
|
|
329
|
+
this.setActiveItem(item);
|
|
330
|
+
}
|
|
331
|
+
setActiveItem(item) {
|
|
332
|
+
this.activeIndex = this.items.indexOf(item);
|
|
333
|
+
this.applyRovingTabIndex();
|
|
334
|
+
item.focus();
|
|
335
|
+
}
|
|
336
|
+
render() {
|
|
337
|
+
return html `
|
|
338
|
+
<div part="trigger" @click=${this.onTriggerClick} @keydown=${this.onTriggerKeyDown}>
|
|
339
|
+
<slot name="trigger" @slotchange=${this.onTriggerSlotChange}></slot>
|
|
340
|
+
</div>
|
|
341
|
+
<div part="popup">
|
|
342
|
+
<div
|
|
343
|
+
part="list"
|
|
344
|
+
id=${this.listId}
|
|
345
|
+
role="menu"
|
|
346
|
+
aria-label=${this.label}
|
|
347
|
+
@keydown=${this.onListKeyDown}
|
|
348
|
+
@lyra-menu-item-select=${this.onItemSelect}
|
|
349
|
+
>
|
|
350
|
+
<slot @slotchange=${this.onItemsSlotChange}></slot>
|
|
351
|
+
</div>
|
|
352
|
+
</div>
|
|
353
|
+
`;
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
__decorate([
|
|
357
|
+
property({ type: Boolean, reflect: true })
|
|
358
|
+
], LyraMenu.prototype, "open", void 0);
|
|
359
|
+
__decorate([
|
|
360
|
+
property({ reflect: true })
|
|
361
|
+
], LyraMenu.prototype, "placement", void 0);
|
|
362
|
+
__decorate([
|
|
363
|
+
property()
|
|
364
|
+
], LyraMenu.prototype, "label", void 0);
|
|
365
|
+
defineElement('menu', LyraMenu);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const styles: import("lit").CSSResult;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { css } from 'lit';
|
|
2
|
+
export const styles = css `
|
|
3
|
+
/* Fully transparent to layout -- the visible/clickable surface is entirely
|
|
4
|
+
the consumer's own slotted trigger element (see [part='trigger'] below),
|
|
5
|
+
so the host contributes no box of its own for that trigger to sit inside
|
|
6
|
+
(no stray margin/inline-block quirks around, say, a plain icon button). */
|
|
7
|
+
:host {
|
|
8
|
+
display: contents;
|
|
9
|
+
}
|
|
10
|
+
[part='trigger'] {
|
|
11
|
+
/* Same reasoning as :host above -- event listeners still fire on a
|
|
12
|
+
display:contents element (only layout/hit-testing are affected), so
|
|
13
|
+
click/keydown delegation from the slotted trigger keeps working. */
|
|
14
|
+
display: contents;
|
|
15
|
+
}
|
|
16
|
+
[part='popup'] {
|
|
17
|
+
position: fixed;
|
|
18
|
+
z-index: 900;
|
|
19
|
+
box-sizing: border-box;
|
|
20
|
+
min-inline-size: 10rem;
|
|
21
|
+
max-inline-size: min(92vw, 20rem);
|
|
22
|
+
background: var(--lyra-color-surface);
|
|
23
|
+
border: 1px solid var(--lyra-color-border);
|
|
24
|
+
border-radius: var(--lyra-radius);
|
|
25
|
+
box-shadow: var(--lyra-shadow);
|
|
26
|
+
/* Closed state: invisible + slightly raised -- visibility (not
|
|
27
|
+
display:none) so opacity/transform can actually transition, and so a
|
|
28
|
+
light-DOM <lyra-menu-item>'s inherited visibility (see
|
|
29
|
+
menu.ts's class doc on tab-order safety) excludes it from sequential
|
|
30
|
+
focus navigation while closed with no separate tabindex bookkeeping
|
|
31
|
+
needed. Mirrors lyra-select's identical [part='listbox'] treatment. */
|
|
32
|
+
visibility: hidden;
|
|
33
|
+
opacity: 0;
|
|
34
|
+
transform: translateY(-0.25rem);
|
|
35
|
+
transition:
|
|
36
|
+
opacity var(--lyra-transition-fast),
|
|
37
|
+
transform var(--lyra-transition-fast),
|
|
38
|
+
visibility var(--lyra-transition-fast);
|
|
39
|
+
}
|
|
40
|
+
:host([open]) [part='popup'] {
|
|
41
|
+
visibility: visible;
|
|
42
|
+
opacity: 1;
|
|
43
|
+
transform: translateY(0);
|
|
44
|
+
}
|
|
45
|
+
@media (prefers-reduced-motion: reduce) {
|
|
46
|
+
[part='popup'] {
|
|
47
|
+
transition: none !important;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
[part='list'] {
|
|
51
|
+
display: flex;
|
|
52
|
+
flex-direction: column;
|
|
53
|
+
max-block-size: 20rem;
|
|
54
|
+
overflow-y: auto;
|
|
55
|
+
padding: var(--lyra-space-xs);
|
|
56
|
+
outline: none;
|
|
57
|
+
}
|
|
58
|
+
/* A plain <hr> divider between item groups -- native <hr> already carries
|
|
59
|
+
an implicit ARIA role of "separator", exactly what role="menu" expects
|
|
60
|
+
between groups of menuitem children, so no role attribute needs adding. */
|
|
61
|
+
::slotted(hr) {
|
|
62
|
+
border: none;
|
|
63
|
+
border-block-start: 1px solid var(--lyra-color-border);
|
|
64
|
+
margin: var(--lyra-space-xs) var(--lyra-space-xs);
|
|
65
|
+
}
|
|
66
|
+
`;
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { type TemplateResult, type PropertyValues } from 'lit';
|
|
2
|
+
import { LyraElement } from '../../internal/lyra-element.js';
|
|
3
|
+
/** A catalog row: a selectable model, keyed by `id` with a display `label`. */
|
|
4
|
+
export interface LyraModelCatalogEntry {
|
|
5
|
+
id: string;
|
|
6
|
+
label: string;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* The `catalog` shape: either every entry is a plain string (used as both id
|
|
10
|
+
* and label) or every entry is a full `{ id, label }` row — not a mix of both.
|
|
11
|
+
*/
|
|
12
|
+
export type LyraModelCatalog = string[] | LyraModelCatalogEntry[];
|
|
13
|
+
/**
|
|
14
|
+
* `<lyra-model-select>` — a provider/model picker that renders as a closed
|
|
15
|
+
* dropdown when a fixed `catalog` is available, or as a filterable free-text
|
|
16
|
+
* combobox when it isn't (or when `allow-custom` explicitly permits typing
|
|
17
|
+
* something outside the catalog). Built directly on the shared
|
|
18
|
+
* trigger-button/aria-activedescendant listbox technique `<lyra-select>` uses
|
|
19
|
+
* and the filter-as-you-type suggestion-popup technique `<lyra-combobox>`
|
|
20
|
+
* uses — not by composing either element, since the mode switch and the
|
|
21
|
+
* stale-value handling below are specific to this control.
|
|
22
|
+
*
|
|
23
|
+
* A `value` that isn't present in `catalog` (e.g. a model id saved from a
|
|
24
|
+
* provider whose live catalog has since changed) is never silently dropped:
|
|
25
|
+
* `effectiveEntries` appends it to the rendered option list as a synthetic,
|
|
26
|
+
* visually-distinct row (dashed border, italic label, "not in catalog"
|
|
27
|
+
* badge — see `model-select.styles.ts`) computed fresh from `catalog` +
|
|
28
|
+
* `value` on every render, without ever mutating the `catalog` property
|
|
29
|
+
* itself.
|
|
30
|
+
*
|
|
31
|
+
* @customElement lyra-model-select
|
|
32
|
+
* @event lyra-change - The selected/typed value changed. `detail: { value: string; inCatalog: boolean }`.
|
|
33
|
+
* @csspart trigger - The trigger button (closed-dropdown mode's positioning anchor).
|
|
34
|
+
* @csspart combobox - The text-input container (free-text mode's positioning anchor).
|
|
35
|
+
* @csspart combobox-input - The free-text mode's text input.
|
|
36
|
+
* @csspart provider-badge - The optional leading `provider` label.
|
|
37
|
+
* @csspart listbox - The options popover (shared by both modes).
|
|
38
|
+
* @csspart option - An option row.
|
|
39
|
+
* @csspart option-label - An option row's label.
|
|
40
|
+
* @csspart option-badge - The "not in catalog" badge on a synthetic stale-value row.
|
|
41
|
+
* @csspart expand-icon - The dropdown indicator.
|
|
42
|
+
*/
|
|
43
|
+
export declare class LyraModelSelect extends LyraElement {
|
|
44
|
+
static formAssociated: boolean;
|
|
45
|
+
static styles: import("lit").CSSResultGroup[];
|
|
46
|
+
static properties: {
|
|
47
|
+
value: {
|
|
48
|
+
noAccessor: boolean;
|
|
49
|
+
};
|
|
50
|
+
};
|
|
51
|
+
/** Informational only — e.g. `'ollama'`. Rendered as a small leading badge for display grouping. */
|
|
52
|
+
provider: string;
|
|
53
|
+
/** The full model list. Omit (or leave empty) to fall back to plain free-text entry. */
|
|
54
|
+
catalog?: LyraModelCatalog;
|
|
55
|
+
/** Let the user type/commit a value that isn't in `catalog`, even when `catalog` is non-empty. */
|
|
56
|
+
allowCustom: boolean;
|
|
57
|
+
placeholder: string;
|
|
58
|
+
name: string;
|
|
59
|
+
disabled: boolean;
|
|
60
|
+
required: boolean;
|
|
61
|
+
open: boolean;
|
|
62
|
+
private activeIndex;
|
|
63
|
+
private query;
|
|
64
|
+
private touched;
|
|
65
|
+
private internals;
|
|
66
|
+
private listId;
|
|
67
|
+
private controlId;
|
|
68
|
+
private cleanup?;
|
|
69
|
+
private _value;
|
|
70
|
+
private _defaultValue;
|
|
71
|
+
constructor();
|
|
72
|
+
connectedCallback(): void;
|
|
73
|
+
disconnectedCallback(): void;
|
|
74
|
+
attributeChangedCallback(name: string, old: string | null, val: string | null): void;
|
|
75
|
+
/** The current model id (empty string when nothing is selected). */
|
|
76
|
+
get value(): string;
|
|
77
|
+
set value(next: string);
|
|
78
|
+
private updateValidity;
|
|
79
|
+
formResetCallback(): void;
|
|
80
|
+
formDisabledCallback(disabled: boolean): void;
|
|
81
|
+
checkValidity(): boolean;
|
|
82
|
+
reportValidity(): boolean;
|
|
83
|
+
/** `catalog`, normalized to `{ id, label }[]` regardless of the plain-string-array shorthand. */
|
|
84
|
+
private get normalizedCatalog();
|
|
85
|
+
/** Closed-dropdown-with-listbox mode vs. free-text filterable mode — see class doc. */
|
|
86
|
+
private get closedMode();
|
|
87
|
+
/**
|
|
88
|
+
* `normalizedCatalog` plus, when `value` isn't one of its ids, a synthetic
|
|
89
|
+
* trailing row for it — recomputed from scratch on every access so it
|
|
90
|
+
* always reflects the *current* `catalog`/`value`, never a snapshot from
|
|
91
|
+
* whenever `value` happened to be assigned.
|
|
92
|
+
*/
|
|
93
|
+
private get effectiveEntries();
|
|
94
|
+
/** `effectiveEntries` filtered by the typed `query` (free-text mode only; id or label substring, case-insensitive). */
|
|
95
|
+
private get filteredEntries();
|
|
96
|
+
private labelFor;
|
|
97
|
+
private show;
|
|
98
|
+
private hide;
|
|
99
|
+
private onDocPointer;
|
|
100
|
+
protected updated(changed: PropertyValues): void;
|
|
101
|
+
private commitValue;
|
|
102
|
+
private selectEntry;
|
|
103
|
+
/** Enter in free-text mode: commit the highlighted suggestion, else the raw typed text. */
|
|
104
|
+
private commitFreeText;
|
|
105
|
+
private onTriggerClick;
|
|
106
|
+
private onTriggerBlur;
|
|
107
|
+
private onTriggerKeyDown;
|
|
108
|
+
private onComboMouseDown;
|
|
109
|
+
private onInputFocus;
|
|
110
|
+
private onInput;
|
|
111
|
+
private onInputBlur;
|
|
112
|
+
private onInputKeyDown;
|
|
113
|
+
private onListboxMouseDown;
|
|
114
|
+
private onListboxClick;
|
|
115
|
+
private renderRows;
|
|
116
|
+
private renderListbox;
|
|
117
|
+
private renderClosed;
|
|
118
|
+
private renderFreeText;
|
|
119
|
+
render(): TemplateResult;
|
|
120
|
+
}
|
|
121
|
+
declare global {
|
|
122
|
+
interface HTMLElementTagNameMap {
|
|
123
|
+
'lyra-model-select': LyraModelSelect;
|
|
124
|
+
}
|
|
125
|
+
}
|