@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
|
@@ -4,8 +4,9 @@ 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
|
+
import { repeat } from 'lit/directives/repeat.js';
|
|
9
10
|
import { LyraElement } from '../../internal/lyra-element.js';
|
|
10
11
|
import { defineElement } from '../../internal/prefix.js';
|
|
11
12
|
import { isRtl } from '../../internal/rtl.js';
|
|
@@ -18,6 +19,22 @@ import { styles } from './tabs.styles.js';
|
|
|
18
19
|
* `slot` name found among the current children — a child with no `label`,
|
|
19
20
|
* or a name with no matching child, simply never produces a tab.
|
|
20
21
|
*
|
|
22
|
+
* A tab button's *visible* content can carry a leading icon without ever
|
|
23
|
+
* changing its *accessible name* (which always stays exactly `label`'s
|
|
24
|
+
* text, nothing else): give a tab an extra direct-child sibling of
|
|
25
|
+
* `<lyra-tabs>` carrying `slot="<id>-icon"` (that sibling's own content --
|
|
26
|
+
* an inline SVG, an emoji span, a custom icon element, anything -- is
|
|
27
|
+
* entirely up to the consumer). It's rendered ahead of the label inside
|
|
28
|
+
* that tab's button, wrapped in an `aria-hidden="true"` part so it's
|
|
29
|
+
* excluded from accessible-name computation no matter what it contains. A
|
|
30
|
+
* tab with no matching `<id>-icon` sibling renders no icon wrapper at all,
|
|
31
|
+
* so existing text-only tabs are completely unaffected. (A named slot,
|
|
32
|
+
* rather than a second attribute holding an icon-name lookup, was chosen
|
|
33
|
+
* because this library's `internal/icons.ts` is a small closed set of
|
|
34
|
+
* chrome glyphs for this library's *own* components, not a public
|
|
35
|
+
* name-keyed registry -- a slot lets a consumer supply an arbitrary,
|
|
36
|
+
* domain-specific icon instead of being limited to that internal set.)
|
|
37
|
+
*
|
|
21
38
|
* Implements the WAI-ARIA APG tabs pattern with automatic activation:
|
|
22
39
|
* Left/Right (swapped under RTL) move focus *and* selection together,
|
|
23
40
|
* Home/End jump to the first/last enabled tab, and a roving `tabindex`
|
|
@@ -25,10 +42,12 @@ import { styles } from './tabs.styles.js';
|
|
|
25
42
|
*
|
|
26
43
|
* @customElement lyra-tabs
|
|
27
44
|
* @slot - Direct children with `slot="<id>" label="<text>"` (and optionally `disabled`); one becomes each tab's panel.
|
|
45
|
+
* @slot <id>-icon - Optional sibling direct child supplying a tab's leading icon content; excluded from the tab button's accessible name.
|
|
28
46
|
* @event lyra-tabs-change - `detail: { tabId }`, fired when the active tab changes via click or keyboard.
|
|
29
47
|
* @csspart base - The root wrapper around the tablist and panels.
|
|
30
48
|
* @csspart tablist - The `role="tablist"` row of tab buttons.
|
|
31
49
|
* @csspart tab - A single tab button.
|
|
50
|
+
* @csspart tab-icon - The optional leading-icon wrapper inside a tab button; only rendered when that tab has a matching `<id>-icon` sibling.
|
|
32
51
|
* @csspart panel - A single `role="tabpanel"` wrapper (one per tab, hidden unless active).
|
|
33
52
|
*/
|
|
34
53
|
export class LyraTabs extends LyraElement {
|
|
@@ -38,17 +57,27 @@ export class LyraTabs extends LyraElement {
|
|
|
38
57
|
this.active = '';
|
|
39
58
|
this.tabs = [];
|
|
40
59
|
this.baseId = nextId('tabs');
|
|
60
|
+
this.nextOpaqueId = 0;
|
|
61
|
+
this.idsBySlot = new Map();
|
|
41
62
|
/** Rebuilds `tabs` from the current direct children. First child wins when two share a `slot` name (matches native slot assignment: both would render into the one panel, but only one label can back the button). */
|
|
42
63
|
this.syncTabs = () => {
|
|
43
64
|
const seen = new Set();
|
|
44
65
|
const next = [];
|
|
45
|
-
|
|
66
|
+
const children = Array.from(this.children);
|
|
67
|
+
for (const child of children) {
|
|
46
68
|
const slotName = child.getAttribute('slot');
|
|
47
69
|
const label = child.getAttribute('label');
|
|
48
70
|
if (!slotName || !label || seen.has(slotName))
|
|
49
71
|
continue;
|
|
50
72
|
seen.add(slotName);
|
|
51
|
-
|
|
73
|
+
const iconSlot = this.iconSlotName(slotName);
|
|
74
|
+
const hasIcon = children.some((c) => c.getAttribute('slot') === iconSlot);
|
|
75
|
+
next.push({ slotName, label, disabled: child.hasAttribute('disabled'), hasIcon });
|
|
76
|
+
}
|
|
77
|
+
const liveSlots = new Set(next.map((tab) => tab.slotName));
|
|
78
|
+
for (const slotName of this.idsBySlot.keys()) {
|
|
79
|
+
if (!liveSlots.has(slotName))
|
|
80
|
+
this.idsBySlot.delete(slotName);
|
|
52
81
|
}
|
|
53
82
|
this.tabs = next;
|
|
54
83
|
};
|
|
@@ -95,10 +124,24 @@ export class LyraTabs extends LyraElement {
|
|
|
95
124
|
// until *this* component has already rendered one for it, and neither
|
|
96
125
|
// `slotchange` nor any Lit lifecycle hook observes a plain attribute
|
|
97
126
|
// edit (label/disabled) on a light-DOM child at all. A mutation observer
|
|
98
|
-
// on the host is the only thing that sees either case.
|
|
99
|
-
|
|
127
|
+
// on the host is the only thing that sees either case. `attributes: true`
|
|
128
|
+
// alone only reports mutations on the observed node itself (`this`), never
|
|
129
|
+
// on its children, so `subtree: true` is required too -- but that widens
|
|
130
|
+
// `childList`/`attributeFilter` to the *entire* descendant tree, including
|
|
131
|
+
// each panel's own projected content. A panel can legitimately churn its
|
|
132
|
+
// own children/attributes fast (a streaming log, a live JSON preview), so
|
|
133
|
+
// every record is filtered down to direct-child mutations only before
|
|
134
|
+
// triggering a resync, keeping panel-internal churn from forcing a tabs
|
|
135
|
+
// recompute and re-render on every unrelated mutation.
|
|
136
|
+
this.mutationObserver = new MutationObserver((records) => {
|
|
137
|
+
const isDirectChild = (node) => node.parentNode === this;
|
|
138
|
+
const relevant = records.some((r) => (r.type === 'childList' ? r.target === this : isDirectChild(r.target)));
|
|
139
|
+
if (relevant)
|
|
140
|
+
this.syncTabs();
|
|
141
|
+
});
|
|
100
142
|
this.mutationObserver.observe(this, {
|
|
101
143
|
childList: true,
|
|
144
|
+
subtree: true,
|
|
102
145
|
attributes: true,
|
|
103
146
|
attributeFilter: ['slot', 'label', 'disabled'],
|
|
104
147
|
});
|
|
@@ -135,10 +178,23 @@ export class LyraTabs extends LyraElement {
|
|
|
135
178
|
}
|
|
136
179
|
}
|
|
137
180
|
tabId(slotName) {
|
|
138
|
-
return
|
|
181
|
+
return this.idsFor(slotName).tab;
|
|
139
182
|
}
|
|
140
183
|
panelId(slotName) {
|
|
141
|
-
return
|
|
184
|
+
return this.idsFor(slotName).panel;
|
|
185
|
+
}
|
|
186
|
+
idsFor(slotName) {
|
|
187
|
+
const existing = this.idsBySlot.get(slotName);
|
|
188
|
+
if (existing)
|
|
189
|
+
return existing;
|
|
190
|
+
const token = `${this.baseId}-${this.nextOpaqueId++}`;
|
|
191
|
+
const ids = { tab: `${token}-tab`, panel: `${token}-panel` };
|
|
192
|
+
this.idsBySlot.set(slotName, ids);
|
|
193
|
+
return ids;
|
|
194
|
+
}
|
|
195
|
+
/** Derives a tab's optional icon-sibling `slot` name from its own `slotName` -- see the class doc. */
|
|
196
|
+
iconSlotName(slotName) {
|
|
197
|
+
return `${slotName}-icon`;
|
|
142
198
|
}
|
|
143
199
|
renderTab(tab) {
|
|
144
200
|
const selected = tab.slotName === this.active;
|
|
@@ -153,9 +209,9 @@ export class LyraTabs extends LyraElement {
|
|
|
153
209
|
aria-controls=${this.panelId(tab.slotName)}
|
|
154
210
|
tabindex=${selected ? '0' : '-1'}
|
|
155
211
|
@click=${() => this.selectTab(tab)}
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
212
|
+
>${tab.hasIcon
|
|
213
|
+
? html `<span part="tab-icon" aria-hidden="true"><slot name=${this.iconSlotName(tab.slotName)}></slot></span>`
|
|
214
|
+
: nothing}${tab.label}</button>`;
|
|
159
215
|
}
|
|
160
216
|
renderPanel(tab) {
|
|
161
217
|
const selected = tab.slotName === this.active;
|
|
@@ -171,12 +227,20 @@ export class LyraTabs extends LyraElement {
|
|
|
171
227
|
</div>`;
|
|
172
228
|
}
|
|
173
229
|
render() {
|
|
230
|
+
// Keyed by `slotName`, not a plain `.map()`: a plain array binding reuses
|
|
231
|
+
// each rendered DOM node by *position*, so removing e.g. the first tab
|
|
232
|
+
// would silently relabel the still-focused second button's DOM node into
|
|
233
|
+
// the (unfocused) third tab's button, leaving real keyboard focus stuck
|
|
234
|
+
// on a tabindex="-1"/aria-selected="false" button instead of following
|
|
235
|
+
// the tab it used to represent. Keying by identity keeps each tab's own
|
|
236
|
+
// DOM node (and any focus on it) attached to that same tab across
|
|
237
|
+
// additions/removals anywhere in the list.
|
|
174
238
|
return html `
|
|
175
239
|
<div part="base">
|
|
176
240
|
<div part="tablist" role="tablist" aria-orientation="horizontal" @keydown=${this.onTabListKeyDown}>
|
|
177
|
-
${this.tabs.
|
|
241
|
+
${repeat(this.tabs, (tab) => tab.slotName, (tab) => this.renderTab(tab))}
|
|
178
242
|
</div>
|
|
179
|
-
${this.tabs.
|
|
243
|
+
${repeat(this.tabs, (tab) => tab.slotName, (tab) => this.renderPanel(tab))}
|
|
180
244
|
</div>
|
|
181
245
|
`;
|
|
182
246
|
}
|
|
@@ -29,10 +29,21 @@ export const styles = css `
|
|
|
29
29
|
color: var(--lyra-color-text-quiet);
|
|
30
30
|
cursor: pointer;
|
|
31
31
|
white-space: nowrap;
|
|
32
|
+
/* inline-flex only matters once a tab-icon part is also present (gap
|
|
33
|
+
has no effect with a single child) -- a text-only tab lays out
|
|
34
|
+
identically to the previous plain inline-block button. */
|
|
35
|
+
display: inline-flex;
|
|
36
|
+
align-items: center;
|
|
37
|
+
gap: var(--lyra-space-xs);
|
|
32
38
|
transition:
|
|
33
39
|
color var(--lyra-transition-fast),
|
|
34
40
|
border-color var(--lyra-transition-fast);
|
|
35
41
|
}
|
|
42
|
+
[part='tab-icon'] {
|
|
43
|
+
display: inline-flex;
|
|
44
|
+
flex: 0 0 auto;
|
|
45
|
+
align-items: center;
|
|
46
|
+
}
|
|
36
47
|
[part='tab']:hover:not([aria-disabled='true']) {
|
|
37
48
|
color: var(--lyra-color-text);
|
|
38
49
|
}
|
|
@@ -77,7 +77,9 @@ export interface ThinkingPanelToggleDetail {
|
|
|
77
77
|
* @csspart label - The `label` text.
|
|
78
78
|
* @csspart duration - The formatted duration / "Thinking…" placeholder, when shown.
|
|
79
79
|
* @csspart toggle - The chevron indicator inside the header.
|
|
80
|
-
* @csspart body - The wrapper around the default slot, `hidden` while
|
|
80
|
+
* @csspart body - The wrapper around the default slot, `hidden` while
|
|
81
|
+
* collapsed. Independently keyboard-focusable (`tabindex="0"`, `role="group"`
|
|
82
|
+
* named from `label`) since it's its own capped-height scrollable region.
|
|
81
83
|
*/
|
|
82
84
|
export declare class LyraThinkingPanel extends LyraElement {
|
|
83
85
|
static styles: import("lit").CSSResultGroup[];
|
|
@@ -102,7 +102,9 @@ function formatDuration(ms) {
|
|
|
102
102
|
* @csspart label - The `label` text.
|
|
103
103
|
* @csspart duration - The formatted duration / "Thinking…" placeholder, when shown.
|
|
104
104
|
* @csspart toggle - The chevron indicator inside the header.
|
|
105
|
-
* @csspart body - The wrapper around the default slot, `hidden` while
|
|
105
|
+
* @csspart body - The wrapper around the default slot, `hidden` while
|
|
106
|
+
* collapsed. Independently keyboard-focusable (`tabindex="0"`, `role="group"`
|
|
107
|
+
* named from `label`) since it's its own capped-height scrollable region.
|
|
106
108
|
*/
|
|
107
109
|
export class LyraThinkingPanel extends LyraElement {
|
|
108
110
|
constructor() {
|
|
@@ -139,7 +141,13 @@ export class LyraThinkingPanel extends LyraElement {
|
|
|
139
141
|
return;
|
|
140
142
|
this.scrollRafId = requestAnimationFrame(() => {
|
|
141
143
|
this.scrollRafId = undefined;
|
|
142
|
-
|
|
144
|
+
// Re-check the same guard here, not just in the caller: mode/expanded/
|
|
145
|
+
// stickToBottom can all change in the ~1-frame window between this
|
|
146
|
+
// mutation being observed and this callback actually firing (e.g. the
|
|
147
|
+
// reader scrolling away mid-frame), and this is the only place that
|
|
148
|
+
// reflects state as of the moment the scroll would actually happen.
|
|
149
|
+
if (this.mode === 'live' && this.expanded && this.stickToBottom)
|
|
150
|
+
this.scrollToBottom();
|
|
143
151
|
});
|
|
144
152
|
};
|
|
145
153
|
this.toggle = () => {
|
|
@@ -167,12 +175,12 @@ export class LyraThinkingPanel extends LyraElement {
|
|
|
167
175
|
}
|
|
168
176
|
}
|
|
169
177
|
updated(changed) {
|
|
170
|
-
if (changed.has('expanded') && this.expanded && this.mode === 'live') {
|
|
171
|
-
// Opening a live panel
|
|
172
|
-
//
|
|
173
|
-
//
|
|
174
|
-
//
|
|
175
|
-
// the class doc's Auto-scroll section.
|
|
178
|
+
if ((changed.has('expanded') || changed.has('mode')) && this.expanded && this.mode === 'live') {
|
|
179
|
+
// Opening a live panel -- or a still-expanded panel later becoming
|
|
180
|
+
// `'live'` -- always jumps straight to the newest content and resets
|
|
181
|
+
// the sticky-bottom flag, so it starts following again regardless of
|
|
182
|
+
// wherever the body happened to be scrolled beforehand. `'post-hoc'`
|
|
183
|
+
// deliberately skips this -- see the class doc's Auto-scroll section.
|
|
176
184
|
this.stickToBottom = true;
|
|
177
185
|
this.scrollToBottom();
|
|
178
186
|
}
|
|
@@ -194,6 +202,15 @@ export class LyraThinkingPanel extends LyraElement {
|
|
|
194
202
|
}
|
|
195
203
|
return this.mode === 'live' ? { text: 'Thinking…', pending: true } : null;
|
|
196
204
|
}
|
|
205
|
+
// `[part="body"]` is `tabindex="0"` because it's a capped-height,
|
|
206
|
+
// independently-scrollable region -- with no other focusable content of
|
|
207
|
+
// its own (e.g. plain text, or a non-interactive `<lyra-streaming-text>`),
|
|
208
|
+
// it would otherwise be unreachable by keyboard, same reasoning
|
|
209
|
+
// code-block.ts's and virtual-list.ts's own identical `[part="body"]`/
|
|
210
|
+
// `[part="base"]` tabindex document. `role="group"` (rather than a page
|
|
211
|
+
// landmark role) plus `aria-label` gives it an accessible name without
|
|
212
|
+
// claiming navigation significance for what is, structurally, just one
|
|
213
|
+
// part of a larger message.
|
|
197
214
|
render() {
|
|
198
215
|
const duration = this.durationDisplay;
|
|
199
216
|
return html `
|
|
@@ -213,7 +230,15 @@ export class LyraThinkingPanel extends LyraElement {
|
|
|
213
230
|
>`
|
|
214
231
|
: nothing}
|
|
215
232
|
</button>
|
|
216
|
-
<div
|
|
233
|
+
<div
|
|
234
|
+
part="body"
|
|
235
|
+
id=${this.bodyId}
|
|
236
|
+
role="group"
|
|
237
|
+
aria-label=${this.label}
|
|
238
|
+
tabindex="0"
|
|
239
|
+
?hidden=${!this.expanded}
|
|
240
|
+
@scroll=${this.onScroll}
|
|
241
|
+
>
|
|
217
242
|
<slot></slot>
|
|
218
243
|
</div>
|
|
219
244
|
</div>
|
|
@@ -95,6 +95,12 @@ export const styles = css `
|
|
|
95
95
|
[part='body'] {
|
|
96
96
|
max-block-size: var(--lyra-thinking-panel-max-block-size);
|
|
97
97
|
overflow-y: auto;
|
|
98
|
+
/* Reaching this region's own top/bottom edge stops there instead of
|
|
99
|
+
chaining the scroll into the host page -- this is precisely the
|
|
100
|
+
component that tracks and respects the reader scrolling within it, so
|
|
101
|
+
it shouldn't hand that scroll off to the page underneath it. Same
|
|
102
|
+
convention as virtual-list.styles.ts's own auto-scrolling region. */
|
|
103
|
+
overscroll-behavior: contain;
|
|
98
104
|
padding: var(--lyra-space-m);
|
|
99
105
|
border-block-start: 1px solid var(--lyra-color-border);
|
|
100
106
|
color: var(--lyra-color-text-quiet);
|
|
@@ -16,26 +16,72 @@ export interface TimeRangePreset {
|
|
|
16
16
|
* shortcut that sets both handles at once, the continuous brush underneath
|
|
17
17
|
* is unaffected and both interaction modes coexist.
|
|
18
18
|
*
|
|
19
|
+
* Form-associated (via a directly-attached `ElementInternals`, mirroring
|
|
20
|
+
* `<lyra-combobox>`'s minimal pattern rather than the single-string-value
|
|
21
|
+
* `FormAssociated` mixin, which doesn't fit a two-handle range): an ancestor
|
|
22
|
+
* `<fieldset disabled>` disables both handles and every preset button
|
|
23
|
+
* through `effectiveDisabled`, the same way it would a native `<input>`,
|
|
24
|
+
* without touching the consumer-facing `disabled` property/attribute itself.
|
|
25
|
+
*
|
|
19
26
|
* @customElement lyra-time-range
|
|
20
27
|
* @event lyra-input - Fired continuously while dragging or on each arrow-key press. `detail: { start, end }`.
|
|
21
28
|
* @event lyra-change - Fired on release / keyup-commit, or when a preset button is clicked. `detail: { start, end }`.
|
|
22
|
-
* @csspart base
|
|
29
|
+
* @csspart base - The time-range wrapper.
|
|
30
|
+
* @csspart track - The complete range track.
|
|
31
|
+
* @csspart range - The selected range.
|
|
32
|
+
* @csspart handle-start - The start handle.
|
|
33
|
+
* @csspart handle-end - The end handle.
|
|
34
|
+
* @csspart presets - The preset controls wrapper.
|
|
35
|
+
* @csspart preset-button - A preset button.
|
|
23
36
|
*/
|
|
24
37
|
export declare class LyraTimeRange extends LyraElement {
|
|
38
|
+
static formAssociated: boolean;
|
|
25
39
|
static styles: import("lit").CSSResultGroup[];
|
|
40
|
+
static properties: {
|
|
41
|
+
disabled: {
|
|
42
|
+
type: BooleanConstructor;
|
|
43
|
+
reflect: boolean;
|
|
44
|
+
noAccessor: boolean;
|
|
45
|
+
};
|
|
46
|
+
};
|
|
26
47
|
min: number;
|
|
27
48
|
max: number;
|
|
28
49
|
start: number;
|
|
29
50
|
end: number;
|
|
30
51
|
step: number;
|
|
31
|
-
|
|
52
|
+
/** Accessible name for the start handle, used as its `aria-label`.
|
|
53
|
+
* Overridable for i18n/custom copy; defaults to the same literal text
|
|
54
|
+
* this component always rendered before the property existed. */
|
|
55
|
+
startLabel: string;
|
|
56
|
+
/** Accessible name for the end handle, used as its `aria-label`.
|
|
57
|
+
* Overridable for i18n/custom copy; defaults to the same literal text
|
|
58
|
+
* this component always rendered before the property existed. */
|
|
59
|
+
endLabel: string;
|
|
32
60
|
/**
|
|
33
61
|
* Optional discrete presets rendered as a `[part="presets"]` button row
|
|
34
62
|
* above the track. Purely additive: leaving this empty (the default)
|
|
35
63
|
* renders nothing extra and leaves the continuous brush untouched.
|
|
36
64
|
*/
|
|
37
65
|
presets: TimeRangePreset[];
|
|
66
|
+
private internals;
|
|
67
|
+
private _disabled;
|
|
68
|
+
private _fieldsetDisabled;
|
|
38
69
|
private drags;
|
|
70
|
+
constructor();
|
|
71
|
+
get disabled(): boolean;
|
|
72
|
+
set disabled(next: boolean);
|
|
73
|
+
/** Effective disabled state: this element's own `disabled` OR an ancestor
|
|
74
|
+
* `<fieldset disabled>`'s inherited state -- mirrors native `<input>`,
|
|
75
|
+
* whose own `disabled` IDL property/attribute is never mutated by a
|
|
76
|
+
* fieldset. */
|
|
77
|
+
get effectiveDisabled(): boolean;
|
|
78
|
+
/**
|
|
79
|
+
* Called by the browser when an ancestor `<fieldset disabled>` toggles.
|
|
80
|
+
* Tracked separately from the consumer's own `disabled` (see
|
|
81
|
+
* `effectiveDisabled`) so a consumer's explicit `disabled` survives the
|
|
82
|
+
* fieldset re-enabling instead of being permanently overwritten.
|
|
83
|
+
*/
|
|
84
|
+
formDisabledCallback(disabled: boolean): void;
|
|
39
85
|
disconnectedCallback(): void;
|
|
40
86
|
private domain;
|
|
41
87
|
private percentOf;
|
|
@@ -50,6 +96,17 @@ export declare class LyraTimeRange extends LyraElement {
|
|
|
50
96
|
/**
|
|
51
97
|
* Apply a discrete preset: sets both handles and emits the same
|
|
52
98
|
* lyra-input/lyra-change pair a committed drag or keyboard step would.
|
|
99
|
+
*
|
|
100
|
+
* Deliberately bypasses `setValue()`/`clamp()` — a preset is an explicit
|
|
101
|
+
* discrete jump to the caller's own exact numbers, not a stepped nudge, so
|
|
102
|
+
* routing it through `clamp()`'s step-grid rounding would silently round
|
|
103
|
+
* a preset's values away from what the caller specified (and desync the
|
|
104
|
+
* active-button match in render(), which compares against these exact
|
|
105
|
+
* numbers). Only the domain bounds and the start<=end invariant apply
|
|
106
|
+
* here, not `step`. Both handles are also assigned before either event
|
|
107
|
+
* fires — routing them through two sequential `setValue()` calls instead
|
|
108
|
+
* would emit an extra lyra-input whose detail still held the stale
|
|
109
|
+
* pre-preset value for whichever handle hadn't been assigned yet.
|
|
53
110
|
*/
|
|
54
111
|
private applyPreset;
|
|
55
112
|
private onKeyDown;
|