@aceshooting/lyra-ui 0.1.2 → 0.1.4

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 (80) hide show
  1. package/README.md +2 -8
  2. package/custom-elements.json +3745 -2953
  3. package/dist/components/chart/bar-chart.js +17 -4
  4. package/dist/components/chart/box-plot.d.ts +8 -1
  5. package/dist/components/chart/box-plot.js +40 -4
  6. package/dist/components/chart/box-plot.styles.js +4 -0
  7. package/dist/components/chart/bubble-chart.d.ts +1 -2
  8. package/dist/components/chart/bubble-chart.js +17 -4
  9. package/dist/components/chart/chart-loader.d.ts +21 -4
  10. package/dist/components/chart/chart-loader.js +39 -12
  11. package/dist/components/chart/chart.d.ts +9 -3
  12. package/dist/components/chart/chart.js +42 -21
  13. package/dist/components/chart/doughnut-chart.d.ts +1 -2
  14. package/dist/components/chart/doughnut-chart.js +17 -4
  15. package/dist/components/chart/histogram-bin.js +12 -2
  16. package/dist/components/chart/histogram.d.ts +2 -2
  17. package/dist/components/chart/histogram.js +16 -2
  18. package/dist/components/chart/line-chart.js +17 -4
  19. package/dist/components/chart/pie-chart.d.ts +1 -2
  20. package/dist/components/chart/pie-chart.js +17 -4
  21. package/dist/components/chart/polar-area-chart.d.ts +1 -2
  22. package/dist/components/chart/polar-area-chart.js +17 -4
  23. package/dist/components/chart/radar-chart.d.ts +1 -2
  24. package/dist/components/chart/radar-chart.js +17 -4
  25. package/dist/components/chart/scatter-chart.js +17 -4
  26. package/dist/components/combobox/combobox.js +3 -4
  27. package/dist/components/combobox/combobox.stories.d.ts +6 -0
  28. package/dist/components/combobox/combobox.stories.js +27 -0
  29. package/dist/components/combobox/combobox.styles.js +2 -2
  30. package/dist/components/date-picker/date-input.stories.d.ts +5 -0
  31. package/dist/components/date-picker/date-input.stories.js +12 -0
  32. package/dist/components/date-picker/date-input.styles.js +2 -2
  33. package/dist/components/date-picker/date-picker.stories.d.ts +6 -0
  34. package/dist/components/date-picker/date-picker.stories.js +13 -0
  35. package/dist/components/empty/empty.d.ts +1 -1
  36. package/dist/components/empty/empty.js +1 -1
  37. package/dist/components/export-button/export-button.d.ts +2 -2
  38. package/dist/components/export-button/export-button.js +2 -2
  39. package/dist/components/file-input/accept.d.ts +1 -2
  40. package/dist/components/file-input/accept.js +1 -2
  41. package/dist/components/file-input/file-input.d.ts +2 -2
  42. package/dist/components/file-input/file-input.js +2 -2
  43. package/dist/components/flag/flag.stories.d.ts +5 -0
  44. package/dist/components/flag/flag.stories.js +17 -0
  45. package/dist/components/flag/flag.styles.js +1 -1
  46. package/dist/components/gauge/gauge.d.ts +1 -1
  47. package/dist/components/gauge/gauge.js +1 -1
  48. package/dist/components/graph/graph.d.ts +33 -4
  49. package/dist/components/graph/graph.js +146 -30
  50. package/dist/components/heatmap/calendar-grid.d.ts +31 -0
  51. package/dist/components/heatmap/calendar-grid.js +52 -0
  52. package/dist/components/heatmap/heatmap-scale.d.ts +8 -0
  53. package/dist/components/heatmap/heatmap-scale.js +21 -0
  54. package/dist/components/heatmap/heatmap.d.ts +19 -0
  55. package/dist/components/heatmap/heatmap.js +133 -20
  56. package/dist/components/heatmap/heatmap.styles.js +4 -0
  57. package/dist/components/map/map.d.ts +15 -0
  58. package/dist/components/map/map.js +89 -7
  59. package/dist/components/playback/playback.d.ts +2 -2
  60. package/dist/components/playback/playback.js +2 -2
  61. package/dist/components/skeleton/skeleton.d.ts +2 -2
  62. package/dist/components/skeleton/skeleton.js +2 -2
  63. package/dist/components/sparkline/sparkline.stories.d.ts +7 -0
  64. package/dist/components/sparkline/sparkline.stories.js +17 -0
  65. package/dist/components/stat/stat.d.ts +1 -1
  66. package/dist/components/stat/stat.js +6 -7
  67. package/dist/components/table/table.d.ts +1 -2
  68. package/dist/components/table/table.js +1 -2
  69. package/dist/components/table/table.styles.js +2 -3
  70. package/dist/components/time-range/time-range.js +4 -4
  71. package/dist/components/toast/toast.stories.d.ts +5 -0
  72. package/dist/components/toast/toast.stories.js +32 -0
  73. package/dist/components/tree/tree-node.d.ts +2 -2
  74. package/dist/components/tree/tree-node.js +2 -2
  75. package/dist/internal/form-associated.js +1 -2
  76. package/dist/internal/icons.js +1 -1
  77. package/dist/internal/tokens.styles.js +3 -3
  78. package/llms-full.txt +59 -83
  79. package/llms.txt +1 -2
  80. package/package.json +4 -1
@@ -6,9 +6,22 @@ import { defineElement } from '../../internal/prefix.js';
6
6
  * @customElement lyra-bar-chart
7
7
  */
8
8
  export class LyraBarChart extends LyraChart {
9
- constructor() {
10
- super(...arguments);
11
- this.type = 'bar';
12
- }
13
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(LyraBarChart.prototype, 'type', {
18
+ configurable: true,
19
+ enumerable: true,
20
+ get() {
21
+ return 'bar';
22
+ },
23
+ set(_v) {
24
+ /* locked to 'bar'; direct writes are ignored */
25
+ },
26
+ });
14
27
  defineElement('bar-chart', LyraBarChart);
@@ -1,5 +1,6 @@
1
1
  import { type TemplateResult, type PropertyValues } from 'lit';
2
2
  import { LyraElement } from '../../internal/lyra-element.js';
3
+ import '../skeleton/skeleton.js';
3
4
  export interface BoxPlotPoint {
4
5
  min: number;
5
6
  q1: number;
@@ -15,7 +16,7 @@ export interface BoxPlotSeries {
15
16
  /**
16
17
  * `<lyra-box-plot>` — a box-and-whisker chart from precomputed five-number
17
18
  * summaries (no raw sample data is shipped to the browser). Beyond Web
18
- * Awesome's chart set — heavily used across ML/clustering dashboards.
19
+ * Awesome's chart set — useful for summarizing distributions.
19
20
  *
20
21
  * @customElement lyra-box-plot
21
22
  * @csspart base, canvas
@@ -28,6 +29,12 @@ export declare class LyraBoxPlot extends LyraElement {
28
29
  height: string;
29
30
  yLabel: string;
30
31
  beginAtZero: boolean;
32
+ /**
33
+ * True until the lazy-loaded `chart.js` + `@sgratzl/chartjs-chart-boxplot`
34
+ * peer dependencies have settled (success or failure) — mirrors
35
+ * `LyraChart`'s `loading` state.
36
+ */
37
+ private loading;
31
38
  private canvasEl?;
32
39
  private chart?;
33
40
  private chartJsModule?;
@@ -5,11 +5,12 @@ 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 } from 'lit';
8
- import { property, query } from 'lit/decorators.js';
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
11
  import { loadChartJs } from './chart-loader.js';
12
12
  import { styles } from './box-plot.styles.js';
13
+ import '../skeleton/skeleton.js';
13
14
  let boxPlotPlugin;
14
15
  /**
15
16
  * Lazily loads `@sgratzl/chartjs-chart-boxplot` and registers its controller
@@ -36,7 +37,7 @@ function loadBoxPlotPlugin() {
36
37
  /**
37
38
  * `<lyra-box-plot>` — a box-and-whisker chart from precomputed five-number
38
39
  * summaries (no raw sample data is shipped to the browser). Beyond Web
39
- * Awesome's chart set — heavily used across ML/clustering dashboards.
40
+ * Awesome's chart set — useful for summarizing distributions.
40
41
  *
41
42
  * @customElement lyra-box-plot
42
43
  * @csspart base, canvas
@@ -50,6 +51,12 @@ export class LyraBoxPlot extends LyraElement {
50
51
  this.height = '280px';
51
52
  this.yLabel = '';
52
53
  this.beginAtZero = true;
54
+ /**
55
+ * True until the lazy-loaded `chart.js` + `@sgratzl/chartjs-chart-boxplot`
56
+ * peer dependencies have settled (success or failure) — mirrors
57
+ * `LyraChart`'s `loading` state.
58
+ */
59
+ this.loading = true;
53
60
  }
54
61
  static { this.styles = [LyraElement.styles, styles]; }
55
62
  connectedCallback() {
@@ -64,6 +71,7 @@ export class LyraBoxPlot extends LyraElement {
64
71
  // established pattern in `LyraChart.connectedCallback()` — instead of
65
72
  // unconditionally re-awaiting the already-cached `loadChartJs()` promise.
66
73
  async onBoxPlotPluginLoaded(boxMod) {
74
+ this.loading = false;
67
75
  if (!boxMod)
68
76
  return;
69
77
  this.chartJsModule = (await loadChartJs()) ?? undefined;
@@ -75,6 +83,10 @@ export class LyraBoxPlot extends LyraElement {
75
83
  this.chart = undefined;
76
84
  }
77
85
  updated(changed) {
86
+ if (this.loading)
87
+ this.setAttribute('aria-busy', 'true');
88
+ else
89
+ this.removeAttribute('aria-busy');
78
90
  // `--lyra-chart-height` is read by `:host`'s `block-size` in
79
91
  // `chart.styles.ts` (shared with `lyra-chart`). Custom properties only
80
92
  // cascade downward (host -> shadow tree), so this must be set on the
@@ -106,13 +118,34 @@ export class LyraBoxPlot extends LyraElement {
106
118
  },
107
119
  };
108
120
  }
121
+ // Mirrors `LyraChart.draw()`'s reuse-existing-instance-when-possible
122
+ // pattern: an update that doesn't need a new Chart.js instance (e.g. a
123
+ // bare `height` change, or new `boxes`/`labels` data) mutates the existing
124
+ // chart and does an incremental `.update('none')` instead of tearing down
125
+ // and rebuilding the whole canvas/instance on every reactive update. Unlike
126
+ // `LyraChart`, `LyraBoxPlot` has no raw `config` passthrough that could
127
+ // change the effective Chart.js type out from under `this.type`, so
128
+ // reusing whenever a chart already exists is always safe here.
109
129
  draw() {
110
130
  if (!this.chartJsModule || !this.canvasEl)
111
131
  return;
112
- this.chart?.destroy();
113
- this.chart = new this.chartJsModule.Chart(this.canvasEl, this.buildConfig());
132
+ const config = this.buildConfig();
133
+ if (this.chart) {
134
+ this.chart.data = config.data;
135
+ this.chart.options = config.options ?? {};
136
+ this.chart.update('none');
137
+ return;
138
+ }
139
+ this.chart = new this.chartJsModule.Chart(this.canvasEl, config);
114
140
  }
115
141
  render() {
142
+ if (this.loading) {
143
+ return html `
144
+ <div part="base">
145
+ <lyra-skeleton variant="rect"></lyra-skeleton>
146
+ </div>
147
+ `;
148
+ }
116
149
  const label = this.boxes.map((b) => b.label).join(', ') || 'Box plot';
117
150
  return html `
118
151
  <div part="base">
@@ -139,6 +172,9 @@ __decorate([
139
172
  __decorate([
140
173
  property({ type: Boolean, attribute: 'begin-at-zero' })
141
174
  ], LyraBoxPlot.prototype, "beginAtZero", void 0);
175
+ __decorate([
176
+ state()
177
+ ], LyraBoxPlot.prototype, "loading", void 0);
142
178
  __decorate([
143
179
  query('canvas')
144
180
  ], LyraBoxPlot.prototype, "canvasEl", void 0);
@@ -15,6 +15,10 @@ export const styles = css `
15
15
  inline-size: 100%;
16
16
  block-size: 100%;
17
17
  }
18
+ lyra-skeleton {
19
+ --lyra-skeleton-w: 100%;
20
+ --lyra-skeleton-h: 100%;
21
+ }
18
22
  canvas {
19
23
  inline-size: 100% !important;
20
24
  block-size: 100% !important;
@@ -1,5 +1,4 @@
1
1
  import { LyraChart } from './chart.js';
2
- import type { LyraChartType } from './chart.js';
3
2
  /**
4
3
  * `<lyra-bubble-chart>` — `<lyra-chart>` with `type` locked to `"bubble"`. Feed
5
4
  * points via `Series.points`, each needing an `x`/`y`/`r` (radius) triple —
@@ -10,7 +9,7 @@ import type { LyraChartType } from './chart.js';
10
9
  * @customElement lyra-bubble-chart
11
10
  */
12
11
  export declare class LyraBubbleChart extends LyraChart {
13
- type: LyraChartType;
12
+ type: 'bubble';
14
13
  }
15
14
  declare global {
16
15
  interface HTMLElementTagNameMap {
@@ -10,9 +10,22 @@ import { defineElement } from '../../internal/prefix.js';
10
10
  * @customElement lyra-bubble-chart
11
11
  */
12
12
  export class LyraBubbleChart extends LyraChart {
13
- constructor() {
14
- super(...arguments);
15
- this.type = 'bubble';
16
- }
17
13
  }
14
+ // `type` is locked read-only rather than a settable class field with just a
15
+ // default value — `LyraChart` declares it as a plain (decorator-managed)
16
+ // class field, and TypeScript forbids a subclass from re-declaring a base
17
+ // field as a getter/setter pair via ordinary class syntax (TS2611) — so the
18
+ // accessor pair is installed directly on the prototype instead, which is
19
+ // runtime-equivalent (same shadowing semantics as a class-syntax override)
20
+ // without tripping that check.
21
+ Object.defineProperty(LyraBubbleChart.prototype, 'type', {
22
+ configurable: true,
23
+ enumerable: true,
24
+ get() {
25
+ return 'bubble';
26
+ },
27
+ set(_v) {
28
+ /* locked to 'bubble'; direct writes are ignored */
29
+ },
30
+ });
18
31
  defineElement('bubble-chart', LyraBubbleChart);
@@ -1,8 +1,25 @@
1
1
  import type * as ChartJsModule from 'chart.js';
2
+ import type ZoomPlugin from 'chartjs-plugin-zoom';
2
3
  /**
3
- * Lazily loads the optional peer dependency `chart.js` (+ `chartjs-plugin-zoom`)
4
- * once per page, registering only the tree-shaken subset this library uses.
5
- * Resolves to `null` (with a one-time warning) if chart.js isn't installed —
6
- * mirrors `<lyra-flag>`'s peer-dependency pattern.
4
+ * Independently loads the mandatory `chart.js` peer dependency and the
5
+ * opt-in `chartjs-plugin-zoom` peer dependency, so a partial install
6
+ * (`chart.js` only, which `peerDependenciesMeta` marks as a valid
7
+ * combination) degrades to "charts render, zoom is inert" instead of every
8
+ * chart breaking. Exported (in addition to `loadChartJs()` below) so both
9
+ * failure paths — and the real caught error each one logs — are directly
10
+ * testable without needing to actually uninstall either package.
11
+ */
12
+ export declare function loadChartAndZoom(importChart?: () => Promise<typeof ChartJsModule>, importZoom?: () => Promise<{
13
+ default: typeof ZoomPlugin;
14
+ }>): Promise<{
15
+ mod: typeof ChartJsModule;
16
+ zoomPlugin: typeof ZoomPlugin | undefined;
17
+ } | null>;
18
+ /**
19
+ * Lazily loads the optional peer dependency `chart.js` (+ `chartjs-plugin-zoom`,
20
+ * loaded independently — see `loadChartAndZoom()`) once per page, registering
21
+ * only the tree-shaken subset this library uses. Resolves to `null` if
22
+ * chart.js isn't installed; still resolves the real module (with zoom simply
23
+ * unregistered) if only `chartjs-plugin-zoom` is missing.
7
24
  */
8
25
  export declare function loadChartJs(): Promise<typeof ChartJsModule | null>;
@@ -1,25 +1,52 @@
1
1
  let chartJs;
2
2
  let registered = false;
3
3
  /**
4
- * Lazily loads the optional peer dependency `chart.js` (+ `chartjs-plugin-zoom`)
5
- * once per page, registering only the tree-shaken subset this library uses.
6
- * Resolves to `null` (with a one-time warning) if chart.js isn't installed —
7
- * mirrors `<lyra-flag>`'s peer-dependency pattern.
4
+ * Independently loads the mandatory `chart.js` peer dependency and the
5
+ * opt-in `chartjs-plugin-zoom` peer dependency, so a partial install
6
+ * (`chart.js` only, which `peerDependenciesMeta` marks as a valid
7
+ * combination) degrades to "charts render, zoom is inert" instead of every
8
+ * chart breaking. Exported (in addition to `loadChartJs()` below) so both
9
+ * failure paths — and the real caught error each one logs — are directly
10
+ * testable without needing to actually uninstall either package.
11
+ */
12
+ export async function loadChartAndZoom(importChart = () => import('chart.js'), importZoom = () => import('chartjs-plugin-zoom')) {
13
+ let mod;
14
+ try {
15
+ mod = await importChart();
16
+ }
17
+ catch (err) {
18
+ console.warn('<lyra-chart> needs the optional peer dependency `chart.js` — install it with `pnpm add chart.js`:', err);
19
+ return null;
20
+ }
21
+ let zoomPlugin;
22
+ try {
23
+ zoomPlugin = (await importZoom()).default;
24
+ }
25
+ catch (err) {
26
+ console.warn('<lyra-chart> zoom support needs the optional peer dependency `chartjs-plugin-zoom` — ' +
27
+ 'charts still render without it, but the `zoom` attribute has no effect until it is ' +
28
+ 'installed with `pnpm add chartjs-plugin-zoom`:', err);
29
+ }
30
+ return { mod, zoomPlugin };
31
+ }
32
+ /**
33
+ * Lazily loads the optional peer dependency `chart.js` (+ `chartjs-plugin-zoom`,
34
+ * loaded independently — see `loadChartAndZoom()`) once per page, registering
35
+ * only the tree-shaken subset this library uses. Resolves to `null` if
36
+ * chart.js isn't installed; still resolves the real module (with zoom simply
37
+ * unregistered) if only `chartjs-plugin-zoom` is missing.
8
38
  */
9
39
  export function loadChartJs() {
10
40
  if (!chartJs) {
11
- chartJs = Promise.all([import('chart.js'), import('chartjs-plugin-zoom')])
12
- .then(([mod, zoomMod]) => {
41
+ chartJs = loadChartAndZoom().then((result) => {
42
+ if (!result)
43
+ return null;
44
+ const { mod, zoomPlugin } = result;
13
45
  if (!registered) {
14
- mod.Chart.register(mod.LineController, mod.BarController, mod.ScatterController, mod.DoughnutController, mod.PieController, mod.RadarController, mod.PolarAreaController, mod.BubbleController, mod.LineElement, mod.PointElement, mod.BarElement, mod.ArcElement, mod.LinearScale, mod.CategoryScale, mod.RadialLinearScale, mod.Filler, mod.Tooltip, mod.Legend, zoomMod.default);
46
+ mod.Chart.register(mod.LineController, mod.BarController, mod.ScatterController, mod.DoughnutController, mod.PieController, mod.RadarController, mod.PolarAreaController, mod.BubbleController, mod.LineElement, mod.PointElement, mod.BarElement, mod.ArcElement, mod.LinearScale, mod.CategoryScale, mod.RadialLinearScale, mod.Filler, mod.Tooltip, mod.Legend, ...(zoomPlugin ? [zoomPlugin] : []));
15
47
  registered = true;
16
48
  }
17
49
  return mod;
18
- })
19
- .catch(() => {
20
- console.warn('<lyra-chart> needs the optional peer dependencies `chart.js` and ' +
21
- '`chartjs-plugin-zoom` — install them with `pnpm add chart.js chartjs-plugin-zoom`.');
22
- return null;
23
50
  });
24
51
  }
25
52
  return chartJs;
@@ -20,12 +20,12 @@ export interface Series {
20
20
  pointRadius?: number;
21
21
  type?: 'line' | 'bar';
22
22
  }
23
- export type LyraChartType = 'line' | 'bar' | 'scatter';
23
+ export type LyraChartType = 'line' | 'bar' | 'scatter' | 'pie' | 'doughnut' | 'radar' | 'polarArea' | 'bubble';
24
24
  /**
25
25
  * `<lyra-chart>` — the core Chart.js wrapper every other `lyra-*-chart` tag
26
26
  * subclasses. Requires the optional peer deps `chart.js` + `chartjs-plugin-zoom`.
27
27
  *
28
- * **API mirror note (Task 0):** the real `wa-chart` docs page
28
+ * **API mirror note:** the real `wa-chart` docs page
29
29
  * (https://webawesome.com/docs/components/chart/) documents a `config:
30
30
  * ChartJS['config']` property alongside its simplified attributes — "a
31
31
  * flexible wrapper around Chart.js" supporting *both* simplified attributes
@@ -73,11 +73,17 @@ export declare class LyraChart extends LyraElement {
73
73
  private chart?;
74
74
  private chartJsModule?;
75
75
  private builtType?;
76
- private onThemeChange;
77
76
  connectedCallback(): void;
78
77
  disconnectedCallback(): void;
79
78
  protected updated(changed: PropertyValues): void;
80
79
  private seriesToDataset;
80
+ /**
81
+ * Builds `options.scales` for the effective chart type: no scale at all for
82
+ * pie/doughnut (a proportional-area chart has no axis), the single radial
83
+ * `r` scale Chart.js v4 uses for radar/polarArea, and the cartesian
84
+ * `x`/`y`(/`y2`) block for every other (line/bar/scatter/bubble) type.
85
+ */
86
+ private buildScales;
81
87
  private buildConfig;
82
88
  private draw;
83
89
  /** Reset any active zoom/pan back to the original view. */
@@ -23,12 +23,18 @@ import '../skeleton/skeleton.js';
23
23
  function isPlainObject(value) {
24
24
  return typeof value === 'object' && value !== null && !Array.isArray(value);
25
25
  }
26
+ // Keys that would let a JSON-sourced `config` (e.g. parsed from an API
27
+ // response) reach up through the merge and mutate `Object.prototype` —
28
+ // skipped unconditionally regardless of `base`'s own shape.
29
+ const UNSAFE_KEYS = new Set(['__proto__', 'constructor', 'prototype']);
26
30
  function deepMerge(base, override) {
27
31
  if (!isPlainObject(base) || !isPlainObject(override)) {
28
32
  return (override === undefined ? base : override);
29
33
  }
30
34
  const result = { ...base };
31
35
  for (const key of Object.keys(override)) {
36
+ if (UNSAFE_KEYS.has(key))
37
+ continue;
32
38
  result[key] = deepMerge(base[key], override[key]);
33
39
  }
34
40
  return result;
@@ -37,7 +43,7 @@ function deepMerge(base, override) {
37
43
  * `<lyra-chart>` — the core Chart.js wrapper every other `lyra-*-chart` tag
38
44
  * subclasses. Requires the optional peer deps `chart.js` + `chartjs-plugin-zoom`.
39
45
  *
40
- * **API mirror note (Task 0):** the real `wa-chart` docs page
46
+ * **API mirror note:** the real `wa-chart` docs page
41
47
  * (https://webawesome.com/docs/components/chart/) documents a `config:
42
48
  * ChartJS['config']` property alongside its simplified attributes — "a
43
49
  * flexible wrapper around Chart.js" supporting *both* simplified attributes
@@ -73,12 +79,10 @@ export class LyraChart extends LyraElement {
73
79
  /** True until the lazy-loaded `chart.js` peer dependency has settled (success or failure). */
74
80
  this.loading = true;
75
81
  this.zoomed = false;
76
- this.onThemeChange = () => this.draw();
77
82
  }
78
83
  static { this.styles = [LyraElement.styles, styles]; }
79
84
  connectedCallback() {
80
85
  super.connectedCallback();
81
- window.addEventListener('lyra-theme', this.onThemeChange);
82
86
  void loadChartJs().then((mod) => {
83
87
  this.loading = false;
84
88
  if (!mod)
@@ -89,7 +93,6 @@ export class LyraChart extends LyraElement {
89
93
  }
90
94
  disconnectedCallback() {
91
95
  super.disconnectedCallback();
92
- window.removeEventListener('lyra-theme', this.onThemeChange);
93
96
  this.chart?.destroy();
94
97
  this.chart = undefined;
95
98
  }
@@ -127,8 +130,41 @@ export class LyraChart extends LyraElement {
127
130
  tooltip: s.noTooltip ? { enabled: false } : undefined,
128
131
  };
129
132
  }
130
- buildConfig() {
133
+ /**
134
+ * Builds `options.scales` for the effective chart type: no scale at all for
135
+ * pie/doughnut (a proportional-area chart has no axis), the single radial
136
+ * `r` scale Chart.js v4 uses for radar/polarArea, and the cartesian
137
+ * `x`/`y`(/`y2`) block for every other (line/bar/scatter/bubble) type.
138
+ */
139
+ buildScales() {
140
+ if (this.type === 'pie' || this.type === 'doughnut')
141
+ return {};
142
+ if (this.type === 'radar' || this.type === 'polarArea') {
143
+ return {
144
+ r: {
145
+ beginAtZero: this.beginAtZero,
146
+ },
147
+ };
148
+ }
131
149
  const hasY2 = this.datasets.some((s) => s.axis === 'y2');
150
+ return {
151
+ x: {
152
+ type: this.type === 'scatter' ? 'linear' : 'category',
153
+ title: { display: !!this.xLabel, text: this.xLabel },
154
+ },
155
+ y: { beginAtZero: this.beginAtZero, title: { display: !!this.yLabel, text: this.yLabel } },
156
+ ...(hasY2
157
+ ? {
158
+ y2: {
159
+ position: 'right',
160
+ grid: { drawOnChartArea: false },
161
+ title: { display: !!this.y2Label, text: this.y2Label },
162
+ },
163
+ }
164
+ : {}),
165
+ };
166
+ }
167
+ buildConfig() {
132
168
  const generated = {
133
169
  type: this.type,
134
170
  data: {
@@ -158,22 +194,7 @@ export class LyraChart extends LyraElement {
158
194
  }
159
195
  : undefined,
160
196
  },
161
- scales: {
162
- x: {
163
- type: this.type === 'scatter' ? 'linear' : 'category',
164
- title: { display: !!this.xLabel, text: this.xLabel },
165
- },
166
- y: { beginAtZero: this.beginAtZero, title: { display: !!this.yLabel, text: this.yLabel } },
167
- ...(hasY2
168
- ? {
169
- y2: {
170
- position: 'right',
171
- grid: { drawOnChartArea: false },
172
- title: { display: !!this.y2Label, text: this.y2Label },
173
- },
174
- }
175
- : {}),
176
- },
197
+ scales: this.buildScales(),
177
198
  },
178
199
  };
179
200
  if (!this.config)
@@ -1,12 +1,11 @@
1
1
  import { LyraChart } from './chart.js';
2
- import type { LyraChartType } from './chart.js';
3
2
  /**
4
3
  * `<lyra-doughnut-chart>` — `<lyra-chart>` with `type` locked to `"doughnut"`.
5
4
  *
6
5
  * @customElement lyra-doughnut-chart
7
6
  */
8
7
  export declare class LyraDoughnutChart extends LyraChart {
9
- type: LyraChartType;
8
+ type: 'doughnut';
10
9
  }
11
10
  declare global {
12
11
  interface HTMLElementTagNameMap {
@@ -6,9 +6,22 @@ import { defineElement } from '../../internal/prefix.js';
6
6
  * @customElement lyra-doughnut-chart
7
7
  */
8
8
  export class LyraDoughnutChart extends LyraChart {
9
- constructor() {
10
- super(...arguments);
11
- this.type = 'doughnut';
12
- }
13
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
+ });
14
27
  defineElement('doughnut-chart', LyraDoughnutChart);
@@ -2,8 +2,18 @@
2
2
  export function binValues(values, binCount) {
3
3
  if (values.length === 0 || binCount <= 0)
4
4
  return [];
5
- const lo = Math.min(...values);
6
- const hi = Math.max(...values);
5
+ // Spreading `values` as call arguments (`Math.min(...values)`) throws
6
+ // `RangeError: Maximum call stack size exceeded` once `values` is large
7
+ // enough to overflow the engine's argument-list limit — reduce manually
8
+ // instead so an arbitrarily large sample never crashes.
9
+ let lo = values[0];
10
+ let hi = values[0];
11
+ for (const v of values) {
12
+ if (v < lo)
13
+ lo = v;
14
+ if (v > hi)
15
+ hi = v;
16
+ }
7
17
  const span = hi - lo || 1;
8
18
  const width = span / binCount;
9
19
  const buckets = Array.from({ length: binCount }, (_, i) => {
@@ -1,8 +1,8 @@
1
1
  import { LyraChart } from './chart.js';
2
2
  /**
3
3
  * `<lyra-histogram>` — bins `values` into `bins` equal-width buckets and
4
- * renders them as a bar chart. No Chart.js histogram controller exists in
5
- * any surveyed seed; this composes `binValues()` with the plain `bar` type.
4
+ * renders them as a bar chart. Chart.js has no built-in histogram
5
+ * controller; this composes `binValues()` with the plain `bar` type.
6
6
  *
7
7
  * @customElement lyra-histogram
8
8
  */
@@ -12,8 +12,8 @@ import { binValues } 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
15
- * renders them as a bar chart. No Chart.js histogram controller exists in
16
- * any surveyed seed; this composes `binValues()` with the plain `bar` type.
15
+ * renders them as a bar chart. Chart.js has no built-in histogram
16
+ * controller; this composes `binValues()` with the plain `bar` type.
17
17
  *
18
18
  * @customElement lyra-histogram
19
19
  */
@@ -67,4 +67,18 @@ Object.defineProperty(LyraHistogram.prototype, 'datasets', {
67
67
  /* derived from `values`/`bins`; direct writes are ignored */
68
68
  },
69
69
  });
70
+ // `type` is locked to `'bar'` the same way — the field initializer above is
71
+ // just a default value, not an enforced lock, so without this a `type="line"`
72
+ // attribute (or `el.type = 'line'`) would silently turn a histogram into a
73
+ // line chart of its own bucket counts.
74
+ Object.defineProperty(LyraHistogram.prototype, 'type', {
75
+ configurable: true,
76
+ enumerable: true,
77
+ get() {
78
+ return 'bar';
79
+ },
80
+ set(_v) {
81
+ /* locked to 'bar'; direct writes are ignored */
82
+ },
83
+ });
70
84
  defineElement('histogram', LyraHistogram);
@@ -6,9 +6,22 @@ import { defineElement } from '../../internal/prefix.js';
6
6
  * @customElement lyra-line-chart
7
7
  */
8
8
  export class LyraLineChart extends LyraChart {
9
- constructor() {
10
- super(...arguments);
11
- this.type = 'line';
12
- }
13
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
+ });
14
27
  defineElement('line-chart', LyraLineChart);
@@ -1,5 +1,4 @@
1
1
  import { LyraChart } from './chart.js';
2
- import type { LyraChartType } from './chart.js';
3
2
  /**
4
3
  * `<lyra-pie-chart>` — `<lyra-chart>` with `type` locked to `"pie"`. Single-series:
5
4
  * one `Series` with `data: number[]` and `color: string[]` as the slice palette.
@@ -7,7 +6,7 @@ import type { LyraChartType } from './chart.js';
7
6
  * @customElement lyra-pie-chart
8
7
  */
9
8
  export declare class LyraPieChart extends LyraChart {
10
- type: LyraChartType;
9
+ type: 'pie';
11
10
  }
12
11
  declare global {
13
12
  interface HTMLElementTagNameMap {
@@ -7,9 +7,22 @@ import { defineElement } from '../../internal/prefix.js';
7
7
  * @customElement lyra-pie-chart
8
8
  */
9
9
  export class LyraPieChart extends LyraChart {
10
- constructor() {
11
- super(...arguments);
12
- this.type = 'pie';
13
- }
14
10
  }
11
+ // `type` is locked read-only rather than a settable class field with just a
12
+ // default value — `LyraChart` declares it as a plain (decorator-managed)
13
+ // class field, and TypeScript forbids a subclass from re-declaring a base
14
+ // field as a getter/setter pair via ordinary class syntax (TS2611) — so the
15
+ // accessor pair is installed directly on the prototype instead, which is
16
+ // runtime-equivalent (same shadowing semantics as a class-syntax override)
17
+ // without tripping that check.
18
+ Object.defineProperty(LyraPieChart.prototype, 'type', {
19
+ configurable: true,
20
+ enumerable: true,
21
+ get() {
22
+ return 'pie';
23
+ },
24
+ set(_v) {
25
+ /* locked to 'pie'; direct writes are ignored */
26
+ },
27
+ });
15
28
  defineElement('pie-chart', LyraPieChart);
@@ -1,12 +1,11 @@
1
1
  import { LyraChart } from './chart.js';
2
- import type { LyraChartType } from './chart.js';
3
2
  /**
4
3
  * `<lyra-polar-area-chart>` — `<lyra-chart>` with `type` locked to `"polarArea"`.
5
4
  *
6
5
  * @customElement lyra-polar-area-chart
7
6
  */
8
7
  export declare class LyraPolarAreaChart extends LyraChart {
9
- type: LyraChartType;
8
+ type: 'polarArea';
10
9
  }
11
10
  declare global {
12
11
  interface HTMLElementTagNameMap {