@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
@@ -8,9 +8,25 @@ import { html, nothing } from 'lit';
8
8
  import { property, query, state } from 'lit/decorators.js';
9
9
  import { LyraElement } from '../../internal/lyra-element.js';
10
10
  import { defineElement } from '../../internal/prefix.js';
11
+ import { nextId, srOnly } from '../../internal/a11y.js';
11
12
  import { loadChartJs, loadChartJsWithZoom } from './chart-loader.js';
12
13
  import { styles } from './chart.styles.js';
13
14
  import '../skeleton/skeleton.js';
15
+ const CHART_TYPES = new Set([
16
+ 'line',
17
+ 'bar',
18
+ 'scatter',
19
+ 'pie',
20
+ 'doughnut',
21
+ 'radar',
22
+ 'polarArea',
23
+ 'bubble',
24
+ ]);
25
+ function normalizeChartType(value) {
26
+ return typeof value === 'string' && CHART_TYPES.has(value)
27
+ ? value
28
+ : 'line';
29
+ }
14
30
  /**
15
31
  * Recursively merges `override` onto `base`, matching JSON-merge semantics:
16
32
  * plain objects are merged key-by-key at every depth; arrays, functions, and
@@ -39,15 +55,21 @@ const FALLBACK_TICK_COLOR = '#6b7280';
39
55
  const FALLBACK_LEGEND_COLOR = '#1a1a1a';
40
56
  const FALLBACK_TOOLTIP_BG = '#fff';
41
57
  const FALLBACK_TOOLTIP_TEXT = '#1a1a1a';
42
- function deepMerge(base, override) {
58
+ function deepMerge(base, override, seen = new WeakMap()) {
59
+ if (typeof override === 'object' && override !== null) {
60
+ const previous = seen.get(override);
61
+ if (previous !== undefined)
62
+ return previous;
63
+ }
43
64
  if (!isPlainObject(base) || !isPlainObject(override)) {
44
65
  return (override === undefined ? base : override);
45
66
  }
46
67
  const result = { ...base };
68
+ seen.set(override, result);
47
69
  for (const key of Object.keys(override)) {
48
70
  if (UNSAFE_KEYS.has(key))
49
71
  continue;
50
- result[key] = deepMerge(base[key], override[key]);
72
+ result[key] = deepMerge(base[key], override[key], seen);
51
73
  }
52
74
  return result;
53
75
  }
@@ -76,7 +98,12 @@ function deepMerge(base, override) {
76
98
  * intersect-only — see `handlePointClick()`) a data point/segment.
77
99
  * `detail: { datasetIndex: number, index: number, label: string |
78
100
  * undefined, value: unknown }`.
79
- * @csspart base, canvas, reset-zoom-button
101
+ * @csspart base - The chart wrapper.
102
+ * @csspart canvas - The Chart.js canvas.
103
+ * @csspart reset-zoom-button - The reset-zoom control when zoom is active.
104
+ * @csspart description - The accessible chart summary.
105
+ * @csspart data-table - The optional generated or slotted data table.
106
+ * @slot data-table - An optional consumer-provided accessible table alternative.
80
107
  */
81
108
  export class LyraChart extends LyraElement {
82
109
  constructor() {
@@ -92,6 +119,12 @@ export class LyraChart extends LyraElement {
92
119
  this.yLabel = '';
93
120
  this.y2Label = '';
94
121
  this.beginAtZero = true;
122
+ /** Accessible name applied to the canvas. Falls back to the dataset labels. */
123
+ this.accessibleLabel = '';
124
+ /** Accessible description for the canvas. When unset, a concise data/trend summary is generated. */
125
+ this.accessibleDescription = '';
126
+ /** Makes the generated data table visible; it remains screen-reader available when false. */
127
+ this.showDataTable = false;
95
128
  /** Sets `options.indexAxis = 'y'`, Chart.js's own mechanism for horizontal bars (also applies to line/area types). */
96
129
  this.horizontal = false;
97
130
  /** Stacks the `x`/`y`(/`y2`) scale entries `buildScales()` returns; only meaningful for `bar` and `line` types. */
@@ -100,15 +133,15 @@ export class LyraChart extends LyraElement {
100
133
  this.loading = true;
101
134
  this.zoomed = false;
102
135
  this.visible = true;
103
- this.lastSignature = '';
104
136
  // `chartjs-plugin-zoom`'s own `resetZoom()` synchronously re-invokes the
105
137
  // `onZoomComplete` callback below as part of its reset, which would emit a
106
138
  // stale `{zoomed: true}` right before `resetZoom()` emits the real
107
139
  // `{zoomed: false}`. Set while this component's own `resetZoom()` is
108
140
  // driving the plugin so that re-entrant callback is ignored.
109
141
  this.suppressZoomComplete = false;
142
+ this.descriptionId = nextId('chart-description');
110
143
  }
111
- static { this.styles = [LyraElement.styles, styles]; }
144
+ static { this.styles = [LyraElement.styles, styles, srOnly]; }
112
145
  connectedCallback() {
113
146
  super.connectedCallback();
114
147
  const load = this.zoom ? loadChartJsWithZoom() : loadChartJs();
@@ -137,6 +170,27 @@ export class LyraChart extends LyraElement {
137
170
  this.chart = undefined;
138
171
  this.intersectionObserver?.disconnect();
139
172
  }
173
+ /**
174
+ * Resets a stale `zoomed` flag before the render pass that's about to call
175
+ * `draw()`: a `type` (or `config.type` override) change that lands on a
176
+ * *different* effective Chart.js type makes `draw()` destroy the old
177
+ * `Chart` instance and construct a brand-new one, which was never zoomed —
178
+ * without this, `render()`'s reset-zoom-button stays visible for an
179
+ * instance that has no zoom/pan state left to reset. Computed here (before
180
+ * render), not in `updated()`/`draw()`, because setting `zoomed` — a
181
+ * reactive `@state` property — from `updated()` would schedule a second,
182
+ * redundant update pass; `willUpdate()` runs before this cycle's render is
183
+ * considered complete, so the same set just folds into the render already
184
+ * in progress. Same rationale as `toast-item.ts`'s `willUpdate()`-vs-
185
+ * `updated()` split.
186
+ */
187
+ willUpdate() {
188
+ if (!this.zoomed)
189
+ return;
190
+ if (this.effectiveType() !== this.builtType) {
191
+ this.zoomed = false;
192
+ }
193
+ }
140
194
  updated(changed) {
141
195
  // Disconnected between the property change that scheduled this update
142
196
  // and Lit's (microtask-deferred) processing of it — e.g. a property
@@ -186,15 +240,27 @@ export class LyraChart extends LyraElement {
186
240
  this.draw();
187
241
  });
188
242
  }
189
- const onlyZoomChanged = changed.size === 1 && changed.has('zoomed');
190
- if (onlyZoomChanged)
243
+ const contentChanged = [
244
+ 'type',
245
+ 'labels',
246
+ 'datasets',
247
+ 'legend',
248
+ 'area',
249
+ 'height',
250
+ 'xLabel',
251
+ 'yLabel',
252
+ 'y2Label',
253
+ 'beginAtZero',
254
+ 'horizontal',
255
+ 'stacked',
256
+ 'config',
257
+ 'zoom',
258
+ 'loading',
259
+ ].some((name) => changed.has(name));
260
+ if (!contentChanged)
191
261
  return;
192
262
  if (!this.visible)
193
263
  return; // becoming visible again triggers its own draw() via the observer above
194
- const signature = this.computeSignature();
195
- if (signature === this.lastSignature)
196
- return;
197
- this.lastSignature = signature;
198
264
  this.draw();
199
265
  }
200
266
  seriesToDataset(s) {
@@ -244,29 +310,6 @@ export class LyraChart extends LyraElement {
244
310
  tooltipText: cs.getPropertyValue('--lyra-chart-tooltip-text').trim() || FALLBACK_TOOLTIP_TEXT,
245
311
  };
246
312
  }
247
- /**
248
- * A content-affecting-properties fingerprint used by `updated()` to skip a
249
- * redundant `draw()` when neither visibility nor any of these properties
250
- * actually changed since the last draw (e.g. an unrelated property/state
251
- * update, or `requestUpdate()` with nothing changed). Mirrors
252
- * `lite-chart.ts`'s `computeSignature()`.
253
- */
254
- computeSignature() {
255
- return JSON.stringify([
256
- this.type,
257
- this.labels,
258
- this.datasets,
259
- this.legend,
260
- this.area,
261
- this.xLabel,
262
- this.yLabel,
263
- this.y2Label,
264
- this.beginAtZero,
265
- this.horizontal,
266
- this.stacked,
267
- this.config,
268
- ]);
269
- }
270
313
  /**
271
314
  * Builds `options.scales` for the effective chart type: no scale at all for
272
315
  * pie/doughnut (a proportional-area chart has no axis), the single radial
@@ -352,6 +395,17 @@ export class LyraChart extends LyraElement {
352
395
  const value = chart.data.datasets[datasetIndex]?.data?.[index] ?? null;
353
396
  this.emit('lyra-point-click', { datasetIndex, index, label, value });
354
397
  }
398
+ /**
399
+ * `config.type` (if set) overrides the attribute `type` post-merge — this
400
+ * is the one place that resolution logic lives, shared by `buildConfig()`
401
+ * (which needs it to build scales/interaction for the right type) and
402
+ * `willUpdate()` (which needs it to detect a type change against the last
403
+ * *actually built* type, `this.builtType`, before the render pass that
404
+ * would rebuild the `Chart` instance).
405
+ */
406
+ effectiveType() {
407
+ return (this.config?.type ?? normalizeChartType(this.type));
408
+ }
355
409
  buildConfig() {
356
410
  const theme = this.themeColors();
357
411
  // Resolve the effective type up front: `config.type` (if set) overrides
@@ -359,8 +413,7 @@ export class LyraChart extends LyraElement {
359
413
  // for *that* type, not `this.type` — otherwise a config.type override
360
414
  // (e.g. line -> radar) ships with the wrong axis shape (categorical x/y
361
415
  // instead of a radial r scale).
362
- const effectiveType = this.config?.type ??
363
- this.type;
416
+ const effectiveType = this.effectiveType();
364
417
  const generated = {
365
418
  type: effectiveType,
366
419
  data: {
@@ -463,6 +516,56 @@ export class LyraChart extends LyraElement {
463
516
  refreshTheme() {
464
517
  this.draw();
465
518
  }
519
+ seriesValues(series) {
520
+ if (series.points)
521
+ return series.points.map((point) => point.y).filter((value) => Number.isFinite(value));
522
+ return (series.data ?? []).filter((value) => value != null && Number.isFinite(value));
523
+ }
524
+ chartDescription() {
525
+ if (this.accessibleDescription)
526
+ return this.accessibleDescription;
527
+ const summaries = this.datasets.map((series) => {
528
+ const values = this.seriesValues(series);
529
+ if (!values.length)
530
+ return `${series.label}: no data`;
531
+ const first = values[0];
532
+ const last = values[values.length - 1];
533
+ const trend = last > first ? 'increasing' : last < first ? 'decreasing' : 'flat';
534
+ let min = values[0];
535
+ let max = values[0];
536
+ for (const value of values) {
537
+ min = Math.min(min, value);
538
+ max = Math.max(max, value);
539
+ }
540
+ return `${series.label}: ${values.length} values, range ${min} to ${max}, ${trend} trend`;
541
+ });
542
+ return `${this.effectiveType()} chart${summaries.length ? `. ${summaries.join('. ')}` : ' with no data'}.`;
543
+ }
544
+ renderDataTable() {
545
+ const rowCount = Math.max(this.labels.length, ...this.datasets.map((series) => (series.points ? series.points.length : series.data?.length ?? 0)), 0);
546
+ return html `
547
+ <table class=${this.showDataTable ? nothing : 'sr-only'}>
548
+ <caption>${this.accessibleLabel || 'Chart data'}</caption>
549
+ <thead>
550
+ <tr>
551
+ <th scope="col">Category</th>
552
+ ${this.datasets.map((series) => html `<th scope="col">${series.label}</th>`)}
553
+ </tr>
554
+ </thead>
555
+ <tbody>
556
+ ${Array.from({ length: rowCount }, (_, index) => html `
557
+ <tr>
558
+ <th scope="row">${this.labels[index] ?? `Point ${index + 1}`}</th>
559
+ ${this.datasets.map((series) => {
560
+ const value = series.points ? series.points[index]?.y : series.data?.[index];
561
+ return html `<td>${value == null || !Number.isFinite(value) ? 'No data' : value}</td>`;
562
+ })}
563
+ </tr>
564
+ `)}
565
+ </tbody>
566
+ </table>
567
+ `;
568
+ }
466
569
  render() {
467
570
  if (this.loading) {
468
571
  return html `
@@ -471,10 +574,16 @@ export class LyraChart extends LyraElement {
471
574
  </div>
472
575
  `;
473
576
  }
474
- const label = this.datasets.map((d) => d.label).join(', ') || 'Chart';
577
+ const label = this.accessibleLabel || this.datasets.map((d) => d.label).join(', ') || 'Chart';
578
+ const description = this.chartDescription();
475
579
  return html `
476
580
  <div part="base">
477
- <canvas part="canvas" role="img" aria-label=${label}></canvas>
581
+ <canvas part="canvas" role="img" aria-label=${label} aria-describedby=${this.descriptionId}></canvas>
582
+ <p part="description" id=${this.descriptionId} class="sr-only">${description}</p>
583
+ <div part="data-table">
584
+ <slot name="data-table"></slot>
585
+ ${this.renderDataTable()}
586
+ </div>
478
587
  ${this.zoom && this.zoomed
479
588
  ? html `<button part="reset-zoom-button" type="button" @click=${() => this.resetZoom()}>
480
589
  Reset zoom
@@ -485,7 +594,7 @@ export class LyraChart extends LyraElement {
485
594
  }
486
595
  }
487
596
  __decorate([
488
- property()
597
+ property({ converter: { fromAttribute: (value) => normalizeChartType(value) } })
489
598
  ], LyraChart.prototype, "type", void 0);
490
599
  __decorate([
491
600
  property({ attribute: false })
@@ -517,6 +626,15 @@ __decorate([
517
626
  __decorate([
518
627
  property({ type: Boolean, attribute: 'begin-at-zero' })
519
628
  ], LyraChart.prototype, "beginAtZero", void 0);
629
+ __decorate([
630
+ property({ attribute: 'accessible-label' })
631
+ ], LyraChart.prototype, "accessibleLabel", void 0);
632
+ __decorate([
633
+ property({ attribute: 'accessible-description' })
634
+ ], LyraChart.prototype, "accessibleDescription", void 0);
635
+ __decorate([
636
+ property({ type: Boolean, attribute: 'show-data-table' })
637
+ ], LyraChart.prototype, "showDataTable", void 0);
520
638
  __decorate([
521
639
  property({ type: Boolean })
522
640
  ], LyraChart.prototype, "horizontal", void 0);
@@ -539,3 +657,28 @@ __decorate([
539
657
  query('canvas')
540
658
  ], LyraChart.prototype, "canvasEl", void 0);
541
659
  defineElement('chart', LyraChart);
660
+ /**
661
+ * Installs a read-only `type` accessor pair on a `LyraChart` subclass's
662
+ * prototype, locking it to `value` — assigning `.type` afterwards (attribute
663
+ * or property) is silently ignored. `LyraChart` declares `type` as a plain
664
+ * (decorator-managed) class field, and TypeScript forbids a subclass from
665
+ * re-declaring a base field as a getter/setter pair via ordinary class
666
+ * syntax (TS2611), so the accessor pair is installed directly on the
667
+ * prototype instead, which is runtime-equivalent (same shadowing semantics
668
+ * as a class-syntax override) without tripping that check. Shared by every
669
+ * `lyra-*-chart` subclass (bar/line/pie/doughnut/scatter/bubble/radar/
670
+ * polarArea) plus `lyra-histogram`, replacing what used to be an identical
671
+ * ~16-line `Object.defineProperty` block copy-pasted into each file.
672
+ */
673
+ export function lockChartType(ctor, value) {
674
+ Object.defineProperty(ctor.prototype, 'type', {
675
+ configurable: true,
676
+ enumerable: true,
677
+ get() {
678
+ return value;
679
+ },
680
+ set(_v) {
681
+ /* locked to `value`; direct writes are ignored */
682
+ },
683
+ });
684
+ }
@@ -1,4 +1,4 @@
1
- import { LyraChart } from './chart.js';
1
+ import { LyraChart, lockChartType } from './chart.js';
2
2
  import { defineElement } from '../../internal/prefix.js';
3
3
  /**
4
4
  * `<lyra-doughnut-chart>` — `<lyra-chart>` with `type` locked to `"doughnut"`.
@@ -7,21 +7,5 @@ import { defineElement } from '../../internal/prefix.js';
7
7
  */
8
8
  export class LyraDoughnutChart extends LyraChart {
9
9
  }
10
- // `type` is locked read-only rather than a settable class field with just a
11
- // default value — `LyraChart` declares it as a plain (decorator-managed)
12
- // class field, and TypeScript forbids a subclass from re-declaring a base
13
- // field as a getter/setter pair via ordinary class syntax (TS2611) — so the
14
- // accessor pair is installed directly on the prototype instead, which is
15
- // runtime-equivalent (same shadowing semantics as a class-syntax override)
16
- // without tripping that check.
17
- Object.defineProperty(LyraDoughnutChart.prototype, 'type', {
18
- configurable: true,
19
- enumerable: true,
20
- get() {
21
- return 'doughnut';
22
- },
23
- set(_v) {
24
- /* locked to 'doughnut'; direct writes are ignored */
25
- },
26
- });
10
+ lockChartType(LyraDoughnutChart, 'doughnut');
27
11
  defineElement('doughnut-chart', LyraDoughnutChart);
@@ -2,8 +2,11 @@ export interface HistogramBucket {
2
2
  label: string;
3
3
  count: number;
4
4
  }
5
+ export declare const MAX_HISTOGRAM_BINS = 1000;
6
+ /** Converts an untyped bucket count to a bounded, non-negative integer. */
7
+ export declare function normalizeHistogramBinCount(binCount: unknown): number;
5
8
  /** Splits `values` into `binCount` equal-width buckets and counts membership.
6
9
  * Non-finite `binCount` (or <= 0) yields no buckets; a fractional `binCount`
7
- * is floored; non-finite samples in `values` are dropped rather than
8
- * corrupting bucket-index math. */
10
+ * is floored and an excessive count is capped; non-finite samples in
11
+ * `values` are dropped rather than corrupting bucket-index math. */
9
12
  export declare function binValues(values: number[], binCount: number): HistogramBucket[];
@@ -1,12 +1,20 @@
1
+ export const MAX_HISTOGRAM_BINS = 1_000;
2
+ /** Converts an untyped bucket count to a bounded, non-negative integer. */
3
+ export function normalizeHistogramBinCount(binCount) {
4
+ const numeric = typeof binCount === 'number' ? binCount : Number(binCount);
5
+ if (!Number.isFinite(numeric))
6
+ return 0;
7
+ return Math.min(MAX_HISTOGRAM_BINS, Math.max(0, Math.floor(numeric)));
8
+ }
1
9
  /** Splits `values` into `binCount` equal-width buckets and counts membership.
2
10
  * Non-finite `binCount` (or <= 0) yields no buckets; a fractional `binCount`
3
- * is floored; non-finite samples in `values` are dropped rather than
4
- * corrupting bucket-index math. */
11
+ * is floored and an excessive count is capped; non-finite samples in
12
+ * `values` are dropped rather than corrupting bucket-index math. */
5
13
  export function binValues(values, binCount) {
6
14
  const finite = values.filter((v) => Number.isFinite(v));
7
- if (finite.length === 0 || !Number.isFinite(binCount))
15
+ if (finite.length === 0)
8
16
  return [];
9
- const bins = Math.floor(binCount);
17
+ const bins = normalizeHistogramBinCount(binCount);
10
18
  if (bins <= 0)
11
19
  return [];
12
20
  let lo = finite[0];
@@ -5,10 +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 { property } from 'lit/decorators.js';
8
- import { LyraChart } from './chart.js';
8
+ import { LyraChart, lockChartType } from './chart.js';
9
9
  import { LyraElement } from '../../internal/lyra-element.js';
10
10
  import { defineElement } from '../../internal/prefix.js';
11
- import { binValues } from './histogram-bin.js';
11
+ import { binValues, normalizeHistogramBinCount, } from './histogram-bin.js';
12
12
  import { styles } from './histogram.styles.js';
13
13
  /**
14
14
  * `<lyra-histogram>` — bins `values` into `bins` equal-width buckets and
@@ -32,7 +32,7 @@ export class LyraHistogram extends LyraChart {
32
32
  static { this.styles = [LyraElement.styles, styles]; }
33
33
  }
34
34
  __decorate([
35
- property({ type: Number })
35
+ property({ converter: { fromAttribute: (value) => normalizeHistogramBinCount(value) } })
36
36
  ], LyraHistogram.prototype, "bins", void 0);
37
37
  __decorate([
38
38
  property({ attribute: false })
@@ -48,12 +48,13 @@ __decorate([
48
48
  // change doesn't re-run the O(n) bucketing loop from scratch on every access.
49
49
  const bucketCache = new WeakMap();
50
50
  export function binnedBuckets(el) {
51
+ const bins = normalizeHistogramBinCount(el.bins);
51
52
  const cached = bucketCache.get(el);
52
- if (cached && cached.values === el.values && cached.bins === el.bins) {
53
+ if (cached && cached.values === el.values && cached.bins === bins) {
53
54
  return cached.buckets;
54
55
  }
55
- const buckets = binValues(el.values, el.bins);
56
- bucketCache.set(el, { values: el.values, bins: el.bins, buckets });
56
+ const buckets = binValues(el.values, bins);
57
+ bucketCache.set(el, { values: el.values, bins, buckets });
57
58
  return buckets;
58
59
  }
59
60
  // `labels`/`datasets` are computed from `values`/`bins` rather than settable
@@ -87,14 +88,5 @@ Object.defineProperty(LyraHistogram.prototype, 'datasets', {
87
88
  // just a default value, not an enforced lock, so without this a `type="line"`
88
89
  // attribute (or `el.type = 'line'`) would silently turn a histogram into a
89
90
  // line chart of its own bucket counts.
90
- Object.defineProperty(LyraHistogram.prototype, 'type', {
91
- configurable: true,
92
- enumerable: true,
93
- get() {
94
- return 'bar';
95
- },
96
- set(_v) {
97
- /* locked to 'bar'; direct writes are ignored */
98
- },
99
- });
91
+ lockChartType(LyraHistogram, 'bar');
100
92
  defineElement('histogram', LyraHistogram);
@@ -1,4 +1,4 @@
1
- import { LyraChart } from './chart.js';
1
+ import { LyraChart, lockChartType } from './chart.js';
2
2
  import { defineElement } from '../../internal/prefix.js';
3
3
  /**
4
4
  * `<lyra-line-chart>` — `<lyra-chart>` with `type` locked to `"line"`.
@@ -7,21 +7,5 @@ import { defineElement } from '../../internal/prefix.js';
7
7
  */
8
8
  export class LyraLineChart extends LyraChart {
9
9
  }
10
- // `type` is locked read-only rather than a settable class field with just a
11
- // default value — `LyraChart` declares it as a plain (decorator-managed)
12
- // class field, and TypeScript forbids a subclass from re-declaring a base
13
- // field as a getter/setter pair via ordinary class syntax (TS2611) — so the
14
- // accessor pair is installed directly on the prototype instead, which is
15
- // runtime-equivalent (same shadowing semantics as a class-syntax override)
16
- // without tripping that check.
17
- Object.defineProperty(LyraLineChart.prototype, 'type', {
18
- configurable: true,
19
- enumerable: true,
20
- get() {
21
- return 'line';
22
- },
23
- set(_v) {
24
- /* locked to 'line'; direct writes are ignored */
25
- },
26
- });
10
+ lockChartType(LyraLineChart, 'line');
27
11
  defineElement('line-chart', LyraLineChart);
@@ -40,6 +40,22 @@ export type LyraLiteChartLayout = 'fit' | 'scroll';
40
40
  * calendar columns — overriding the internal slot math for both bars and
41
41
  * their labels. All three are additive and no-ops when left unset.
42
42
  *
43
+ * Seven further additive, opt-in properties: `pointText` overrides the
44
+ * per-bar/per-point `<title>`/`aria-label` tooltip text (mirrors
45
+ * `lyra-heatmap`'s `cellText` hook), falling back to the built-in raw-value
46
+ * template when unset; `roundedBars` draws bars as a rounded-top path
47
+ * instead of a square-cornered rect; `skipZero` omits a bar entirely (not
48
+ * just zero-height) for an exactly-`0` value; `padLeft`/`barGapRatio`
49
+ * override the internal `PAD_LEFT`/`BAR_GROUP_GAP` layout constants; `scale`
50
+ * (`type="bar"` only) switches the bar-height mapping from the default
51
+ * linear `niceDomain` fraction to a `Math.sqrt(value / domainMax)`
52
+ * compression (mirroring `lyra-heatmap`'s matrix-mode `sqrt` scale) so a
53
+ * skewed dataset's smaller bars don't get washed out by one dominant value
54
+ * — gridlines/tick labels stay on the linear domain regardless, only the bar
55
+ * marks' own height changes, and `type="line"` ignores `scale` entirely; and
56
+ * `hideAxis` suppresses `renderGrid()`'s gridlines/tick labels altogether
57
+ * (x-axis category labels, rendered separately, are unaffected).
58
+ *
43
59
  * @customElement lyra-lite-chart
44
60
  * @event lyra-point-click - Fired when a bar/point is activated (click, or
45
61
  * Enter/Space while focused). `detail: { datasetIndex: number, index:
@@ -55,6 +71,8 @@ export type LyraLiteChartLayout = 'fit' | 'scroll';
55
71
  * @csspart legend - The legend row, when `legend` is set.
56
72
  * @csspart legend-item - Each legend entry.
57
73
  * @csspart legend-swatch - Each legend entry's color swatch.
74
+ * @csspart live-region - The current mark announcement for keyboard users.
75
+ * @csspart data-list - A visually hidden list of all plotted data points.
58
76
  */
59
77
  export declare class LyraLiteChart extends LyraElement {
60
78
  static styles: import("lit").CSSResultGroup[];
@@ -92,21 +110,80 @@ export declare class LyraLiteChart extends LyraElement {
92
110
  * coordinate function. Unset (the default) uses the existing internal per-category slot math,
93
111
  * unchanged from before this property existed. */
94
112
  barX?: (index: number) => number;
113
+ /** Formats the per-bar/per-point `<title>`/`aria-label` tooltip text — receives the category
114
+ * label, the raw value, and the dataset index. Falls back to the built-in raw-value template
115
+ * when unset (mirrors `lyra-heatmap`'s `cellText` hook). */
116
+ pointText?: (label: string, value: number, datasetIndex: number) => string;
117
+ /** `type="bar"` only: draws each bar as a rounded-top-corner shape instead of the default
118
+ * square-cornered rect. Default `false` renders exactly today's plain `<rect>`. */
119
+ roundedBars: boolean;
120
+ /** `type="bar"` only: omits a bar entirely (no mark, no `tabindex`, no tooltip) for a value that
121
+ * is exactly `0` — `null`/non-finite values are always skipped regardless of this flag. Default
122
+ * `false` preserves today's behavior of a zero-height but focusable/titled bar. */
123
+ skipZero: boolean;
124
+ /** Overrides the internal `PAD_LEFT` (36px) plot-left-padding constant. Unset (the default) keeps
125
+ * today's fixed 36px. */
126
+ padLeft?: number;
127
+ /** Overrides the internal `BAR_GROUP_GAP` (0.2) fraction of a category slot left as a gap between
128
+ * categories. Unset (the default) keeps today's fixed 0.2. */
129
+ barGapRatio?: number;
130
+ /** `type="bar"` only (no effect on `type="line"`): `'linear'` (default) maps a bar's value to
131
+ * height via the standard `niceDomain`-based fraction, unchanged from before this property
132
+ * existed. `'sqrt'` instead maps via `Math.sqrt(value / domainMax)` (mirroring `lyra-heatmap`'s
133
+ * matrix-mode `sqrt` scale), boosting smaller values relative to one dominant value so a skewed
134
+ * dataset's smaller bars don't get washed out. Gridlines/tick labels stay on the linear domain
135
+ * either way — only the bar marks' own height mapping changes. */
136
+ scale: 'linear' | 'sqrt';
137
+ /** Suppresses `renderGrid()` entirely — no gridlines, no y-axis tick labels. x-axis category
138
+ * labels (rendered separately) are unaffected. Default `false` preserves today's behavior. */
139
+ hideAxis: boolean;
95
140
  private plotWidth;
96
141
  private plotHeight;
97
142
  private visible;
143
+ /** One roving tab stop across all bar/point marks. */
144
+ private activeMarkIndex;
145
+ private liveText;
98
146
  private svgEl?;
99
147
  private resizeObserver?;
100
148
  private intersectionObserver?;
101
- private lastSignature;
102
- private lastResult?;
103
149
  connectedCallback(): void;
104
150
  disconnectedCallback(): void;
105
151
  protected firstUpdated(): void;
106
152
  protected updated(changed: PropertyValues): void;
107
153
  private colorFor;
154
+ /** Dispatches to the host-provided `pointText` formatter when set, otherwise `undefined` (the
155
+ * caller falls back to its own built-in template) — mirrors `lyra-heatmap`'s `resolveCellText()`. */
156
+ private resolvePointText;
157
+ /** The ordered set of visible marks used by both keyboard navigation and
158
+ * the screen-reader data alternative. */
159
+ private interactiveMarks;
160
+ private markIndexMap;
161
+ private normalizedMarkIndex;
162
+ private markAnnouncement;
163
+ private onMarkFocus;
164
+ private focusMark;
165
+ /**
166
+ * A value-to-y-pixel mapping for a bar's top/bottom edge. `'linear'` (the
167
+ * default) is the standard `niceDomain`-fraction formula, unchanged from
168
+ * before `scale` existed. `'sqrt'` instead compresses via
169
+ * `Math.sqrt(value / domainMax)` (mirroring `lyra-heatmap`'s matrix-mode
170
+ * `sqrt` branch), clamping `value` to `[0, hi]` first since a negative
171
+ * input has no real square root — bar charts using `scale="sqrt"` are
172
+ * expected to carry non-negative data, same assumption as the heatmap
173
+ * precedent this mirrors.
174
+ */
175
+ private barValueToY;
176
+ /**
177
+ * A rounded-top-corners `<path>` `d` string for a bar occupying
178
+ * `[x, y, x+w, y+h]` — an SVG `<rect>` can only express a uniform radius
179
+ * on all four corners, so `roundedBars` switches the mark to a path
180
+ * instead of adding `rx`/`ry` to keep the bottom edge square against the
181
+ * baseline. `r` is clamped so it never exceeds half the bar's width or its
182
+ * full height (a thin/short bar degrades to a plain rectangle path rather
183
+ * than self-intersecting).
184
+ */
185
+ private roundedBarPath;
108
186
  private domain;
109
- private computeSignature;
110
187
  private emitPoint;
111
188
  private onPointKeyDown;
112
189
  private renderGrid;