@x-plat/design-system 0.5.37 → 0.5.39
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/Chart/index.cjs +35 -46
- package/dist/components/Chart/index.css +26 -6
- package/dist/components/Chart/index.d.cts +1 -0
- package/dist/components/Chart/index.d.ts +1 -0
- package/dist/components/Chart/index.js +35 -46
- package/dist/components/Tooltip/index.cjs +98 -8
- package/dist/components/Tooltip/index.css +38 -38
- package/dist/components/Tooltip/index.d.cts +6 -8
- package/dist/components/Tooltip/index.d.ts +6 -8
- package/dist/components/Tooltip/index.js +98 -8
- package/dist/components/index.cjs +118 -53
- package/dist/components/index.css +61 -41
- package/dist/components/index.js +119 -54
- package/dist/index.cjs +118 -53
- package/dist/index.css +61 -41
- package/dist/index.js +121 -56
- package/package.json +1 -1
|
@@ -186,28 +186,11 @@ var useChartTooltip = (enabled) => {
|
|
|
186
186
|
if (!rect) return;
|
|
187
187
|
setTooltip({ visible: true, x: e.clientX - rect.left, y: e.clientY - rect.top, content });
|
|
188
188
|
}, [enabled]);
|
|
189
|
-
const showAt = import_react.default.useCallback((svgX, svgY, content, svgEl) => {
|
|
190
|
-
if (!enabled) return;
|
|
191
|
-
const container = containerRef.current;
|
|
192
|
-
if (!container) return;
|
|
193
|
-
let x = svgX;
|
|
194
|
-
let y = svgY;
|
|
195
|
-
if (svgEl) {
|
|
196
|
-
const svgRect = svgEl.getBoundingClientRect();
|
|
197
|
-
const containerRect = container.getBoundingClientRect();
|
|
198
|
-
const vb = svgEl.viewBox.baseVal;
|
|
199
|
-
const scaleX = svgRect.width / (vb.width || 1);
|
|
200
|
-
const scaleY = svgRect.height / (vb.height || 1);
|
|
201
|
-
x = svgX * scaleX + (svgRect.left - containerRect.left);
|
|
202
|
-
y = svgY * scaleY + (svgRect.top - containerRect.top);
|
|
203
|
-
}
|
|
204
|
-
setTooltip({ visible: true, x, y, content });
|
|
205
|
-
}, [enabled]);
|
|
206
189
|
const hide = import_react.default.useCallback(() => {
|
|
207
190
|
cancelAnimationFrame(rafRef.current);
|
|
208
191
|
setTooltip((prev) => ({ ...prev, visible: false }));
|
|
209
192
|
}, []);
|
|
210
|
-
return { tooltip, show,
|
|
193
|
+
return { tooltip, show, hide, move, containerRef };
|
|
211
194
|
};
|
|
212
195
|
var GridLines = import_react.default.memo(({ width, height, chartH, maxVal }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children: [0, 0.25, 0.5, 0.75, 1].map((ratio) => {
|
|
213
196
|
const y = PADDING.top + (1 - ratio) * chartH;
|
|
@@ -272,7 +255,7 @@ var useCrosshair = (seriesPoints, entries, labels, chartH) => {
|
|
|
272
255
|
}, [entries, seriesPoints]);
|
|
273
256
|
return { activeIndex, handleMouseMove, handleMouseLeave, tooltipContent, getTooltipAt };
|
|
274
257
|
};
|
|
275
|
-
var LineChart = import_react.default.memo(({ data, labels, width, height, animate, onHover,
|
|
258
|
+
var LineChart = import_react.default.memo(({ data, labels, width, height, animate, onHover, onMove, onLeave }) => {
|
|
276
259
|
const entries = import_react.default.useMemo(() => Object.entries(data), [data]);
|
|
277
260
|
const maxVal = import_react.default.useMemo(() => {
|
|
278
261
|
const allValues = entries.flatMap(([, v]) => v);
|
|
@@ -292,7 +275,6 @@ var LineChart = import_react.default.memo(({ data, labels, width, height, animat
|
|
|
292
275
|
[entries, count, chartW, chartH, maxVal]
|
|
293
276
|
);
|
|
294
277
|
const clipRef = import_react.default.useRef(null);
|
|
295
|
-
const svgRef = import_react.default.useRef(null);
|
|
296
278
|
const { activeIndex, handleMouseMove, handleMouseLeave, getTooltipAt } = useCrosshair(seriesPoints, entries, labels, chartH);
|
|
297
279
|
import_react.default.useEffect(() => {
|
|
298
280
|
if (!animate || !clipRef.current) return;
|
|
@@ -309,14 +291,12 @@ var LineChart = import_react.default.memo(({ data, labels, width, height, animat
|
|
|
309
291
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
310
292
|
"svg",
|
|
311
293
|
{
|
|
312
|
-
ref: svgRef,
|
|
313
294
|
viewBox: `0 0 ${width} ${height}`,
|
|
314
295
|
className: "chart-svg",
|
|
315
296
|
onMouseMove: (e) => {
|
|
316
297
|
handleMouseMove(e);
|
|
317
|
-
if (activeIndex !== null
|
|
318
|
-
|
|
319
|
-
onShowAt(p.x, p.y, `${labels[activeIndex]} \u2014 ${getTooltipAt(activeIndex)}`, svgRef.current);
|
|
298
|
+
if (activeIndex !== null) {
|
|
299
|
+
onHover(e, `${labels[activeIndex]} \u2014 ${getTooltipAt(activeIndex)}`);
|
|
320
300
|
} else {
|
|
321
301
|
onLeave();
|
|
322
302
|
}
|
|
@@ -384,7 +364,7 @@ var LineChart = import_react.default.memo(({ data, labels, width, height, animat
|
|
|
384
364
|
);
|
|
385
365
|
});
|
|
386
366
|
LineChart.displayName = "LineChart";
|
|
387
|
-
var CurveChart = import_react.default.memo(({ data, labels, width, height, animate, onHover,
|
|
367
|
+
var CurveChart = import_react.default.memo(({ data, labels, width, height, animate, onHover, onMove, onLeave }) => {
|
|
388
368
|
const entries = import_react.default.useMemo(() => Object.entries(data), [data]);
|
|
389
369
|
const maxVal = import_react.default.useMemo(() => {
|
|
390
370
|
const allValues = entries.flatMap(([, v]) => v);
|
|
@@ -404,7 +384,6 @@ var CurveChart = import_react.default.memo(({ data, labels, width, height, anima
|
|
|
404
384
|
[entries, count, chartW, chartH, maxVal]
|
|
405
385
|
);
|
|
406
386
|
const curveClipRef = import_react.default.useRef(null);
|
|
407
|
-
const curveSvgRef = import_react.default.useRef(null);
|
|
408
387
|
const { activeIndex, handleMouseMove, handleMouseLeave, getTooltipAt } = useCrosshair(seriesPoints, entries, labels, chartH);
|
|
409
388
|
import_react.default.useEffect(() => {
|
|
410
389
|
if (!animate || !curveClipRef.current) return;
|
|
@@ -421,14 +400,13 @@ var CurveChart = import_react.default.memo(({ data, labels, width, height, anima
|
|
|
421
400
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
422
401
|
"svg",
|
|
423
402
|
{
|
|
424
|
-
ref: curveSvgRef,
|
|
425
403
|
viewBox: `0 0 ${width} ${height}`,
|
|
426
404
|
className: "chart-svg",
|
|
427
405
|
onMouseMove: (e) => {
|
|
428
406
|
handleMouseMove(e);
|
|
429
407
|
if (activeIndex !== null && seriesPoints[0]?.[activeIndex]) {
|
|
430
408
|
const p = seriesPoints[0][activeIndex];
|
|
431
|
-
|
|
409
|
+
onHover(e, `${labels[activeIndex]} \u2014 ${getTooltipAt(activeIndex)}`);
|
|
432
410
|
} else {
|
|
433
411
|
onLeave();
|
|
434
412
|
}
|
|
@@ -496,8 +474,7 @@ var CurveChart = import_react.default.memo(({ data, labels, width, height, anima
|
|
|
496
474
|
);
|
|
497
475
|
});
|
|
498
476
|
CurveChart.displayName = "CurveChart";
|
|
499
|
-
var BarChart = import_react.default.memo(({ data, labels, width, height, animate, onHover,
|
|
500
|
-
const barSvgRef = import_react.default.useRef(null);
|
|
477
|
+
var BarChart = import_react.default.memo(({ data, labels, width, height, animate, onHover, onMove, onLeave }) => {
|
|
501
478
|
const entries = import_react.default.useMemo(() => Object.entries(data), [data]);
|
|
502
479
|
const maxVal = import_react.default.useMemo(() => {
|
|
503
480
|
const allValues = entries.flatMap(([, v]) => v);
|
|
@@ -524,7 +501,7 @@ var BarChart = import_react.default.memo(({ data, labels, width, height, animate
|
|
|
524
501
|
[entries, maxVal, chartH, groupW, barW, barGap, groupCount]
|
|
525
502
|
);
|
|
526
503
|
const barLabelStep = getLabelStep(count, chartW);
|
|
527
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", {
|
|
504
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { viewBox: `0 0 ${width} ${height}`, className: "chart-svg", children: [
|
|
528
505
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(GridLines, { width, height, chartH, maxVal }),
|
|
529
506
|
labels.map((label, i) => {
|
|
530
507
|
if (i % barLabelStep !== 0) return null;
|
|
@@ -547,7 +524,8 @@ var BarChart = import_react.default.memo(({ data, labels, width, height, animate
|
|
|
547
524
|
transformOrigin: `${b.x + b.w / 2}px ${baseline}px`,
|
|
548
525
|
animationDelay: `${delay}ms`
|
|
549
526
|
} : void 0,
|
|
550
|
-
onMouseEnter: () =>
|
|
527
|
+
onMouseEnter: (e) => onHover(e, `${key}: ${labels[i]} \u2014 ${b.v}`),
|
|
528
|
+
onMouseMove: onMove,
|
|
551
529
|
onMouseLeave: onLeave
|
|
552
530
|
},
|
|
553
531
|
`${di}-${i}`
|
|
@@ -635,14 +613,17 @@ var PieDonutChart = import_react.default.memo(
|
|
|
635
613
|
{
|
|
636
614
|
d: s.d,
|
|
637
615
|
fill: PIE_COLORS[(i + colorOffset) % PIE_COLORS.length],
|
|
638
|
-
className: "chart-slice"
|
|
616
|
+
className: "chart-slice",
|
|
617
|
+
onMouseEnter: (e) => onHover(e, `${s.label} \u2014 ${s.v.toLocaleString()} (${s.pct}%)`),
|
|
618
|
+
onMouseMove: onMove,
|
|
619
|
+
onMouseLeave: onLeave
|
|
639
620
|
}
|
|
640
621
|
) }, i)) })
|
|
641
622
|
] });
|
|
642
623
|
}
|
|
643
624
|
);
|
|
644
625
|
PieDonutChart.displayName = "PieDonutChart";
|
|
645
|
-
var ChartTooltip = ({ x, y, containerWidth, containerHeight, children }) => {
|
|
626
|
+
var ChartTooltip = ({ x, y, containerWidth, containerHeight, tooltipType, children }) => {
|
|
646
627
|
const ref = import_react.default.useRef(null);
|
|
647
628
|
const [pos, setPos] = import_react.default.useState({ left: 0, top: 0 });
|
|
648
629
|
import_react.default.useLayoutEffect(() => {
|
|
@@ -653,17 +634,25 @@ var ChartTooltip = ({ x, y, containerWidth, containerHeight, children }) => {
|
|
|
653
634
|
let left = x + TOOLTIP_OFFSET;
|
|
654
635
|
let top = y - h - TOOLTIP_OFFSET;
|
|
655
636
|
if (left + w > containerWidth) left = x - w - TOOLTIP_OFFSET;
|
|
656
|
-
if (top < 0) top = y + TOOLTIP_OFFSET;
|
|
657
637
|
if (left < 0) left = 0;
|
|
638
|
+
if (top < 0) top = y + TOOLTIP_OFFSET;
|
|
639
|
+
if (top + h > containerHeight) top = containerHeight - h;
|
|
658
640
|
setPos({ left, top });
|
|
659
641
|
}, [x, y, containerWidth, containerHeight]);
|
|
660
|
-
|
|
642
|
+
const content = typeof children === "string" ? children : "";
|
|
643
|
+
const sepIdx = content.indexOf(" \u2014 ");
|
|
644
|
+
const title = sepIdx >= 0 ? content.slice(0, sepIdx) : content;
|
|
645
|
+
const desc = sepIdx >= 0 ? content.slice(sepIdx + 3) : "";
|
|
646
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
661
647
|
"div",
|
|
662
648
|
{
|
|
663
649
|
ref,
|
|
664
|
-
className:
|
|
650
|
+
className: `chart-tooltip chart-tooltip-show chart-tooltip-${tooltipType}`,
|
|
665
651
|
style: { left: pos.left, top: pos.top },
|
|
666
|
-
children
|
|
652
|
+
children: [
|
|
653
|
+
title && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "chart-tooltip-title", children: title }),
|
|
654
|
+
desc && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "chart-tooltip-desc", children: desc })
|
|
655
|
+
]
|
|
667
656
|
}
|
|
668
657
|
);
|
|
669
658
|
};
|
|
@@ -706,8 +695,8 @@ var ChartLegend = ({ data, labels, type }) => {
|
|
|
706
695
|
}) });
|
|
707
696
|
};
|
|
708
697
|
var Chart = import_react.default.memo((props) => {
|
|
709
|
-
const { type, data, labels, tooltip: showTooltip = true } = props;
|
|
710
|
-
const { tooltip, show,
|
|
698
|
+
const { type, data, labels, tooltip: showTooltip = true, tooltipType = "dark" } = props;
|
|
699
|
+
const { tooltip, show, hide, move, containerRef } = useChartTooltip(showTooltip);
|
|
711
700
|
const { width, height } = useChartSize(containerRef);
|
|
712
701
|
const stableData = import_react.default.useMemo(() => data, [JSON.stringify(data)]);
|
|
713
702
|
const stableLabels = import_react.default.useMemo(() => labels, [JSON.stringify(labels)]);
|
|
@@ -715,13 +704,13 @@ var Chart = import_react.default.memo((props) => {
|
|
|
715
704
|
const animate = useChartAnimation(containerRef, dataKey);
|
|
716
705
|
const ready = width > 0 && height > 0;
|
|
717
706
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "lib-xplat-chart", ref: containerRef, children: [
|
|
718
|
-
ready && type === "line" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(LineChart, { data: stableData, labels: stableLabels, width, height, animate, onHover: show,
|
|
719
|
-
ready && type === "curve" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CurveChart, { data: stableData, labels: stableLabels, width, height, animate, onHover: show,
|
|
720
|
-
ready && type === "bar" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(BarChart, { data: stableData, labels: stableLabels, width, height, animate, onHover: show,
|
|
721
|
-
ready && type === "pie" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PieDonutChart, { data: stableData, labels: stableLabels, width, height, animate, onHover: show,
|
|
722
|
-
ready && type === "doughnut" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PieDonutChart, { data: stableData, labels: stableLabels, width, height, animate, isDoughnut: true, onHover: show,
|
|
707
|
+
ready && type === "line" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(LineChart, { data: stableData, labels: stableLabels, width, height, animate, onHover: show, onMove: move, onLeave: hide }),
|
|
708
|
+
ready && type === "curve" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CurveChart, { data: stableData, labels: stableLabels, width, height, animate, onHover: show, onMove: move, onLeave: hide }),
|
|
709
|
+
ready && type === "bar" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(BarChart, { data: stableData, labels: stableLabels, width, height, animate, onHover: show, onMove: move, onLeave: hide }),
|
|
710
|
+
ready && type === "pie" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PieDonutChart, { data: stableData, labels: stableLabels, width, height, animate, onHover: show, onMove: move, onLeave: hide }),
|
|
711
|
+
ready && type === "doughnut" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PieDonutChart, { data: stableData, labels: stableLabels, width, height, animate, isDoughnut: true, onHover: show, onMove: move, onLeave: hide }),
|
|
723
712
|
ready && (type === "pie" || type === "doughnut") && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChartLegend, { data: stableData, labels: stableLabels, type }),
|
|
724
|
-
tooltip.visible && tooltip.content && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChartTooltip, { x: tooltip.x, y: tooltip.y, containerWidth: width, containerHeight: height, children: tooltip.content })
|
|
713
|
+
tooltip.visible && tooltip.content && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChartTooltip, { x: tooltip.x, y: tooltip.y, containerWidth: width, containerHeight: height, tooltipType, children: tooltip.content })
|
|
725
714
|
] });
|
|
726
715
|
});
|
|
727
716
|
Chart.displayName = "Chart";
|
|
@@ -109,17 +109,37 @@
|
|
|
109
109
|
position: absolute;
|
|
110
110
|
z-index: 10;
|
|
111
111
|
padding: var(--spacing-space-3);
|
|
112
|
-
background-color: var(--semantic-surface-neutral-strong);
|
|
113
|
-
color: var(--semantic-text-inverse);
|
|
114
|
-
font-size: 12px;
|
|
115
|
-
line-height: 18px;
|
|
116
|
-
font-weight: 500;
|
|
117
112
|
border-radius: var(--spacing-radius-md);
|
|
118
113
|
max-width: 240px;
|
|
119
114
|
pointer-events: none;
|
|
120
|
-
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
|
|
121
115
|
animation: chart-tooltip-in 120ms ease-out;
|
|
122
116
|
}
|
|
117
|
+
.lib-xplat-chart .chart-tooltip .chart-tooltip-title {
|
|
118
|
+
font-size: 13px;
|
|
119
|
+
line-height: 18px;
|
|
120
|
+
font-weight: 400;
|
|
121
|
+
}
|
|
122
|
+
.lib-xplat-chart .chart-tooltip .chart-tooltip-desc {
|
|
123
|
+
font-size: 12px;
|
|
124
|
+
line-height: 18px;
|
|
125
|
+
font-weight: 400;
|
|
126
|
+
}
|
|
127
|
+
.lib-xplat-chart .chart-tooltip.chart-tooltip-dark {
|
|
128
|
+
background-color: var(--semantic-surface-neutral-strong);
|
|
129
|
+
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
|
130
|
+
}
|
|
131
|
+
.lib-xplat-chart .chart-tooltip.chart-tooltip-dark .chart-tooltip-title,
|
|
132
|
+
.lib-xplat-chart .chart-tooltip.chart-tooltip-dark .chart-tooltip-desc {
|
|
133
|
+
color: var(--semantic-text-inverse);
|
|
134
|
+
}
|
|
135
|
+
.lib-xplat-chart .chart-tooltip.chart-tooltip-light {
|
|
136
|
+
background-color: var(--semantic-surface-neutral-default);
|
|
137
|
+
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
|
|
138
|
+
}
|
|
139
|
+
.lib-xplat-chart .chart-tooltip.chart-tooltip-light .chart-tooltip-title,
|
|
140
|
+
.lib-xplat-chart .chart-tooltip.chart-tooltip-light .chart-tooltip-desc {
|
|
141
|
+
color: var(--semantic-text-subtle);
|
|
142
|
+
}
|
|
123
143
|
.lib-xplat-chart .chart-legend {
|
|
124
144
|
display: flex;
|
|
125
145
|
flex-wrap: wrap;
|
|
@@ -150,28 +150,11 @@ var useChartTooltip = (enabled) => {
|
|
|
150
150
|
if (!rect) return;
|
|
151
151
|
setTooltip({ visible: true, x: e.clientX - rect.left, y: e.clientY - rect.top, content });
|
|
152
152
|
}, [enabled]);
|
|
153
|
-
const showAt = React.useCallback((svgX, svgY, content, svgEl) => {
|
|
154
|
-
if (!enabled) return;
|
|
155
|
-
const container = containerRef.current;
|
|
156
|
-
if (!container) return;
|
|
157
|
-
let x = svgX;
|
|
158
|
-
let y = svgY;
|
|
159
|
-
if (svgEl) {
|
|
160
|
-
const svgRect = svgEl.getBoundingClientRect();
|
|
161
|
-
const containerRect = container.getBoundingClientRect();
|
|
162
|
-
const vb = svgEl.viewBox.baseVal;
|
|
163
|
-
const scaleX = svgRect.width / (vb.width || 1);
|
|
164
|
-
const scaleY = svgRect.height / (vb.height || 1);
|
|
165
|
-
x = svgX * scaleX + (svgRect.left - containerRect.left);
|
|
166
|
-
y = svgY * scaleY + (svgRect.top - containerRect.top);
|
|
167
|
-
}
|
|
168
|
-
setTooltip({ visible: true, x, y, content });
|
|
169
|
-
}, [enabled]);
|
|
170
153
|
const hide = React.useCallback(() => {
|
|
171
154
|
cancelAnimationFrame(rafRef.current);
|
|
172
155
|
setTooltip((prev) => ({ ...prev, visible: false }));
|
|
173
156
|
}, []);
|
|
174
|
-
return { tooltip, show,
|
|
157
|
+
return { tooltip, show, hide, move, containerRef };
|
|
175
158
|
};
|
|
176
159
|
var GridLines = React.memo(({ width, height, chartH, maxVal }) => /* @__PURE__ */ jsx(Fragment, { children: [0, 0.25, 0.5, 0.75, 1].map((ratio) => {
|
|
177
160
|
const y = PADDING.top + (1 - ratio) * chartH;
|
|
@@ -236,7 +219,7 @@ var useCrosshair = (seriesPoints, entries, labels, chartH) => {
|
|
|
236
219
|
}, [entries, seriesPoints]);
|
|
237
220
|
return { activeIndex, handleMouseMove, handleMouseLeave, tooltipContent, getTooltipAt };
|
|
238
221
|
};
|
|
239
|
-
var LineChart = React.memo(({ data, labels, width, height, animate, onHover,
|
|
222
|
+
var LineChart = React.memo(({ data, labels, width, height, animate, onHover, onMove, onLeave }) => {
|
|
240
223
|
const entries = React.useMemo(() => Object.entries(data), [data]);
|
|
241
224
|
const maxVal = React.useMemo(() => {
|
|
242
225
|
const allValues = entries.flatMap(([, v]) => v);
|
|
@@ -256,7 +239,6 @@ var LineChart = React.memo(({ data, labels, width, height, animate, onHover, onS
|
|
|
256
239
|
[entries, count, chartW, chartH, maxVal]
|
|
257
240
|
);
|
|
258
241
|
const clipRef = React.useRef(null);
|
|
259
|
-
const svgRef = React.useRef(null);
|
|
260
242
|
const { activeIndex, handleMouseMove, handleMouseLeave, getTooltipAt } = useCrosshair(seriesPoints, entries, labels, chartH);
|
|
261
243
|
React.useEffect(() => {
|
|
262
244
|
if (!animate || !clipRef.current) return;
|
|
@@ -273,14 +255,12 @@ var LineChart = React.memo(({ data, labels, width, height, animate, onHover, onS
|
|
|
273
255
|
return /* @__PURE__ */ jsxs(
|
|
274
256
|
"svg",
|
|
275
257
|
{
|
|
276
|
-
ref: svgRef,
|
|
277
258
|
viewBox: `0 0 ${width} ${height}`,
|
|
278
259
|
className: "chart-svg",
|
|
279
260
|
onMouseMove: (e) => {
|
|
280
261
|
handleMouseMove(e);
|
|
281
|
-
if (activeIndex !== null
|
|
282
|
-
|
|
283
|
-
onShowAt(p.x, p.y, `${labels[activeIndex]} \u2014 ${getTooltipAt(activeIndex)}`, svgRef.current);
|
|
262
|
+
if (activeIndex !== null) {
|
|
263
|
+
onHover(e, `${labels[activeIndex]} \u2014 ${getTooltipAt(activeIndex)}`);
|
|
284
264
|
} else {
|
|
285
265
|
onLeave();
|
|
286
266
|
}
|
|
@@ -348,7 +328,7 @@ var LineChart = React.memo(({ data, labels, width, height, animate, onHover, onS
|
|
|
348
328
|
);
|
|
349
329
|
});
|
|
350
330
|
LineChart.displayName = "LineChart";
|
|
351
|
-
var CurveChart = React.memo(({ data, labels, width, height, animate, onHover,
|
|
331
|
+
var CurveChart = React.memo(({ data, labels, width, height, animate, onHover, onMove, onLeave }) => {
|
|
352
332
|
const entries = React.useMemo(() => Object.entries(data), [data]);
|
|
353
333
|
const maxVal = React.useMemo(() => {
|
|
354
334
|
const allValues = entries.flatMap(([, v]) => v);
|
|
@@ -368,7 +348,6 @@ var CurveChart = React.memo(({ data, labels, width, height, animate, onHover, on
|
|
|
368
348
|
[entries, count, chartW, chartH, maxVal]
|
|
369
349
|
);
|
|
370
350
|
const curveClipRef = React.useRef(null);
|
|
371
|
-
const curveSvgRef = React.useRef(null);
|
|
372
351
|
const { activeIndex, handleMouseMove, handleMouseLeave, getTooltipAt } = useCrosshair(seriesPoints, entries, labels, chartH);
|
|
373
352
|
React.useEffect(() => {
|
|
374
353
|
if (!animate || !curveClipRef.current) return;
|
|
@@ -385,14 +364,13 @@ var CurveChart = React.memo(({ data, labels, width, height, animate, onHover, on
|
|
|
385
364
|
return /* @__PURE__ */ jsxs(
|
|
386
365
|
"svg",
|
|
387
366
|
{
|
|
388
|
-
ref: curveSvgRef,
|
|
389
367
|
viewBox: `0 0 ${width} ${height}`,
|
|
390
368
|
className: "chart-svg",
|
|
391
369
|
onMouseMove: (e) => {
|
|
392
370
|
handleMouseMove(e);
|
|
393
371
|
if (activeIndex !== null && seriesPoints[0]?.[activeIndex]) {
|
|
394
372
|
const p = seriesPoints[0][activeIndex];
|
|
395
|
-
|
|
373
|
+
onHover(e, `${labels[activeIndex]} \u2014 ${getTooltipAt(activeIndex)}`);
|
|
396
374
|
} else {
|
|
397
375
|
onLeave();
|
|
398
376
|
}
|
|
@@ -460,8 +438,7 @@ var CurveChart = React.memo(({ data, labels, width, height, animate, onHover, on
|
|
|
460
438
|
);
|
|
461
439
|
});
|
|
462
440
|
CurveChart.displayName = "CurveChart";
|
|
463
|
-
var BarChart = React.memo(({ data, labels, width, height, animate, onHover,
|
|
464
|
-
const barSvgRef = React.useRef(null);
|
|
441
|
+
var BarChart = React.memo(({ data, labels, width, height, animate, onHover, onMove, onLeave }) => {
|
|
465
442
|
const entries = React.useMemo(() => Object.entries(data), [data]);
|
|
466
443
|
const maxVal = React.useMemo(() => {
|
|
467
444
|
const allValues = entries.flatMap(([, v]) => v);
|
|
@@ -488,7 +465,7 @@ var BarChart = React.memo(({ data, labels, width, height, animate, onHover, onSh
|
|
|
488
465
|
[entries, maxVal, chartH, groupW, barW, barGap, groupCount]
|
|
489
466
|
);
|
|
490
467
|
const barLabelStep = getLabelStep(count, chartW);
|
|
491
|
-
return /* @__PURE__ */ jsxs("svg", {
|
|
468
|
+
return /* @__PURE__ */ jsxs("svg", { viewBox: `0 0 ${width} ${height}`, className: "chart-svg", children: [
|
|
492
469
|
/* @__PURE__ */ jsx(GridLines, { width, height, chartH, maxVal }),
|
|
493
470
|
labels.map((label, i) => {
|
|
494
471
|
if (i % barLabelStep !== 0) return null;
|
|
@@ -511,7 +488,8 @@ var BarChart = React.memo(({ data, labels, width, height, animate, onHover, onSh
|
|
|
511
488
|
transformOrigin: `${b.x + b.w / 2}px ${baseline}px`,
|
|
512
489
|
animationDelay: `${delay}ms`
|
|
513
490
|
} : void 0,
|
|
514
|
-
onMouseEnter: () =>
|
|
491
|
+
onMouseEnter: (e) => onHover(e, `${key}: ${labels[i]} \u2014 ${b.v}`),
|
|
492
|
+
onMouseMove: onMove,
|
|
515
493
|
onMouseLeave: onLeave
|
|
516
494
|
},
|
|
517
495
|
`${di}-${i}`
|
|
@@ -599,14 +577,17 @@ var PieDonutChart = React.memo(
|
|
|
599
577
|
{
|
|
600
578
|
d: s.d,
|
|
601
579
|
fill: PIE_COLORS[(i + colorOffset) % PIE_COLORS.length],
|
|
602
|
-
className: "chart-slice"
|
|
580
|
+
className: "chart-slice",
|
|
581
|
+
onMouseEnter: (e) => onHover(e, `${s.label} \u2014 ${s.v.toLocaleString()} (${s.pct}%)`),
|
|
582
|
+
onMouseMove: onMove,
|
|
583
|
+
onMouseLeave: onLeave
|
|
603
584
|
}
|
|
604
585
|
) }, i)) })
|
|
605
586
|
] });
|
|
606
587
|
}
|
|
607
588
|
);
|
|
608
589
|
PieDonutChart.displayName = "PieDonutChart";
|
|
609
|
-
var ChartTooltip = ({ x, y, containerWidth, containerHeight, children }) => {
|
|
590
|
+
var ChartTooltip = ({ x, y, containerWidth, containerHeight, tooltipType, children }) => {
|
|
610
591
|
const ref = React.useRef(null);
|
|
611
592
|
const [pos, setPos] = React.useState({ left: 0, top: 0 });
|
|
612
593
|
React.useLayoutEffect(() => {
|
|
@@ -617,17 +598,25 @@ var ChartTooltip = ({ x, y, containerWidth, containerHeight, children }) => {
|
|
|
617
598
|
let left = x + TOOLTIP_OFFSET;
|
|
618
599
|
let top = y - h - TOOLTIP_OFFSET;
|
|
619
600
|
if (left + w > containerWidth) left = x - w - TOOLTIP_OFFSET;
|
|
620
|
-
if (top < 0) top = y + TOOLTIP_OFFSET;
|
|
621
601
|
if (left < 0) left = 0;
|
|
602
|
+
if (top < 0) top = y + TOOLTIP_OFFSET;
|
|
603
|
+
if (top + h > containerHeight) top = containerHeight - h;
|
|
622
604
|
setPos({ left, top });
|
|
623
605
|
}, [x, y, containerWidth, containerHeight]);
|
|
624
|
-
|
|
606
|
+
const content = typeof children === "string" ? children : "";
|
|
607
|
+
const sepIdx = content.indexOf(" \u2014 ");
|
|
608
|
+
const title = sepIdx >= 0 ? content.slice(0, sepIdx) : content;
|
|
609
|
+
const desc = sepIdx >= 0 ? content.slice(sepIdx + 3) : "";
|
|
610
|
+
return /* @__PURE__ */ jsxs(
|
|
625
611
|
"div",
|
|
626
612
|
{
|
|
627
613
|
ref,
|
|
628
|
-
className:
|
|
614
|
+
className: `chart-tooltip chart-tooltip-show chart-tooltip-${tooltipType}`,
|
|
629
615
|
style: { left: pos.left, top: pos.top },
|
|
630
|
-
children
|
|
616
|
+
children: [
|
|
617
|
+
title && /* @__PURE__ */ jsx("div", { className: "chart-tooltip-title", children: title }),
|
|
618
|
+
desc && /* @__PURE__ */ jsx("div", { className: "chart-tooltip-desc", children: desc })
|
|
619
|
+
]
|
|
631
620
|
}
|
|
632
621
|
);
|
|
633
622
|
};
|
|
@@ -670,8 +659,8 @@ var ChartLegend = ({ data, labels, type }) => {
|
|
|
670
659
|
}) });
|
|
671
660
|
};
|
|
672
661
|
var Chart = React.memo((props) => {
|
|
673
|
-
const { type, data, labels, tooltip: showTooltip = true } = props;
|
|
674
|
-
const { tooltip, show,
|
|
662
|
+
const { type, data, labels, tooltip: showTooltip = true, tooltipType = "dark" } = props;
|
|
663
|
+
const { tooltip, show, hide, move, containerRef } = useChartTooltip(showTooltip);
|
|
675
664
|
const { width, height } = useChartSize(containerRef);
|
|
676
665
|
const stableData = React.useMemo(() => data, [JSON.stringify(data)]);
|
|
677
666
|
const stableLabels = React.useMemo(() => labels, [JSON.stringify(labels)]);
|
|
@@ -679,13 +668,13 @@ var Chart = React.memo((props) => {
|
|
|
679
668
|
const animate = useChartAnimation(containerRef, dataKey);
|
|
680
669
|
const ready = width > 0 && height > 0;
|
|
681
670
|
return /* @__PURE__ */ jsxs("div", { className: "lib-xplat-chart", ref: containerRef, children: [
|
|
682
|
-
ready && type === "line" && /* @__PURE__ */ jsx(LineChart, { data: stableData, labels: stableLabels, width, height, animate, onHover: show,
|
|
683
|
-
ready && type === "curve" && /* @__PURE__ */ jsx(CurveChart, { data: stableData, labels: stableLabels, width, height, animate, onHover: show,
|
|
684
|
-
ready && type === "bar" && /* @__PURE__ */ jsx(BarChart, { data: stableData, labels: stableLabels, width, height, animate, onHover: show,
|
|
685
|
-
ready && type === "pie" && /* @__PURE__ */ jsx(PieDonutChart, { data: stableData, labels: stableLabels, width, height, animate, onHover: show,
|
|
686
|
-
ready && type === "doughnut" && /* @__PURE__ */ jsx(PieDonutChart, { data: stableData, labels: stableLabels, width, height, animate, isDoughnut: true, onHover: show,
|
|
671
|
+
ready && type === "line" && /* @__PURE__ */ jsx(LineChart, { data: stableData, labels: stableLabels, width, height, animate, onHover: show, onMove: move, onLeave: hide }),
|
|
672
|
+
ready && type === "curve" && /* @__PURE__ */ jsx(CurveChart, { data: stableData, labels: stableLabels, width, height, animate, onHover: show, onMove: move, onLeave: hide }),
|
|
673
|
+
ready && type === "bar" && /* @__PURE__ */ jsx(BarChart, { data: stableData, labels: stableLabels, width, height, animate, onHover: show, onMove: move, onLeave: hide }),
|
|
674
|
+
ready && type === "pie" && /* @__PURE__ */ jsx(PieDonutChart, { data: stableData, labels: stableLabels, width, height, animate, onHover: show, onMove: move, onLeave: hide }),
|
|
675
|
+
ready && type === "doughnut" && /* @__PURE__ */ jsx(PieDonutChart, { data: stableData, labels: stableLabels, width, height, animate, isDoughnut: true, onHover: show, onMove: move, onLeave: hide }),
|
|
687
676
|
ready && (type === "pie" || type === "doughnut") && /* @__PURE__ */ jsx(ChartLegend, { data: stableData, labels: stableLabels, type }),
|
|
688
|
-
tooltip.visible && tooltip.content && /* @__PURE__ */ jsx(ChartTooltip, { x: tooltip.x, y: tooltip.y, containerWidth: width, containerHeight: height, children: tooltip.content })
|
|
677
|
+
tooltip.visible && tooltip.content && /* @__PURE__ */ jsx(ChartTooltip, { x: tooltip.x, y: tooltip.y, containerWidth: width, containerHeight: height, tooltipType, children: tooltip.content })
|
|
689
678
|
] });
|
|
690
679
|
});
|
|
691
680
|
Chart.displayName = "Chart";
|
|
@@ -35,7 +35,21 @@ __export(Tooltip_exports, {
|
|
|
35
35
|
module.exports = __toCommonJS(Tooltip_exports);
|
|
36
36
|
|
|
37
37
|
// src/components/Tooltip/Tooltip.tsx
|
|
38
|
+
var import_react2 = __toESM(require("react"), 1);
|
|
39
|
+
|
|
40
|
+
// src/tokens/hooks/Portal.tsx
|
|
38
41
|
var import_react = __toESM(require("react"), 1);
|
|
42
|
+
var import_react_dom = __toESM(require("react-dom"), 1);
|
|
43
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
44
|
+
var PortalContainerContext = import_react.default.createContext(null);
|
|
45
|
+
var Portal = ({ children }) => {
|
|
46
|
+
const contextContainer = import_react.default.useContext(PortalContainerContext);
|
|
47
|
+
if (typeof document === "undefined") return null;
|
|
48
|
+
const container = contextContainer ?? document.body;
|
|
49
|
+
return import_react_dom.default.createPortal(children, container);
|
|
50
|
+
};
|
|
51
|
+
Portal.displayName = "Portal";
|
|
52
|
+
var Portal_default = Portal;
|
|
39
53
|
|
|
40
54
|
// ../../node_modules/clsx/dist/clsx.mjs
|
|
41
55
|
function r(e) {
|
|
@@ -54,18 +68,94 @@ function clsx() {
|
|
|
54
68
|
var clsx_default = clsx;
|
|
55
69
|
|
|
56
70
|
// src/components/Tooltip/Tooltip.tsx
|
|
57
|
-
var
|
|
71
|
+
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
72
|
+
var OFFSET = 12;
|
|
73
|
+
var SHOW_DELAY = 300;
|
|
58
74
|
var Tooltip = (props) => {
|
|
59
75
|
const {
|
|
60
|
-
type = "
|
|
76
|
+
type = "dark",
|
|
77
|
+
title,
|
|
61
78
|
description,
|
|
62
|
-
children
|
|
79
|
+
children,
|
|
80
|
+
disabled = false
|
|
63
81
|
} = props;
|
|
64
|
-
const
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
82
|
+
const triggerRef = import_react2.default.useRef(null);
|
|
83
|
+
const tooltipRef = import_react2.default.useRef(null);
|
|
84
|
+
const [visible, setVisible] = import_react2.default.useState(false);
|
|
85
|
+
const [pos, setPos] = import_react2.default.useState({ left: 0, top: 0 });
|
|
86
|
+
const delayTimer = import_react2.default.useRef(0);
|
|
87
|
+
const calculatePos = import_react2.default.useCallback((clientX, clientY) => {
|
|
88
|
+
const el = tooltipRef.current;
|
|
89
|
+
if (!el) return;
|
|
90
|
+
const w = el.offsetWidth;
|
|
91
|
+
const h = el.offsetHeight;
|
|
92
|
+
const vw = window.innerWidth;
|
|
93
|
+
let left = clientX + OFFSET;
|
|
94
|
+
let top = clientY - h - OFFSET;
|
|
95
|
+
if (left + w > vw - 8) left = clientX - w - OFFSET;
|
|
96
|
+
if (top < 8) top = clientY + OFFSET;
|
|
97
|
+
if (left < 8) left = 8;
|
|
98
|
+
setPos({ left, top });
|
|
99
|
+
}, []);
|
|
100
|
+
const handleMouseEnter = import_react2.default.useCallback(() => {
|
|
101
|
+
if (disabled) return;
|
|
102
|
+
delayTimer.current = window.setTimeout(() => {
|
|
103
|
+
setVisible(true);
|
|
104
|
+
}, SHOW_DELAY);
|
|
105
|
+
}, [disabled]);
|
|
106
|
+
const handleMouseMove = import_react2.default.useCallback((e) => {
|
|
107
|
+
if (!visible) return;
|
|
108
|
+
calculatePos(e.clientX, e.clientY);
|
|
109
|
+
}, [visible, calculatePos]);
|
|
110
|
+
const handleMouseLeave = import_react2.default.useCallback(() => {
|
|
111
|
+
window.clearTimeout(delayTimer.current);
|
|
112
|
+
setVisible(false);
|
|
113
|
+
}, []);
|
|
114
|
+
const handleClick = import_react2.default.useCallback(() => {
|
|
115
|
+
window.clearTimeout(delayTimer.current);
|
|
116
|
+
setVisible(false);
|
|
117
|
+
}, []);
|
|
118
|
+
const handleFocus = import_react2.default.useCallback(() => {
|
|
119
|
+
if (disabled) return;
|
|
120
|
+
setVisible(true);
|
|
121
|
+
}, [disabled]);
|
|
122
|
+
const handleBlur = import_react2.default.useCallback(() => {
|
|
123
|
+
setVisible(false);
|
|
124
|
+
}, []);
|
|
125
|
+
import_react2.default.useLayoutEffect(() => {
|
|
126
|
+
if (!visible || !triggerRef.current) return;
|
|
127
|
+
const rect = triggerRef.current.getBoundingClientRect();
|
|
128
|
+
calculatePos(rect.right, rect.top);
|
|
129
|
+
}, [visible, calculatePos]);
|
|
130
|
+
if (!title && !description) return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_jsx_runtime2.Fragment, { children });
|
|
131
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
132
|
+
"div",
|
|
133
|
+
{
|
|
134
|
+
ref: triggerRef,
|
|
135
|
+
className: "lib-xplat-tooltip-trigger",
|
|
136
|
+
onMouseEnter: handleMouseEnter,
|
|
137
|
+
onMouseMove: handleMouseMove,
|
|
138
|
+
onMouseLeave: handleMouseLeave,
|
|
139
|
+
onClick: handleClick,
|
|
140
|
+
onFocus: handleFocus,
|
|
141
|
+
onBlur: handleBlur,
|
|
142
|
+
children: [
|
|
143
|
+
children,
|
|
144
|
+
visible && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Portal_default, { children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
145
|
+
"div",
|
|
146
|
+
{
|
|
147
|
+
ref: tooltipRef,
|
|
148
|
+
className: clsx_default("lib-xplat-tooltip", type),
|
|
149
|
+
style: { position: "fixed", left: pos.left, top: pos.top },
|
|
150
|
+
children: [
|
|
151
|
+
title && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "tooltip-title", children: title }),
|
|
152
|
+
description && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "tooltip-desc", children: description })
|
|
153
|
+
]
|
|
154
|
+
}
|
|
155
|
+
) })
|
|
156
|
+
]
|
|
157
|
+
}
|
|
158
|
+
);
|
|
69
159
|
};
|
|
70
160
|
Tooltip.displayName = "Tooltip";
|
|
71
161
|
var Tooltip_default = Tooltip;
|