@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.
- package/dist/components/{Card → Box}/index.cjs +34 -13
- package/dist/components/Box/index.css +55 -0
- package/dist/components/Box/index.d.cts +13 -0
- package/dist/components/Box/index.d.ts +13 -0
- package/dist/components/Box/index.js +34 -0
- package/dist/components/Button/index.css +5 -0
- package/dist/components/Chart/index.cjs +185 -56
- package/dist/components/Chart/index.css +25 -9
- package/dist/components/Chart/index.d.cts +1 -1
- package/dist/components/Chart/index.d.ts +1 -1
- package/dist/components/Chart/index.js +185 -56
- package/dist/components/DatePicker/index.cjs +667 -682
- package/dist/components/DatePicker/index.css +211 -128
- package/dist/components/DatePicker/index.d.cts +2 -0
- package/dist/components/DatePicker/index.d.ts +2 -0
- package/dist/components/DatePicker/index.js +656 -671
- package/dist/components/index.cjs +720 -643
- package/dist/components/index.css +197 -156
- package/dist/components/index.d.cts +1 -1
- package/dist/components/index.d.ts +1 -1
- package/dist/components/index.js +714 -637
- package/dist/index.cjs +720 -643
- package/dist/index.css +197 -156
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +714 -637
- package/package.json +1 -1
- package/dist/components/Card/index.css +0 -28
- package/dist/components/Card/index.d.cts +0 -9
- package/dist/components/Card/index.d.ts +0 -9
- package/dist/components/Card/index.js +0 -13
|
@@ -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/
|
|
21
|
-
var
|
|
22
|
-
__export(
|
|
23
|
-
|
|
20
|
+
// src/components/Box/index.ts
|
|
21
|
+
var Box_exports = {};
|
|
22
|
+
__export(Box_exports, {
|
|
23
|
+
Box: () => Box_default
|
|
24
24
|
});
|
|
25
|
-
module.exports = __toCommonJS(
|
|
25
|
+
module.exports = __toCommonJS(Box_exports);
|
|
26
26
|
|
|
27
|
-
//
|
|
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
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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
|
-
|
|
36
|
-
var
|
|
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
|
-
|
|
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
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
[
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
var
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
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 =
|
|
101
|
-
const chartH =
|
|
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:
|
|
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:
|
|
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:
|
|
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[
|
|
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
|
|
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 =
|
|
167
|
-
const chartH =
|
|
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:
|
|
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:
|
|
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:
|
|
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[
|
|
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:
|
|
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
|
|
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
|
|
219
|
-
const
|
|
220
|
-
const
|
|
221
|
-
const
|
|
222
|
-
const
|
|
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:
|
|
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:
|
|
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 === "
|
|
275
|
-
type === "
|
|
276
|
-
type === "
|
|
277
|
-
|
|
278
|
-
|
|
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:
|
|
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:
|
|
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
|
-
|
|
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:
|
|
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.
|
|
50
|
+
opacity: 0.85;
|
|
51
|
+
filter: brightness(1.08);
|
|
43
52
|
}
|
|
44
53
|
.lib-xplat-chart .chart-slice {
|
|
45
54
|
cursor: pointer;
|
|
46
|
-
|
|
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.
|
|
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-
|
|
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;
|