@x-plat/design-system 0.5.7 → 0.5.9

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.
@@ -17,24 +17,45 @@ var __copyProps = (to, from, except, desc) => {
17
17
  };
18
18
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
19
 
20
- // src/components/Card/index.ts
21
- var Card_exports = {};
22
- __export(Card_exports, {
23
- Card: () => Card_default
20
+ // src/components/Box/index.ts
21
+ var Box_exports = {};
22
+ __export(Box_exports, {
23
+ Box: () => Box_default
24
24
  });
25
- module.exports = __toCommonJS(Card_exports);
25
+ module.exports = __toCommonJS(Box_exports);
26
26
 
27
- // src/components/Card/Card.tsx
27
+ // ../../node_modules/clsx/dist/clsx.mjs
28
+ function r(e) {
29
+ var t, f, n = "";
30
+ if ("string" == typeof e || "number" == typeof e) n += e;
31
+ else if ("object" == typeof e) if (Array.isArray(e)) {
32
+ var o = e.length;
33
+ for (t = 0; t < o; t++) e[t] && (f = r(e[t])) && (n && (n += " "), n += f);
34
+ } else for (f in e) e[f] && (n && (n += " "), n += f);
35
+ return n;
36
+ }
37
+ function clsx() {
38
+ for (var e, t, f = 0, n = "", o = arguments.length; f < o; f++) (e = arguments[f]) && (t = r(e)) && (n && (n += " "), n += t);
39
+ return n;
40
+ }
41
+ var clsx_default = clsx;
42
+
43
+ // src/components/Box/Box.tsx
28
44
  var import_jsx_runtime = require("react/jsx-runtime");
29
- var Card = ({ children, title }) => {
30
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "lib-xplat-card", children: [
31
- title && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "title", children: title }),
32
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "content", children })
45
+ var Box = ({
46
+ children,
47
+ title,
48
+ variant = "outlined",
49
+ padding = "md"
50
+ }) => {
51
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: clsx_default("lib-xplat-box", variant, `pad-${padding}`), children: [
52
+ title && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "box-title", children: title }),
53
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "box-content", children })
33
54
  ] });
34
55
  };
35
- Card.displayName = "Card";
36
- var Card_default = Card;
56
+ Box.displayName = "Box";
57
+ var Box_default = Box;
37
58
  // Annotate the CommonJS export names for ESM import in node:
38
59
  0 && (module.exports = {
39
- Card
60
+ Box
40
61
  });
@@ -0,0 +1,55 @@
1
+ /* src/components/Box/box.scss */
2
+ .lib-xplat-box {
3
+ contain: layout style;
4
+ width: 100%;
5
+ height: 100%;
6
+ display: flex;
7
+ flex-direction: column;
8
+ flex: 1;
9
+ min-width: 0;
10
+ border-radius: var(--spacing-radius-xl);
11
+ background-color: var(--semantic-surface-neutral-default);
12
+ overflow: hidden;
13
+ }
14
+ .lib-xplat-box.outlined {
15
+ border: 1px solid var(--semantic-border-default);
16
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
17
+ }
18
+ .lib-xplat-box.elevated {
19
+ border: none;
20
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.06), 0 8px 24px rgba(0, 0, 0, 0.08);
21
+ }
22
+ .lib-xplat-box.flat {
23
+ border: none;
24
+ background-color: var(--semantic-surface-neutral-subtle);
25
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.03);
26
+ }
27
+ .lib-xplat-box > .box-title {
28
+ font-weight: 600;
29
+ font-size: 14px;
30
+ color: var(--semantic-text-strong);
31
+ padding: var(--spacing-space-3) var(--spacing-space-4);
32
+ border-bottom: 1px solid var(--semantic-border-subtle);
33
+ min-width: 0;
34
+ overflow: hidden;
35
+ text-overflow: ellipsis;
36
+ white-space: nowrap;
37
+ }
38
+ .lib-xplat-box > .box-content {
39
+ display: flex;
40
+ flex-direction: column;
41
+ flex: 1;
42
+ min-height: 0;
43
+ }
44
+ .lib-xplat-box.pad-none > .box-content {
45
+ padding: 0;
46
+ }
47
+ .lib-xplat-box.pad-sm > .box-content {
48
+ padding: var(--spacing-space-2) var(--spacing-space-3);
49
+ }
50
+ .lib-xplat-box.pad-md > .box-content {
51
+ padding: var(--spacing-space-4);
52
+ }
53
+ .lib-xplat-box.pad-lg > .box-content {
54
+ padding: var(--spacing-space-6);
55
+ }
@@ -0,0 +1,13 @@
1
+ import React from 'react';
2
+
3
+ type BoxVariant = "outlined" | "elevated" | "flat";
4
+ type BoxPadding = "none" | "sm" | "md" | "lg";
5
+ interface BoxProps {
6
+ children: React.ReactNode;
7
+ title?: React.ReactNode;
8
+ variant?: BoxVariant;
9
+ padding?: BoxPadding;
10
+ }
11
+ declare const Box: React.FC<BoxProps>;
12
+
13
+ export { Box };
@@ -0,0 +1,13 @@
1
+ import React from 'react';
2
+
3
+ type BoxVariant = "outlined" | "elevated" | "flat";
4
+ type BoxPadding = "none" | "sm" | "md" | "lg";
5
+ interface BoxProps {
6
+ children: React.ReactNode;
7
+ title?: React.ReactNode;
8
+ variant?: BoxVariant;
9
+ padding?: BoxPadding;
10
+ }
11
+ declare const Box: React.FC<BoxProps>;
12
+
13
+ export { Box };
@@ -0,0 +1,34 @@
1
+ // ../../node_modules/clsx/dist/clsx.mjs
2
+ function r(e) {
3
+ var t, f, n = "";
4
+ if ("string" == typeof e || "number" == typeof e) n += e;
5
+ else if ("object" == typeof e) if (Array.isArray(e)) {
6
+ var o = e.length;
7
+ for (t = 0; t < o; t++) e[t] && (f = r(e[t])) && (n && (n += " "), n += f);
8
+ } else for (f in e) e[f] && (n && (n += " "), n += f);
9
+ return n;
10
+ }
11
+ function clsx() {
12
+ for (var e, t, f = 0, n = "", o = arguments.length; f < o; f++) (e = arguments[f]) && (t = r(e)) && (n && (n += " "), n += t);
13
+ return n;
14
+ }
15
+ var clsx_default = clsx;
16
+
17
+ // src/components/Box/Box.tsx
18
+ import { jsx, jsxs } from "react/jsx-runtime";
19
+ var Box = ({
20
+ children,
21
+ title,
22
+ variant = "outlined",
23
+ padding = "md"
24
+ }) => {
25
+ return /* @__PURE__ */ jsxs("div", { className: clsx_default("lib-xplat-box", variant, `pad-${padding}`), children: [
26
+ title && /* @__PURE__ */ jsx("div", { className: "box-title", children: title }),
27
+ /* @__PURE__ */ jsx("div", { className: "box-content", children })
28
+ ] });
29
+ };
30
+ Box.displayName = "Box";
31
+ var Box_default = Box;
32
+ export {
33
+ Box_default as Box
34
+ };
@@ -1,6 +1,11 @@
1
1
  /* src/components/Button/button.scss */
2
2
  .lib-xplat-button {
3
3
  width: 100%;
4
+ display: inline-flex;
5
+ align-items: center;
6
+ justify-content: center;
7
+ gap: var(--spacing-space-2);
8
+ white-space: nowrap;
4
9
  border-radius: var(--spacing-radius-md);
5
10
  font-weight: 500;
6
11
  cursor: pointer;
@@ -37,25 +37,65 @@ module.exports = __toCommonJS(Chart_exports);
37
37
  // src/components/Chart/Chart.tsx
38
38
  var import_react = __toESM(require("react"), 1);
39
39
  var import_jsx_runtime = require("react/jsx-runtime");
40
- var LINE_BAR_PALETTES = [
41
- ["var(--primitive-red-100)", "var(--primitive-red-200)", "var(--primitive-red-300)", "var(--primitive-red-400)", "var(--primitive-red-500)", "var(--primitive-red-600)"],
42
- ["var(--primitive-orange-100)", "var(--primitive-orange-200)", "var(--primitive-orange-300)", "var(--primitive-orange-400)", "var(--primitive-orange-500)", "var(--primitive-orange-600)"],
43
- ["var(--primitive-yellow-100)", "var(--primitive-yellow-200)", "var(--primitive-yellow-300)", "var(--primitive-yellow-400)", "var(--primitive-yellow-500)", "var(--primitive-yellow-600)"],
44
- ["var(--primitive-green-100)", "var(--primitive-green-200)", "var(--primitive-green-300)", "var(--primitive-green-400)", "var(--primitive-green-500)", "var(--primitive-green-600)"],
45
- ["var(--primitive-blue-100)", "var(--primitive-blue-200)", "var(--primitive-blue-300)", "var(--primitive-blue-400)", "var(--primitive-blue-500)", "var(--primitive-blue-600)"],
46
- ["var(--primitive-light-blue-100)", "var(--primitive-light-blue-200)", "var(--primitive-light-blue-300)", "var(--primitive-light-blue-400)", "var(--primitive-light-blue-500)", "var(--primitive-light-blue-600)"],
47
- ["var(--primitive-purple-100)", "var(--primitive-purple-200)", "var(--primitive-purple-300)", "var(--primitive-purple-400)", "var(--primitive-purple-500)", "var(--primitive-purple-600)"],
48
- ["var(--primitive-pink-100)", "var(--primitive-pink-200)", "var(--primitive-pink-300)", "var(--primitive-pink-400)", "var(--primitive-pink-500)", "var(--primitive-pink-600)"]
49
- ];
50
- var PIE_PALETTES = [
51
- ["var(--primitive-orange-300)", "var(--primitive-red-300)", "var(--primitive-yellow-300)", "var(--primitive-green-300)", "var(--primitive-blue-300)", "var(--primitive-light-blue-300)"],
52
- ["var(--primitive-orange-400)", "var(--primitive-red-400)", "var(--primitive-yellow-400)", "var(--primitive-green-400)", "var(--primitive-blue-400)", "var(--primitive-light-blue-400)"],
53
- ["var(--primitive-orange-500)", "var(--primitive-red-500)", "var(--primitive-yellow-500)", "var(--primitive-green-500)", "var(--primitive-blue-500)", "var(--primitive-light-blue-500)"]
54
- ];
55
- var getPalette = (palettes, index) => {
56
- return palettes[index % palettes.length];
40
+ var CATEGORICAL_COUNT = 8;
41
+ var LINE_BAR_PALETTES = Array.from({ length: CATEGORICAL_COUNT }, (_, i) => {
42
+ const n = i + 1;
43
+ return [
44
+ `var(--semantic-categorical-${n}-bg)`,
45
+ `var(--semantic-categorical-${n}-area)`,
46
+ `var(--semantic-categorical-${n}-fill)`,
47
+ `var(--semantic-categorical-${n}-text)`
48
+ ];
49
+ });
50
+ var PIE_COLORS = Array.from(
51
+ { length: CATEGORICAL_COUNT },
52
+ (_, i) => `var(--semantic-categorical-${i + 1}-fill)`
53
+ );
54
+ var hashString = (str) => {
55
+ let hash = 0;
56
+ for (let i = 0; i < str.length; i++) {
57
+ hash = (hash << 5) - hash + str.charCodeAt(i) | 0;
58
+ }
59
+ return Math.abs(hash);
60
+ };
61
+ var getPalette = (palettes, index, key) => {
62
+ const offset = key ? hashString(key) : 0;
63
+ return palettes[(index + offset) % palettes.length];
57
64
  };
58
65
  var PADDING = { top: 20, right: 20, bottom: 30, left: 40 };
66
+ var toSmoothPath = (points) => {
67
+ if (points.length < 2) return "";
68
+ const p = points;
69
+ let d = `M ${p[0].x} ${p[0].y}`;
70
+ for (let i = 0; i < p.length - 1; i++) {
71
+ const p0 = p[Math.max(0, i - 1)];
72
+ const p1 = p[i];
73
+ const p2 = p[i + 1];
74
+ const p3 = p[Math.min(p.length - 1, i + 2)];
75
+ const cp1x = p1.x + (p2.x - p0.x) / 6;
76
+ const cp1y = p1.y + (p2.y - p0.y) / 6;
77
+ const cp2x = p2.x - (p3.x - p1.x) / 6;
78
+ const cp2y = p2.y - (p3.y - p1.y) / 6;
79
+ d += ` C ${cp1x} ${cp1y}, ${cp2x} ${cp2y}, ${p2.x} ${p2.y}`;
80
+ }
81
+ return d;
82
+ };
83
+ var useChartSize = (ref) => {
84
+ const [size, setSize] = import_react.default.useState({ width: 0, height: 0 });
85
+ import_react.default.useEffect(() => {
86
+ const el = ref.current;
87
+ if (!el) return;
88
+ const observer = new ResizeObserver((entries) => {
89
+ const entry = entries[0];
90
+ if (!entry) return;
91
+ const { width, height } = entry.contentRect;
92
+ setSize({ width: Math.floor(width), height: Math.floor(height) });
93
+ });
94
+ observer.observe(el);
95
+ return () => observer.disconnect();
96
+ }, [ref]);
97
+ return size;
98
+ };
59
99
  var useChartTooltip = (enabled) => {
60
100
  const [tooltip, setTooltip] = import_react.default.useState({
61
101
  visible: false,
@@ -90,15 +130,15 @@ var useChartTooltip = (enabled) => {
90
130
  }, []);
91
131
  return { tooltip, show, hide, move, containerRef };
92
132
  };
93
- var LineChart = import_react.default.memo(({ data, labels, onHover, onMove, onLeave }) => {
133
+ var LineChart = import_react.default.memo(({ data, labels, width, height, onHover, onMove, onLeave }) => {
94
134
  const entries = import_react.default.useMemo(() => Object.entries(data), [data]);
95
135
  const maxVal = import_react.default.useMemo(() => {
96
136
  const allValues = entries.flatMap(([, v]) => v);
97
137
  return Math.max(...allValues) * 1.2 || 1;
98
138
  }, [entries]);
99
139
  const count = labels.length;
100
- const chartW = 600 - PADDING.left - PADDING.right;
101
- const chartH = 300 - PADDING.top - PADDING.bottom;
140
+ const chartW = width - PADDING.left - PADDING.right;
141
+ const chartH = height - PADDING.top - PADDING.bottom;
102
142
  const seriesPoints = import_react.default.useMemo(
103
143
  () => entries.map(
104
144
  ([, values]) => values.map((v, i) => ({
@@ -109,22 +149,22 @@ var LineChart = import_react.default.memo(({ data, labels, onHover, onMove, onLe
109
149
  ),
110
150
  [entries, count, chartW, chartH, maxVal]
111
151
  );
112
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { viewBox: "0 0 600 300", className: "chart-svg", children: [
152
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { viewBox: `0 0 ${width} ${height}`, className: "chart-svg", children: [
113
153
  [0, 0.25, 0.5, 0.75, 1].map((ratio) => {
114
154
  const y = PADDING.top + (1 - ratio) * chartH;
115
155
  const val = Math.round(maxVal * ratio);
116
156
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("g", { children: [
117
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: PADDING.left, y1: y, x2: 600 - PADDING.right, y2: y, className: "chart-grid" }),
157
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: PADDING.left, y1: y, x2: width - PADDING.right, y2: y, className: "chart-grid" }),
118
158
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("text", { x: PADDING.left - 8, y: y + 4, className: "chart-axis-label", textAnchor: "end", children: val })
119
159
  ] }, ratio);
120
160
  }),
121
161
  labels.map((label, i) => {
122
162
  const x = PADDING.left + i / (count - 1 || 1) * chartW;
123
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("text", { x, y: 300 - 8, className: "chart-axis-label", textAnchor: "middle", children: label }, i);
163
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("text", { x, y: height - 8, className: "chart-axis-label", textAnchor: "middle", children: label }, i);
124
164
  }),
125
165
  entries.map(([key], di) => {
126
- const palette = getPalette(LINE_BAR_PALETTES, di);
127
- const color = palette[4];
166
+ const palette = getPalette(LINE_BAR_PALETTES, di, key);
167
+ const color = palette[2];
128
168
  const points = seriesPoints[di];
129
169
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("g", { children: [
130
170
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
@@ -155,7 +195,73 @@ var LineChart = import_react.default.memo(({ data, labels, onHover, onMove, onLe
155
195
  ] });
156
196
  });
157
197
  LineChart.displayName = "LineChart";
158
- var BarChart = import_react.default.memo(({ data, labels, onHover, onMove, onLeave }) => {
198
+ var CurveChart = import_react.default.memo(({ data, labels, width, height, onHover, onMove, onLeave }) => {
199
+ const entries = import_react.default.useMemo(() => Object.entries(data), [data]);
200
+ const maxVal = import_react.default.useMemo(() => {
201
+ const allValues = entries.flatMap(([, v]) => v);
202
+ return Math.max(...allValues) * 1.2 || 1;
203
+ }, [entries]);
204
+ const count = labels.length;
205
+ const chartW = width - PADDING.left - PADDING.right;
206
+ const chartH = height - PADDING.top - PADDING.bottom;
207
+ const seriesPoints = import_react.default.useMemo(
208
+ () => entries.map(
209
+ ([, values]) => values.map((v, i) => ({
210
+ x: PADDING.left + i / (count - 1 || 1) * chartW,
211
+ y: PADDING.top + (1 - v / maxVal) * chartH,
212
+ v
213
+ }))
214
+ ),
215
+ [entries, count, chartW, chartH, maxVal]
216
+ );
217
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { viewBox: `0 0 ${width} ${height}`, className: "chart-svg", children: [
218
+ [0, 0.25, 0.5, 0.75, 1].map((ratio) => {
219
+ const y = PADDING.top + (1 - ratio) * chartH;
220
+ const val = Math.round(maxVal * ratio);
221
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("g", { children: [
222
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: PADDING.left, y1: y, x2: width - PADDING.right, y2: y, className: "chart-grid" }),
223
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("text", { x: PADDING.left - 8, y: y + 4, className: "chart-axis-label", textAnchor: "end", children: val })
224
+ ] }, ratio);
225
+ }),
226
+ labels.map((label, i) => {
227
+ const x = PADDING.left + i / (count - 1 || 1) * chartW;
228
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("text", { x, y: height - 8, className: "chart-axis-label", textAnchor: "middle", children: label }, i);
229
+ }),
230
+ entries.map(([key], di) => {
231
+ const palette = getPalette(LINE_BAR_PALETTES, di, key);
232
+ const color = palette[2];
233
+ const areaColor = palette[0];
234
+ const points = seriesPoints[di];
235
+ const gradientId = `curve-gradient-${di}`;
236
+ const linePath = toSmoothPath(points);
237
+ const areaPath = linePath + ` L ${points[points.length - 1].x} ${PADDING.top + chartH} L ${points[0].x} ${PADDING.top + chartH} Z`;
238
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("g", { children: [
239
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("defs", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("linearGradient", { id: gradientId, x1: "0", y1: "0", x2: "0", y2: "1", children: [
240
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("stop", { offset: "0%", stopColor: areaColor, stopOpacity: "0.4" }),
241
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("stop", { offset: "100%", stopColor: areaColor, stopOpacity: "0.02" })
242
+ ] }) }),
243
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: areaPath, fill: `url(#${gradientId})` }),
244
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: linePath, fill: "none", stroke: color, strokeWidth: "2" }),
245
+ points.map((p, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
246
+ "circle",
247
+ {
248
+ cx: p.x,
249
+ cy: p.y,
250
+ r: "4",
251
+ fill: color,
252
+ className: "chart-point",
253
+ onMouseEnter: (e) => onHover(e, `${key}: ${labels[i]} \u2014 ${p.v}`),
254
+ onMouseMove: onMove,
255
+ onMouseLeave: onLeave
256
+ },
257
+ i
258
+ ))
259
+ ] }, di);
260
+ })
261
+ ] });
262
+ });
263
+ CurveChart.displayName = "CurveChart";
264
+ var BarChart = import_react.default.memo(({ data, labels, width, height, onHover, onMove, onLeave }) => {
159
265
  const entries = import_react.default.useMemo(() => Object.entries(data), [data]);
160
266
  const maxVal = import_react.default.useMemo(() => {
161
267
  const allValues = entries.flatMap(([, v]) => v);
@@ -163,8 +269,8 @@ var BarChart = import_react.default.memo(({ data, labels, onHover, onMove, onLea
163
269
  }, [entries]);
164
270
  const count = labels.length;
165
271
  const groupCount = entries.length;
166
- const chartW = 600 - PADDING.left - PADDING.right;
167
- const chartH = 300 - PADDING.top - PADDING.bottom;
272
+ const chartW = width - PADDING.left - PADDING.right;
273
+ const chartH = height - PADDING.top - PADDING.bottom;
168
274
  const groupW = chartW / count;
169
275
  const barW = Math.min(32, groupW * 0.7 / groupCount);
170
276
  const bars = import_react.default.useMemo(
@@ -178,19 +284,19 @@ var BarChart = import_react.default.memo(({ data, labels, onHover, onMove, onLea
178
284
  ),
179
285
  [entries, maxVal, chartH, groupW, barW, groupCount]
180
286
  );
181
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { viewBox: "0 0 600 300", className: "chart-svg", children: [
287
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { viewBox: `0 0 ${width} ${height}`, className: "chart-svg", children: [
182
288
  [0, 0.25, 0.5, 0.75, 1].map((ratio) => {
183
289
  const y = PADDING.top + (1 - ratio) * chartH;
184
290
  const val = Math.round(maxVal * ratio);
185
291
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("g", { children: [
186
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: PADDING.left, y1: y, x2: 600 - PADDING.right, y2: y, className: "chart-grid" }),
292
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: PADDING.left, y1: y, x2: width - PADDING.right, y2: y, className: "chart-grid" }),
187
293
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("text", { x: PADDING.left - 8, y: y + 4, className: "chart-axis-label", textAnchor: "end", children: val })
188
294
  ] }, ratio);
189
295
  }),
190
- labels.map((label, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("text", { x: PADDING.left + groupW * i + groupW / 2, y: 300 - 8, className: "chart-axis-label", textAnchor: "middle", children: label }, i)),
296
+ labels.map((label, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("text", { x: PADDING.left + groupW * i + groupW / 2, y: height - 8, className: "chart-axis-label", textAnchor: "middle", children: label }, i)),
191
297
  entries.map(([key], di) => {
192
- const palette = getPalette(LINE_BAR_PALETTES, di);
193
- const color = palette[4];
298
+ const palette = getPalette(LINE_BAR_PALETTES, di, key);
299
+ const color = palette[2];
194
300
  return bars[di].map((b, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
195
301
  "rect",
196
302
  {
@@ -198,7 +304,7 @@ var BarChart = import_react.default.memo(({ data, labels, onHover, onMove, onLea
198
304
  y: b.y,
199
305
  width: b.w,
200
306
  height: b.h,
201
- rx: "2",
307
+ rx: Math.min(4, b.w / 2),
202
308
  fill: color,
203
309
  className: "chart-bar",
204
310
  onMouseEnter: (e) => onHover(e, `${key}: ${labels[i]} \u2014 ${b.v}`),
@@ -212,14 +318,17 @@ var BarChart = import_react.default.memo(({ data, labels, onHover, onMove, onLea
212
318
  });
213
319
  BarChart.displayName = "BarChart";
214
320
  var PieDonutChart = import_react.default.memo(
215
- ({ data, labels, isDoughnut, onHover, onMove, onLeave }) => {
216
- const values = import_react.default.useMemo(() => Object.entries(data).flatMap(([, v]) => v), [data]);
321
+ ({ data, labels, width, height, isDoughnut, onHover, onMove, onLeave }) => {
322
+ const entries = import_react.default.useMemo(() => Object.entries(data), [data]);
323
+ const values = import_react.default.useMemo(() => entries.flatMap(([, v]) => v), [entries]);
217
324
  const total = import_react.default.useMemo(() => values.reduce((a, b) => a + b, 0) || 1, [values]);
218
- const cx = 150;
219
- const cy = 150;
220
- const r = 120;
221
- const innerR = isDoughnut ? 60 : 0;
222
- const palette = getPalette(PIE_PALETTES, 0);
325
+ const size = Math.min(width, height);
326
+ const cx = size / 2;
327
+ const cy = size / 2;
328
+ const r = size * 0.4;
329
+ const innerR = isDoughnut ? r * 0.5 : 0;
330
+ const firstKey = entries[0]?.[0] ?? "";
331
+ const colorOffset = hashString(firstKey);
223
332
  const sliceData = import_react.default.useMemo(() => {
224
333
  let angle0 = -Math.PI / 2;
225
334
  return values.map((v, i) => {
@@ -248,13 +357,13 @@ var PieDonutChart = import_react.default.memo(
248
357
  angle0 = endAngle;
249
358
  return { d, lx, ly, v, pct, angle, label: labels[i] || `${i + 1}` };
250
359
  });
251
- }, [values, total, innerR, labels]);
252
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { viewBox: "0 0 300 300", className: "chart-svg chart-pie", children: sliceData.map((s, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("g", { children: [
360
+ }, [values, total, cx, cy, r, innerR, labels]);
361
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { viewBox: `0 0 ${size} ${size}`, className: "chart-svg chart-pie", children: sliceData.map((s, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("g", { children: [
253
362
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
254
363
  "path",
255
364
  {
256
365
  d: s.d,
257
- fill: palette[i % palette.length],
366
+ fill: PIE_COLORS[(i + colorOffset) % PIE_COLORS.length],
258
367
  className: "chart-slice",
259
368
  onMouseEnter: (e) => onHover(e, `${s.label}: ${s.v} (${s.pct}%)`),
260
369
  onMouseMove: onMove,
@@ -266,22 +375,42 @@ var PieDonutChart = import_react.default.memo(
266
375
  }
267
376
  );
268
377
  PieDonutChart.displayName = "PieDonutChart";
378
+ var TooltipBubble = ({ x, y, containerWidth, children }) => {
379
+ const ref = import_react.default.useRef(null);
380
+ const [adjustedX, setAdjustedX] = import_react.default.useState(x);
381
+ import_react.default.useEffect(() => {
382
+ const el = ref.current;
383
+ if (!el) return;
384
+ const w = el.offsetWidth;
385
+ const half = w / 2;
386
+ const margin = 8;
387
+ let nx = x;
388
+ if (x - half < margin) nx = half + margin;
389
+ else if (x + half > containerWidth - margin) nx = containerWidth - half - margin;
390
+ setAdjustedX(nx);
391
+ }, [x, containerWidth]);
392
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
393
+ "div",
394
+ {
395
+ ref,
396
+ className: "chart-tooltip",
397
+ style: { left: adjustedX, top: y },
398
+ children
399
+ }
400
+ );
401
+ };
269
402
  var Chart = (props) => {
270
403
  const { type, data, labels, tooltip: showTooltip = true } = props;
271
404
  const { tooltip, show, hide, move, containerRef } = useChartTooltip(showTooltip);
405
+ const { width, height } = useChartSize(containerRef);
406
+ const ready = width > 0 && height > 0;
272
407
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "lib-xplat-chart", ref: containerRef, children: [
273
- type === "line" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(LineChart, { data, labels, onHover: show, onMove: move, onLeave: hide }),
274
- type === "bar" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(BarChart, { data, labels, onHover: show, onMove: move, onLeave: hide }),
275
- type === "pie" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PieDonutChart, { data, labels, onHover: show, onMove: move, onLeave: hide }),
276
- type === "doughnut" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PieDonutChart, { data, labels, isDoughnut: true, onHover: show, onMove: move, onLeave: hide }),
277
- tooltip.visible && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
278
- "div",
279
- {
280
- className: "chart-tooltip",
281
- style: { left: tooltip.x, top: tooltip.y },
282
- children: tooltip.content
283
- }
284
- )
408
+ ready && type === "line" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(LineChart, { data, labels, width, height, onHover: show, onMove: move, onLeave: hide }),
409
+ ready && type === "curve" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CurveChart, { data, labels, width, height, onHover: show, onMove: move, onLeave: hide }),
410
+ ready && type === "bar" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(BarChart, { data, labels, width, height, onHover: show, onMove: move, onLeave: hide }),
411
+ ready && type === "pie" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PieDonutChart, { data, labels, width, height, onHover: show, onMove: move, onLeave: hide }),
412
+ ready && type === "doughnut" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PieDonutChart, { data, labels, width, height, isDoughnut: true, onHover: show, onMove: move, onLeave: hide }),
413
+ tooltip.visible && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TooltipBubble, { x: tooltip.x, y: tooltip.y, containerWidth: width, children: tooltip.content })
285
414
  ] });
286
415
  };
287
416
  Chart.displayName = "Chart";
@@ -1,13 +1,14 @@
1
1
  /* src/components/Chart/chart.scss */
2
2
  .lib-xplat-chart {
3
- contain: content;
3
+ contain: layout style;
4
4
  width: 100%;
5
5
  height: 100%;
6
6
  position: relative;
7
7
  }
8
8
  .lib-xplat-chart .chart-svg {
9
+ display: block;
9
10
  width: 100%;
10
- height: auto;
11
+ height: 100%;
11
12
  }
12
13
  .lib-xplat-chart .chart-grid {
13
14
  stroke: var(--semantic-border-subtle);
@@ -29,29 +30,44 @@
29
30
  }
30
31
  .lib-xplat-chart .chart-point {
31
32
  cursor: pointer;
32
- transition: r 0.15s;
33
+ r: 0;
34
+ opacity: 0;
35
+ transition: r 0.15s, opacity 0.15s;
33
36
  }
34
37
  .lib-xplat-chart .chart-point:hover {
35
- r: 6;
38
+ r: 5;
39
+ opacity: 1;
40
+ }
41
+ .lib-xplat-chart .chart-svg:hover .chart-point {
42
+ r: 3;
43
+ opacity: 0.6;
36
44
  }
37
45
  .lib-xplat-chart .chart-bar {
38
46
  cursor: pointer;
39
- transition: opacity 0.15s;
47
+ transition: opacity 0.15s, filter 0.15s;
40
48
  }
41
49
  .lib-xplat-chart .chart-bar:hover {
42
- opacity: 0.8;
50
+ opacity: 0.85;
51
+ filter: brightness(1.08);
43
52
  }
44
53
  .lib-xplat-chart .chart-slice {
45
54
  cursor: pointer;
46
- transition: opacity 0.15s;
55
+ stroke: var(--semantic-surface-neutral-default);
56
+ stroke-width: 2;
57
+ transition:
58
+ opacity 0.15s,
59
+ filter 0.15s,
60
+ transform 0.15s;
61
+ transform-origin: center;
47
62
  }
48
63
  .lib-xplat-chart .chart-slice:hover {
49
- opacity: 0.85;
64
+ opacity: 0.9;
65
+ filter: brightness(1.05) drop-shadow(0 2px 6px rgba(0, 0, 0, 0.2));
50
66
  }
51
67
  .lib-xplat-chart .chart-tooltip {
52
68
  position: absolute;
53
69
  transform: translate(-50%, -100%);
54
- padding: var(--spacing-space-2) var(--spacing-space-2);
70
+ padding: var(--spacing-space-2) var(--spacing-space-3);
55
71
  background-color: var(--semantic-surface-neutral-strong);
56
72
  color: var(--semantic-text-inverse);
57
73
  font-size: 12px;
@@ -1,7 +1,7 @@
1
1
  import React from 'react';
2
2
 
3
3
  interface ChartProps {
4
- type: "line" | "bar" | "pie" | "doughnut";
4
+ type: "line" | "curve" | "bar" | "pie" | "doughnut";
5
5
  data: Record<string, number[]>;
6
6
  labels: string[];
7
7
  tooltip?: boolean;
@@ -1,7 +1,7 @@
1
1
  import React from 'react';
2
2
 
3
3
  interface ChartProps {
4
- type: "line" | "bar" | "pie" | "doughnut";
4
+ type: "line" | "curve" | "bar" | "pie" | "doughnut";
5
5
  data: Record<string, number[]>;
6
6
  labels: string[];
7
7
  tooltip?: boolean;