@x-plat/design-system 0.5.31 → 0.5.33
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 +326 -132
- package/dist/components/Chart/index.css +70 -8
- package/dist/components/Chart/index.js +326 -132
- package/dist/components/Table/index.css +4 -0
- package/dist/components/index.cjs +326 -132
- package/dist/components/index.css +74 -8
- package/dist/components/index.js +326 -132
- package/dist/index.cjs +326 -132
- package/dist/index.css +74 -8
- package/dist/index.js +326 -132
- package/package.json +1 -1
|
@@ -121,40 +121,28 @@ var useChartAnimation = (containerRef, dataKey) => {
|
|
|
121
121
|
}, [dataKey]);
|
|
122
122
|
return animate || prefersReducedMotion();
|
|
123
123
|
};
|
|
124
|
+
var TOOLTIP_OFFSET = 12;
|
|
124
125
|
var useChartTooltip = (enabled) => {
|
|
125
126
|
const [tooltip, setTooltip] = React.useState({
|
|
126
127
|
visible: false,
|
|
127
|
-
|
|
128
|
-
|
|
128
|
+
clientX: 0,
|
|
129
|
+
clientY: 0,
|
|
129
130
|
content: ""
|
|
130
131
|
});
|
|
131
132
|
const containerRef = React.useRef(null);
|
|
132
133
|
const rafRef = React.useRef(0);
|
|
133
134
|
const move = React.useCallback((e) => {
|
|
134
135
|
if (!enabled) return;
|
|
135
|
-
const
|
|
136
|
-
const
|
|
136
|
+
const cx = e.clientX;
|
|
137
|
+
const cy = e.clientY;
|
|
137
138
|
cancelAnimationFrame(rafRef.current);
|
|
138
139
|
rafRef.current = requestAnimationFrame(() => {
|
|
139
|
-
|
|
140
|
-
if (!rect) return;
|
|
141
|
-
setTooltip((prev) => ({
|
|
142
|
-
...prev,
|
|
143
|
-
x: clientX - rect.left,
|
|
144
|
-
y: clientY - rect.top - 12
|
|
145
|
-
}));
|
|
140
|
+
setTooltip((prev) => ({ ...prev, clientX: cx, clientY: cy }));
|
|
146
141
|
});
|
|
147
142
|
}, [enabled]);
|
|
148
143
|
const show = React.useCallback((e, content) => {
|
|
149
144
|
if (!enabled) return;
|
|
150
|
-
|
|
151
|
-
if (!rect) return;
|
|
152
|
-
setTooltip({
|
|
153
|
-
visible: true,
|
|
154
|
-
x: e.clientX - rect.left,
|
|
155
|
-
y: e.clientY - rect.top - 12,
|
|
156
|
-
content
|
|
157
|
-
});
|
|
145
|
+
setTooltip({ visible: true, clientX: e.clientX, clientY: e.clientY, content });
|
|
158
146
|
}, [enabled]);
|
|
159
147
|
const hide = React.useCallback(() => {
|
|
160
148
|
cancelAnimationFrame(rafRef.current);
|
|
@@ -186,6 +174,45 @@ var AxisLabels = React.memo(({ labels, count, chartW, height }) => {
|
|
|
186
174
|
}) });
|
|
187
175
|
});
|
|
188
176
|
AxisLabels.displayName = "AxisLabels";
|
|
177
|
+
var useCrosshair = (seriesPoints, entries, labels, chartH) => {
|
|
178
|
+
const [activeIndex, setActiveIndex] = React.useState(null);
|
|
179
|
+
const handleMouseMove = React.useCallback((e) => {
|
|
180
|
+
const svg = e.currentTarget;
|
|
181
|
+
const rect = svg.getBoundingClientRect();
|
|
182
|
+
const mx = (e.clientX - rect.left) / rect.width * svg.viewBox.baseVal.width;
|
|
183
|
+
if (seriesPoints.length === 0 || seriesPoints[0].length === 0) return;
|
|
184
|
+
const points = seriesPoints[0];
|
|
185
|
+
const step = points.length > 1 ? Math.abs(points[1].x - points[0].x) : 20;
|
|
186
|
+
const threshold = step / 2;
|
|
187
|
+
let closest = 0;
|
|
188
|
+
let minDist = Math.abs(points[0].x - mx);
|
|
189
|
+
for (let i = 1; i < points.length; i++) {
|
|
190
|
+
const dist = Math.abs(points[i].x - mx);
|
|
191
|
+
if (dist < minDist) {
|
|
192
|
+
minDist = dist;
|
|
193
|
+
closest = i;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
setActiveIndex(minDist <= threshold ? closest : null);
|
|
197
|
+
}, [seriesPoints]);
|
|
198
|
+
const handleMouseLeave = React.useCallback(() => {
|
|
199
|
+
setActiveIndex(null);
|
|
200
|
+
}, []);
|
|
201
|
+
const tooltipContent = React.useMemo(() => {
|
|
202
|
+
if (activeIndex === null) return "";
|
|
203
|
+
return entries.map(([key], di) => {
|
|
204
|
+
const p = seriesPoints[di]?.[activeIndex];
|
|
205
|
+
return p ? `${key}: ${p.v}` : "";
|
|
206
|
+
}).filter(Boolean).join(" / ");
|
|
207
|
+
}, [activeIndex, entries, seriesPoints]);
|
|
208
|
+
const getTooltipAt = React.useCallback((idx) => {
|
|
209
|
+
return entries.map(([key], di) => {
|
|
210
|
+
const p = seriesPoints[di]?.[idx];
|
|
211
|
+
return p ? `${key}: ${p.v}` : "";
|
|
212
|
+
}).filter(Boolean).join(" / ");
|
|
213
|
+
}, [entries, seriesPoints]);
|
|
214
|
+
return { activeIndex, handleMouseMove, handleMouseLeave, tooltipContent, getTooltipAt };
|
|
215
|
+
};
|
|
189
216
|
var LineChart = React.memo(({ data, labels, width, height, animate, onHover, onMove, onLeave }) => {
|
|
190
217
|
const entries = React.useMemo(() => Object.entries(data), [data]);
|
|
191
218
|
const maxVal = React.useMemo(() => {
|
|
@@ -205,8 +232,9 @@ var LineChart = React.memo(({ data, labels, width, height, animate, onHover, onM
|
|
|
205
232
|
),
|
|
206
233
|
[entries, count, chartW, chartH, maxVal]
|
|
207
234
|
);
|
|
208
|
-
const showPoints = count <= 100;
|
|
209
235
|
const lineRefs = React.useRef([]);
|
|
236
|
+
const clipRef = React.useRef(null);
|
|
237
|
+
const { activeIndex, handleMouseMove, handleMouseLeave, getTooltipAt } = useCrosshair(seriesPoints, entries, labels, chartH);
|
|
210
238
|
React.useEffect(() => {
|
|
211
239
|
if (!animate) return;
|
|
212
240
|
lineRefs.current.forEach((el) => {
|
|
@@ -219,61 +247,123 @@ var LineChart = React.memo(({ data, labels, width, height, animate, onHover, onM
|
|
|
219
247
|
el.style.strokeDashoffset = "0";
|
|
220
248
|
});
|
|
221
249
|
});
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
250
|
+
if (clipRef.current) {
|
|
251
|
+
clipRef.current.setAttribute("width", "0");
|
|
252
|
+
requestAnimationFrame(() => {
|
|
253
|
+
if (clipRef.current) {
|
|
254
|
+
clipRef.current.style.transition = "width 1200ms ease-out 200ms";
|
|
255
|
+
clipRef.current.setAttribute("width", `${width}`);
|
|
256
|
+
}
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
}, [animate, seriesPoints, width]);
|
|
260
|
+
const activeX = activeIndex !== null ? seriesPoints[0]?.[activeIndex]?.x ?? null : null;
|
|
261
|
+
const lineClipId = "line-area-clip";
|
|
262
|
+
return /* @__PURE__ */ jsxs(
|
|
263
|
+
"svg",
|
|
264
|
+
{
|
|
265
|
+
viewBox: `0 0 ${width} ${height}`,
|
|
266
|
+
className: "chart-svg",
|
|
267
|
+
onMouseMove: (e) => {
|
|
268
|
+
handleMouseMove(e);
|
|
269
|
+
const svg = e.currentTarget;
|
|
270
|
+
const rect = svg.getBoundingClientRect();
|
|
271
|
+
const mx = (e.clientX - rect.left) / rect.width * svg.viewBox.baseVal.width;
|
|
272
|
+
const points = seriesPoints[0];
|
|
273
|
+
if (!points || points.length === 0) return;
|
|
274
|
+
const step = points.length > 1 ? Math.abs(points[1].x - points[0].x) : 20;
|
|
275
|
+
let closest = 0;
|
|
276
|
+
let minDist = Math.abs(points[0].x - mx);
|
|
277
|
+
for (let i = 1; i < points.length; i++) {
|
|
278
|
+
const dist = Math.abs(points[i].x - mx);
|
|
279
|
+
if (dist < minDist) {
|
|
280
|
+
minDist = dist;
|
|
281
|
+
closest = i;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
if (minDist <= step / 2) {
|
|
285
|
+
onHover(e, `${labels[closest]} \u2014 ${getTooltipAt(closest)}`);
|
|
286
|
+
} else {
|
|
287
|
+
onLeave();
|
|
288
|
+
}
|
|
289
|
+
},
|
|
290
|
+
onMouseLeave: () => {
|
|
291
|
+
handleMouseLeave();
|
|
292
|
+
onLeave();
|
|
293
|
+
},
|
|
294
|
+
children: [
|
|
295
|
+
animate && /* @__PURE__ */ jsx("defs", { children: /* @__PURE__ */ jsx("clipPath", { id: lineClipId, children: /* @__PURE__ */ jsx("rect", { ref: clipRef, x: "0", y: "0", width: animate ? 0 : width, height }) }) }),
|
|
296
|
+
/* @__PURE__ */ jsx(GridLines, { width, height, chartH, maxVal }),
|
|
297
|
+
/* @__PURE__ */ jsx(AxisLabels, { labels, count, chartW, height }),
|
|
298
|
+
entries.map(([key], di) => {
|
|
299
|
+
const palette = getPalette(LINE_BAR_PALETTES, di, key);
|
|
300
|
+
const color = palette[2];
|
|
301
|
+
const areaColor = palette[0];
|
|
302
|
+
const points = seriesPoints[di];
|
|
303
|
+
const gradientId = `line-gradient-${di}`;
|
|
304
|
+
const polyPoints = points.map((p) => `${p.x},${p.y}`).join(" ");
|
|
305
|
+
const areaD = `M ${points[0].x},${points[0].y} ${points.map((p) => `L ${p.x},${p.y}`).join(" ")} L ${points[points.length - 1].x},${PADDING.top + chartH} L ${points[0].x},${PADDING.top + chartH} Z`;
|
|
306
|
+
return /* @__PURE__ */ jsxs("g", { children: [
|
|
307
|
+
/* @__PURE__ */ jsx("defs", { children: /* @__PURE__ */ jsxs("linearGradient", { id: gradientId, x1: "0", y1: "0", x2: "0", y2: "1", children: [
|
|
308
|
+
/* @__PURE__ */ jsx("stop", { offset: "0%", stopColor: areaColor, stopOpacity: "0.2" }),
|
|
309
|
+
/* @__PURE__ */ jsx("stop", { offset: "100%", stopColor: areaColor, stopOpacity: "0" })
|
|
310
|
+
] }) }),
|
|
311
|
+
/* @__PURE__ */ jsx(
|
|
312
|
+
"path",
|
|
313
|
+
{
|
|
314
|
+
d: areaD,
|
|
315
|
+
fill: `url(#${gradientId})`,
|
|
316
|
+
clipPath: animate ? `url(#${lineClipId})` : void 0
|
|
317
|
+
}
|
|
318
|
+
),
|
|
319
|
+
/* @__PURE__ */ jsx(
|
|
320
|
+
"polyline",
|
|
321
|
+
{
|
|
322
|
+
ref: (el) => {
|
|
323
|
+
lineRefs.current[di] = el;
|
|
324
|
+
},
|
|
325
|
+
points: polyPoints,
|
|
326
|
+
fill: "none",
|
|
327
|
+
stroke: color,
|
|
328
|
+
strokeWidth: "2"
|
|
329
|
+
}
|
|
330
|
+
),
|
|
331
|
+
activeIndex !== null && points[activeIndex] && /* @__PURE__ */ jsx(
|
|
332
|
+
"circle",
|
|
333
|
+
{
|
|
334
|
+
cx: points[activeIndex].x,
|
|
335
|
+
cy: points[activeIndex].y,
|
|
336
|
+
r: "5",
|
|
337
|
+
fill: color,
|
|
338
|
+
className: "chart-point-active"
|
|
339
|
+
}
|
|
340
|
+
)
|
|
341
|
+
] }, di);
|
|
342
|
+
}),
|
|
343
|
+
activeX !== null && /* @__PURE__ */ jsx(
|
|
344
|
+
"line",
|
|
241
345
|
{
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
346
|
+
x1: activeX,
|
|
347
|
+
y1: PADDING.top,
|
|
348
|
+
x2: activeX,
|
|
349
|
+
y2: PADDING.top + chartH,
|
|
350
|
+
className: "chart-crosshair"
|
|
246
351
|
}
|
|
247
352
|
),
|
|
248
353
|
/* @__PURE__ */ jsx(
|
|
249
|
-
"
|
|
354
|
+
"rect",
|
|
250
355
|
{
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
fill: "
|
|
256
|
-
|
|
257
|
-
strokeWidth: "2"
|
|
356
|
+
x: PADDING.left,
|
|
357
|
+
y: PADDING.top,
|
|
358
|
+
width: chartW,
|
|
359
|
+
height: chartH,
|
|
360
|
+
fill: "transparent",
|
|
361
|
+
style: { cursor: "crosshair" }
|
|
258
362
|
}
|
|
259
|
-
)
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
cx: p.x,
|
|
264
|
-
cy: p.y,
|
|
265
|
-
r: "4",
|
|
266
|
-
fill: color,
|
|
267
|
-
className: "chart-point",
|
|
268
|
-
onMouseEnter: (e) => onHover(e, `${key}: ${labels[i]} \u2014 ${p.v}`),
|
|
269
|
-
onMouseMove: onMove,
|
|
270
|
-
onMouseLeave: onLeave
|
|
271
|
-
},
|
|
272
|
-
i
|
|
273
|
-
))
|
|
274
|
-
] }, di);
|
|
275
|
-
})
|
|
276
|
-
] });
|
|
363
|
+
)
|
|
364
|
+
]
|
|
365
|
+
}
|
|
366
|
+
);
|
|
277
367
|
});
|
|
278
368
|
LineChart.displayName = "LineChart";
|
|
279
369
|
var CurveChart = React.memo(({ data, labels, width, height, animate, onHover, onMove, onLeave }) => {
|
|
@@ -295,8 +385,9 @@ var CurveChart = React.memo(({ data, labels, width, height, animate, onHover, on
|
|
|
295
385
|
),
|
|
296
386
|
[entries, count, chartW, chartH, maxVal]
|
|
297
387
|
);
|
|
298
|
-
const showPoints = count <= 100;
|
|
299
388
|
const lineRefs = React.useRef([]);
|
|
389
|
+
const curveClipRef = React.useRef(null);
|
|
390
|
+
const { activeIndex, handleMouseMove, handleMouseLeave, getTooltipAt } = useCrosshair(seriesPoints, entries, labels, chartH);
|
|
300
391
|
React.useEffect(() => {
|
|
301
392
|
if (!animate) return;
|
|
302
393
|
lineRefs.current.forEach((el) => {
|
|
@@ -309,61 +400,123 @@ var CurveChart = React.memo(({ data, labels, width, height, animate, onHover, on
|
|
|
309
400
|
el.style.strokeDashoffset = "0";
|
|
310
401
|
});
|
|
311
402
|
});
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
403
|
+
if (curveClipRef.current) {
|
|
404
|
+
curveClipRef.current.setAttribute("width", "0");
|
|
405
|
+
requestAnimationFrame(() => {
|
|
406
|
+
if (curveClipRef.current) {
|
|
407
|
+
curveClipRef.current.style.transition = "width 1200ms ease-out 200ms";
|
|
408
|
+
curveClipRef.current.setAttribute("width", `${width}`);
|
|
409
|
+
}
|
|
410
|
+
});
|
|
411
|
+
}
|
|
412
|
+
}, [animate, seriesPoints, width]);
|
|
413
|
+
const activeX = activeIndex !== null ? seriesPoints[0]?.[activeIndex]?.x ?? null : null;
|
|
414
|
+
const curveClipId = "curve-area-clip";
|
|
415
|
+
return /* @__PURE__ */ jsxs(
|
|
416
|
+
"svg",
|
|
417
|
+
{
|
|
418
|
+
viewBox: `0 0 ${width} ${height}`,
|
|
419
|
+
className: "chart-svg",
|
|
420
|
+
onMouseMove: (e) => {
|
|
421
|
+
handleMouseMove(e);
|
|
422
|
+
const svg = e.currentTarget;
|
|
423
|
+
const rect = svg.getBoundingClientRect();
|
|
424
|
+
const mx = (e.clientX - rect.left) / rect.width * svg.viewBox.baseVal.width;
|
|
425
|
+
const points = seriesPoints[0];
|
|
426
|
+
if (!points || points.length === 0) return;
|
|
427
|
+
const step = points.length > 1 ? Math.abs(points[1].x - points[0].x) : 20;
|
|
428
|
+
let closest = 0;
|
|
429
|
+
let minDist = Math.abs(points[0].x - mx);
|
|
430
|
+
for (let i = 1; i < points.length; i++) {
|
|
431
|
+
const dist = Math.abs(points[i].x - mx);
|
|
432
|
+
if (dist < minDist) {
|
|
433
|
+
minDist = dist;
|
|
434
|
+
closest = i;
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
if (minDist <= step / 2) {
|
|
438
|
+
onHover(e, `${labels[closest]} \u2014 ${getTooltipAt(closest)}`);
|
|
439
|
+
} else {
|
|
440
|
+
onLeave();
|
|
441
|
+
}
|
|
442
|
+
},
|
|
443
|
+
onMouseLeave: () => {
|
|
444
|
+
handleMouseLeave();
|
|
445
|
+
onLeave();
|
|
446
|
+
},
|
|
447
|
+
children: [
|
|
448
|
+
animate && /* @__PURE__ */ jsx("defs", { children: /* @__PURE__ */ jsx("clipPath", { id: curveClipId, children: /* @__PURE__ */ jsx("rect", { ref: curveClipRef, x: "0", y: "0", width: animate ? 0 : width, height }) }) }),
|
|
449
|
+
/* @__PURE__ */ jsx(GridLines, { width, height, chartH, maxVal }),
|
|
450
|
+
/* @__PURE__ */ jsx(AxisLabels, { labels, count, chartW, height }),
|
|
451
|
+
entries.map(([key], di) => {
|
|
452
|
+
const palette = getPalette(LINE_BAR_PALETTES, di, key);
|
|
453
|
+
const color = palette[2];
|
|
454
|
+
const areaColor = palette[0];
|
|
455
|
+
const points = seriesPoints[di];
|
|
456
|
+
const gradientId = `curve-gradient-${di}`;
|
|
457
|
+
const linePath = toSmoothPath(points);
|
|
458
|
+
const areaPath = linePath + ` L ${points[points.length - 1].x} ${PADDING.top + chartH} L ${points[0].x} ${PADDING.top + chartH} Z`;
|
|
459
|
+
return /* @__PURE__ */ jsxs("g", { children: [
|
|
460
|
+
/* @__PURE__ */ jsx("defs", { children: /* @__PURE__ */ jsxs("linearGradient", { id: gradientId, x1: "0", y1: "0", x2: "0", y2: "1", children: [
|
|
461
|
+
/* @__PURE__ */ jsx("stop", { offset: "0%", stopColor: areaColor, stopOpacity: "0.4" }),
|
|
462
|
+
/* @__PURE__ */ jsx("stop", { offset: "100%", stopColor: areaColor, stopOpacity: "0.02" })
|
|
463
|
+
] }) }),
|
|
464
|
+
/* @__PURE__ */ jsx(
|
|
465
|
+
"path",
|
|
466
|
+
{
|
|
467
|
+
d: areaPath,
|
|
468
|
+
fill: `url(#${gradientId})`,
|
|
469
|
+
clipPath: animate ? `url(#${curveClipId})` : void 0
|
|
470
|
+
}
|
|
471
|
+
),
|
|
472
|
+
/* @__PURE__ */ jsx(
|
|
473
|
+
"path",
|
|
474
|
+
{
|
|
475
|
+
ref: (el) => {
|
|
476
|
+
lineRefs.current[di] = el;
|
|
477
|
+
},
|
|
478
|
+
d: linePath,
|
|
479
|
+
fill: "none",
|
|
480
|
+
stroke: color,
|
|
481
|
+
strokeWidth: "2"
|
|
482
|
+
}
|
|
483
|
+
),
|
|
484
|
+
activeIndex !== null && points[activeIndex] && /* @__PURE__ */ jsx(
|
|
485
|
+
"circle",
|
|
486
|
+
{
|
|
487
|
+
cx: points[activeIndex].x,
|
|
488
|
+
cy: points[activeIndex].y,
|
|
489
|
+
r: "5",
|
|
490
|
+
fill: color,
|
|
491
|
+
className: "chart-point-active"
|
|
492
|
+
}
|
|
493
|
+
)
|
|
494
|
+
] }, di);
|
|
495
|
+
}),
|
|
496
|
+
activeX !== null && /* @__PURE__ */ jsx(
|
|
497
|
+
"line",
|
|
331
498
|
{
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
499
|
+
x1: activeX,
|
|
500
|
+
y1: PADDING.top,
|
|
501
|
+
x2: activeX,
|
|
502
|
+
y2: PADDING.top + chartH,
|
|
503
|
+
className: "chart-crosshair"
|
|
336
504
|
}
|
|
337
505
|
),
|
|
338
506
|
/* @__PURE__ */ jsx(
|
|
339
|
-
"
|
|
507
|
+
"rect",
|
|
340
508
|
{
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
fill: "
|
|
346
|
-
|
|
347
|
-
strokeWidth: "2"
|
|
509
|
+
x: PADDING.left,
|
|
510
|
+
y: PADDING.top,
|
|
511
|
+
width: chartW,
|
|
512
|
+
height: chartH,
|
|
513
|
+
fill: "transparent",
|
|
514
|
+
style: { cursor: "crosshair" }
|
|
348
515
|
}
|
|
349
|
-
)
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
cx: p.x,
|
|
354
|
-
cy: p.y,
|
|
355
|
-
r: "4",
|
|
356
|
-
fill: color,
|
|
357
|
-
className: "chart-point",
|
|
358
|
-
onMouseEnter: (e) => onHover(e, `${key}: ${labels[i]} \u2014 ${p.v}`),
|
|
359
|
-
onMouseMove: onMove,
|
|
360
|
-
onMouseLeave: onLeave
|
|
361
|
-
},
|
|
362
|
-
i
|
|
363
|
-
))
|
|
364
|
-
] }, di);
|
|
365
|
-
})
|
|
366
|
-
] });
|
|
516
|
+
)
|
|
517
|
+
]
|
|
518
|
+
}
|
|
519
|
+
);
|
|
367
520
|
});
|
|
368
521
|
CurveChart.displayName = "CurveChart";
|
|
369
522
|
var BarChart = React.memo(({ data, labels, width, height, animate, onHover, onMove, onLeave }) => {
|
|
@@ -528,30 +681,70 @@ var PieDonutChart = React.memo(
|
|
|
528
681
|
}
|
|
529
682
|
);
|
|
530
683
|
PieDonutChart.displayName = "PieDonutChart";
|
|
531
|
-
var
|
|
684
|
+
var ChartTooltipPortal = ({ clientX, clientY, visible, children }) => {
|
|
532
685
|
const ref = React.useRef(null);
|
|
533
|
-
const [
|
|
534
|
-
React.
|
|
686
|
+
const [pos, setPos] = React.useState({ left: 0, top: 0 });
|
|
687
|
+
React.useLayoutEffect(() => {
|
|
535
688
|
const el = ref.current;
|
|
536
689
|
if (!el) return;
|
|
537
690
|
const w = el.offsetWidth;
|
|
538
|
-
const
|
|
539
|
-
const
|
|
540
|
-
let
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
691
|
+
const h = el.offsetHeight;
|
|
692
|
+
const vw = window.innerWidth;
|
|
693
|
+
let left = clientX + TOOLTIP_OFFSET;
|
|
694
|
+
let top = clientY - h - TOOLTIP_OFFSET;
|
|
695
|
+
if (left + w > vw - 8) left = clientX - w - TOOLTIP_OFFSET;
|
|
696
|
+
if (top < 8) top = clientY + TOOLTIP_OFFSET;
|
|
697
|
+
if (left < 8) left = 8;
|
|
698
|
+
setPos({ left, top });
|
|
699
|
+
}, [clientX, clientY]);
|
|
545
700
|
return /* @__PURE__ */ jsx(
|
|
546
701
|
"div",
|
|
547
702
|
{
|
|
548
703
|
ref,
|
|
549
|
-
className: "chart-tooltip"
|
|
550
|
-
style: { left:
|
|
704
|
+
className: `chart-tooltip ${visible ? "chart-tooltip-show" : "chart-tooltip-hide"}`,
|
|
705
|
+
style: { position: "fixed", left: pos.left, top: pos.top, zIndex: 1100 },
|
|
551
706
|
children
|
|
552
707
|
}
|
|
553
708
|
);
|
|
554
709
|
};
|
|
710
|
+
var ChartLegend = ({ data, labels, type }) => {
|
|
711
|
+
const entries = Object.entries(data);
|
|
712
|
+
if (type === "pie" || type === "doughnut") {
|
|
713
|
+
const values = entries.flatMap(([, v]) => v);
|
|
714
|
+
const total = values.reduce((a, b) => a + b, 0) || 1;
|
|
715
|
+
const firstKey = entries[0]?.[0] ?? "";
|
|
716
|
+
const colorOffset = hashString(firstKey);
|
|
717
|
+
return /* @__PURE__ */ jsx("div", { className: "chart-legend", children: values.map((v, i) => {
|
|
718
|
+
const pct = Math.round(v / total * 100);
|
|
719
|
+
const color = PIE_COLORS[(i + colorOffset) % PIE_COLORS.length];
|
|
720
|
+
return /* @__PURE__ */ jsxs("div", { className: "chart-legend-item", children: [
|
|
721
|
+
/* @__PURE__ */ jsx("span", { className: "chart-legend-dot", style: { backgroundColor: color } }),
|
|
722
|
+
/* @__PURE__ */ jsxs("div", { className: "chart-legend-text", children: [
|
|
723
|
+
/* @__PURE__ */ jsx("span", { className: "chart-legend-label", children: labels[i] || `${i + 1}` }),
|
|
724
|
+
/* @__PURE__ */ jsxs("span", { className: "chart-legend-value", children: [
|
|
725
|
+
v.toLocaleString(),
|
|
726
|
+
"(",
|
|
727
|
+
pct,
|
|
728
|
+
"%)"
|
|
729
|
+
] })
|
|
730
|
+
] })
|
|
731
|
+
] }, i);
|
|
732
|
+
}) });
|
|
733
|
+
}
|
|
734
|
+
return /* @__PURE__ */ jsx("div", { className: "chart-legend", children: entries.map(([key], di) => {
|
|
735
|
+
const palette = getPalette(LINE_BAR_PALETTES, di, key);
|
|
736
|
+
const color = palette[2];
|
|
737
|
+
const values = entries[di][1];
|
|
738
|
+
const sum = values.reduce((a, b) => a + b, 0);
|
|
739
|
+
return /* @__PURE__ */ jsxs("div", { className: "chart-legend-item", children: [
|
|
740
|
+
/* @__PURE__ */ jsx("span", { className: "chart-legend-dot", style: { backgroundColor: color } }),
|
|
741
|
+
/* @__PURE__ */ jsxs("div", { className: "chart-legend-text", children: [
|
|
742
|
+
/* @__PURE__ */ jsx("span", { className: "chart-legend-label", children: key }),
|
|
743
|
+
/* @__PURE__ */ jsx("span", { className: "chart-legend-value", children: sum.toLocaleString() })
|
|
744
|
+
] })
|
|
745
|
+
] }, di);
|
|
746
|
+
}) });
|
|
747
|
+
};
|
|
555
748
|
var Chart = React.memo((props) => {
|
|
556
749
|
const { type, data, labels, tooltip: showTooltip = true } = props;
|
|
557
750
|
const { tooltip, show, hide, move, containerRef } = useChartTooltip(showTooltip);
|
|
@@ -567,7 +760,8 @@ var Chart = React.memo((props) => {
|
|
|
567
760
|
ready && type === "bar" && /* @__PURE__ */ jsx(BarChart, { data: stableData, labels: stableLabels, width, height, animate, onHover: show, onMove: move, onLeave: hide }),
|
|
568
761
|
ready && type === "pie" && /* @__PURE__ */ jsx(PieDonutChart, { data: stableData, labels: stableLabels, width, height, animate, onHover: show, onMove: move, onLeave: hide }),
|
|
569
762
|
ready && type === "doughnut" && /* @__PURE__ */ jsx(PieDonutChart, { data: stableData, labels: stableLabels, width, height, animate, isDoughnut: true, onHover: show, onMove: move, onLeave: hide }),
|
|
570
|
-
|
|
763
|
+
ready && (type === "bar" || type === "pie" || type === "doughnut") && /* @__PURE__ */ jsx(ChartLegend, { data: stableData, labels: stableLabels, type }),
|
|
764
|
+
tooltip.content && /* @__PURE__ */ jsx(ChartTooltipPortal, { clientX: tooltip.clientX, clientY: tooltip.clientY, visible: tooltip.visible, children: tooltip.content })
|
|
571
765
|
] });
|
|
572
766
|
});
|
|
573
767
|
Chart.displayName = "Chart";
|
|
@@ -5,6 +5,10 @@
|
|
|
5
5
|
height: 100%;
|
|
6
6
|
position: relative;
|
|
7
7
|
overflow: auto;
|
|
8
|
+
scrollbar-width: none;
|
|
9
|
+
}
|
|
10
|
+
.lib-xplat-table-wrapper::-webkit-scrollbar {
|
|
11
|
+
display: none;
|
|
8
12
|
}
|
|
9
13
|
.lib-xplat-table-wrapper.sm > .lib-xplat-table > thead > tr > th,
|
|
10
14
|
.lib-xplat-table-wrapper.sm > .lib-xplat-table > thead > tr > td,
|