@iyulab/chat-components 0.14.1 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,20 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.15.0] - 2026-09-30
4
+
5
+ ### Changed
6
+
7
+ - **`u-chart-block` no longer changes Chart.js global defaults.** Its theme colors (text, ticks,
8
+ grid, tooltip) used to be written to `Chart.defaults`, which also recolored any other Chart.js chart
9
+ on the page. They are now options of that chart instance, merged under the `options` you pass —
10
+ your values win, down to a single key.
11
+ - **`u-chart-block` uses the design-token chart palette.** Datasets that set no `backgroundColor` or
12
+ `borderColor` take `--u-chart-color-1..N` (one color per slice for `pie`, `doughnut`, and
13
+ `polarArea`), matching dashboards and `@iyulab/u-widgets`. Colors you set are kept, and the `data`
14
+ object you pass is not modified. Without the token sheet, Chart.js defaults apply as before.
15
+ - **Peer ranges promise only tested majors:** `chart.js` is `^4.5.1` (was `>=4.5.1`), `@lit/react`
16
+ `^1.0.8` (was `>=1.0.8`), and `react` `^18.0.0 || ^19.0.0` (was `>=18.0.0`).
17
+
3
18
  ## [0.14.1] - 2026-09-30
4
19
 
5
20
  ### Fixed
@@ -26,8 +26,11 @@ export declare class UChartBlock extends UElement {
26
26
  private createChart;
27
27
  /** 기존 차트 인스턴스 제거 */
28
28
  private destroyChart;
29
- /** 현재 테마의 CSS 변수를 읽어 Chart.defaults에 전역 적용 */
30
- private applyTheme;
29
+ /**
30
+ * 현재 테마의 CSS 변수를 읽는다. ⚠`Chart.defaults` 를 바꾸지 않는다 — 전역이라 같은 페이지에서
31
+ * 소비자가 직접 만든 chart.js 차트의 색까지 바꿨다. 값은 이 인스턴스의 옵션으로만 들어간다.
32
+ */
33
+ private readTheme;
31
34
  private handleDownloadPNG;
32
35
  private handleDownloadJSON;
33
36
  private handleFullscreen;
@@ -85,15 +85,15 @@ var UChartBlock = class UChartBlock extends UElement {
85
85
  return;
86
86
  }
87
87
  try {
88
- this.applyTheme();
88
+ const theme = this.readTheme();
89
89
  this.chartjs = new Chart(ctx, {
90
90
  type: this.type,
91
- data: this.data,
92
- options: {
91
+ data: paintDatasets(this.type, this.data, theme),
92
+ options: mergeOptions(themeOptions(this.type, this.data, this.options, theme), {
93
93
  responsive: true,
94
94
  maintainAspectRatio: true,
95
95
  ...this.options
96
- }
96
+ })
97
97
  });
98
98
  } catch (e) {
99
99
  this.error = e instanceof Error ? e.message : String(e);
@@ -106,22 +106,26 @@ var UChartBlock = class UChartBlock extends UElement {
106
106
  this.chartjs = null;
107
107
  }
108
108
  }
109
- /** 현재 테마의 CSS 변수를 읽어 Chart.defaults에 전역 적용 */
110
- applyTheme() {
111
- const css = (v) => getComputedStyle(this).getPropertyValue(v).trim();
112
- const textColor = css("--u-txt-color");
113
- const weakColor = css("--u-txt-color-weak");
114
- const borderColor = css("--u-border-color");
115
- const bgColor = css("--u-bg-color");
116
- Chart.defaults.color = textColor;
117
- Chart.defaults.borderColor = borderColor;
118
- Chart.defaults.plugins.tooltip.backgroundColor = bgColor;
119
- Chart.defaults.plugins.tooltip.titleColor = textColor;
120
- Chart.defaults.plugins.tooltip.bodyColor = weakColor;
121
- Chart.defaults.plugins.tooltip.borderColor = borderColor;
122
- Chart.defaults.plugins.tooltip.borderWidth = 1;
123
- Chart.defaults.scale.ticks.color = weakColor;
124
- Chart.defaults.scale.grid.color = borderColor;
109
+ /**
110
+ * 현재 테마의 CSS 변수를 읽는다. ⚠`Chart.defaults` 를 바꾸지 않는다 — 전역이라 같은 페이지에서
111
+ * 소비자가 직접 만든 chart.js 차트의 색까지 바꿨다. 값은 이 인스턴스의 옵션으로만 들어간다.
112
+ */
113
+ readTheme() {
114
+ const style = getComputedStyle(this);
115
+ const css = (v) => style.getPropertyValue(v).trim();
116
+ const palette = [];
117
+ for (let i = 1; i <= PALETTE_MAX; i++) {
118
+ const c = css(`--u-chart-color-${i}`);
119
+ if (!c) break;
120
+ palette.push(c);
121
+ }
122
+ return {
123
+ text: css("--u-txt-color"),
124
+ weak: css("--u-txt-color-weak"),
125
+ border: css("--u-border-color"),
126
+ background: css("--u-bg-color"),
127
+ palette
128
+ };
125
129
  }
126
130
  handleDownloadPNG() {
127
131
  if (!this.chartjs) return;
@@ -155,5 +159,100 @@ __decorate([query("canvas"), __decorateMetadata("design:type", typeof HTMLCanvas
155
159
  __decorate([query(".viewport"), __decorateMetadata("design:type", typeof HTMLElement === "undefined" ? Object : HTMLElement)], UChartBlock.prototype, "viewport", void 0);
156
160
  __decorate([state(), __decorateMetadata("design:type", Object)], UChartBlock.prototype, "error", void 0);
157
161
  UChartBlock = __decorate([customElement("u-chart-block")], UChartBlock);
162
+ /** 읽어 볼 팔레트 토큰의 상한 — 시트는 여덟을 선언한다. 비어 있는 첫 번호에서 멈춘다. */
163
+ var PALETTE_MAX = 12;
164
+ function isPlain(v) {
165
+ return typeof v === "object" && v !== null && !Array.isArray(v);
166
+ }
167
+ /** `base` 위에 `over` 를 깊게 덮는다 — 소비자가 준 값이 항상 이긴다. 입력은 바꾸지 않는다. */
168
+ function mergeOptions(base, over) {
169
+ const out = { ...base };
170
+ for (const [k, v] of Object.entries(over)) out[k] = isPlain(v) && isPlain(out[k]) ? mergeOptions(out[k], v) : v;
171
+ return out;
172
+ }
173
+ var CARTESIAN = /* @__PURE__ */ new Set([
174
+ "bar",
175
+ "line",
176
+ "scatter",
177
+ "bubble"
178
+ ]);
179
+ var RADIAL = /* @__PURE__ */ new Set(["radar", "polarArea"]);
180
+ var PER_POINT = /* @__PURE__ */ new Set([
181
+ "pie",
182
+ "doughnut",
183
+ "polarArea"
184
+ ]);
185
+ /**
186
+ * 테마 색을 담은 인스턴스 옵션. 축은 «이 차트에 실제로 생길 축» 에만 준다 — 원형 차트에 `x`/`y`
187
+ * 설정을 주면 축이 생길 수 있어서다. 축 id 는 차트 종류의 기본 축 · 소비자가 선언한 축 · 데이터셋이
188
+ * 가리키는 축의 합집합이다.
189
+ */
190
+ function themeOptions(type, data, options, t) {
191
+ const tooltip = {
192
+ backgroundColor: t.background,
193
+ titleColor: t.text,
194
+ bodyColor: t.weak,
195
+ borderColor: t.border,
196
+ borderWidth: 1
197
+ };
198
+ const ids = new Set(Object.keys(options?.scales ?? {}));
199
+ if (CARTESIAN.has(type)) {
200
+ ids.add("x");
201
+ ids.add("y");
202
+ }
203
+ if (RADIAL.has(type)) ids.add("r");
204
+ for (const ds of data.datasets ?? []) {
205
+ const d = ds;
206
+ for (const key of [
207
+ "xAxisID",
208
+ "yAxisID",
209
+ "rAxisID"
210
+ ]) if (typeof d[key] === "string") ids.add(d[key]);
211
+ }
212
+ const scales = {};
213
+ for (const id of ids) scales[id] = {
214
+ ticks: { color: t.weak },
215
+ grid: { color: t.border },
216
+ ...RADIAL.has(type) && id === "r" ? {
217
+ angleLines: { color: t.border },
218
+ pointLabels: { color: t.text }
219
+ } : {}
220
+ };
221
+ return {
222
+ color: t.text,
223
+ borderColor: t.border,
224
+ plugins: {
225
+ tooltip,
226
+ legend: { labels: { color: t.text } }
227
+ },
228
+ ...ids.size ? { scales } : {}
229
+ };
230
+ }
231
+ /**
232
+ * 색을 지정하지 않은 데이터셋에 팔레트를 입힌다. 소비자가 준 `backgroundColor`/`borderColor` 는
233
+ * 건드리지 않고, 입력 객체도 바꾸지 않는다(사본을 돌려준다). 원형 차트는 조각마다 한 색이다.
234
+ */
235
+ function paintDatasets(type, data, t) {
236
+ const p = t.palette;
237
+ if (!p.length || !data.datasets) return data;
238
+ const datasets = data.datasets.map((ds, i) => {
239
+ const d = { ...ds };
240
+ const has = (k) => d[k] !== void 0;
241
+ if (PER_POINT.has(type)) {
242
+ const n = Array.isArray(d.data) ? d.data.length : 0;
243
+ if (!has("backgroundColor")) d.backgroundColor = Array.from({ length: n }, (_, j) => p[j % p.length]);
244
+ if (!has("borderColor") && t.background) d.borderColor = t.background;
245
+ } else {
246
+ const c = p[i % p.length];
247
+ if (!has("borderColor")) d.borderColor = c;
248
+ if (!has("backgroundColor")) d.backgroundColor = c;
249
+ }
250
+ return d;
251
+ });
252
+ return {
253
+ ...data,
254
+ datasets
255
+ };
256
+ }
158
257
  //#endregion
159
258
  export { UChartBlock };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@iyulab/chat-components",
3
3
  "description": "llm chat components for building chat interfaces",
4
- "version": "0.14.1",
4
+ "version": "0.15.0",
5
5
  "author": "iyulab",
6
6
  "license": "MIT",
7
7
  "keywords": [
@@ -70,9 +70,9 @@
70
70
  },
71
71
  "peerDependencies": {
72
72
  "@iyulab/components": ">=1.51.0",
73
- "@lit/react": ">=1.0.8",
74
- "chart.js": ">=4.5.1",
75
- "react": ">=18.0.0"
73
+ "@lit/react": "^1.0.8",
74
+ "chart.js": "^4.5.1",
75
+ "react": "^18.0.0 || ^19.0.0"
76
76
  },
77
77
  "peerDependenciesMeta": {
78
78
  "@lit/react": {
@@ -65,6 +65,23 @@ of it was clipped by the block's `overflow: hidden` with no scrollbar left to re
65
65
 
66
66
  There is no size property or custom property for this — the host is the lever.
67
67
 
68
+ ## Colors and theme
69
+
70
+ Text, tick, grid, and tooltip colors come from the design tokens (`--u-txt-color`,
71
+ `--u-txt-color-weak`, `--u-border-color`, `--u-bg-color`), and dataset colors from the chart palette
72
+ `--u-chart-color-1..N` — the same palette dashboards and `@iyulab/u-widgets` use. Both apply to **this
73
+ chart only**: `Chart.defaults` is not modified, so other Chart.js charts on the page keep their own
74
+ look.
75
+
76
+ Anything you pass wins: a dataset's own `backgroundColor`/`borderColor` is kept, and `options` are
77
+ merged over the theme (`options.scales.x.ticks.color` overrides just that value). Without the token
78
+ sheet the palette is empty and Chart.js defaults apply.
79
+
80
+ ```css
81
+ /* Recolour the palette for one area */
82
+ .report u-chart-block { --u-chart-color-1: #0b6e4f; }
83
+ ```
84
+
68
85
  ## Properties
69
86
 
70
87
  | Property | Type | Default | Description |
@@ -80,6 +97,7 @@ There is no size property or custom property for this — the host is the lever.
80
97
  | Download | Export chart as PNG or raw JSON data |
81
98
  | Full screen | Toggle the chart viewport into full-screen mode |
82
99
  | Theme sync | Watches `document.documentElement[theme]` and re-renders on change |
100
+ | Palette | Datasets without `backgroundColor`/`borderColor` take `--u-chart-color-1..N` from the design-token sheet (per slice for `pie`/`doughnut`/`polarArea`) |
83
101
 
84
102
  ## Requires
85
103