@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
|
@@ -5,9 +5,10 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
5
5
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
6
|
};
|
|
7
7
|
import { html, nothing } from 'lit';
|
|
8
|
-
import {
|
|
8
|
+
import { state } from 'lit/decorators.js';
|
|
9
9
|
import { LyraElement } from '../../internal/lyra-element.js';
|
|
10
10
|
import { defineElement } from '../../internal/prefix.js';
|
|
11
|
+
import { AnchoredValidityController, VALIDITY_ANCHOR } from '../../internal/anchored-validity.js';
|
|
11
12
|
import { styles } from './switch.styles.js';
|
|
12
13
|
/**
|
|
13
14
|
* `<lyra-switch>` — a boolean toggle-switch form control. Structurally the
|
|
@@ -36,13 +37,71 @@ import { styles } from './switch.styles.js';
|
|
|
36
37
|
export class LyraSwitch extends LyraElement {
|
|
37
38
|
static { this.styles = [LyraElement.styles, styles]; }
|
|
38
39
|
static { this.formAssociated = true; }
|
|
40
|
+
static { this.properties = {
|
|
41
|
+
checked: { type: Boolean, reflect: true, noAccessor: true },
|
|
42
|
+
disabled: { type: Boolean, reflect: true, noAccessor: true },
|
|
43
|
+
name: { reflect: true, noAccessor: true },
|
|
44
|
+
required: { type: Boolean, reflect: true, noAccessor: true },
|
|
45
|
+
value: { noAccessor: true },
|
|
46
|
+
}; }
|
|
47
|
+
/** Whether the control is disabled explicitly or by an ancestor fieldset. */
|
|
48
|
+
get effectiveDisabled() {
|
|
49
|
+
return this.disabled || this._fieldsetDisabled;
|
|
50
|
+
}
|
|
51
|
+
get checked() {
|
|
52
|
+
return this._checked;
|
|
53
|
+
}
|
|
54
|
+
set checked(next) {
|
|
55
|
+
const old = this._checked;
|
|
56
|
+
this._checked = Boolean(next);
|
|
57
|
+
this.syncFormState();
|
|
58
|
+
this.requestUpdate('checked', old);
|
|
59
|
+
}
|
|
60
|
+
get disabled() {
|
|
61
|
+
return this._disabled;
|
|
62
|
+
}
|
|
63
|
+
set disabled(next) {
|
|
64
|
+
const old = this._disabled;
|
|
65
|
+
this._disabled = Boolean(next);
|
|
66
|
+
this.toggleAttribute('disabled', this._disabled);
|
|
67
|
+
this.requestUpdate('disabled', old);
|
|
68
|
+
}
|
|
69
|
+
/** The form submission key, reflected synchronously for native form APIs. */
|
|
70
|
+
get name() {
|
|
71
|
+
return this._name;
|
|
72
|
+
}
|
|
73
|
+
set name(next) {
|
|
74
|
+
const old = this._name;
|
|
75
|
+
this._name = next ?? '';
|
|
76
|
+
if (this._name) {
|
|
77
|
+
this.setAttribute('name', this._name);
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
this.removeAttribute('name');
|
|
81
|
+
}
|
|
82
|
+
this.requestUpdate('name', old);
|
|
83
|
+
}
|
|
84
|
+
get required() {
|
|
85
|
+
return this._required;
|
|
86
|
+
}
|
|
87
|
+
set required(next) {
|
|
88
|
+
const old = this._required;
|
|
89
|
+
this._required = Boolean(next);
|
|
90
|
+
this.toggleAttribute('required', this._required);
|
|
91
|
+
this.updateValidity();
|
|
92
|
+
this.requestUpdate('required', old);
|
|
93
|
+
}
|
|
94
|
+
get value() {
|
|
95
|
+
return this._value;
|
|
96
|
+
}
|
|
97
|
+
set value(next) {
|
|
98
|
+
const old = this._value;
|
|
99
|
+
this._value = next ?? 'on';
|
|
100
|
+
this.syncFormState();
|
|
101
|
+
this.requestUpdate('value', old);
|
|
102
|
+
}
|
|
39
103
|
constructor() {
|
|
40
104
|
super();
|
|
41
|
-
this.checked = false;
|
|
42
|
-
this.disabled = false;
|
|
43
|
-
this.required = false;
|
|
44
|
-
this.name = '';
|
|
45
|
-
this.value = 'on';
|
|
46
105
|
// Tracks whether the default slot carries any real (non-whitespace)
|
|
47
106
|
// content, so the label wrapper — and the `gap` next to the track — can
|
|
48
107
|
// collapse to nothing for an icon-only/aria-label-only switch instead of
|
|
@@ -52,10 +111,10 @@ export class LyraSwitch extends LyraElement {
|
|
|
52
111
|
// common case here, e.g. `<lyra-switch>Enable notifications</lyra-switch>`)
|
|
53
112
|
// is a text node, which `assignedElements` would silently ignore.
|
|
54
113
|
this.hasLabelSlot = false;
|
|
55
|
-
// What `form.reset()` restores to — captured once
|
|
56
|
-
// `checked`
|
|
57
|
-
//
|
|
58
|
-
// `
|
|
114
|
+
// What `form.reset()` restores to — captured once from the declarative
|
|
115
|
+
// `checked` content attribute at first connect. A pre-connect `.checked`
|
|
116
|
+
// property assignment changes live state but not the reset default, matching
|
|
117
|
+
// native `checked`/`defaultChecked` semantics. `checked` reflects, so unlike
|
|
59
118
|
// `FormAssociated`'s non-reflecting `value` this can't be captured from
|
|
60
119
|
// `attributeChangedCallback` alone — that would also fire (and wrongly
|
|
61
120
|
// redefine the default) every time the property setter itself reflects a
|
|
@@ -63,11 +122,17 @@ export class LyraSwitch extends LyraElement {
|
|
|
63
122
|
// instead mirrors `<lyra-combobox>`'s `_defaultCaptured`/`_defaultSelected`.
|
|
64
123
|
this._defaultChecked = false;
|
|
65
124
|
this._defaultCaptured = false;
|
|
125
|
+
this._fieldsetDisabled = false;
|
|
126
|
+
this._name = '';
|
|
127
|
+
this._checked = false;
|
|
128
|
+
this._disabled = false;
|
|
129
|
+
this._required = false;
|
|
130
|
+
this._value = 'on';
|
|
66
131
|
this.onClick = () => {
|
|
67
132
|
this.toggle();
|
|
68
133
|
};
|
|
69
134
|
this.onKeyDown = (e) => {
|
|
70
|
-
if (this.
|
|
135
|
+
if (this.effectiveDisabled)
|
|
71
136
|
return;
|
|
72
137
|
// Space/Enter both activate, matching `<lyra-table>`'s sortable
|
|
73
138
|
// header/row convention (`table.ts`'s `onHeaderKeyDown`/`onRowKeyDown`)
|
|
@@ -83,12 +148,33 @@ export class LyraSwitch extends LyraElement {
|
|
|
83
148
|
this.hasLabelSlot = nodes.some((n) => (n.textContent ?? '').trim().length > 0);
|
|
84
149
|
};
|
|
85
150
|
this.internals = this.attachInternals();
|
|
151
|
+
this.validityController = new AnchoredValidityController(this, this.internals, () => this[VALIDITY_ANCHOR]());
|
|
152
|
+
this.syncFormState();
|
|
153
|
+
}
|
|
154
|
+
get form() {
|
|
155
|
+
return this.internals.form;
|
|
156
|
+
}
|
|
157
|
+
get labels() {
|
|
158
|
+
return this.internals.labels;
|
|
159
|
+
}
|
|
160
|
+
get validity() {
|
|
161
|
+
return this.internals.validity;
|
|
162
|
+
}
|
|
163
|
+
get validationMessage() {
|
|
164
|
+
return this.internals.validationMessage;
|
|
165
|
+
}
|
|
166
|
+
get willValidate() {
|
|
167
|
+
return this.internals.willValidate;
|
|
168
|
+
}
|
|
169
|
+
/** @internal */
|
|
170
|
+
[VALIDITY_ANCHOR]() {
|
|
171
|
+
return this.renderRoot?.querySelector('[part="base"]') ?? null;
|
|
86
172
|
}
|
|
87
173
|
connectedCallback() {
|
|
88
174
|
super.connectedCallback();
|
|
89
175
|
if (!this._defaultCaptured) {
|
|
90
176
|
this._defaultCaptured = true;
|
|
91
|
-
this._defaultChecked = this.checked;
|
|
177
|
+
this._defaultChecked = this.hasAttribute('checked');
|
|
92
178
|
}
|
|
93
179
|
this.updateValidity();
|
|
94
180
|
}
|
|
@@ -103,25 +189,25 @@ export class LyraSwitch extends LyraElement {
|
|
|
103
189
|
}
|
|
104
190
|
updateValidity() {
|
|
105
191
|
if (this.required && !this.checked) {
|
|
106
|
-
this.
|
|
192
|
+
this.validityController.setValidity({ valueMissing: true }, 'Please turn this on.');
|
|
107
193
|
}
|
|
108
194
|
else {
|
|
109
|
-
this.
|
|
195
|
+
this.validityController.setValidity({});
|
|
110
196
|
}
|
|
111
197
|
}
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
}
|
|
116
|
-
if (changed.has('checked') || changed.has('required')) {
|
|
117
|
-
this.updateValidity();
|
|
118
|
-
}
|
|
198
|
+
syncFormState() {
|
|
199
|
+
this.internals.setFormValue(this.checked ? this.value : null, this.checked ? 'checked' : 'unchecked');
|
|
200
|
+
this.updateValidity();
|
|
119
201
|
}
|
|
120
202
|
formResetCallback() {
|
|
121
203
|
this.checked = this._defaultChecked;
|
|
122
204
|
}
|
|
205
|
+
formStateRestoreCallback(state, _mode) {
|
|
206
|
+
this.checked = state === 'checked';
|
|
207
|
+
}
|
|
123
208
|
formDisabledCallback(disabled) {
|
|
124
|
-
this.
|
|
209
|
+
this._fieldsetDisabled = disabled;
|
|
210
|
+
this.requestUpdate();
|
|
125
211
|
}
|
|
126
212
|
checkValidity() {
|
|
127
213
|
return this.internals.checkValidity();
|
|
@@ -130,7 +216,7 @@ export class LyraSwitch extends LyraElement {
|
|
|
130
216
|
return this.internals.reportValidity();
|
|
131
217
|
}
|
|
132
218
|
toggle() {
|
|
133
|
-
if (this.
|
|
219
|
+
if (this.effectiveDisabled)
|
|
134
220
|
return;
|
|
135
221
|
this.checked = !this.checked;
|
|
136
222
|
this.emit('lyra-change', { checked: this.checked });
|
|
@@ -140,10 +226,10 @@ export class LyraSwitch extends LyraElement {
|
|
|
140
226
|
<span
|
|
141
227
|
part="base"
|
|
142
228
|
role="switch"
|
|
143
|
-
tabindex=${this.
|
|
229
|
+
tabindex=${this.effectiveDisabled ? '-1' : '0'}
|
|
144
230
|
aria-checked=${this.checked ? 'true' : 'false'}
|
|
145
231
|
aria-required=${this.required ? 'true' : nothing}
|
|
146
|
-
aria-disabled=${this.
|
|
232
|
+
aria-disabled=${this.effectiveDisabled ? 'true' : nothing}
|
|
147
233
|
aria-label=${this.getAttribute('aria-label') || nothing}
|
|
148
234
|
@click=${this.onClick}
|
|
149
235
|
@keydown=${this.onKeyDown}
|
|
@@ -158,21 +244,6 @@ export class LyraSwitch extends LyraElement {
|
|
|
158
244
|
`;
|
|
159
245
|
}
|
|
160
246
|
}
|
|
161
|
-
__decorate([
|
|
162
|
-
property({ type: Boolean, reflect: true })
|
|
163
|
-
], LyraSwitch.prototype, "checked", void 0);
|
|
164
|
-
__decorate([
|
|
165
|
-
property({ type: Boolean, reflect: true })
|
|
166
|
-
], LyraSwitch.prototype, "disabled", void 0);
|
|
167
|
-
__decorate([
|
|
168
|
-
property({ type: Boolean, reflect: true })
|
|
169
|
-
], LyraSwitch.prototype, "required", void 0);
|
|
170
|
-
__decorate([
|
|
171
|
-
property()
|
|
172
|
-
], LyraSwitch.prototype, "name", void 0);
|
|
173
|
-
__decorate([
|
|
174
|
-
property()
|
|
175
|
-
], LyraSwitch.prototype, "value", void 0);
|
|
176
247
|
__decorate([
|
|
177
248
|
state()
|
|
178
249
|
], LyraSwitch.prototype, "hasLabelSlot", void 0);
|
|
@@ -21,7 +21,7 @@ export const styles = css `
|
|
|
21
21
|
outline: var(--lyra-focus-ring-width) solid var(--lyra-focus-ring-color);
|
|
22
22
|
outline-offset: var(--lyra-focus-ring-offset);
|
|
23
23
|
}
|
|
24
|
-
:host(
|
|
24
|
+
:host(:disabled) [part='base'] {
|
|
25
25
|
cursor: not-allowed;
|
|
26
26
|
opacity: var(--lyra-opacity-disabled);
|
|
27
27
|
}
|
|
@@ -37,6 +37,9 @@ export interface TableColumn<T> {
|
|
|
37
37
|
* Enter/Space still only sort/activate (see `activateColumn()` /
|
|
38
38
|
* `activateRow()`).
|
|
39
39
|
*
|
|
40
|
+
* Set `aria-label` on the host to give the `role="grid"` element an
|
|
41
|
+
* accessible name; it's forwarded into the shadow DOM's `<table>`.
|
|
42
|
+
*
|
|
40
43
|
* `columns[].priority` ('medium' | 'low') hides that column under
|
|
41
44
|
* `[part='base']`'s `@container` breakpoints; `[part='reveal-columns-button']`
|
|
42
45
|
* forces them all back into view. Rather than a *static* check of whether any
|
|
@@ -56,7 +59,15 @@ export interface TableColumn<T> {
|
|
|
56
59
|
* (a `priority` column just became hidden/un-hidden by the `@container`
|
|
57
60
|
* rules, or `showAllColumns` force-visible mode was toggled while a
|
|
58
61
|
* `priority` column was hidden). `detail: { hidden: boolean }`.
|
|
59
|
-
* @csspart base
|
|
62
|
+
* @csspart base - The root wrapper around the `<table>` and its footer controls.
|
|
63
|
+
* @csspart table - The `<table role="grid">` element.
|
|
64
|
+
* @csspart head - The `<thead>` element.
|
|
65
|
+
* @csspart header-cell - Each `<th>` header cell.
|
|
66
|
+
* @csspart row - Each body `<tr>`.
|
|
67
|
+
* @csspart cell - Each body `<td>`.
|
|
68
|
+
* @csspart more-button - The "load more" control, shown when `hasMore` is true.
|
|
69
|
+
* @csspart sort-icon - The chevron shown in the active sortable column's header cell.
|
|
70
|
+
* @csspart reveal-columns-button - The button that toggles `priority`-hidden columns back into view.
|
|
60
71
|
*/
|
|
61
72
|
export declare class LyraTable<T = unknown> extends LyraElement {
|
|
62
73
|
static styles: import("lit").CSSResultGroup[];
|
|
@@ -119,9 +130,11 @@ export declare class LyraTable<T = unknown> extends LyraElement {
|
|
|
119
130
|
* identity on the next non-empty render, so `updated()` re-observes
|
|
120
131
|
* whenever this no longer matches the live element. */
|
|
121
132
|
private observedBase?;
|
|
133
|
+
private readonly observedHeaders;
|
|
122
134
|
connectedCallback(): void;
|
|
123
135
|
disconnectedCallback(): void;
|
|
124
136
|
private observeBase;
|
|
137
|
+
private observeHeaders;
|
|
125
138
|
/** Recomputes `columnsHidden` from the live DOM (mirrors `visibleHeaders()`'s
|
|
126
139
|
* own `offsetParent !== null` technique for detecting `@container`-hidden
|
|
127
140
|
* cells) and dispatches `lyra-columns-hidden-change` only on a real
|
|
@@ -130,19 +143,21 @@ export declare class LyraTable<T = unknown> extends LyraElement {
|
|
|
130
143
|
* from the `ResizeObserver` callback (covers a container resize with no
|
|
131
144
|
* Lit-tracked property change at all). */
|
|
132
145
|
private recomputeColumnsHidden;
|
|
146
|
+
private rehomeFocusedColumn;
|
|
133
147
|
private keyOf;
|
|
134
148
|
/** The header cell that currently owns `tabindex="0"`. */
|
|
135
149
|
private focusedColKey;
|
|
136
150
|
/** The body row that currently owns `tabindex="0"`. */
|
|
137
151
|
private focusedRowKey;
|
|
138
152
|
protected willUpdate(changed: PropertyValues): void;
|
|
139
|
-
/** Each sticky column's cumulative
|
|
153
|
+
/** Each sticky column's cumulative inline-start offset — the sum of the *rendered
|
|
140
154
|
* width* of every earlier sticky column — so multiple sticky columns
|
|
141
155
|
* stack left-to-right instead of all pinning to inset-inline-start: 0 and
|
|
142
156
|
* overlapping. Table columns are intrinsically sized (not fixed-width), so
|
|
143
157
|
* this can't be computed in CSS alone; it requires measuring the actual
|
|
144
158
|
* laid-out `offsetWidth` of each earlier sticky column's header cell. */
|
|
145
159
|
private stickyOffsets;
|
|
160
|
+
private applyStickyOffsets;
|
|
146
161
|
/** Applies stickyOffsets()'s measured per-column offsets as an inline
|
|
147
162
|
* `--lyra-table-sticky-offset` custom property on every header cell and
|
|
148
163
|
* body cell in that column (addressed by the shared `data-col-key`
|
|
@@ -158,7 +173,7 @@ export declare class LyraTable<T = unknown> extends LyraElement {
|
|
|
158
173
|
* when `hasSticky` is true (opt-in) and simply recomputes on every
|
|
159
174
|
* update; a future optimization pass could cache column widths instead of
|
|
160
175
|
* re-querying every time — out of scope for this correctness fix. */
|
|
161
|
-
protected updated(): void;
|
|
176
|
+
protected updated(changed: PropertyValues): void;
|
|
162
177
|
private activateColumn;
|
|
163
178
|
private activateRow;
|
|
164
179
|
/** Header cells currently in the tab sequence — excludes columns hidden by
|
|
@@ -16,7 +16,7 @@ import '../empty/empty.js';
|
|
|
16
16
|
* actions-column button). Clicks/keydowns landing on one of these — or
|
|
17
17
|
* bubbling up through one — must not be re-interpreted as row/column
|
|
18
18
|
* activation by the table's own delegated listeners. */
|
|
19
|
-
const INTERACTIVE_SELECTOR = 'button, a[href], input, select, textarea, [role="button"], [role="combobox"], [role="listbox"], [role="slider"]';
|
|
19
|
+
const INTERACTIVE_SELECTOR = 'button, a[href], input, select, textarea, summary, audio[controls], video[controls], [contenteditable]:not([contenteditable="false"]), [tabindex]:not([tabindex="-1"]), [role="button"], [role="checkbox"], [role="combobox"], [role="listbox"], [role="menu"], [role="menuitem"], [role="option"], [role="radio"], [role="slider"], [role="spinbutton"], [role="switch"], [role="tab"], [role="textbox"]';
|
|
20
20
|
/** Encodes a row/column identity key for use as a Map key or a DOM
|
|
21
21
|
* `data-row-key` attribute value, preserving the distinction between a
|
|
22
22
|
* numeric key and a string key that happen to stringify the same way
|
|
@@ -34,7 +34,8 @@ function encodeKey(key) {
|
|
|
34
34
|
function closestInteractive(target, boundary) {
|
|
35
35
|
let el = target;
|
|
36
36
|
while (el && el !== boundary) {
|
|
37
|
-
if (el.matches(INTERACTIVE_SELECTOR)
|
|
37
|
+
if ((el.matches(INTERACTIVE_SELECTOR) && !el.matches('[data-row-key], th[data-col-key]')) ||
|
|
38
|
+
el.tagName.includes('-'))
|
|
38
39
|
return el;
|
|
39
40
|
el = el.parentElement;
|
|
40
41
|
}
|
|
@@ -60,6 +61,9 @@ function closestInteractive(target, boundary) {
|
|
|
60
61
|
* Enter/Space still only sort/activate (see `activateColumn()` /
|
|
61
62
|
* `activateRow()`).
|
|
62
63
|
*
|
|
64
|
+
* Set `aria-label` on the host to give the `role="grid"` element an
|
|
65
|
+
* accessible name; it's forwarded into the shadow DOM's `<table>`.
|
|
66
|
+
*
|
|
63
67
|
* `columns[].priority` ('medium' | 'low') hides that column under
|
|
64
68
|
* `[part='base']`'s `@container` breakpoints; `[part='reveal-columns-button']`
|
|
65
69
|
* forces them all back into view. Rather than a *static* check of whether any
|
|
@@ -79,7 +83,15 @@ function closestInteractive(target, boundary) {
|
|
|
79
83
|
* (a `priority` column just became hidden/un-hidden by the `@container`
|
|
80
84
|
* rules, or `showAllColumns` force-visible mode was toggled while a
|
|
81
85
|
* `priority` column was hidden). `detail: { hidden: boolean }`.
|
|
82
|
-
* @csspart base
|
|
86
|
+
* @csspart base - The root wrapper around the `<table>` and its footer controls.
|
|
87
|
+
* @csspart table - The `<table role="grid">` element.
|
|
88
|
+
* @csspart head - The `<thead>` element.
|
|
89
|
+
* @csspart header-cell - Each `<th>` header cell.
|
|
90
|
+
* @csspart row - Each body `<tr>`.
|
|
91
|
+
* @csspart cell - Each body `<td>`.
|
|
92
|
+
* @csspart more-button - The "load more" control, shown when `hasMore` is true.
|
|
93
|
+
* @csspart sort-icon - The chevron shown in the active sortable column's header cell.
|
|
94
|
+
* @csspart reveal-columns-button - The button that toggles `priority`-hidden columns back into view.
|
|
83
95
|
*/
|
|
84
96
|
export class LyraTable extends LyraElement {
|
|
85
97
|
constructor() {
|
|
@@ -121,6 +133,7 @@ export class LyraTable extends LyraElement {
|
|
|
121
133
|
this.activeRowKey = null;
|
|
122
134
|
this.rowsByKey = new Map();
|
|
123
135
|
this.columnsByKey = new Map();
|
|
136
|
+
this.observedHeaders = new Set();
|
|
124
137
|
this.toggleColumns = () => {
|
|
125
138
|
this.showAllColumns = !this.showAllColumns;
|
|
126
139
|
};
|
|
@@ -164,7 +177,10 @@ export class LyraTable extends LyraElement {
|
|
|
164
177
|
static { this.styles = [LyraElement.styles, styles]; }
|
|
165
178
|
connectedCallback() {
|
|
166
179
|
super.connectedCallback();
|
|
167
|
-
this.resizeObserver = new ResizeObserver(() =>
|
|
180
|
+
this.resizeObserver = new ResizeObserver(() => {
|
|
181
|
+
this.recomputeColumnsHidden();
|
|
182
|
+
this.applyStickyOffsets();
|
|
183
|
+
});
|
|
168
184
|
// A reconnect re-creates the observer above but the shadow root content
|
|
169
185
|
// survives across disconnect/reconnect (Lit doesn't tear down the shadow
|
|
170
186
|
// root) — re-observe [part='base'] here if it already exists from before
|
|
@@ -180,6 +196,7 @@ export class LyraTable extends LyraElement {
|
|
|
180
196
|
super.disconnectedCallback();
|
|
181
197
|
this.resizeObserver?.disconnect();
|
|
182
198
|
this.observedBase = undefined;
|
|
199
|
+
this.observedHeaders.clear();
|
|
183
200
|
}
|
|
184
201
|
observeBase(base) {
|
|
185
202
|
if (this.observedBase === base)
|
|
@@ -189,6 +206,23 @@ export class LyraTable extends LyraElement {
|
|
|
189
206
|
this.resizeObserver?.observe(base);
|
|
190
207
|
this.observedBase = base;
|
|
191
208
|
}
|
|
209
|
+
observeHeaders() {
|
|
210
|
+
const headers = new Set(this.columns.some((column) => column.sticky)
|
|
211
|
+
? this.renderRoot.querySelectorAll('th[data-col-key]')
|
|
212
|
+
: []);
|
|
213
|
+
for (const header of this.observedHeaders) {
|
|
214
|
+
if (!headers.has(header)) {
|
|
215
|
+
this.resizeObserver?.unobserve(header);
|
|
216
|
+
this.observedHeaders.delete(header);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
for (const header of headers) {
|
|
220
|
+
if (!this.observedHeaders.has(header)) {
|
|
221
|
+
this.observedHeaders.add(header);
|
|
222
|
+
this.resizeObserver?.observe(header);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
192
226
|
/** Recomputes `columnsHidden` from the live DOM (mirrors `visibleHeaders()`'s
|
|
193
227
|
* own `offsetParent !== null` technique for detecting `@container`-hidden
|
|
194
228
|
* cells) and dispatches `lyra-columns-hidden-change` only on a real
|
|
@@ -201,20 +235,32 @@ export class LyraTable extends LyraElement {
|
|
|
201
235
|
const anyPriorityHidden = hasPriorityColumns &&
|
|
202
236
|
[...this.renderRoot.querySelectorAll('th[data-priority]')].some((el) => el.offsetParent === null);
|
|
203
237
|
const next = anyPriorityHidden || (hasPriorityColumns && this.showAllColumns);
|
|
238
|
+
this.rehomeFocusedColumn();
|
|
204
239
|
if (this.columnsHidden === next)
|
|
205
240
|
return;
|
|
206
241
|
this.columnsHidden = next;
|
|
207
242
|
this.emit('lyra-columns-hidden-change', { hidden: next });
|
|
208
243
|
}
|
|
244
|
+
rehomeFocusedColumn() {
|
|
245
|
+
const visible = this.visibleHeaders();
|
|
246
|
+
if (visible.length === 0 || this.activeColKey === null)
|
|
247
|
+
return;
|
|
248
|
+
if (!visible.some((header) => header.dataset.colKey === this.activeColKey)) {
|
|
249
|
+
this.activeColKey = visible[0].dataset.colKey ?? null;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
209
252
|
keyOf(row, index) {
|
|
210
253
|
return this.rowKey ? this.rowKey(row) : index;
|
|
211
254
|
}
|
|
212
255
|
/** The header cell that currently owns `tabindex="0"`. */
|
|
213
256
|
focusedColKey() {
|
|
257
|
+
const visible = this.visibleHeaders();
|
|
258
|
+
const visibleKeys = new Set(visible.map((el) => el.dataset.colKey).filter((key) => key !== undefined && this.columnsByKey.has(key)));
|
|
214
259
|
if (this.activeColKey !== null && this.columnsByKey.has(this.activeColKey)) {
|
|
215
|
-
|
|
260
|
+
if (visibleKeys.size === 0 || visibleKeys.has(this.activeColKey))
|
|
261
|
+
return this.activeColKey;
|
|
216
262
|
}
|
|
217
|
-
return this.columns[0]?.key ?? null;
|
|
263
|
+
return visible.find((el) => el.dataset.colKey !== undefined && this.columnsByKey.has(el.dataset.colKey))?.dataset.colKey ?? this.columns[0]?.key ?? null;
|
|
218
264
|
}
|
|
219
265
|
/** The body row that currently owns `tabindex="0"`. */
|
|
220
266
|
focusedRowKey() {
|
|
@@ -236,7 +282,7 @@ export class LyraTable extends LyraElement {
|
|
|
236
282
|
this.columnsByKey = new Map(this.columns.map((c) => [c.key, c]));
|
|
237
283
|
}
|
|
238
284
|
}
|
|
239
|
-
/** Each sticky column's cumulative
|
|
285
|
+
/** Each sticky column's cumulative inline-start offset — the sum of the *rendered
|
|
240
286
|
* width* of every earlier sticky column — so multiple sticky columns
|
|
241
287
|
* stack left-to-right instead of all pinning to inset-inline-start: 0 and
|
|
242
288
|
* overlapping. Table columns are intrinsically sized (not fixed-width), so
|
|
@@ -248,12 +294,23 @@ export class LyraTable extends LyraElement {
|
|
|
248
294
|
for (const col of this.columns) {
|
|
249
295
|
if (!col.sticky)
|
|
250
296
|
continue;
|
|
297
|
+
const headerEl = [...this.renderRoot.querySelectorAll('th[data-col-key]')].find((el) => el.dataset.colKey === col.key);
|
|
251
298
|
offsets.set(col.key, running);
|
|
252
|
-
const headerEl = this.renderRoot.querySelector(`th[data-col-key="${col.key}"]`);
|
|
253
299
|
running += headerEl?.offsetWidth ?? 0;
|
|
254
300
|
}
|
|
255
301
|
return offsets;
|
|
256
302
|
}
|
|
303
|
+
applyStickyOffsets() {
|
|
304
|
+
if (!this.columns.some((c) => c.sticky))
|
|
305
|
+
return;
|
|
306
|
+
const offsets = this.stickyOffsets();
|
|
307
|
+
this.renderRoot.querySelectorAll('[data-col-key]').forEach((el) => {
|
|
308
|
+
const key = el.dataset.colKey;
|
|
309
|
+
if (key !== undefined && offsets.has(key)) {
|
|
310
|
+
el.style.setProperty('--lyra-table-sticky-offset', `${offsets.get(key)}px`);
|
|
311
|
+
}
|
|
312
|
+
});
|
|
313
|
+
}
|
|
257
314
|
/** Applies stickyOffsets()'s measured per-column offsets as an inline
|
|
258
315
|
* `--lyra-table-sticky-offset` custom property on every header cell and
|
|
259
316
|
* body cell in that column (addressed by the shared `data-col-key`
|
|
@@ -269,14 +326,9 @@ export class LyraTable extends LyraElement {
|
|
|
269
326
|
* when `hasSticky` is true (opt-in) and simply recomputes on every
|
|
270
327
|
* update; a future optimization pass could cache column widths instead of
|
|
271
328
|
* re-querying every time — out of scope for this correctness fix. */
|
|
272
|
-
updated() {
|
|
273
|
-
if (
|
|
274
|
-
|
|
275
|
-
this.renderRoot
|
|
276
|
-
.querySelectorAll(`[data-col-key="${key}"]`)
|
|
277
|
-
.forEach((el) => el.style.setProperty('--lyra-table-sticky-offset', `${offset}px`));
|
|
278
|
-
}
|
|
279
|
-
}
|
|
329
|
+
updated(changed) {
|
|
330
|
+
if (changed.has('columns') || changed.has('rows') || changed.has('rowKey'))
|
|
331
|
+
this.applyStickyOffsets();
|
|
280
332
|
// Re-observe [part='base'] whenever this update's render() produced a
|
|
281
333
|
// fresh one (first mount, or a swap to/from the <lyra-empty> template
|
|
282
334
|
// shape) — observeBase() itself no-ops when it's the same element as
|
|
@@ -284,7 +336,16 @@ export class LyraTable extends LyraElement {
|
|
|
284
336
|
const base = this.renderRoot.querySelector('[part="base"]');
|
|
285
337
|
if (base)
|
|
286
338
|
this.observeBase(base);
|
|
287
|
-
this.
|
|
339
|
+
this.observeHeaders();
|
|
340
|
+
// Deferred to a microtask rather than called synchronously here: a real
|
|
341
|
+
// priority-hidden transition mutates the reactive `columnsHidden`
|
|
342
|
+
// property, and doing that from inside this same updated() call stack
|
|
343
|
+
// schedules a second update from within the first update's own lifecycle
|
|
344
|
+
// callback -- Lit's dev-mode "scheduled an update ... after an update
|
|
345
|
+
// completed" warning. Pushing the write out to a microtask lets this
|
|
346
|
+
// update finish first, so the follow-up update is a normal externally
|
|
347
|
+
// triggered one instead.
|
|
348
|
+
queueMicrotask(() => this.recomputeColumnsHidden());
|
|
288
349
|
}
|
|
289
350
|
activateColumn(key) {
|
|
290
351
|
this.activeColKey = key;
|
|
@@ -329,14 +390,15 @@ export class LyraTable extends LyraElement {
|
|
|
329
390
|
const index = headers.indexOf(th);
|
|
330
391
|
if (index < 0)
|
|
331
392
|
return;
|
|
393
|
+
const rtl = getComputedStyle(this).direction === 'rtl';
|
|
332
394
|
switch (e.key) {
|
|
333
395
|
case 'ArrowLeft':
|
|
334
396
|
e.preventDefault();
|
|
335
|
-
this.focusHeader(headers[Math.max(0, index - 1)]);
|
|
397
|
+
this.focusHeader(headers[rtl ? Math.min(headers.length - 1, index + 1) : Math.max(0, index - 1)]);
|
|
336
398
|
return;
|
|
337
399
|
case 'ArrowRight':
|
|
338
400
|
e.preventDefault();
|
|
339
|
-
this.focusHeader(headers[Math.min(headers.length - 1, index + 1)]);
|
|
401
|
+
this.focusHeader(headers[rtl ? Math.max(0, index - 1) : Math.min(headers.length - 1, index + 1)]);
|
|
340
402
|
return;
|
|
341
403
|
case 'Home':
|
|
342
404
|
e.preventDefault();
|
|
@@ -412,7 +474,13 @@ export class LyraTable extends LyraElement {
|
|
|
412
474
|
const focusedRow = this.focusedRowKey();
|
|
413
475
|
return html `
|
|
414
476
|
<div part="base" ?data-force-visible=${this.showAllColumns}>
|
|
415
|
-
<table
|
|
477
|
+
<table
|
|
478
|
+
part="table"
|
|
479
|
+
role="grid"
|
|
480
|
+
aria-label=${this.getAttribute('aria-label') || nothing}
|
|
481
|
+
@click=${this.onTableClick}
|
|
482
|
+
@keydown=${this.onTableKeyDown}
|
|
483
|
+
>
|
|
416
484
|
<thead part="head">
|
|
417
485
|
<tr role="row">
|
|
418
486
|
${this.columns.map((col) => {
|
|
@@ -7,6 +7,22 @@ import { LyraElement } from '../../internal/lyra-element.js';
|
|
|
7
7
|
* `slot` name found among the current children — a child with no `label`,
|
|
8
8
|
* or a name with no matching child, simply never produces a tab.
|
|
9
9
|
*
|
|
10
|
+
* A tab button's *visible* content can carry a leading icon without ever
|
|
11
|
+
* changing its *accessible name* (which always stays exactly `label`'s
|
|
12
|
+
* text, nothing else): give a tab an extra direct-child sibling of
|
|
13
|
+
* `<lyra-tabs>` carrying `slot="<id>-icon"` (that sibling's own content --
|
|
14
|
+
* an inline SVG, an emoji span, a custom icon element, anything -- is
|
|
15
|
+
* entirely up to the consumer). It's rendered ahead of the label inside
|
|
16
|
+
* that tab's button, wrapped in an `aria-hidden="true"` part so it's
|
|
17
|
+
* excluded from accessible-name computation no matter what it contains. A
|
|
18
|
+
* tab with no matching `<id>-icon` sibling renders no icon wrapper at all,
|
|
19
|
+
* so existing text-only tabs are completely unaffected. (A named slot,
|
|
20
|
+
* rather than a second attribute holding an icon-name lookup, was chosen
|
|
21
|
+
* because this library's `internal/icons.ts` is a small closed set of
|
|
22
|
+
* chrome glyphs for this library's *own* components, not a public
|
|
23
|
+
* name-keyed registry -- a slot lets a consumer supply an arbitrary,
|
|
24
|
+
* domain-specific icon instead of being limited to that internal set.)
|
|
25
|
+
*
|
|
10
26
|
* Implements the WAI-ARIA APG tabs pattern with automatic activation:
|
|
11
27
|
* Left/Right (swapped under RTL) move focus *and* selection together,
|
|
12
28
|
* Home/End jump to the first/last enabled tab, and a roving `tabindex`
|
|
@@ -14,10 +30,12 @@ import { LyraElement } from '../../internal/lyra-element.js';
|
|
|
14
30
|
*
|
|
15
31
|
* @customElement lyra-tabs
|
|
16
32
|
* @slot - Direct children with `slot="<id>" label="<text>"` (and optionally `disabled`); one becomes each tab's panel.
|
|
33
|
+
* @slot <id>-icon - Optional sibling direct child supplying a tab's leading icon content; excluded from the tab button's accessible name.
|
|
17
34
|
* @event lyra-tabs-change - `detail: { tabId }`, fired when the active tab changes via click or keyboard.
|
|
18
35
|
* @csspart base - The root wrapper around the tablist and panels.
|
|
19
36
|
* @csspart tablist - The `role="tablist"` row of tab buttons.
|
|
20
37
|
* @csspart tab - A single tab button.
|
|
38
|
+
* @csspart tab-icon - The optional leading-icon wrapper inside a tab button; only rendered when that tab has a matching `<id>-icon` sibling.
|
|
21
39
|
* @csspart panel - A single `role="tabpanel"` wrapper (one per tab, hidden unless active).
|
|
22
40
|
*/
|
|
23
41
|
export declare class LyraTabs extends LyraElement {
|
|
@@ -26,6 +44,8 @@ export declare class LyraTabs extends LyraElement {
|
|
|
26
44
|
active: string;
|
|
27
45
|
private tabs;
|
|
28
46
|
private baseId;
|
|
47
|
+
private nextOpaqueId;
|
|
48
|
+
private readonly idsBySlot;
|
|
29
49
|
private mutationObserver?;
|
|
30
50
|
connectedCallback(): void;
|
|
31
51
|
disconnectedCallback(): void;
|
|
@@ -40,6 +60,9 @@ export declare class LyraTabs extends LyraElement {
|
|
|
40
60
|
private onTabListKeyDown;
|
|
41
61
|
private tabId;
|
|
42
62
|
private panelId;
|
|
63
|
+
private idsFor;
|
|
64
|
+
/** Derives a tab's optional icon-sibling `slot` name from its own `slotName` -- see the class doc. */
|
|
65
|
+
private iconSlotName;
|
|
43
66
|
private renderTab;
|
|
44
67
|
private renderPanel;
|
|
45
68
|
render(): TemplateResult;
|