@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.
Files changed (224) hide show
  1. package/README.md +87 -14
  2. package/custom-elements.json +11555 -5539
  3. package/dist/components/app-rail/app-rail-item.d.ts +30 -0
  4. package/dist/components/app-rail/app-rail-item.js +68 -0
  5. package/dist/components/app-rail/app-rail-item.styles.d.ts +1 -0
  6. package/dist/components/app-rail/app-rail-item.styles.js +65 -0
  7. package/dist/components/app-rail/app-rail.d.ts +21 -24
  8. package/dist/components/app-rail/app-rail.js +68 -132
  9. package/dist/components/app-rail/app-rail.styles.js +5 -2
  10. package/dist/components/attachment-chip/attachment-chip.d.ts +38 -11
  11. package/dist/components/attachment-chip/attachment-chip.js +73 -29
  12. package/dist/components/attachment-trigger/attachment-trigger.d.ts +13 -0
  13. package/dist/components/attachment-trigger/attachment-trigger.js +13 -1
  14. package/dist/components/attachment-trigger/attachment-trigger.styles.js +28 -6
  15. package/dist/components/chart/bar-chart.js +2 -18
  16. package/dist/components/chart/box-plot.d.ts +25 -1
  17. package/dist/components/chart/box-plot.js +136 -6
  18. package/dist/components/chart/box-plot.styles.js +13 -0
  19. package/dist/components/chart/bubble-chart.js +2 -18
  20. package/dist/components/chart/chart.d.ts +54 -10
  21. package/dist/components/chart/chart.js +182 -39
  22. package/dist/components/chart/doughnut-chart.js +2 -18
  23. package/dist/components/chart/histogram-bin.d.ts +5 -2
  24. package/dist/components/chart/histogram-bin.js +12 -4
  25. package/dist/components/chart/histogram.js +8 -16
  26. package/dist/components/chart/line-chart.js +2 -18
  27. package/dist/components/chart/lite-chart.d.ts +80 -3
  28. package/dist/components/chart/lite-chart.js +250 -53
  29. package/dist/components/chart/pie-chart.js +2 -18
  30. package/dist/components/chart/polar-area-chart.js +2 -18
  31. package/dist/components/chart/radar-chart.js +2 -18
  32. package/dist/components/chart/scatter-chart.js +2 -18
  33. package/dist/components/chat-composer/chat-composer.d.ts +42 -1
  34. package/dist/components/chat-composer/chat-composer.js +84 -3
  35. package/dist/components/chat-composer/chat-composer.styles.js +11 -1
  36. package/dist/components/chat-message/chat-message.d.ts +32 -4
  37. package/dist/components/chat-message/chat-message.js +57 -17
  38. package/dist/components/checkbox/checkbox.d.ts +66 -8
  39. package/dist/components/checkbox/checkbox.js +173 -50
  40. package/dist/components/checkbox/checkbox.styles.js +9 -2
  41. package/dist/components/chip/chip-group.js +8 -2
  42. package/dist/components/chip/chip.js +9 -2
  43. package/dist/components/citation-badge/citation-badge.js +21 -3
  44. package/dist/components/code-block/code-block.d.ts +38 -0
  45. package/dist/components/code-block/code-block.js +72 -7
  46. package/dist/components/code-block/code-loader.d.ts +44 -1
  47. package/dist/components/code-block/code-loader.js +63 -0
  48. package/dist/components/combobox/combobox.d.ts +46 -5
  49. package/dist/components/combobox/combobox.js +175 -35
  50. package/dist/components/combobox/combobox.styles.js +3 -3
  51. package/dist/components/context-meter/context-meter.js +17 -8
  52. package/dist/components/context-meter/context-meter.styles.js +0 -6
  53. package/dist/components/conversation-item/conversation-item.d.ts +13 -16
  54. package/dist/components/conversation-item/conversation-item.js +29 -20
  55. package/dist/components/date-picker/calendar-core.d.ts +24 -0
  56. package/dist/components/date-picker/calendar-core.js +51 -4
  57. package/dist/components/date-picker/date-input.d.ts +77 -10
  58. package/dist/components/date-picker/date-input.js +278 -73
  59. package/dist/components/date-picker/date-picker.d.ts +28 -4
  60. package/dist/components/date-picker/date-picker.js +95 -37
  61. package/dist/components/dialog/confirm.js +3 -6
  62. package/dist/components/dialog/dialog.d.ts +75 -31
  63. package/dist/components/dialog/dialog.js +147 -131
  64. package/dist/components/dialog/dialog.styles.js +46 -2
  65. package/dist/components/dock-panel/dock-panel.d.ts +8 -0
  66. package/dist/components/dock-panel/dock-panel.js +15 -1
  67. package/dist/components/dock-panel/dock-panel.styles.js +15 -1
  68. package/dist/components/document-preview/document-preview.d.ts +15 -7
  69. package/dist/components/document-preview/document-preview.js +29 -13
  70. package/dist/components/empty/empty.d.ts +1 -2
  71. package/dist/components/empty/empty.js +18 -12
  72. package/dist/components/empty/empty.styles.js +0 -3
  73. package/dist/components/export-button/export-button.d.ts +12 -0
  74. package/dist/components/export-button/export-button.js +33 -5
  75. package/dist/components/export-button/export-button.styles.js +5 -1
  76. package/dist/components/file-input/accept.js +2 -0
  77. package/dist/components/file-input/file-input.d.ts +10 -1
  78. package/dist/components/file-input/file-input.js +36 -5
  79. package/dist/components/flag/flag.d.ts +1 -1
  80. package/dist/components/flag/flag.js +15 -1
  81. package/dist/components/flag/flag.styles.js +1 -1
  82. package/dist/components/flag/language-map.d.ts +4 -1
  83. package/dist/components/flag/language-map.js +6 -3
  84. package/dist/components/gauge/gauge.d.ts +10 -1
  85. package/dist/components/gauge/gauge.js +28 -9
  86. package/dist/components/generation-status/generation-status.d.ts +13 -5
  87. package/dist/components/generation-status/generation-status.js +59 -5
  88. package/dist/components/graph/graph.d.ts +37 -3
  89. package/dist/components/graph/graph.js +132 -20
  90. package/dist/components/heatmap/calendar-grid.d.ts +15 -6
  91. package/dist/components/heatmap/calendar-grid.js +17 -6
  92. package/dist/components/heatmap/heatmap-scale.d.ts +5 -1
  93. package/dist/components/heatmap/heatmap-scale.js +8 -2
  94. package/dist/components/heatmap/heatmap.d.ts +166 -25
  95. package/dist/components/heatmap/heatmap.js +372 -89
  96. package/dist/components/json-viewer/json-viewer.d.ts +22 -5
  97. package/dist/components/json-viewer/json-viewer.js +115 -37
  98. package/dist/components/json-viewer/json-viewer.styles.js +2 -1
  99. package/dist/components/kbd/kbd.js +24 -7
  100. package/dist/components/live-region/live-region.d.ts +10 -1
  101. package/dist/components/live-region/live-region.js +42 -6
  102. package/dist/components/map/map.d.ts +24 -1
  103. package/dist/components/map/map.js +89 -24
  104. package/dist/components/markdown/markdown-loader.d.ts +24 -0
  105. package/dist/components/markdown/markdown-loader.js +35 -1
  106. package/dist/components/markdown/markdown.d.ts +46 -8
  107. package/dist/components/markdown/markdown.js +136 -31
  108. package/dist/components/markdown/markdown.styles.js +7 -1
  109. package/dist/components/media-card/media-card.d.ts +25 -17
  110. package/dist/components/media-card/media-card.js +43 -65
  111. package/dist/components/mention-popover/mention-popover.d.ts +4 -1
  112. package/dist/components/mention-popover/mention-popover.js +23 -5
  113. package/dist/components/menu/menu-item.d.ts +32 -3
  114. package/dist/components/menu/menu-item.js +94 -7
  115. package/dist/components/menu/menu-item.styles.js +8 -0
  116. package/dist/components/menu/menu.d.ts +25 -1
  117. package/dist/components/menu/menu.js +99 -3
  118. package/dist/components/menu/menu.styles.js +3 -2
  119. package/dist/components/model-select/model-select.d.ts +48 -3
  120. package/dist/components/model-select/model-select.js +120 -22
  121. package/dist/components/model-select/model-select.styles.js +17 -3
  122. package/dist/components/model-settings-panel/model-settings-panel.d.ts +25 -1
  123. package/dist/components/model-settings-panel/model-settings-panel.js +65 -1
  124. package/dist/components/playback/playback.d.ts +6 -2
  125. package/dist/components/playback/playback.js +72 -29
  126. package/dist/components/playback/playback.styles.js +6 -1
  127. package/dist/components/responsive-panel/responsive-panel.d.ts +29 -16
  128. package/dist/components/responsive-panel/responsive-panel.js +130 -132
  129. package/dist/components/responsive-panel/responsive-panel.styles.js +1 -1
  130. package/dist/components/result-card/result-card.d.ts +8 -2
  131. package/dist/components/result-card/result-card.js +9 -3
  132. package/dist/components/result-card/result-card.styles.js +0 -1
  133. package/dist/components/select/select.d.ts +59 -20
  134. package/dist/components/select/select.js +125 -43
  135. package/dist/components/skeleton/skeleton.d.ts +3 -0
  136. package/dist/components/skeleton/skeleton.js +7 -1
  137. package/dist/components/skeleton/skeleton.styles.js +5 -4
  138. package/dist/components/slider/slider.d.ts +57 -5
  139. package/dist/components/slider/slider.js +195 -80
  140. package/dist/components/source-card/source-card.d.ts +17 -5
  141. package/dist/components/source-card/source-card.js +55 -8
  142. package/dist/components/source-card/source-card.styles.js +1 -1
  143. package/dist/components/source-list/source-list.js +9 -1
  144. package/dist/components/sparkline/sparkline.js +34 -10
  145. package/dist/components/split/split.d.ts +141 -14
  146. package/dist/components/split/split.js +357 -52
  147. package/dist/components/split/split.styles.js +18 -1
  148. package/dist/components/stat/stat.d.ts +10 -4
  149. package/dist/components/stat/stat.js +46 -11
  150. package/dist/components/stream-status/stream-status.d.ts +27 -11
  151. package/dist/components/stream-status/stream-status.js +95 -13
  152. package/dist/components/streaming-text/streaming-text.d.ts +9 -3
  153. package/dist/components/streaming-text/streaming-text.js +25 -10
  154. package/dist/components/streaming-text/streaming-text.styles.js +3 -2
  155. package/dist/components/switch/switch.d.ts +52 -7
  156. package/dist/components/switch/switch.js +111 -40
  157. package/dist/components/switch/switch.styles.js +1 -1
  158. package/dist/components/table/table.d.ts +18 -3
  159. package/dist/components/table/table.js +88 -20
  160. package/dist/components/tabs/tabs.d.ts +23 -0
  161. package/dist/components/tabs/tabs.js +76 -12
  162. package/dist/components/tabs/tabs.styles.js +11 -0
  163. package/dist/components/thinking-panel/thinking-panel.d.ts +3 -1
  164. package/dist/components/thinking-panel/thinking-panel.js +34 -9
  165. package/dist/components/thinking-panel/thinking-panel.styles.js +6 -0
  166. package/dist/components/time-range/time-range.d.ts +59 -2
  167. package/dist/components/time-range/time-range.js +163 -67
  168. package/dist/components/toast/toast-item.d.ts +12 -3
  169. package/dist/components/toast/toast-item.js +90 -53
  170. package/dist/components/toast/toast-item.styles.js +59 -28
  171. package/dist/components/toast/toast.js +17 -5
  172. package/dist/components/toast/toaster.js +10 -1
  173. package/dist/components/tool-approval-dialog/tool-approval-dialog.d.ts +11 -10
  174. package/dist/components/tool-approval-dialog/tool-approval-dialog.js +50 -124
  175. package/dist/components/tool-approval-dialog/tool-approval-dialog.styles.js +1 -1
  176. package/dist/components/tool-call-chip/tool-call-chip.d.ts +18 -2
  177. package/dist/components/tool-call-chip/tool-call-chip.js +86 -25
  178. package/dist/components/tool-param-form/tool-param-form.d.ts +89 -28
  179. package/dist/components/tool-param-form/tool-param-form.js +350 -106
  180. package/dist/components/tool-result-dialog/tool-result-dialog.d.ts +27 -7
  181. package/dist/components/tool-result-dialog/tool-result-dialog.js +80 -115
  182. package/dist/components/tool-result-dialog/tool-result-dialog.styles.js +1 -1
  183. package/dist/components/tool-result-view/registry.d.ts +0 -8
  184. package/dist/components/tool-result-view/registry.js +7 -2
  185. package/dist/components/tool-result-view/tool-result-view.d.ts +6 -3
  186. package/dist/components/tool-result-view/tool-result-view.js +18 -4
  187. package/dist/components/tool-select-dialog/tool-select-dialog.d.ts +11 -11
  188. package/dist/components/tool-select-dialog/tool-select-dialog.js +44 -133
  189. package/dist/components/tool-select-dialog/tool-select-dialog.styles.js +13 -4
  190. package/dist/components/tree/tree-node.d.ts +4 -1
  191. package/dist/components/tree/tree-node.js +4 -1
  192. package/dist/components/tree/tree.d.ts +17 -8
  193. package/dist/components/tree/tree.js +21 -20
  194. package/dist/components/typing-indicator/typing-indicator.d.ts +12 -2
  195. package/dist/components/typing-indicator/typing-indicator.js +16 -4
  196. package/dist/components/typing-indicator/typing-indicator.styles.js +2 -2
  197. package/dist/components/virtual-list/virtual-list.d.ts +56 -19
  198. package/dist/components/virtual-list/virtual-list.js +184 -38
  199. package/dist/components/virtual-list/virtual-list.styles.js +12 -0
  200. package/dist/components/widget/widget.d.ts +5 -4
  201. package/dist/components/widget/widget.js +54 -120
  202. package/dist/components/widget/widget.styles.js +8 -2
  203. package/dist/components/word-cloud/word-cloud-layout.d.ts +7 -0
  204. package/dist/components/word-cloud/word-cloud-layout.js +20 -6
  205. package/dist/components/word-cloud/word-cloud.d.ts +22 -2
  206. package/dist/components/word-cloud/word-cloud.js +45 -6
  207. package/dist/components/word-cloud/word-cloud.styles.js +13 -1
  208. package/dist/internal/anchored-validity.d.ts +1 -0
  209. package/dist/internal/anchored-validity.js +86 -0
  210. package/dist/internal/form-associated.d.ts +7 -0
  211. package/dist/internal/form-associated.js +43 -4
  212. package/dist/internal/numbers.d.ts +12 -0
  213. package/dist/internal/numbers.js +26 -0
  214. package/dist/internal/overlay-manager.d.ts +51 -0
  215. package/dist/internal/overlay-manager.js +523 -0
  216. package/dist/internal/positioner.js +27 -3
  217. package/dist/internal/safe-url.d.ts +12 -0
  218. package/dist/internal/safe-url.js +41 -0
  219. package/dist/internal/tokens.styles.js +17 -11
  220. package/dist/lyra.d.ts +5 -2
  221. package/dist/lyra.js +3 -1
  222. package/llms-full.txt +970 -426
  223. package/llms.txt +5 -4
  224. package/package.json +19 -2
@@ -11,6 +11,7 @@ import { defineElement } from '../../internal/prefix.js';
11
11
  import { place } from '../../internal/positioner.js';
12
12
  import { nextId } from '../../internal/a11y.js';
13
13
  import { chevronIcon } from '../../internal/icons.js';
14
+ import { AnchoredValidityController, VALIDITY_ANCHOR } from '../../internal/anchored-validity.js';
14
15
  import { styles } from './select.styles.js';
15
16
  import { LyraOption } from '../combobox/option.js';
16
17
  import '../combobox/option.js';
@@ -33,16 +34,21 @@ import '../combobox/option.js';
33
34
  * "active" row is conveyed via `aria-activedescendant`, never actual focus),
34
35
  * matching the WAI-ARIA "select-only combobox" pattern.
35
36
  *
36
- * When exactly one option is enabled (regardless of how many disabled ones
37
- * exist alongside it), the popup never opens at all: a click, Enter, Space,
38
- * ArrowDown, or ArrowUp on the trigger commits that sole option directly,
39
- * and the trigger renders as a plain `role="button"` with no chevron/
40
- * `aria-haspopup`/`aria-expanded` rather than a combobox with a permanently
41
- * inert popup state — opening a one-row list to pick the only available
42
- * choice is pure friction with no real decision behind it. This never
43
- * changes `value`/validity defaults on its own — an unselected single-option
44
- * select stays unselected (and a `required` one stays invalid) exactly like
45
- * the multi-option case, until the trigger is actually activated.
37
+ * When `autoCommitSingleOption` is set and exactly one option is enabled
38
+ * (regardless of how many disabled ones exist alongside it), the popup never
39
+ * opens at all: a click, Enter, Space, ArrowDown, or ArrowUp on the trigger
40
+ * commits that sole option directly, and the trigger renders as a plain
41
+ * `role="button"` with no chevron/`aria-haspopup`/`aria-expanded` rather than
42
+ * a combobox with a permanently inert popup state — opening a one-row list to
43
+ * pick the only available choice is pure friction with no real decision
44
+ * behind it. This never changes `value`/validity defaults on its own — an
45
+ * unselected single-option select stays unselected (and a `required` one
46
+ * stays invalid) exactly like the multi-option case, until the trigger is
47
+ * actually activated. `autoCommitSingleOption` defaults to `false`: by
48
+ * default a select always renders the normal combobox/listbox/chevron
49
+ * trigger no matter how many options are enabled, matching pre-1.3.0
50
+ * behavior — opt in explicitly if a narrowing-to-one option list should
51
+ * auto-commit.
46
52
  *
47
53
  * @customElement lyra-select
48
54
  * @slot - `<lyra-option>` elements.
@@ -70,20 +76,30 @@ export class LyraSelect extends LyraElement {
70
76
  static { this.formAssociated = true; }
71
77
  static { this.styles = [LyraElement.styles, styles]; }
72
78
  static { this.properties = {
79
+ disabled: { type: Boolean, reflect: true, noAccessor: true },
80
+ required: { type: Boolean, reflect: true, noAccessor: true },
73
81
  value: { noAccessor: true },
74
82
  name: { reflect: true, noAccessor: true },
75
83
  }; }
76
84
  constructor() {
77
85
  super();
78
86
  this.placeholder = '';
79
- this.disabled = false;
80
- this.required = false;
81
87
  this.label = '';
82
88
  this.hint = '';
83
89
  this.errorText = '';
84
90
  this.open = false;
85
91
  /** Visual size — same `xs`–`xl` scale as `lyra-toast-item`'s `size`. */
86
92
  this.size = 'm';
93
+ /**
94
+ * Opt-in: when `true` and exactly one `<lyra-option>` is enabled, the
95
+ * trigger commits that option directly (click/Enter/Space/ArrowDown/
96
+ * ArrowUp) instead of opening the listbox, and renders as a plain
97
+ * `role="button"` with no chevron. Defaults to `false`, which always
98
+ * renders the normal combobox/listbox/chevron trigger regardless of how
99
+ * many options are enabled — the pre-1.3.0 behavior. See the class doc
100
+ * above and `onlyOption` below for the full rationale.
101
+ */
102
+ this.autoCommitSingleOption = false;
87
103
  this.activeIndex = -1;
88
104
  this.options = [];
89
105
  // Set on the trigger button's first `blur`; gates the `data-invalid`
@@ -105,6 +121,8 @@ export class LyraSelect extends LyraElement {
105
121
  this.triggerId = nextId('select-trigger');
106
122
  this._isFirstUpdate = true;
107
123
  this._selected = '';
124
+ this._disabled = false;
125
+ this._required = false;
108
126
  // What `form.reset()` restores to. Captured exactly once, from whatever
109
127
  // `<lyra-option selected>` markup was present the first time slotted
110
128
  // options are collected (mirrors native `<select><option selected>`) --
@@ -113,6 +131,9 @@ export class LyraSelect extends LyraElement {
113
131
  // the reset default. See lyra-combobox's identical `_defaultSelected`.
114
132
  this._defaultSelected = '';
115
133
  this._defaultCaptured = false;
134
+ // A restored value must win over declarative selected markup collected by
135
+ // the first asynchronous slotchange. Cleared by the next ordinary value write.
136
+ this._restoredStateActive = false;
116
137
  // Standard listbox type-ahead: printable keystrokes accumulate into this
117
138
  // buffer and reset ~500ms after the last one, so "b" then "a" narrows to
118
139
  // "ba" instead of restarting the search on every keystroke.
@@ -143,7 +164,7 @@ export class LyraSelect extends LyraElement {
143
164
  // the reset default.
144
165
  const declared = this.options.filter((o) => o.selected).map((o) => o.value);
145
166
  this._defaultSelected = declared[0] ?? '';
146
- if (declared.length) {
167
+ if (declared.length && !this._restoredStateActive) {
147
168
  this.value = declared[0];
148
169
  return; // `value=`'s setter already called reflectSelected()
149
170
  }
@@ -155,7 +176,7 @@ export class LyraSelect extends LyraElement {
155
176
  // into the live selection instead of letting reflectSelected() below
156
177
  // strip its `selected` attribute back off.
157
178
  const newlySelected = this.options.filter((o) => !previous.has(o) && o.selected).map((o) => o.value);
158
- if (newlySelected.length) {
179
+ if (newlySelected.length && !this._restoredStateActive) {
159
180
  this.value = newlySelected[newlySelected.length - 1];
160
181
  return; // `value=`'s setter already called reflectSelected()
161
182
  }
@@ -276,6 +297,27 @@ export class LyraSelect extends LyraElement {
276
297
  this.selectOption(option);
277
298
  };
278
299
  this.internals = this.attachInternals();
300
+ this.validityController = new AnchoredValidityController(this, this.internals, () => this[VALIDITY_ANCHOR]());
301
+ this.syncFormValue();
302
+ }
303
+ get form() {
304
+ return this.internals.form;
305
+ }
306
+ get labels() {
307
+ return this.internals.labels;
308
+ }
309
+ get validity() {
310
+ return this.internals.validity;
311
+ }
312
+ get validationMessage() {
313
+ return this.internals.validationMessage;
314
+ }
315
+ get willValidate() {
316
+ return this.internals.willValidate;
317
+ }
318
+ /** @internal */
319
+ [VALIDITY_ANCHOR]() {
320
+ return this.renderRoot?.querySelector('[part="trigger"]') ?? null;
279
321
  }
280
322
  connectedCallback() {
281
323
  super.connectedCallback();
@@ -307,12 +349,34 @@ export class LyraSelect extends LyraElement {
307
349
  }
308
350
  this.requestUpdate('name', old);
309
351
  }
352
+ get disabled() {
353
+ return this._disabled;
354
+ }
355
+ set disabled(next) {
356
+ const old = this._disabled;
357
+ this._disabled = Boolean(next);
358
+ this.toggleAttribute('disabled', this._disabled);
359
+ if (this._disabled)
360
+ this.hide();
361
+ this.requestUpdate('disabled', old);
362
+ }
363
+ get required() {
364
+ return this._required;
365
+ }
366
+ set required(next) {
367
+ const old = this._required;
368
+ this._required = Boolean(next);
369
+ this.toggleAttribute('required', this._required);
370
+ this.updateValidity();
371
+ this.requestUpdate('required', old);
372
+ }
310
373
  /** The selected value: a single string (empty when nothing is selected). */
311
374
  get value() {
312
375
  return this._selected;
313
376
  }
314
377
  set value(next) {
315
378
  const old = this._selected;
379
+ this._restoredStateActive = false;
316
380
  this._selected = next ?? '';
317
381
  this.syncFormValue();
318
382
  this.reflectSelected();
@@ -321,10 +385,10 @@ export class LyraSelect extends LyraElement {
321
385
  }
322
386
  updateValidity() {
323
387
  if (this.required && !this._selected) {
324
- this.internals.setValidity({ valueMissing: true }, 'Please select an option.');
388
+ this.validityController.setValidity({ valueMissing: true }, 'Please select an option.');
325
389
  }
326
390
  else {
327
- this.internals.setValidity({});
391
+ this.validityController.setValidity({});
328
392
  }
329
393
  }
330
394
  syncFormValue() {
@@ -339,6 +403,10 @@ export class LyraSelect extends LyraElement {
339
403
  formResetCallback() {
340
404
  this.value = this._defaultSelected;
341
405
  }
406
+ formStateRestoreCallback(state, _mode) {
407
+ this.value = typeof state === 'string' ? state : '';
408
+ this._restoredStateActive = true;
409
+ }
342
410
  /**
343
411
  * Called by the browser when an ancestor `<fieldset disabled>` toggles.
344
412
  * Tracked separately from the consumer's own `disabled` (see
@@ -347,6 +415,8 @@ export class LyraSelect extends LyraElement {
347
415
  */
348
416
  formDisabledCallback(disabled) {
349
417
  this._fieldsetDisabled = disabled;
418
+ if (disabled)
419
+ this.hide();
350
420
  this.requestUpdate();
351
421
  }
352
422
  checkValidity() {
@@ -373,17 +443,21 @@ export class LyraSelect extends LyraElement {
373
443
  o.selected = o.value === this._selected;
374
444
  }
375
445
  /**
376
- * The sole navigable (non-disabled) option, when there's exactly one. Opening a one-row
377
- * popup to pick the only available choice is pure friction with no real decision behind
378
- * it, so a single-option select skips the popup entirely -- see `onTriggerClick`/`onKeyDown`,
379
- * which commit this option directly on activation instead of opening the listbox, and
380
- * `render()`, which drops the popup-trigger ARIA semantics (`role="combobox"`,
381
- * `aria-haspopup`, `aria-expanded`, the chevron) in favor of a plain button's, since no
382
- * popup can ever appear while this getter returns a value. Does not affect `value`/
383
- * validity defaults in any way -- an unselected single-option select stays unselected
446
+ * The sole navigable (non-disabled) option, when `autoCommitSingleOption` is set and
447
+ * there's exactly one. Opening a one-row popup to pick the only available choice is pure
448
+ * friction with no real decision behind it, so an opted-in single-option select skips the
449
+ * popup entirely -- see `onTriggerClick`/`onKeyDown`, which commit this option directly on
450
+ * activation instead of opening the listbox, and `render()`, which drops the popup-trigger
451
+ * ARIA semantics (`role="combobox"`, `aria-haspopup`, `aria-expanded`, the chevron) in favor
452
+ * of a plain button's, since no popup can ever appear while this getter returns a value.
453
+ * Returns `undefined` whenever `autoCommitSingleOption` is `false` (the default), regardless
454
+ * of option count, so the normal combobox trigger renders unconditionally. Does not affect
455
+ * `value`/validity defaults in any way -- an unselected single-option select stays unselected
384
456
  * until the trigger is actually activated, exactly like the multi-option case.
385
457
  */
386
458
  get onlyOption() {
459
+ if (!this.autoCommitSingleOption)
460
+ return undefined;
387
461
  const navigable = this.options.filter((o) => !o.disabled);
388
462
  return navigable.length === 1 ? navigable[0] : undefined;
389
463
  }
@@ -425,19 +499,26 @@ export class LyraSelect extends LyraElement {
425
499
  this.emit('lyra-hide');
426
500
  }
427
501
  }
428
- if (changed.has('required'))
429
- this.updateValidity();
430
502
  if (changed.has('touched') || changed.has('required') || changed.has('value')) {
431
503
  this.toggleAttribute('data-invalid', this.touched && !this.internals.validity.valid);
432
504
  }
433
505
  }
434
506
  selectOption(option) {
435
- if (option.disabled)
507
+ if (this.effectiveDisabled || option.disabled)
436
508
  return;
509
+ // Reopening the listbox (or, on a single-option select, simply
510
+ // reactivating the trigger) and landing back on the already-selected
511
+ // row is not a selection change -- `change`/`input` are documented as
512
+ // firing when "the selection changed", so only emit them when the
513
+ // value actually moves, matching a native <select> (which never fires
514
+ // `change` for re-picking the currently-selected <option>).
515
+ const changed = option.value !== this._selected;
437
516
  this.value = option.value;
438
517
  this.hide();
439
- this.emit('input');
440
- this.emit('change');
518
+ if (changed) {
519
+ this.emit('input');
520
+ this.emit('change');
521
+ }
441
522
  }
442
523
  /**
443
524
  * Standard listbox type-ahead: moves to the next non-disabled option whose
@@ -515,12 +596,15 @@ export class LyraSelect extends LyraElement {
515
596
  const hasHint = this.hasHintSlot || this.hint.length > 0;
516
597
  const hasError = this.hasErrorSlot || this.errorText.length > 0;
517
598
  const hasLabel = this.hasLabelSlot || this.label.length > 0;
518
- // A single navigable option has no popup to expand into -- the trigger commits it
519
- // directly on activation (see onTriggerClick/onKeyDown) instead of opening the listbox,
520
- // so it's exposed as a plain button rather than a combobox with a permanently-closed
521
- // popup: no aria-haspopup/aria-expanded/aria-controls/aria-activedescendant (all
522
- // meaningless without a popup that can ever appear) and no chevron.
523
- const isSingleOption = navigable.length === 1;
599
+ const describedBy = [hasError ? 'select-error' : '', hasHint ? 'select-hint' : ''].filter(Boolean).join(' ');
600
+ // A single navigable option has no popup to expand into -- when opted in via
601
+ // autoCommitSingleOption, the trigger commits it directly on activation (see
602
+ // onTriggerClick/onKeyDown) instead of opening the listbox, so it's exposed as a plain
603
+ // button rather than a combobox with a permanently-closed popup: no aria-haspopup/
604
+ // aria-expanded/aria-controls/aria-activedescendant (all meaningless without a popup that
605
+ // can ever appear) and no chevron. Without the opt-in (the default), this is always
606
+ // `false` and the normal combobox/listbox/chevron trigger renders regardless of option count.
607
+ const isSingleOption = this.autoCommitSingleOption && navigable.length === 1;
524
608
  return html `
525
609
  <div part="form-control">
526
610
  <label part="form-control-label" for=${this.triggerId} ?hidden=${!hasLabel}>
@@ -536,6 +620,7 @@ export class LyraSelect extends LyraElement {
536
620
  aria-controls=${isSingleOption ? nothing : this.listId}
537
621
  aria-activedescendant=${isSingleOption ? nothing : activeId}
538
622
  aria-label=${this.getAttribute('aria-label') || (hasLabel ? nothing : this.placeholder || 'Select')}
623
+ aria-describedby=${describedBy || nothing}
539
624
  aria-required=${this.required ? 'true' : 'false'}
540
625
  aria-invalid=${this.touched && !this.internals.validity.valid ? 'true' : 'false'}
541
626
  ?disabled=${this.effectiveDisabled}
@@ -557,10 +642,10 @@ export class LyraSelect extends LyraElement {
557
642
  >
558
643
  ${this.renderRows(options, activeId)}
559
644
  </div>
560
- <div part="error" ?hidden=${!hasError}>
645
+ <div id="select-error" part="error" ?hidden=${!hasError}>
561
646
  ${this.errorText}<slot name="error" @slotchange=${this.onErrorSlotChange}></slot>
562
647
  </div>
563
- <div part="hint" ?hidden=${!hasHint}>
648
+ <div id="select-hint" part="hint" ?hidden=${!hasHint}>
564
649
  ${this.hint}<slot name="hint" @slotchange=${this.onHintSlotChange}></slot>
565
650
  </div>
566
651
  </div>
@@ -571,12 +656,6 @@ export class LyraSelect extends LyraElement {
571
656
  __decorate([
572
657
  property()
573
658
  ], LyraSelect.prototype, "placeholder", void 0);
574
- __decorate([
575
- property({ type: Boolean, reflect: true })
576
- ], LyraSelect.prototype, "disabled", void 0);
577
- __decorate([
578
- property({ type: Boolean, reflect: true })
579
- ], LyraSelect.prototype, "required", void 0);
580
659
  __decorate([
581
660
  property()
582
661
  ], LyraSelect.prototype, "label", void 0);
@@ -592,6 +671,9 @@ __decorate([
592
671
  __decorate([
593
672
  property({ reflect: true })
594
673
  ], LyraSelect.prototype, "size", void 0);
674
+ __decorate([
675
+ property({ type: Boolean, attribute: 'auto-commit-single-option' })
676
+ ], LyraSelect.prototype, "autoCommitSingleOption", void 0);
595
677
  __decorate([
596
678
  state()
597
679
  ], LyraSelect.prototype, "activeIndex", void 0);
@@ -15,6 +15,9 @@ export declare class LyraSkeleton extends LyraElement {
15
15
  effect: SkeletonEffect;
16
16
  width?: string;
17
17
  height?: string;
18
+ /** Accessible name announced via `role="status"`. Override with a
19
+ * description of what is actually loading (e.g. "Loading chart"). */
20
+ label: string;
18
21
  protected willUpdate(): void;
19
22
  protected updated(changed: PropertyValues): void;
20
23
  render(): TemplateResult;
@@ -22,6 +22,9 @@ export class LyraSkeleton extends LyraElement {
22
22
  super(...arguments);
23
23
  this.variant = 'text';
24
24
  this.effect = 'pulse';
25
+ /** Accessible name announced via `role="status"`. Override with a
26
+ * description of what is actually loading (e.g. "Loading chart"). */
27
+ this.label = 'Loading…';
25
28
  }
26
29
  static { this.styles = [LyraElement.styles, styles, srOnly]; }
27
30
  willUpdate() {
@@ -44,7 +47,7 @@ export class LyraSkeleton extends LyraElement {
44
47
  }
45
48
  }
46
49
  render() {
47
- return html `<span part="base"><span class="sr-only">Loading…</span></span>`;
50
+ return html `<span part="base"><span class="sr-only">${this.label}</span></span>`;
48
51
  }
49
52
  }
50
53
  __decorate([
@@ -59,4 +62,7 @@ __decorate([
59
62
  __decorate([
60
63
  property()
61
64
  ], LyraSkeleton.prototype, "height", void 0);
65
+ __decorate([
66
+ property()
67
+ ], LyraSkeleton.prototype, "label", void 0);
62
68
  defineElement('skeleton', LyraSkeleton);
@@ -10,15 +10,13 @@ export const styles = css `
10
10
  block-size: 100%;
11
11
  background: var(--lyra-color-border);
12
12
  }
13
- :host([variant='text']) [part='base'] {
13
+ :host([variant='text']) [part='base'],
14
+ :host([variant='rect']) [part='base'] {
14
15
  border-radius: var(--lyra-radius);
15
16
  }
16
17
  :host([variant='circle']) [part='base'] {
17
18
  border-radius: 50%;
18
19
  }
19
- :host([variant='rect']) [part='base'] {
20
- border-radius: var(--lyra-radius);
21
- }
22
20
  :host([effect='pulse']) [part='base'] {
23
21
  animation: lyra-skeleton-pulse 1.5s ease-in-out infinite;
24
22
  }
@@ -53,5 +51,8 @@ export const styles = css `
53
51
  [part='base'] {
54
52
  animation: none !important;
55
53
  }
54
+ :host([effect='sheen']) [part='base'] {
55
+ background-image: none;
56
+ }
56
57
  }
57
58
  `;
@@ -1,4 +1,4 @@
1
- import { type TemplateResult, type PropertyValues } from 'lit';
1
+ import { type TemplateResult } from 'lit';
2
2
  import { LyraElement } from '../../internal/lyra-element.js';
3
3
  declare const LyraSlider_base: typeof LyraElement & (new (...args: any[]) => import("../../internal/form-associated.js").FormAssociatedInterface);
4
4
  /**
@@ -17,6 +17,10 @@ declare const LyraSlider_base: typeof LyraElement & (new (...args: any[]) => imp
17
17
  * to block submission before that default lands, matching how `required`
18
18
  * isn't a meaningful constraint on a native range input either.
19
19
  *
20
+ * Clicking anywhere on the track (not just the 16px thumb) jumps the thumb
21
+ * to that point and continues the same gesture as a drag, matching native
22
+ * `<input type=range>` click-to-seek.
23
+ *
20
24
  * @customElement lyra-slider
21
25
  * @event lyra-input - Fired continuously during an active drag or a
22
26
  * keyboard step (including OS key-repeat while a key is held), mirroring
@@ -34,9 +38,30 @@ declare const LyraSlider_base: typeof LyraElement & (new (...args: any[]) => imp
34
38
  */
35
39
  export declare class LyraSlider extends LyraSlider_base {
36
40
  static styles: import("lit").CSSResultGroup[];
37
- min: number;
38
- max: number;
39
- step: number;
41
+ static properties: {
42
+ min: {
43
+ type: NumberConstructor;
44
+ noAccessor: boolean;
45
+ };
46
+ max: {
47
+ type: NumberConstructor;
48
+ noAccessor: boolean;
49
+ };
50
+ step: {
51
+ type: NumberConstructor;
52
+ noAccessor: boolean;
53
+ };
54
+ };
55
+ private _min;
56
+ private _max;
57
+ private _step;
58
+ private pendingValue;
59
+ get min(): number;
60
+ set min(next: number);
61
+ get max(): number;
62
+ set max(next: number);
63
+ get step(): number;
64
+ set step(next: number);
40
65
  /** Accessible name for the slider, used when no visible label context
41
66
  * exists around it (e.g. no wrapping `<label>` or adjacent heading). Set
42
67
  * as `aria-label` on the interactive `role="slider"` element. A plain
@@ -51,6 +76,8 @@ export declare class LyraSlider extends LyraSlider_base {
51
76
  * boolean converter checks). */
52
77
  showValue: boolean;
53
78
  private activePointers;
79
+ private changedPointers;
80
+ private keyboardChanged;
54
81
  connectedCallback(): void;
55
82
  disconnectedCallback(): void;
56
83
  /** The numeric counterpart of `value`, mirroring native `<input
@@ -61,10 +88,20 @@ export declare class LyraSlider extends LyraSlider_base {
61
88
  * back into `value`. */
62
89
  get valueAsNumber(): number;
63
90
  set valueAsNumber(next: number);
91
+ /**
92
+ * `FormAssociated` provides the form plumbing; this override adds the
93
+ * slider's native-range sanitization at the IDL boundary so invalid direct
94
+ * assignments cannot briefly submit a literal `NaN`/`Infinity`.
95
+ */
96
+ get value(): string;
97
+ set value(next: string);
98
+ formResetCallback(): void;
64
99
  /** If `value` is still unset, seed it with the sanitized default — the
65
100
  * midpoint of `[min, max]`, snapped to `step` — so `value`/`valueAsNumber`
66
101
  * and rendering never have to treat "" as a real, distinct state. */
67
102
  private ensureValue;
103
+ /** Re-sanitize an already assigned value immediately after range settings change. */
104
+ private sanitizeCurrentValue;
68
105
  private domain;
69
106
  private defaultNumericValue;
70
107
  private percentOf;
@@ -73,12 +110,27 @@ export declare class LyraSlider extends LyraSlider_base {
73
110
  private setValue;
74
111
  private onKeyDown;
75
112
  private onKeyUp;
113
+ /** Start tracking `pointerId` as an active drag, transferring pointer
114
+ * capture to `captureTarget` and wiring the shared window-level move/end
115
+ * listeners. Shared by a pointerdown that starts on the thumb itself and
116
+ * one that starts elsewhere on the track (see `onBasePointerDown`), so
117
+ * both gestures continue identically from here on. */
118
+ private beginDrag;
76
119
  private onPointerDown;
120
+ /** A pointerdown anywhere on `[part="base"]` other than the thumb itself
121
+ * (the vast majority of the control's clickable area) jumps the thumb to
122
+ * that point and continues the same gesture as a drag, mirroring native
123
+ * `<input type=range>` click-to-seek. A pointerdown that started on the
124
+ * thumb bubbles up to this same listener too — already fully handled by
125
+ * `onPointerDown` above, so it's ignored here via the `e.target === thumb`
126
+ * check. Unlike the two-handle `lyra-time-range` (where a track click is
127
+ * ambiguous about which handle should move), a single thumb has no such
128
+ * ambiguity. */
129
+ private onBasePointerDown;
77
130
  private onPointerMove;
78
131
  private onPointerUp;
79
132
  /** Stop the drag owned by `pointerId`, optionally committing a final lyra-change. */
80
133
  private endDrag;
81
- protected willUpdate(changed: PropertyValues): void;
82
134
  render(): TemplateResult;
83
135
  }
84
136
  declare global {