@marcoschwartz/lite-ui 0.11.0 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +13 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.js +1021 -466
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1021 -466
- package/dist/index.mjs.map +1 -1
- package/dist/styles.css +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -3333,6 +3333,7 @@ var RichTextEditor = ({
|
|
|
3333
3333
|
const [showImageModal, setShowImageModal] = (0, import_react20.useState)(false);
|
|
3334
3334
|
const [imageUrl, setImageUrl] = (0, import_react20.useState)("");
|
|
3335
3335
|
const [imageAlt, setImageAlt] = (0, import_react20.useState)("");
|
|
3336
|
+
const savedSelection = (0, import_react20.useRef)(null);
|
|
3336
3337
|
(0, import_react20.useLayoutEffect)(() => {
|
|
3337
3338
|
const styleId = "rich-text-editor-styles";
|
|
3338
3339
|
if (!document.getElementById(styleId)) {
|
|
@@ -3359,6 +3360,15 @@ var RichTextEditor = ({
|
|
|
3359
3360
|
font-weight: bold;
|
|
3360
3361
|
margin: 0.83em 0;
|
|
3361
3362
|
}
|
|
3363
|
+
[contenteditable] p {
|
|
3364
|
+
margin: 0.5em 0;
|
|
3365
|
+
}
|
|
3366
|
+
[contenteditable] p:first-child {
|
|
3367
|
+
margin-top: 0;
|
|
3368
|
+
}
|
|
3369
|
+
[contenteditable] p:last-child {
|
|
3370
|
+
margin-bottom: 0;
|
|
3371
|
+
}
|
|
3362
3372
|
[contenteditable] ul, [contenteditable] ol {
|
|
3363
3373
|
margin: 1em 0;
|
|
3364
3374
|
padding-left: 2em;
|
|
@@ -3387,11 +3397,60 @@ var RichTextEditor = ({
|
|
|
3387
3397
|
}
|
|
3388
3398
|
}, []);
|
|
3389
3399
|
const isInitialRender = (0, import_react20.useRef)(true);
|
|
3400
|
+
const isInternalUpdate = (0, import_react20.useRef)(false);
|
|
3390
3401
|
(0, import_react20.useEffect)(() => {
|
|
3391
|
-
if (
|
|
3402
|
+
if (isInitialRender.current && editorRef.current) {
|
|
3392
3403
|
editorRef.current.innerHTML = value;
|
|
3404
|
+
isInitialRender.current = false;
|
|
3405
|
+
try {
|
|
3406
|
+
document.execCommand("defaultParagraphSeparator", false, "p");
|
|
3407
|
+
} catch (e) {
|
|
3408
|
+
console.warn("Could not set defaultParagraphSeparator", e);
|
|
3409
|
+
}
|
|
3393
3410
|
}
|
|
3394
|
-
|
|
3411
|
+
}, []);
|
|
3412
|
+
(0, import_react20.useEffect)(() => {
|
|
3413
|
+
if (!isInitialRender.current && !isInternalUpdate.current && editorRef.current) {
|
|
3414
|
+
const currentHtml = editorRef.current.innerHTML;
|
|
3415
|
+
if (currentHtml !== value) {
|
|
3416
|
+
const selection = window.getSelection();
|
|
3417
|
+
const range = selection && selection.rangeCount > 0 ? selection.getRangeAt(0) : null;
|
|
3418
|
+
const preSelectionRange = range ? range.cloneRange() : null;
|
|
3419
|
+
if (preSelectionRange && editorRef.current.contains(preSelectionRange.startContainer)) {
|
|
3420
|
+
preSelectionRange.selectNodeContents(editorRef.current);
|
|
3421
|
+
preSelectionRange.setEnd(range.startContainer, range.startOffset);
|
|
3422
|
+
const start = preSelectionRange.toString().length;
|
|
3423
|
+
editorRef.current.innerHTML = value;
|
|
3424
|
+
const textNodes = [];
|
|
3425
|
+
const getTextNodes = (node) => {
|
|
3426
|
+
if (node.nodeType === Node.TEXT_NODE) {
|
|
3427
|
+
textNodes.push(node);
|
|
3428
|
+
} else {
|
|
3429
|
+
node.childNodes.forEach(getTextNodes);
|
|
3430
|
+
}
|
|
3431
|
+
};
|
|
3432
|
+
getTextNodes(editorRef.current);
|
|
3433
|
+
let charCount = 0;
|
|
3434
|
+
let foundStart = false;
|
|
3435
|
+
for (const textNode of textNodes) {
|
|
3436
|
+
const textLength = textNode.textContent?.length || 0;
|
|
3437
|
+
if (!foundStart && charCount + textLength >= start) {
|
|
3438
|
+
const newRange = document.createRange();
|
|
3439
|
+
newRange.setStart(textNode, start - charCount);
|
|
3440
|
+
newRange.collapse(true);
|
|
3441
|
+
selection?.removeAllRanges();
|
|
3442
|
+
selection?.addRange(newRange);
|
|
3443
|
+
foundStart = true;
|
|
3444
|
+
break;
|
|
3445
|
+
}
|
|
3446
|
+
charCount += textLength;
|
|
3447
|
+
}
|
|
3448
|
+
} else {
|
|
3449
|
+
editorRef.current.innerHTML = value;
|
|
3450
|
+
}
|
|
3451
|
+
}
|
|
3452
|
+
}
|
|
3453
|
+
isInternalUpdate.current = false;
|
|
3395
3454
|
}, [value]);
|
|
3396
3455
|
const updateActiveFormats = (0, import_react20.useCallback)(() => {
|
|
3397
3456
|
const formats = /* @__PURE__ */ new Set();
|
|
@@ -3412,10 +3471,25 @@ var RichTextEditor = ({
|
|
|
3412
3471
|
}, []);
|
|
3413
3472
|
const handleInput = (0, import_react20.useCallback)(() => {
|
|
3414
3473
|
if (editorRef.current && onChange) {
|
|
3474
|
+
isInternalUpdate.current = true;
|
|
3415
3475
|
onChange(editorRef.current.innerHTML);
|
|
3416
3476
|
}
|
|
3417
3477
|
updateActiveFormats();
|
|
3418
3478
|
}, [onChange, updateActiveFormats]);
|
|
3479
|
+
const handleFocus = (0, import_react20.useCallback)(() => {
|
|
3480
|
+
setIsFocused(true);
|
|
3481
|
+
if (editorRef.current && (!editorRef.current.innerHTML || editorRef.current.innerHTML === "")) {
|
|
3482
|
+
editorRef.current.innerHTML = "<p><br></p>";
|
|
3483
|
+
const selection = window.getSelection();
|
|
3484
|
+
const range = document.createRange();
|
|
3485
|
+
if (editorRef.current.firstChild) {
|
|
3486
|
+
range.setStart(editorRef.current.firstChild, 0);
|
|
3487
|
+
range.collapse(true);
|
|
3488
|
+
selection?.removeAllRanges();
|
|
3489
|
+
selection?.addRange(range);
|
|
3490
|
+
}
|
|
3491
|
+
}
|
|
3492
|
+
}, []);
|
|
3419
3493
|
const handleFormat = (0, import_react20.useCallback)((command) => {
|
|
3420
3494
|
if (disabled) return;
|
|
3421
3495
|
document.execCommand(command, false);
|
|
@@ -3439,16 +3513,29 @@ var RichTextEditor = ({
|
|
|
3439
3513
|
}, [disabled, updateActiveFormats, handleInput]);
|
|
3440
3514
|
const handleLink = (0, import_react20.useCallback)(() => {
|
|
3441
3515
|
if (disabled) return;
|
|
3516
|
+
const selection = window.getSelection();
|
|
3517
|
+
if (selection && selection.rangeCount > 0) {
|
|
3518
|
+
savedSelection.current = selection.getRangeAt(0).cloneRange();
|
|
3519
|
+
}
|
|
3442
3520
|
setShowLinkModal(true);
|
|
3443
3521
|
}, [disabled]);
|
|
3444
3522
|
const insertLink = (0, import_react20.useCallback)(() => {
|
|
3445
|
-
if (linkUrl)
|
|
3446
|
-
|
|
3447
|
-
|
|
3448
|
-
|
|
3449
|
-
|
|
3450
|
-
|
|
3523
|
+
if (!linkUrl || !editorRef.current) return;
|
|
3524
|
+
const selection = window.getSelection();
|
|
3525
|
+
if (savedSelection.current && selection) {
|
|
3526
|
+
try {
|
|
3527
|
+
selection.removeAllRanges();
|
|
3528
|
+
selection.addRange(savedSelection.current);
|
|
3529
|
+
document.execCommand("createLink", false, linkUrl);
|
|
3530
|
+
savedSelection.current = null;
|
|
3531
|
+
} catch (e) {
|
|
3532
|
+
console.warn("Could not insert link at saved position", e);
|
|
3533
|
+
}
|
|
3451
3534
|
}
|
|
3535
|
+
setShowLinkModal(false);
|
|
3536
|
+
setLinkUrl("");
|
|
3537
|
+
editorRef.current?.focus();
|
|
3538
|
+
handleInput();
|
|
3452
3539
|
}, [linkUrl, handleInput]);
|
|
3453
3540
|
const handleCode = (0, import_react20.useCallback)(() => {
|
|
3454
3541
|
if (disabled) return;
|
|
@@ -3468,33 +3555,82 @@ var RichTextEditor = ({
|
|
|
3468
3555
|
}, [disabled, handleInput]);
|
|
3469
3556
|
const handleImage = (0, import_react20.useCallback)(() => {
|
|
3470
3557
|
if (disabled) return;
|
|
3558
|
+
const selection = window.getSelection();
|
|
3559
|
+
if (selection && selection.rangeCount > 0) {
|
|
3560
|
+
savedSelection.current = selection.getRangeAt(0).cloneRange();
|
|
3561
|
+
}
|
|
3471
3562
|
setShowImageModal(true);
|
|
3472
3563
|
}, [disabled]);
|
|
3473
3564
|
const insertImage = (0, import_react20.useCallback)(() => {
|
|
3474
|
-
if (!imageUrl) return;
|
|
3565
|
+
if (!imageUrl || !editorRef.current) return;
|
|
3566
|
+
editorRef.current.focus();
|
|
3475
3567
|
const img = document.createElement("img");
|
|
3476
3568
|
img.src = imageUrl;
|
|
3477
3569
|
img.alt = imageAlt || "";
|
|
3478
3570
|
img.style.maxWidth = "100%";
|
|
3479
3571
|
img.style.height = "auto";
|
|
3480
3572
|
const selection = window.getSelection();
|
|
3481
|
-
if (
|
|
3482
|
-
|
|
3483
|
-
|
|
3484
|
-
|
|
3485
|
-
|
|
3486
|
-
|
|
3487
|
-
|
|
3488
|
-
|
|
3489
|
-
|
|
3490
|
-
|
|
3573
|
+
if (savedSelection.current && selection && editorRef.current.contains(savedSelection.current.commonAncestorContainer)) {
|
|
3574
|
+
try {
|
|
3575
|
+
selection.removeAllRanges();
|
|
3576
|
+
selection.addRange(savedSelection.current);
|
|
3577
|
+
savedSelection.current.deleteContents();
|
|
3578
|
+
savedSelection.current.insertNode(img);
|
|
3579
|
+
const br = document.createElement("br");
|
|
3580
|
+
savedSelection.current.setStartAfter(img);
|
|
3581
|
+
savedSelection.current.insertNode(br);
|
|
3582
|
+
savedSelection.current.setStartAfter(br);
|
|
3583
|
+
savedSelection.current.collapse(true);
|
|
3584
|
+
selection.removeAllRanges();
|
|
3585
|
+
selection.addRange(savedSelection.current);
|
|
3586
|
+
savedSelection.current = null;
|
|
3587
|
+
} catch (e) {
|
|
3588
|
+
console.warn("Could not insert at saved position", e);
|
|
3589
|
+
if (selection.rangeCount > 0) {
|
|
3590
|
+
const range = selection.getRangeAt(0);
|
|
3591
|
+
if (editorRef.current.contains(range.commonAncestorContainer)) {
|
|
3592
|
+
range.insertNode(img);
|
|
3593
|
+
const br = document.createElement("br");
|
|
3594
|
+
range.setStartAfter(img);
|
|
3595
|
+
range.insertNode(br);
|
|
3596
|
+
range.setStartAfter(br);
|
|
3597
|
+
range.collapse(true);
|
|
3598
|
+
} else {
|
|
3599
|
+
editorRef.current.appendChild(img);
|
|
3600
|
+
editorRef.current.appendChild(document.createElement("br"));
|
|
3601
|
+
}
|
|
3602
|
+
} else {
|
|
3603
|
+
editorRef.current.appendChild(img);
|
|
3604
|
+
editorRef.current.appendChild(document.createElement("br"));
|
|
3605
|
+
}
|
|
3606
|
+
}
|
|
3607
|
+
} else {
|
|
3608
|
+
if (selection && selection.rangeCount > 0) {
|
|
3609
|
+
const range = selection.getRangeAt(0);
|
|
3610
|
+
if (editorRef.current.contains(range.commonAncestorContainer)) {
|
|
3611
|
+
range.deleteContents();
|
|
3612
|
+
range.insertNode(img);
|
|
3613
|
+
const br = document.createElement("br");
|
|
3614
|
+
range.setStartAfter(img);
|
|
3615
|
+
range.insertNode(br);
|
|
3616
|
+
range.setStartAfter(br);
|
|
3617
|
+
range.collapse(true);
|
|
3618
|
+
selection.removeAllRanges();
|
|
3619
|
+
selection.addRange(range);
|
|
3620
|
+
} else {
|
|
3621
|
+
editorRef.current.appendChild(img);
|
|
3622
|
+
editorRef.current.appendChild(document.createElement("br"));
|
|
3623
|
+
}
|
|
3624
|
+
} else {
|
|
3625
|
+
editorRef.current.appendChild(img);
|
|
3626
|
+
editorRef.current.appendChild(document.createElement("br"));
|
|
3627
|
+
}
|
|
3491
3628
|
}
|
|
3492
3629
|
setShowImageModal(false);
|
|
3493
3630
|
setImageUrl("");
|
|
3494
3631
|
setImageAlt("");
|
|
3495
|
-
editorRef.current?.focus();
|
|
3496
3632
|
handleInput();
|
|
3497
|
-
}, [imageUrl, imageAlt, handleInput
|
|
3633
|
+
}, [imageUrl, imageAlt, handleInput]);
|
|
3498
3634
|
const getButtonClass = (isActive) => {
|
|
3499
3635
|
const baseClass = themeName === "minimalistic" ? "border border-white text-white transition-colors" : "border border-gray-300 dark:border-gray-600 text-gray-700 dark:text-gray-300 transition-colors";
|
|
3500
3636
|
const activeClass = themeName === "minimalistic" ? "bg-white text-black" : "bg-blue-100 dark:bg-blue-900 border-blue-500 dark:border-blue-400";
|
|
@@ -3658,11 +3794,10 @@ var RichTextEditor = ({
|
|
|
3658
3794
|
ref: editorRef,
|
|
3659
3795
|
contentEditable: !disabled,
|
|
3660
3796
|
onInput: handleInput,
|
|
3661
|
-
onFocus:
|
|
3797
|
+
onFocus: handleFocus,
|
|
3662
3798
|
onBlur: () => setIsFocused(false),
|
|
3663
3799
|
onMouseUp: updateActiveFormats,
|
|
3664
3800
|
onKeyUp: updateActiveFormats,
|
|
3665
|
-
dangerouslySetInnerHTML: { __html: value },
|
|
3666
3801
|
className: `
|
|
3667
3802
|
w-full px-4 py-3 rounded-b-lg outline-none overflow-y-auto
|
|
3668
3803
|
${editorBaseClass} ${focusClass} ${errorClass}
|
|
@@ -5151,8 +5286,10 @@ var defaultColors = [
|
|
|
5151
5286
|
];
|
|
5152
5287
|
var LineChart = ({
|
|
5153
5288
|
data,
|
|
5154
|
-
width
|
|
5155
|
-
height
|
|
5289
|
+
width: providedWidth,
|
|
5290
|
+
height: providedHeight,
|
|
5291
|
+
aspectRatio = 16 / 9,
|
|
5292
|
+
responsive = true,
|
|
5156
5293
|
showGrid = true,
|
|
5157
5294
|
showXAxis = true,
|
|
5158
5295
|
showYAxis = true,
|
|
@@ -5163,12 +5300,32 @@ var LineChart = ({
|
|
|
5163
5300
|
strokeWidth = 2,
|
|
5164
5301
|
className = "",
|
|
5165
5302
|
xAxisLabel,
|
|
5166
|
-
yAxisLabel
|
|
5303
|
+
yAxisLabel,
|
|
5304
|
+
baseFontSize = 14
|
|
5167
5305
|
}) => {
|
|
5306
|
+
const viewBoxWidth = 1e3;
|
|
5307
|
+
const viewBoxHeight = 600;
|
|
5308
|
+
const containerRef = import_react26.default.useRef(null);
|
|
5309
|
+
const svgRef = import_react26.default.useRef(null);
|
|
5310
|
+
const [tooltipPosition, setTooltipPosition] = import_react26.default.useState(null);
|
|
5168
5311
|
const [hoveredPoint, setHoveredPoint] = import_react26.default.useState(null);
|
|
5169
|
-
const
|
|
5170
|
-
const
|
|
5171
|
-
const
|
|
5312
|
+
const chartId = import_react26.default.useId();
|
|
5313
|
+
const wrapperClass = `chart-svg-wrapper-${chartId.replace(/:/g, "-")}`;
|
|
5314
|
+
const gridLabelClass = `chart-grid-label-${chartId.replace(/:/g, "-")}`;
|
|
5315
|
+
const axisLabelClass = `chart-axis-label-${chartId.replace(/:/g, "-")}`;
|
|
5316
|
+
const lineClass = `chart-line-${chartId.replace(/:/g, "-")}`;
|
|
5317
|
+
const pointClass = `chart-point-${chartId.replace(/:/g, "-")}`;
|
|
5318
|
+
const padding = {
|
|
5319
|
+
top: 50,
|
|
5320
|
+
right: 50,
|
|
5321
|
+
bottom: showXAxis ? 120 : 50,
|
|
5322
|
+
left: showYAxis ? 130 : 50
|
|
5323
|
+
};
|
|
5324
|
+
const chartWidth = viewBoxWidth - padding.left - padding.right;
|
|
5325
|
+
const chartHeight = viewBoxHeight - padding.top - padding.bottom;
|
|
5326
|
+
const gridLabelFontSize = viewBoxWidth * 0.045;
|
|
5327
|
+
const axisLabelFontSize = viewBoxWidth * 0.05;
|
|
5328
|
+
const titleFontSize = viewBoxWidth * 0.055;
|
|
5172
5329
|
const allPoints = data.flatMap((series) => series.data);
|
|
5173
5330
|
const allYValues = allPoints.map((p) => p.y);
|
|
5174
5331
|
const firstXValue = data[0]?.data[0]?.x;
|
|
@@ -5181,7 +5338,7 @@ var LineChart = ({
|
|
|
5181
5338
|
const minY = Math.min(0, ...allYValues);
|
|
5182
5339
|
const maxY = Math.max(...allYValues);
|
|
5183
5340
|
const yRange = maxY - minY;
|
|
5184
|
-
const yMin = minY - yRange * 0.1;
|
|
5341
|
+
const yMin = Math.max(0, minY - yRange * 0.1);
|
|
5185
5342
|
const yMax = maxY + yRange * 0.1;
|
|
5186
5343
|
const scaleX = (x, index) => {
|
|
5187
5344
|
const numX = isStringX ? index : x;
|
|
@@ -5228,28 +5385,32 @@ var LineChart = ({
|
|
|
5228
5385
|
x2: chartWidth,
|
|
5229
5386
|
y2: y,
|
|
5230
5387
|
stroke: "currentColor",
|
|
5231
|
-
strokeWidth: "
|
|
5388
|
+
strokeWidth: "0.5",
|
|
5232
5389
|
className: "text-gray-200 dark:text-gray-700",
|
|
5233
|
-
strokeDasharray: "
|
|
5390
|
+
strokeDasharray: "4,4"
|
|
5234
5391
|
}
|
|
5235
5392
|
),
|
|
5236
5393
|
showYAxis && /* @__PURE__ */ (0, import_jsx_runtime108.jsx)(
|
|
5237
5394
|
"text",
|
|
5238
5395
|
{
|
|
5239
|
-
x: -
|
|
5240
|
-
y
|
|
5396
|
+
x: -25,
|
|
5397
|
+
y,
|
|
5241
5398
|
textAnchor: "end",
|
|
5242
|
-
|
|
5399
|
+
dominantBaseline: "middle",
|
|
5400
|
+
fontSize: gridLabelFontSize,
|
|
5401
|
+
className: `fill-gray-600 dark:fill-gray-400 ${gridLabelClass}`,
|
|
5243
5402
|
children: yValue.toFixed(1)
|
|
5244
5403
|
}
|
|
5245
5404
|
)
|
|
5246
5405
|
] }, `grid-h-${i}`)
|
|
5247
5406
|
);
|
|
5248
5407
|
}
|
|
5249
|
-
const numXGridLines = Math.min(allPoints.length, 6);
|
|
5408
|
+
const numXGridLines = Math.max(2, Math.min(allPoints.length, 6));
|
|
5409
|
+
const xGridStep = numXGridLines > 1 ? 1 / (numXGridLines - 1) : 0;
|
|
5250
5410
|
for (let i = 0; i < numXGridLines; i++) {
|
|
5251
|
-
const x = chartWidth
|
|
5252
|
-
const
|
|
5411
|
+
const x = chartWidth * (i * xGridStep);
|
|
5412
|
+
const dataIndex = Math.floor((data[0]?.data.length - 1) * (i * xGridStep));
|
|
5413
|
+
const xValue = data[0]?.data[Math.max(0, Math.min(dataIndex, data[0].data.length - 1))]?.x || "";
|
|
5253
5414
|
gridLines.push(
|
|
5254
5415
|
/* @__PURE__ */ (0, import_jsx_runtime108.jsxs)("g", { children: [
|
|
5255
5416
|
/* @__PURE__ */ (0, import_jsx_runtime108.jsx)(
|
|
@@ -5260,18 +5421,20 @@ var LineChart = ({
|
|
|
5260
5421
|
x2: x,
|
|
5261
5422
|
y2: chartHeight,
|
|
5262
5423
|
stroke: "currentColor",
|
|
5263
|
-
strokeWidth: "
|
|
5424
|
+
strokeWidth: "0.5",
|
|
5264
5425
|
className: "text-gray-200 dark:text-gray-700",
|
|
5265
|
-
strokeDasharray: "
|
|
5426
|
+
strokeDasharray: "4,4"
|
|
5266
5427
|
}
|
|
5267
5428
|
),
|
|
5268
5429
|
showXAxis && /* @__PURE__ */ (0, import_jsx_runtime108.jsx)(
|
|
5269
5430
|
"text",
|
|
5270
5431
|
{
|
|
5271
5432
|
x,
|
|
5272
|
-
y: chartHeight +
|
|
5433
|
+
y: chartHeight + 35,
|
|
5273
5434
|
textAnchor: "middle",
|
|
5274
|
-
|
|
5435
|
+
dominantBaseline: "hanging",
|
|
5436
|
+
fontSize: gridLabelFontSize,
|
|
5437
|
+
className: `fill-gray-600 dark:fill-gray-400 ${gridLabelClass}`,
|
|
5275
5438
|
children: xValue
|
|
5276
5439
|
}
|
|
5277
5440
|
)
|
|
@@ -5279,108 +5442,197 @@ var LineChart = ({
|
|
|
5279
5442
|
);
|
|
5280
5443
|
}
|
|
5281
5444
|
}
|
|
5282
|
-
|
|
5283
|
-
|
|
5284
|
-
|
|
5285
|
-
|
|
5286
|
-
|
|
5287
|
-
|
|
5288
|
-
|
|
5289
|
-
|
|
5290
|
-
|
|
5291
|
-
|
|
5292
|
-
|
|
5293
|
-
|
|
5294
|
-
|
|
5295
|
-
|
|
5296
|
-
|
|
5297
|
-
|
|
5298
|
-
|
|
5299
|
-
|
|
5300
|
-
|
|
5301
|
-
|
|
5302
|
-
|
|
5303
|
-
|
|
5304
|
-
|
|
5305
|
-
|
|
5306
|
-
|
|
5307
|
-
|
|
5308
|
-
|
|
5309
|
-
|
|
5310
|
-
|
|
5445
|
+
const getTooltipPosition = import_react26.default.useCallback((x, y) => {
|
|
5446
|
+
if (!containerRef.current) return { x, y };
|
|
5447
|
+
const rect = containerRef.current.getBoundingClientRect();
|
|
5448
|
+
const tooltipWidth = 120;
|
|
5449
|
+
const tooltipHeight = 80;
|
|
5450
|
+
let tooltipX = x + 10;
|
|
5451
|
+
let tooltipY = y - 30;
|
|
5452
|
+
if (tooltipX + tooltipWidth > rect.width) {
|
|
5453
|
+
tooltipX = x - tooltipWidth - 10;
|
|
5454
|
+
}
|
|
5455
|
+
if (tooltipY + tooltipHeight > rect.height) {
|
|
5456
|
+
tooltipY = y + 30;
|
|
5457
|
+
}
|
|
5458
|
+
return { x: tooltipX, y: tooltipY };
|
|
5459
|
+
}, []);
|
|
5460
|
+
const containerStyle = {
|
|
5461
|
+
"--font-size-base": `${baseFontSize}px`,
|
|
5462
|
+
"--font-size-lg": `calc(var(--font-size-base) * 1.125)`,
|
|
5463
|
+
"--font-size-sm": `calc(var(--font-size-base) * 0.875)`,
|
|
5464
|
+
"--font-size-xs": `calc(var(--font-size-base) * 0.75)`
|
|
5465
|
+
};
|
|
5466
|
+
return /* @__PURE__ */ (0, import_jsx_runtime108.jsxs)(
|
|
5467
|
+
"div",
|
|
5468
|
+
{
|
|
5469
|
+
ref: containerRef,
|
|
5470
|
+
className: `relative flex flex-col gap-4 ${responsive ? "w-full" : "inline-block"} ${className}`,
|
|
5471
|
+
style: {
|
|
5472
|
+
...containerStyle
|
|
5473
|
+
},
|
|
5474
|
+
children: [
|
|
5475
|
+
/* @__PURE__ */ (0, import_jsx_runtime108.jsx)("style", { children: `
|
|
5476
|
+
/* Mobile: Large fonts, hidden axis titles, thicker lines (default) */
|
|
5477
|
+
.${gridLabelClass} { font-size: ${gridLabelFontSize}px !important; }
|
|
5478
|
+
.${axisLabelClass} { display: none; }
|
|
5479
|
+
.${lineClass} { stroke-width: ${strokeWidth * 2.5} !important; }
|
|
5480
|
+
.${pointClass} { r: 6 !important; stroke-width: 3 !important; }
|
|
5481
|
+
|
|
5482
|
+
/* Tablet: Medium fonts, show axis titles, medium lines */
|
|
5483
|
+
@media (min-width: 640px) {
|
|
5484
|
+
.${gridLabelClass} { font-size: ${gridLabelFontSize * 0.7}px !important; }
|
|
5485
|
+
.${axisLabelClass} {
|
|
5486
|
+
font-size: ${axisLabelFontSize * 0.7}px !important;
|
|
5487
|
+
display: block;
|
|
5488
|
+
}
|
|
5489
|
+
.${lineClass} { stroke-width: ${strokeWidth * 1.5} !important; }
|
|
5490
|
+
.${pointClass} { r: 4 !important; stroke-width: 2 !important; }
|
|
5491
|
+
}
|
|
5492
|
+
|
|
5493
|
+
/* Desktop: Small fonts, show axis titles, normal lines */
|
|
5494
|
+
@media (min-width: 1024px) {
|
|
5495
|
+
.${gridLabelClass} { font-size: ${gridLabelFontSize * 0.4}px !important; }
|
|
5496
|
+
.${axisLabelClass} {
|
|
5497
|
+
font-size: ${axisLabelFontSize * 0.4}px !important;
|
|
5498
|
+
display: block;
|
|
5499
|
+
}
|
|
5500
|
+
.${lineClass} { stroke-width: ${strokeWidth} !important; }
|
|
5501
|
+
.${pointClass} { r: 3 !important; stroke-width: 1.5 !important; }
|
|
5502
|
+
}
|
|
5503
|
+
` }),
|
|
5504
|
+
/* @__PURE__ */ (0, import_jsx_runtime108.jsx)(
|
|
5505
|
+
"svg",
|
|
5506
|
+
{
|
|
5507
|
+
ref: svgRef,
|
|
5508
|
+
width: "100%",
|
|
5509
|
+
viewBox: `0 0 ${viewBoxWidth} ${viewBoxHeight}`,
|
|
5510
|
+
preserveAspectRatio: "xMidYMid meet",
|
|
5511
|
+
className: "bg-white dark:bg-gray-800 block w-full",
|
|
5512
|
+
style: { height: "auto", overflow: "visible" },
|
|
5513
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime108.jsxs)("g", { transform: `translate(${padding.left}, ${padding.top})`, children: [
|
|
5514
|
+
gridLines,
|
|
5515
|
+
data.map((series, seriesIndex) => /* @__PURE__ */ (0, import_jsx_runtime108.jsx)(
|
|
5516
|
+
"path",
|
|
5311
5517
|
{
|
|
5312
|
-
|
|
5313
|
-
|
|
5314
|
-
|
|
5315
|
-
|
|
5316
|
-
|
|
5317
|
-
|
|
5318
|
-
className:
|
|
5319
|
-
onMouseEnter: () => showTooltip && setHoveredPoint({ seriesIndex, pointIndex, x, y }),
|
|
5320
|
-
onMouseLeave: () => setHoveredPoint(null)
|
|
5518
|
+
d: generatePath(series),
|
|
5519
|
+
fill: "none",
|
|
5520
|
+
stroke: series.color || defaultColors[seriesIndex % defaultColors.length],
|
|
5521
|
+
strokeWidth,
|
|
5522
|
+
strokeLinecap: "round",
|
|
5523
|
+
strokeLinejoin: "round",
|
|
5524
|
+
className: lineClass
|
|
5321
5525
|
},
|
|
5322
|
-
`
|
|
5323
|
-
)
|
|
5324
|
-
|
|
5325
|
-
|
|
5326
|
-
|
|
5327
|
-
|
|
5526
|
+
`line-${seriesIndex}`
|
|
5527
|
+
)),
|
|
5528
|
+
showDots && data.map(
|
|
5529
|
+
(series, seriesIndex) => series.data.map((point, pointIndex) => {
|
|
5530
|
+
const x = scaleX(point.x, pointIndex);
|
|
5531
|
+
const y = scaleY(point.y);
|
|
5532
|
+
const isHovered = hoveredPoint?.seriesIndex === seriesIndex && hoveredPoint?.pointIndex === pointIndex;
|
|
5533
|
+
return /* @__PURE__ */ (0, import_jsx_runtime108.jsx)(
|
|
5534
|
+
"circle",
|
|
5535
|
+
{
|
|
5536
|
+
cx: x,
|
|
5537
|
+
cy: y,
|
|
5538
|
+
r: isHovered ? 5 : 3,
|
|
5539
|
+
fill: series.color || defaultColors[seriesIndex % defaultColors.length],
|
|
5540
|
+
stroke: "white",
|
|
5541
|
+
strokeWidth: "1.5",
|
|
5542
|
+
className: `cursor-pointer transition-all ${pointClass}`,
|
|
5543
|
+
onMouseEnter: (e) => {
|
|
5544
|
+
if (showTooltip && containerRef.current) {
|
|
5545
|
+
setHoveredPoint({ seriesIndex, pointIndex, x, y });
|
|
5546
|
+
const containerRect = containerRef.current.getBoundingClientRect();
|
|
5547
|
+
const mouseX = e.clientX - containerRect.left;
|
|
5548
|
+
const mouseY = e.clientY - containerRect.top;
|
|
5549
|
+
setTooltipPosition(getTooltipPosition(mouseX, mouseY));
|
|
5550
|
+
}
|
|
5551
|
+
},
|
|
5552
|
+
onMouseMove: (e) => {
|
|
5553
|
+
if (showTooltip && hoveredPoint?.seriesIndex === seriesIndex && hoveredPoint?.pointIndex === pointIndex && containerRef.current) {
|
|
5554
|
+
const containerRect = containerRef.current.getBoundingClientRect();
|
|
5555
|
+
const mouseX = e.clientX - containerRect.left;
|
|
5556
|
+
const mouseY = e.clientY - containerRect.top;
|
|
5557
|
+
setTooltipPosition(getTooltipPosition(mouseX, mouseY));
|
|
5558
|
+
}
|
|
5559
|
+
},
|
|
5560
|
+
onMouseLeave: () => {
|
|
5561
|
+
setHoveredPoint(null);
|
|
5562
|
+
setTooltipPosition(null);
|
|
5563
|
+
}
|
|
5564
|
+
},
|
|
5565
|
+
`point-${seriesIndex}-${pointIndex}`
|
|
5566
|
+
);
|
|
5567
|
+
})
|
|
5568
|
+
),
|
|
5569
|
+
xAxisLabel && showXAxis && /* @__PURE__ */ (0, import_jsx_runtime108.jsx)(
|
|
5570
|
+
"text",
|
|
5571
|
+
{
|
|
5572
|
+
x: chartWidth / 2,
|
|
5573
|
+
y: chartHeight + 80,
|
|
5574
|
+
textAnchor: "middle",
|
|
5575
|
+
dominantBaseline: "hanging",
|
|
5576
|
+
fontSize: axisLabelFontSize,
|
|
5577
|
+
fontWeight: "600",
|
|
5578
|
+
className: `fill-gray-700 dark:fill-gray-300 ${axisLabelClass}`,
|
|
5579
|
+
children: xAxisLabel
|
|
5580
|
+
}
|
|
5581
|
+
),
|
|
5582
|
+
yAxisLabel && showYAxis && /* @__PURE__ */ (0, import_jsx_runtime108.jsx)(
|
|
5583
|
+
"text",
|
|
5584
|
+
{
|
|
5585
|
+
x: -chartHeight / 2,
|
|
5586
|
+
y: -100,
|
|
5587
|
+
textAnchor: "middle",
|
|
5588
|
+
dominantBaseline: "middle",
|
|
5589
|
+
fontSize: axisLabelFontSize,
|
|
5590
|
+
fontWeight: "600",
|
|
5591
|
+
transform: "rotate(-90)",
|
|
5592
|
+
className: `fill-gray-700 dark:fill-gray-300 ${axisLabelClass}`,
|
|
5593
|
+
children: yAxisLabel
|
|
5594
|
+
}
|
|
5595
|
+
)
|
|
5596
|
+
] })
|
|
5597
|
+
}
|
|
5598
|
+
),
|
|
5599
|
+
showLegend && /* @__PURE__ */ (0, import_jsx_runtime108.jsx)("div", { className: "flex flex-wrap gap-3 justify-center px-4", children: data.map((series, index) => /* @__PURE__ */ (0, import_jsx_runtime108.jsxs)("div", { className: "flex items-center gap-2 text-sm", children: [
|
|
5600
|
+
/* @__PURE__ */ (0, import_jsx_runtime108.jsx)(
|
|
5601
|
+
"div",
|
|
5328
5602
|
{
|
|
5329
|
-
|
|
5330
|
-
|
|
5331
|
-
|
|
5332
|
-
|
|
5333
|
-
children: xAxisLabel
|
|
5603
|
+
className: "w-3 h-3 rounded-sm flex-shrink-0",
|
|
5604
|
+
style: {
|
|
5605
|
+
backgroundColor: series.color || defaultColors[index % defaultColors.length]
|
|
5606
|
+
}
|
|
5334
5607
|
}
|
|
5335
5608
|
),
|
|
5336
|
-
|
|
5337
|
-
|
|
5338
|
-
|
|
5339
|
-
|
|
5340
|
-
|
|
5341
|
-
|
|
5342
|
-
|
|
5343
|
-
|
|
5344
|
-
|
|
5345
|
-
|
|
5346
|
-
|
|
5347
|
-
|
|
5348
|
-
|
|
5349
|
-
|
|
5350
|
-
|
|
5351
|
-
|
|
5352
|
-
|
|
5353
|
-
|
|
5354
|
-
|
|
5355
|
-
|
|
5356
|
-
|
|
5609
|
+
/* @__PURE__ */ (0, import_jsx_runtime108.jsx)("span", { className: "text-gray-700 dark:text-gray-300", children: series.name })
|
|
5610
|
+
] }, `legend-${index}`)) }),
|
|
5611
|
+
showTooltip && hoveredPoint && tooltipPosition && /* @__PURE__ */ (0, import_jsx_runtime108.jsxs)(
|
|
5612
|
+
"div",
|
|
5613
|
+
{
|
|
5614
|
+
className: "absolute bg-gray-900 dark:bg-gray-700 text-white px-3 py-2 rounded shadow-lg pointer-events-none z-50 text-sm whitespace-nowrap",
|
|
5615
|
+
style: {
|
|
5616
|
+
left: `${tooltipPosition.x}px`,
|
|
5617
|
+
top: `${tooltipPosition.y}px`,
|
|
5618
|
+
transform: "translateZ(0)"
|
|
5619
|
+
},
|
|
5620
|
+
children: [
|
|
5621
|
+
/* @__PURE__ */ (0, import_jsx_runtime108.jsx)("div", { className: "font-semibold", children: data[hoveredPoint.seriesIndex].name }),
|
|
5622
|
+
/* @__PURE__ */ (0, import_jsx_runtime108.jsxs)("div", { className: "text-xs opacity-90", children: [
|
|
5623
|
+
"x: ",
|
|
5624
|
+
data[hoveredPoint.seriesIndex].data[hoveredPoint.pointIndex].x
|
|
5625
|
+
] }),
|
|
5626
|
+
/* @__PURE__ */ (0, import_jsx_runtime108.jsxs)("div", { className: "text-xs opacity-90", children: [
|
|
5627
|
+
"y: ",
|
|
5628
|
+
data[hoveredPoint.seriesIndex].data[hoveredPoint.pointIndex].y
|
|
5629
|
+
] })
|
|
5630
|
+
]
|
|
5357
5631
|
}
|
|
5358
|
-
|
|
5359
|
-
|
|
5360
|
-
|
|
5361
|
-
|
|
5362
|
-
showTooltip && hoveredPoint && /* @__PURE__ */ (0, import_jsx_runtime108.jsxs)(
|
|
5363
|
-
"div",
|
|
5364
|
-
{
|
|
5365
|
-
className: "absolute bg-gray-900 dark:bg-gray-700 text-white px-3 py-2 rounded shadow-lg text-sm pointer-events-none z-50",
|
|
5366
|
-
style: {
|
|
5367
|
-
left: `${padding.left + hoveredPoint.x + 10}px`,
|
|
5368
|
-
top: `${padding.top + hoveredPoint.y - 30}px`
|
|
5369
|
-
},
|
|
5370
|
-
children: [
|
|
5371
|
-
/* @__PURE__ */ (0, import_jsx_runtime108.jsx)("div", { className: "font-semibold", children: data[hoveredPoint.seriesIndex].name }),
|
|
5372
|
-
/* @__PURE__ */ (0, import_jsx_runtime108.jsxs)("div", { children: [
|
|
5373
|
-
"x: ",
|
|
5374
|
-
data[hoveredPoint.seriesIndex].data[hoveredPoint.pointIndex].x
|
|
5375
|
-
] }),
|
|
5376
|
-
/* @__PURE__ */ (0, import_jsx_runtime108.jsxs)("div", { children: [
|
|
5377
|
-
"y: ",
|
|
5378
|
-
data[hoveredPoint.seriesIndex].data[hoveredPoint.pointIndex].y
|
|
5379
|
-
] })
|
|
5380
|
-
]
|
|
5381
|
-
}
|
|
5382
|
-
)
|
|
5383
|
-
] });
|
|
5632
|
+
)
|
|
5633
|
+
]
|
|
5634
|
+
}
|
|
5635
|
+
);
|
|
5384
5636
|
};
|
|
5385
5637
|
|
|
5386
5638
|
// src/components/BarChart.tsx
|
|
@@ -5402,8 +5654,10 @@ var defaultColors2 = [
|
|
|
5402
5654
|
];
|
|
5403
5655
|
var BarChart = ({
|
|
5404
5656
|
data,
|
|
5405
|
-
width
|
|
5406
|
-
height
|
|
5657
|
+
width: providedWidth,
|
|
5658
|
+
height: providedHeight,
|
|
5659
|
+
aspectRatio = 16 / 9,
|
|
5660
|
+
responsive = true,
|
|
5407
5661
|
showGrid = true,
|
|
5408
5662
|
showXAxis = true,
|
|
5409
5663
|
showYAxis = true,
|
|
@@ -5415,24 +5669,38 @@ var BarChart = ({
|
|
|
5415
5669
|
barWidth = 0.8,
|
|
5416
5670
|
className = "",
|
|
5417
5671
|
xAxisLabel,
|
|
5418
|
-
yAxisLabel
|
|
5672
|
+
yAxisLabel,
|
|
5673
|
+
baseFontSize = 14,
|
|
5674
|
+
colors = defaultColors2
|
|
5419
5675
|
}) => {
|
|
5676
|
+
const viewBoxWidth = 1e3;
|
|
5677
|
+
const viewBoxHeight = 600;
|
|
5678
|
+
const containerRef = import_react27.default.useRef(null);
|
|
5679
|
+
const [tooltipPosition, setTooltipPosition] = import_react27.default.useState(null);
|
|
5420
5680
|
const [hoveredBar, setHoveredBar] = import_react27.default.useState(null);
|
|
5681
|
+
const chartId = import_react27.default.useId();
|
|
5682
|
+
const wrapperClass = `chart-svg-wrapper-${chartId.replace(/:/g, "-")}`;
|
|
5683
|
+
const gridLabelClass = `chart-grid-label-${chartId.replace(/:/g, "-")}`;
|
|
5684
|
+
const axisLabelClass = `chart-axis-label-${chartId.replace(/:/g, "-")}`;
|
|
5685
|
+
const barClass = `chart-bar-${chartId.replace(/:/g, "-")}`;
|
|
5421
5686
|
const padding = {
|
|
5422
|
-
top:
|
|
5423
|
-
right:
|
|
5424
|
-
bottom: showXAxis ? horizontal ?
|
|
5425
|
-
left: showYAxis ? horizontal ?
|
|
5687
|
+
top: 50,
|
|
5688
|
+
right: 50,
|
|
5689
|
+
bottom: showXAxis ? horizontal ? 120 : 140 : 50,
|
|
5690
|
+
left: showYAxis ? horizontal ? 140 : 130 : 50
|
|
5426
5691
|
};
|
|
5427
|
-
const chartWidth =
|
|
5428
|
-
const chartHeight =
|
|
5692
|
+
const chartWidth = viewBoxWidth - padding.left - padding.right;
|
|
5693
|
+
const chartHeight = viewBoxHeight - padding.top - padding.bottom;
|
|
5694
|
+
const gridLabelFontSize = viewBoxWidth * 0.045;
|
|
5695
|
+
const axisLabelFontSize = viewBoxWidth * 0.05;
|
|
5696
|
+
const titleFontSize = viewBoxWidth * 0.055;
|
|
5429
5697
|
const allYValues = stacked ? data[0]?.data.map(
|
|
5430
5698
|
(_, i) => data.reduce((sum, series) => sum + (series.data[i]?.y || 0), 0)
|
|
5431
5699
|
) || [] : data.flatMap((series) => series.data.map((p) => p.y));
|
|
5432
5700
|
const minY = Math.min(0, ...allYValues);
|
|
5433
5701
|
const maxY = Math.max(...allYValues);
|
|
5434
5702
|
const yRange = maxY - minY;
|
|
5435
|
-
const yMin = minY - yRange * 0.1;
|
|
5703
|
+
const yMin = Math.max(0, minY - yRange * 0.1);
|
|
5436
5704
|
const yMax = maxY + yRange * 0.1;
|
|
5437
5705
|
const numBars = data[0]?.data.length || 0;
|
|
5438
5706
|
const numSeries = data.length;
|
|
@@ -5459,18 +5727,20 @@ var BarChart = ({
|
|
|
5459
5727
|
x2: chartWidth,
|
|
5460
5728
|
y2: y,
|
|
5461
5729
|
stroke: "currentColor",
|
|
5462
|
-
strokeWidth: "
|
|
5730
|
+
strokeWidth: "0.5",
|
|
5463
5731
|
className: "text-gray-200 dark:text-gray-700",
|
|
5464
|
-
strokeDasharray: "
|
|
5732
|
+
strokeDasharray: "4,4"
|
|
5465
5733
|
}
|
|
5466
5734
|
),
|
|
5467
5735
|
showYAxis && !horizontal && /* @__PURE__ */ (0, import_jsx_runtime109.jsx)(
|
|
5468
5736
|
"text",
|
|
5469
5737
|
{
|
|
5470
|
-
x: -
|
|
5471
|
-
y
|
|
5738
|
+
x: -25,
|
|
5739
|
+
y,
|
|
5472
5740
|
textAnchor: "end",
|
|
5473
|
-
|
|
5741
|
+
dominantBaseline: "middle",
|
|
5742
|
+
fontSize: gridLabelFontSize,
|
|
5743
|
+
className: `fill-gray-600 dark:fill-gray-400 ${gridLabelClass}`,
|
|
5474
5744
|
children: yValue.toFixed(0)
|
|
5475
5745
|
}
|
|
5476
5746
|
)
|
|
@@ -5519,12 +5789,20 @@ var BarChart = ({
|
|
|
5519
5789
|
x: xPos,
|
|
5520
5790
|
y: yPos,
|
|
5521
5791
|
width: barW,
|
|
5522
|
-
height:
|
|
5523
|
-
fill: data[seriesIndex].color ||
|
|
5524
|
-
className:
|
|
5792
|
+
height: Math.abs(scaleY(0) - yPos),
|
|
5793
|
+
fill: data[seriesIndex].color || colors[seriesIndex % colors.length],
|
|
5794
|
+
className: `cursor-pointer transition-opacity ${barClass}`,
|
|
5525
5795
|
opacity: isHovered ? 0.8 : 1,
|
|
5526
|
-
onMouseEnter: () =>
|
|
5527
|
-
|
|
5796
|
+
onMouseEnter: () => {
|
|
5797
|
+
if (showTooltip) {
|
|
5798
|
+
setHoveredBar({ seriesIndex, barIndex, x: xPos + barW / 2, y: yPos });
|
|
5799
|
+
setTooltipPosition(getTooltipPosition(padding.left + xPos + barW / 2, padding.top + yPos));
|
|
5800
|
+
}
|
|
5801
|
+
},
|
|
5802
|
+
onMouseLeave: () => {
|
|
5803
|
+
setHoveredBar(null);
|
|
5804
|
+
setTooltipPosition(null);
|
|
5805
|
+
}
|
|
5528
5806
|
},
|
|
5529
5807
|
`bar-${seriesIndex}-${barIndex}`
|
|
5530
5808
|
)
|
|
@@ -5549,8 +5827,8 @@ var BarChart = ({
|
|
|
5549
5827
|
y: yPos,
|
|
5550
5828
|
width: actualBarWidth,
|
|
5551
5829
|
height: barHeight,
|
|
5552
|
-
fill: data[seriesIndex].color ||
|
|
5553
|
-
className:
|
|
5830
|
+
fill: data[seriesIndex].color || colors[seriesIndex % colors.length],
|
|
5831
|
+
className: `cursor-pointer transition-opacity ${barClass}`,
|
|
5554
5832
|
opacity: isHovered ? 0.8 : 1,
|
|
5555
5833
|
onMouseEnter: () => showTooltip && setHoveredBar({ seriesIndex, barIndex, x: xPos + actualBarWidth / 2, y: yPos }),
|
|
5556
5834
|
onMouseLeave: () => setHoveredBar(null)
|
|
@@ -5564,9 +5842,12 @@ var BarChart = ({
|
|
|
5564
5842
|
"text",
|
|
5565
5843
|
{
|
|
5566
5844
|
x: xPos + actualBarWidth / 2,
|
|
5567
|
-
y: yPos -
|
|
5845
|
+
y: yPos - 10,
|
|
5568
5846
|
textAnchor: "middle",
|
|
5569
|
-
|
|
5847
|
+
dominantBaseline: "middle",
|
|
5848
|
+
fontSize: "11",
|
|
5849
|
+
fontWeight: "600",
|
|
5850
|
+
className: "fill-gray-700 dark:fill-gray-300",
|
|
5570
5851
|
children: point.y
|
|
5571
5852
|
},
|
|
5572
5853
|
`value-${seriesIndex}-${barIndex}`
|
|
@@ -5584,81 +5865,145 @@ var BarChart = ({
|
|
|
5584
5865
|
"text",
|
|
5585
5866
|
{
|
|
5586
5867
|
x: xPos,
|
|
5587
|
-
y: chartHeight +
|
|
5868
|
+
y: chartHeight + 35,
|
|
5588
5869
|
textAnchor: "middle",
|
|
5589
|
-
|
|
5870
|
+
dominantBaseline: "hanging",
|
|
5871
|
+
fontSize: gridLabelFontSize,
|
|
5872
|
+
className: `fill-gray-600 dark:fill-gray-400 ${gridLabelClass}`,
|
|
5590
5873
|
children: point.x
|
|
5591
5874
|
},
|
|
5592
5875
|
`x-label-${i}`
|
|
5593
5876
|
);
|
|
5594
5877
|
});
|
|
5595
|
-
|
|
5596
|
-
|
|
5597
|
-
|
|
5598
|
-
|
|
5599
|
-
|
|
5600
|
-
|
|
5601
|
-
|
|
5602
|
-
|
|
5603
|
-
|
|
5604
|
-
|
|
5605
|
-
|
|
5606
|
-
|
|
5607
|
-
|
|
5608
|
-
|
|
5878
|
+
const getTooltipPosition = import_react27.default.useCallback((x, y) => {
|
|
5879
|
+
if (!containerRef.current) return { x, y };
|
|
5880
|
+
const rect = containerRef.current.getBoundingClientRect();
|
|
5881
|
+
const tooltipWidth = 140;
|
|
5882
|
+
const tooltipHeight = 80;
|
|
5883
|
+
let tooltipX = x + 10;
|
|
5884
|
+
let tooltipY = y - 30;
|
|
5885
|
+
if (tooltipX + tooltipWidth > rect.width) {
|
|
5886
|
+
tooltipX = x - tooltipWidth - 10;
|
|
5887
|
+
}
|
|
5888
|
+
if (tooltipY + tooltipHeight > rect.height) {
|
|
5889
|
+
tooltipY = y + 30;
|
|
5890
|
+
}
|
|
5891
|
+
return { x: tooltipX, y: tooltipY };
|
|
5892
|
+
}, []);
|
|
5893
|
+
const containerStyle = {
|
|
5894
|
+
"--font-size-base": `${baseFontSize}px`,
|
|
5895
|
+
"--font-size-lg": `calc(var(--font-size-base) * 1.125)`,
|
|
5896
|
+
"--font-size-sm": `calc(var(--font-size-base) * 0.875)`,
|
|
5897
|
+
"--font-size-xs": `calc(var(--font-size-base) * 0.75)`
|
|
5898
|
+
};
|
|
5899
|
+
return /* @__PURE__ */ (0, import_jsx_runtime109.jsxs)(
|
|
5900
|
+
"div",
|
|
5901
|
+
{
|
|
5902
|
+
ref: containerRef,
|
|
5903
|
+
className: `relative flex flex-col gap-4 ${responsive ? "w-full" : "inline-block"} ${className}`,
|
|
5904
|
+
style: {
|
|
5905
|
+
...containerStyle
|
|
5906
|
+
},
|
|
5907
|
+
children: [
|
|
5908
|
+
/* @__PURE__ */ (0, import_jsx_runtime109.jsx)("style", { children: `
|
|
5909
|
+
/* Mobile: Large fonts, hidden axis titles, thicker bars (default) */
|
|
5910
|
+
.${gridLabelClass} { font-size: ${gridLabelFontSize}px !important; }
|
|
5911
|
+
.${axisLabelClass} { display: none; }
|
|
5912
|
+
|
|
5913
|
+
/* Tablet: Medium fonts, show axis titles */
|
|
5914
|
+
@media (min-width: 640px) {
|
|
5915
|
+
.${gridLabelClass} { font-size: ${gridLabelFontSize * 0.7}px !important; }
|
|
5916
|
+
.${axisLabelClass} {
|
|
5917
|
+
font-size: ${axisLabelFontSize * 0.7}px !important;
|
|
5918
|
+
display: block;
|
|
5919
|
+
}
|
|
5920
|
+
}
|
|
5921
|
+
|
|
5922
|
+
/* Desktop: Small fonts, show axis titles */
|
|
5923
|
+
@media (min-width: 1024px) {
|
|
5924
|
+
.${gridLabelClass} { font-size: ${gridLabelFontSize * 0.4}px !important; }
|
|
5925
|
+
.${axisLabelClass} {
|
|
5926
|
+
font-size: ${axisLabelFontSize * 0.4}px !important;
|
|
5927
|
+
display: block;
|
|
5928
|
+
}
|
|
5929
|
+
}
|
|
5930
|
+
` }),
|
|
5931
|
+
/* @__PURE__ */ (0, import_jsx_runtime109.jsx)(
|
|
5932
|
+
"svg",
|
|
5933
|
+
{
|
|
5934
|
+
width: "100%",
|
|
5935
|
+
viewBox: `0 0 ${viewBoxWidth} ${viewBoxHeight}`,
|
|
5936
|
+
preserveAspectRatio: "xMidYMid meet",
|
|
5937
|
+
className: "bg-white dark:bg-gray-800 block w-full",
|
|
5938
|
+
style: { height: "auto", overflow: "visible" },
|
|
5939
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime109.jsxs)("g", { transform: `translate(${padding.left}, ${padding.top})`, children: [
|
|
5940
|
+
gridLines,
|
|
5941
|
+
renderBars(),
|
|
5942
|
+
showXAxis && xAxisLabels,
|
|
5943
|
+
xAxisLabel && showXAxis && /* @__PURE__ */ (0, import_jsx_runtime109.jsx)(
|
|
5944
|
+
"text",
|
|
5945
|
+
{
|
|
5946
|
+
x: chartWidth / 2,
|
|
5947
|
+
y: chartHeight + 80,
|
|
5948
|
+
textAnchor: "middle",
|
|
5949
|
+
dominantBaseline: "hanging",
|
|
5950
|
+
fontSize: axisLabelFontSize,
|
|
5951
|
+
fontWeight: "600",
|
|
5952
|
+
className: `fill-gray-700 dark:fill-gray-300 ${axisLabelClass}`,
|
|
5953
|
+
children: xAxisLabel
|
|
5954
|
+
}
|
|
5955
|
+
),
|
|
5956
|
+
yAxisLabel && showYAxis && /* @__PURE__ */ (0, import_jsx_runtime109.jsx)(
|
|
5957
|
+
"text",
|
|
5958
|
+
{
|
|
5959
|
+
x: -chartHeight / 2,
|
|
5960
|
+
y: -100,
|
|
5961
|
+
textAnchor: "middle",
|
|
5962
|
+
dominantBaseline: "middle",
|
|
5963
|
+
fontSize: axisLabelFontSize,
|
|
5964
|
+
fontWeight: "600",
|
|
5965
|
+
transform: "rotate(-90)",
|
|
5966
|
+
className: `fill-gray-700 dark:fill-gray-300 ${axisLabelClass}`,
|
|
5967
|
+
children: yAxisLabel
|
|
5968
|
+
}
|
|
5969
|
+
)
|
|
5970
|
+
] })
|
|
5971
|
+
}
|
|
5972
|
+
),
|
|
5973
|
+
showLegend && /* @__PURE__ */ (0, import_jsx_runtime109.jsx)("div", { className: "flex flex-wrap gap-3 justify-center px-4", children: data.map((series, index) => /* @__PURE__ */ (0, import_jsx_runtime109.jsxs)("div", { className: "flex items-center gap-2 text-sm", children: [
|
|
5974
|
+
/* @__PURE__ */ (0, import_jsx_runtime109.jsx)(
|
|
5975
|
+
"div",
|
|
5609
5976
|
{
|
|
5610
|
-
|
|
5611
|
-
|
|
5612
|
-
|
|
5613
|
-
|
|
5614
|
-
children: xAxisLabel
|
|
5977
|
+
className: "w-3 h-3 rounded-sm flex-shrink-0",
|
|
5978
|
+
style: {
|
|
5979
|
+
backgroundColor: series.color || colors[index % colors.length]
|
|
5980
|
+
}
|
|
5615
5981
|
}
|
|
5616
5982
|
),
|
|
5617
|
-
|
|
5618
|
-
|
|
5619
|
-
|
|
5620
|
-
|
|
5621
|
-
|
|
5622
|
-
|
|
5623
|
-
|
|
5624
|
-
|
|
5625
|
-
|
|
5626
|
-
|
|
5627
|
-
|
|
5628
|
-
|
|
5629
|
-
|
|
5630
|
-
|
|
5631
|
-
|
|
5632
|
-
|
|
5633
|
-
|
|
5634
|
-
|
|
5635
|
-
|
|
5636
|
-
style: {
|
|
5637
|
-
backgroundColor: series.color || defaultColors2[index % defaultColors2.length]
|
|
5983
|
+
/* @__PURE__ */ (0, import_jsx_runtime109.jsx)("span", { className: "text-gray-700 dark:text-gray-300", children: series.name })
|
|
5984
|
+
] }, `legend-${index}`)) }),
|
|
5985
|
+
showTooltip && hoveredBar && tooltipPosition && /* @__PURE__ */ (0, import_jsx_runtime109.jsxs)(
|
|
5986
|
+
"div",
|
|
5987
|
+
{
|
|
5988
|
+
className: "absolute bg-gray-900 dark:bg-gray-700 text-white px-3 py-2 rounded shadow-lg pointer-events-none z-50 text-sm whitespace-nowrap",
|
|
5989
|
+
style: {
|
|
5990
|
+
left: `${tooltipPosition.x}px`,
|
|
5991
|
+
top: `${tooltipPosition.y}px`,
|
|
5992
|
+
transform: "translateZ(0)"
|
|
5993
|
+
},
|
|
5994
|
+
children: [
|
|
5995
|
+
/* @__PURE__ */ (0, import_jsx_runtime109.jsx)("div", { className: "font-semibold", children: data[hoveredBar.seriesIndex].name }),
|
|
5996
|
+
/* @__PURE__ */ (0, import_jsx_runtime109.jsxs)("div", { className: "text-xs opacity-90", children: [
|
|
5997
|
+
data[hoveredBar.seriesIndex].data[hoveredBar.barIndex].x,
|
|
5998
|
+
": ",
|
|
5999
|
+
data[hoveredBar.seriesIndex].data[hoveredBar.barIndex].y
|
|
6000
|
+
] })
|
|
6001
|
+
]
|
|
5638
6002
|
}
|
|
5639
|
-
|
|
5640
|
-
|
|
5641
|
-
|
|
5642
|
-
|
|
5643
|
-
showTooltip && hoveredBar && /* @__PURE__ */ (0, import_jsx_runtime109.jsxs)(
|
|
5644
|
-
"div",
|
|
5645
|
-
{
|
|
5646
|
-
className: "absolute bg-gray-900 dark:bg-gray-700 text-white px-3 py-2 rounded shadow-lg text-sm pointer-events-none z-50",
|
|
5647
|
-
style: {
|
|
5648
|
-
left: `${padding.left + hoveredBar.x + 10}px`,
|
|
5649
|
-
top: `${padding.top + hoveredBar.y - 30}px`
|
|
5650
|
-
},
|
|
5651
|
-
children: [
|
|
5652
|
-
/* @__PURE__ */ (0, import_jsx_runtime109.jsx)("div", { className: "font-semibold", children: data[hoveredBar.seriesIndex].name }),
|
|
5653
|
-
/* @__PURE__ */ (0, import_jsx_runtime109.jsxs)("div", { children: [
|
|
5654
|
-
data[hoveredBar.seriesIndex].data[hoveredBar.barIndex].x,
|
|
5655
|
-
": ",
|
|
5656
|
-
data[hoveredBar.seriesIndex].data[hoveredBar.barIndex].y
|
|
5657
|
-
] })
|
|
5658
|
-
]
|
|
5659
|
-
}
|
|
5660
|
-
)
|
|
5661
|
-
] });
|
|
6003
|
+
)
|
|
6004
|
+
]
|
|
6005
|
+
}
|
|
6006
|
+
);
|
|
5662
6007
|
};
|
|
5663
6008
|
|
|
5664
6009
|
// src/components/AreaChart.tsx
|
|
@@ -5680,8 +6025,10 @@ var defaultColors3 = [
|
|
|
5680
6025
|
];
|
|
5681
6026
|
var AreaChart = ({
|
|
5682
6027
|
data,
|
|
5683
|
-
width
|
|
5684
|
-
height
|
|
6028
|
+
width: providedWidth,
|
|
6029
|
+
height: providedHeight,
|
|
6030
|
+
aspectRatio = 16 / 9,
|
|
6031
|
+
responsive = true,
|
|
5685
6032
|
showGrid = true,
|
|
5686
6033
|
showXAxis = true,
|
|
5687
6034
|
showYAxis = true,
|
|
@@ -5694,12 +6041,32 @@ var AreaChart = ({
|
|
|
5694
6041
|
strokeWidth = 2,
|
|
5695
6042
|
className = "",
|
|
5696
6043
|
xAxisLabel,
|
|
5697
|
-
yAxisLabel
|
|
6044
|
+
yAxisLabel,
|
|
6045
|
+
baseFontSize = 14
|
|
5698
6046
|
}) => {
|
|
6047
|
+
const viewBoxWidth = 1e3;
|
|
6048
|
+
const viewBoxHeight = 600;
|
|
6049
|
+
const containerRef = import_react28.default.useRef(null);
|
|
6050
|
+
const svgRef = import_react28.default.useRef(null);
|
|
6051
|
+
const [tooltipPosition, setTooltipPosition] = import_react28.default.useState(null);
|
|
5699
6052
|
const [hoveredPoint, setHoveredPoint] = import_react28.default.useState(null);
|
|
5700
|
-
const
|
|
5701
|
-
const
|
|
5702
|
-
const
|
|
6053
|
+
const chartId = import_react28.default.useId();
|
|
6054
|
+
const wrapperClass = `chart-svg-wrapper-${chartId.replace(/:/g, "-")}`;
|
|
6055
|
+
const gridLabelClass = `chart-grid-label-${chartId.replace(/:/g, "-")}`;
|
|
6056
|
+
const axisLabelClass = `chart-axis-label-${chartId.replace(/:/g, "-")}`;
|
|
6057
|
+
const lineClass = `chart-line-${chartId.replace(/:/g, "-")}`;
|
|
6058
|
+
const pointClass = `chart-point-${chartId.replace(/:/g, "-")}`;
|
|
6059
|
+
const padding = {
|
|
6060
|
+
top: 50,
|
|
6061
|
+
right: 50,
|
|
6062
|
+
bottom: showXAxis ? 120 : 50,
|
|
6063
|
+
left: showYAxis ? 130 : 50
|
|
6064
|
+
};
|
|
6065
|
+
const chartWidth = viewBoxWidth - padding.left - padding.right;
|
|
6066
|
+
const chartHeight = viewBoxHeight - padding.top - padding.bottom;
|
|
6067
|
+
const gridLabelFontSize = viewBoxWidth * 0.045;
|
|
6068
|
+
const axisLabelFontSize = viewBoxWidth * 0.05;
|
|
6069
|
+
const titleFontSize = viewBoxWidth * 0.055;
|
|
5703
6070
|
const allPoints = data.flatMap((series) => series.data);
|
|
5704
6071
|
const allYValues = stacked ? data[0]?.data.map(
|
|
5705
6072
|
(_, i) => data.reduce((sum, series) => sum + (series.data[i]?.y || 0), 0)
|
|
@@ -5713,7 +6080,7 @@ var AreaChart = ({
|
|
|
5713
6080
|
const minY = Math.min(0, ...allYValues);
|
|
5714
6081
|
const maxY = Math.max(...allYValues);
|
|
5715
6082
|
const yRange = maxY - minY;
|
|
5716
|
-
const yMin = minY - yRange * 0.1;
|
|
6083
|
+
const yMin = Math.max(0, minY - yRange * 0.1);
|
|
5717
6084
|
const yMax = maxY + yRange * 0.1;
|
|
5718
6085
|
const scaleX = (x, index) => {
|
|
5719
6086
|
const numX = isStringX ? index : x;
|
|
@@ -5794,19 +6161,21 @@ var AreaChart = ({
|
|
|
5794
6161
|
x2: chartWidth,
|
|
5795
6162
|
y2: y,
|
|
5796
6163
|
stroke: "currentColor",
|
|
5797
|
-
strokeWidth: "
|
|
6164
|
+
strokeWidth: "0.5",
|
|
5798
6165
|
className: "text-gray-200 dark:text-gray-700",
|
|
5799
|
-
strokeDasharray: "
|
|
6166
|
+
strokeDasharray: "4,4"
|
|
5800
6167
|
}
|
|
5801
6168
|
),
|
|
5802
6169
|
showYAxis && /* @__PURE__ */ (0, import_jsx_runtime110.jsx)(
|
|
5803
6170
|
"text",
|
|
5804
6171
|
{
|
|
5805
|
-
x: -
|
|
5806
|
-
y
|
|
6172
|
+
x: -25,
|
|
6173
|
+
y,
|
|
5807
6174
|
textAnchor: "end",
|
|
5808
|
-
|
|
5809
|
-
|
|
6175
|
+
dominantBaseline: "middle",
|
|
6176
|
+
fontSize: gridLabelFontSize,
|
|
6177
|
+
className: `fill-gray-600 dark:fill-gray-400 ${gridLabelClass}`,
|
|
6178
|
+
children: yValue.toFixed(1)
|
|
5810
6179
|
}
|
|
5811
6180
|
)
|
|
5812
6181
|
] }, `grid-h-${i}`)
|
|
@@ -5826,18 +6195,20 @@ var AreaChart = ({
|
|
|
5826
6195
|
x2: x,
|
|
5827
6196
|
y2: chartHeight,
|
|
5828
6197
|
stroke: "currentColor",
|
|
5829
|
-
strokeWidth: "
|
|
6198
|
+
strokeWidth: "0.5",
|
|
5830
6199
|
className: "text-gray-200 dark:text-gray-700",
|
|
5831
|
-
strokeDasharray: "
|
|
6200
|
+
strokeDasharray: "4,4"
|
|
5832
6201
|
}
|
|
5833
6202
|
),
|
|
5834
6203
|
showXAxis && /* @__PURE__ */ (0, import_jsx_runtime110.jsx)(
|
|
5835
6204
|
"text",
|
|
5836
6205
|
{
|
|
5837
6206
|
x,
|
|
5838
|
-
y: chartHeight +
|
|
6207
|
+
y: chartHeight + 35,
|
|
5839
6208
|
textAnchor: "middle",
|
|
5840
|
-
|
|
6209
|
+
dominantBaseline: "hanging",
|
|
6210
|
+
fontSize: gridLabelFontSize,
|
|
6211
|
+
className: `fill-gray-600 dark:fill-gray-400 ${gridLabelClass}`,
|
|
5841
6212
|
children: xValue
|
|
5842
6213
|
}
|
|
5843
6214
|
)
|
|
@@ -5849,130 +6220,217 @@ var AreaChart = ({
|
|
|
5849
6220
|
if (stacked) {
|
|
5850
6221
|
cumulativeValues = Array(data[0]?.data.length || 0).fill(0);
|
|
5851
6222
|
}
|
|
5852
|
-
|
|
5853
|
-
|
|
5854
|
-
|
|
5855
|
-
|
|
5856
|
-
|
|
5857
|
-
|
|
5858
|
-
|
|
5859
|
-
|
|
5860
|
-
|
|
5861
|
-
|
|
5862
|
-
|
|
5863
|
-
|
|
5864
|
-
|
|
5865
|
-
|
|
5866
|
-
|
|
5867
|
-
|
|
5868
|
-
|
|
5869
|
-
|
|
5870
|
-
|
|
5871
|
-
|
|
5872
|
-
|
|
5873
|
-
|
|
6223
|
+
const getTooltipPosition = import_react28.default.useCallback((x, y) => {
|
|
6224
|
+
if (!containerRef.current) return { x, y };
|
|
6225
|
+
const rect = containerRef.current.getBoundingClientRect();
|
|
6226
|
+
const tooltipWidth = 120;
|
|
6227
|
+
const tooltipHeight = 80;
|
|
6228
|
+
let tooltipX = x + 10;
|
|
6229
|
+
let tooltipY = y - 30;
|
|
6230
|
+
if (tooltipX + tooltipWidth > rect.width) {
|
|
6231
|
+
tooltipX = x - tooltipWidth - 10;
|
|
6232
|
+
}
|
|
6233
|
+
if (tooltipY + tooltipHeight > rect.height) {
|
|
6234
|
+
tooltipY = y + 30;
|
|
6235
|
+
}
|
|
6236
|
+
return { x: tooltipX, y: tooltipY };
|
|
6237
|
+
}, []);
|
|
6238
|
+
const containerStyle = {
|
|
6239
|
+
"--font-size-base": `${baseFontSize}px`,
|
|
6240
|
+
"--font-size-lg": `calc(var(--font-size-base) * 1.125)`,
|
|
6241
|
+
"--font-size-sm": `calc(var(--font-size-base) * 0.875)`,
|
|
6242
|
+
"--font-size-xs": `calc(var(--font-size-base) * 0.75)`
|
|
6243
|
+
};
|
|
6244
|
+
return /* @__PURE__ */ (0, import_jsx_runtime110.jsxs)(
|
|
6245
|
+
"div",
|
|
6246
|
+
{
|
|
6247
|
+
ref: containerRef,
|
|
6248
|
+
className: `relative flex flex-col gap-4 ${responsive ? "w-full" : "inline-block"} ${className}`,
|
|
6249
|
+
style: containerStyle,
|
|
6250
|
+
children: [
|
|
6251
|
+
/* @__PURE__ */ (0, import_jsx_runtime110.jsx)("style", { children: `
|
|
6252
|
+
/* Mobile: Large fonts, hidden axis titles, thicker lines (default) */
|
|
6253
|
+
.${gridLabelClass} { font-size: ${gridLabelFontSize}px !important; }
|
|
6254
|
+
.${axisLabelClass} { display: none; }
|
|
6255
|
+
.${lineClass} { stroke-width: ${strokeWidth * 2.5} !important; }
|
|
6256
|
+
.${pointClass} { r: 6 !important; stroke-width: 3 !important; }
|
|
6257
|
+
|
|
6258
|
+
/* Tablet: Medium fonts, show axis titles, medium lines */
|
|
6259
|
+
@media (min-width: 640px) {
|
|
6260
|
+
.${gridLabelClass} { font-size: ${gridLabelFontSize * 0.7}px !important; }
|
|
6261
|
+
.${axisLabelClass} {
|
|
6262
|
+
font-size: ${axisLabelFontSize * 0.7}px !important;
|
|
6263
|
+
display: block;
|
|
6264
|
+
}
|
|
6265
|
+
.${lineClass} { stroke-width: ${strokeWidth * 1.5} !important; }
|
|
6266
|
+
.${pointClass} { r: 4 !important; stroke-width: 2 !important; }
|
|
6267
|
+
}
|
|
6268
|
+
|
|
6269
|
+
/* Desktop: Small fonts, show axis titles, normal lines */
|
|
6270
|
+
@media (min-width: 1024px) {
|
|
6271
|
+
.${gridLabelClass} { font-size: ${gridLabelFontSize * 0.4}px !important; }
|
|
6272
|
+
.${axisLabelClass} {
|
|
6273
|
+
font-size: ${axisLabelFontSize * 0.4}px !important;
|
|
6274
|
+
display: block;
|
|
6275
|
+
}
|
|
6276
|
+
.${lineClass} { stroke-width: ${strokeWidth} !important; }
|
|
6277
|
+
.${pointClass} { r: 3 !important; stroke-width: 1.5 !important; }
|
|
6278
|
+
}
|
|
6279
|
+
` }),
|
|
6280
|
+
/* @__PURE__ */ (0, import_jsx_runtime110.jsx)(
|
|
6281
|
+
"svg",
|
|
6282
|
+
{
|
|
6283
|
+
ref: svgRef,
|
|
6284
|
+
width: "100%",
|
|
6285
|
+
viewBox: `0 0 ${viewBoxWidth} ${viewBoxHeight}`,
|
|
6286
|
+
preserveAspectRatio: "xMidYMid meet",
|
|
6287
|
+
className: "bg-white dark:bg-gray-800 block w-full",
|
|
6288
|
+
style: { height: "auto", overflow: "visible" },
|
|
6289
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime110.jsxs)("g", { transform: `translate(${padding.left}, ${padding.top})`, children: [
|
|
6290
|
+
gridLines,
|
|
6291
|
+
data.map((series, seriesIndex) => {
|
|
6292
|
+
const baselineYValues = stacked ? [...cumulativeValues] : void 0;
|
|
6293
|
+
const areaPath = generateAreaPath(series, baselineYValues);
|
|
6294
|
+
const linePath = generateLinePath(series, baselineYValues);
|
|
6295
|
+
if (stacked) {
|
|
6296
|
+
series.data.forEach((point, i) => {
|
|
6297
|
+
cumulativeValues[i] += point.y;
|
|
6298
|
+
});
|
|
6299
|
+
}
|
|
6300
|
+
return /* @__PURE__ */ (0, import_jsx_runtime110.jsxs)("g", { children: [
|
|
6301
|
+
/* @__PURE__ */ (0, import_jsx_runtime110.jsx)(
|
|
6302
|
+
"path",
|
|
6303
|
+
{
|
|
6304
|
+
d: areaPath,
|
|
6305
|
+
fill: series.color || defaultColors3[seriesIndex % defaultColors3.length],
|
|
6306
|
+
opacity: fillOpacity
|
|
6307
|
+
}
|
|
6308
|
+
),
|
|
6309
|
+
/* @__PURE__ */ (0, import_jsx_runtime110.jsx)(
|
|
6310
|
+
"path",
|
|
6311
|
+
{
|
|
6312
|
+
d: linePath,
|
|
6313
|
+
fill: "none",
|
|
6314
|
+
stroke: series.color || defaultColors3[seriesIndex % defaultColors3.length],
|
|
6315
|
+
strokeWidth,
|
|
6316
|
+
strokeLinecap: "round",
|
|
6317
|
+
strokeLinejoin: "round",
|
|
6318
|
+
className: lineClass
|
|
6319
|
+
}
|
|
6320
|
+
)
|
|
6321
|
+
] }, `area-${seriesIndex}`);
|
|
6322
|
+
}),
|
|
6323
|
+
showDots && data.map((series, seriesIndex) => {
|
|
6324
|
+
const baselineYValues = stacked ? data.slice(0, seriesIndex).reduce((acc, s) => {
|
|
6325
|
+
return acc.map((val, i) => val + (s.data[i]?.y || 0));
|
|
6326
|
+
}, Array(series.data.length).fill(0)) : void 0;
|
|
6327
|
+
return series.data.map((point, pointIndex) => {
|
|
6328
|
+
const x = scaleX(point.x, pointIndex);
|
|
6329
|
+
const y = baselineYValues ? scaleY(baselineYValues[pointIndex] + point.y) : scaleY(point.y);
|
|
6330
|
+
const isHovered = hoveredPoint?.seriesIndex === seriesIndex && hoveredPoint?.pointIndex === pointIndex;
|
|
6331
|
+
return /* @__PURE__ */ (0, import_jsx_runtime110.jsx)(
|
|
6332
|
+
"circle",
|
|
6333
|
+
{
|
|
6334
|
+
cx: x,
|
|
6335
|
+
cy: y,
|
|
6336
|
+
r: isHovered ? 6 : 4,
|
|
6337
|
+
fill: series.color || defaultColors3[seriesIndex % defaultColors3.length],
|
|
6338
|
+
stroke: "white",
|
|
6339
|
+
strokeWidth: "2",
|
|
6340
|
+
className: `cursor-pointer transition-all ${pointClass}`,
|
|
6341
|
+
onMouseEnter: (e) => {
|
|
6342
|
+
if (showTooltip && containerRef.current) {
|
|
6343
|
+
setHoveredPoint({ seriesIndex, pointIndex, x, y });
|
|
6344
|
+
const containerRect = containerRef.current.getBoundingClientRect();
|
|
6345
|
+
const mouseX = e.clientX - containerRect.left;
|
|
6346
|
+
const mouseY = e.clientY - containerRect.top;
|
|
6347
|
+
setTooltipPosition(getTooltipPosition(mouseX, mouseY));
|
|
6348
|
+
}
|
|
6349
|
+
},
|
|
6350
|
+
onMouseMove: (e) => {
|
|
6351
|
+
if (showTooltip && hoveredPoint?.seriesIndex === seriesIndex && hoveredPoint?.pointIndex === pointIndex && containerRef.current) {
|
|
6352
|
+
const containerRect = containerRef.current.getBoundingClientRect();
|
|
6353
|
+
const mouseX = e.clientX - containerRect.left;
|
|
6354
|
+
const mouseY = e.clientY - containerRect.top;
|
|
6355
|
+
setTooltipPosition(getTooltipPosition(mouseX, mouseY));
|
|
6356
|
+
}
|
|
6357
|
+
},
|
|
6358
|
+
onMouseLeave: () => {
|
|
6359
|
+
setHoveredPoint(null);
|
|
6360
|
+
setTooltipPosition(null);
|
|
6361
|
+
}
|
|
6362
|
+
},
|
|
6363
|
+
`point-${seriesIndex}-${pointIndex}`
|
|
6364
|
+
);
|
|
6365
|
+
});
|
|
6366
|
+
}),
|
|
6367
|
+
xAxisLabel && showXAxis && /* @__PURE__ */ (0, import_jsx_runtime110.jsx)(
|
|
6368
|
+
"text",
|
|
5874
6369
|
{
|
|
5875
|
-
|
|
5876
|
-
|
|
5877
|
-
|
|
6370
|
+
x: chartWidth / 2,
|
|
6371
|
+
y: chartHeight + 80,
|
|
6372
|
+
textAnchor: "middle",
|
|
6373
|
+
dominantBaseline: "hanging",
|
|
6374
|
+
fontSize: axisLabelFontSize,
|
|
6375
|
+
fontWeight: "600",
|
|
6376
|
+
className: `fill-gray-700 dark:fill-gray-300 ${axisLabelClass}`,
|
|
6377
|
+
children: xAxisLabel
|
|
5878
6378
|
}
|
|
5879
6379
|
),
|
|
5880
|
-
/* @__PURE__ */ (0, import_jsx_runtime110.jsx)(
|
|
5881
|
-
"
|
|
6380
|
+
yAxisLabel && showYAxis && /* @__PURE__ */ (0, import_jsx_runtime110.jsx)(
|
|
6381
|
+
"text",
|
|
5882
6382
|
{
|
|
5883
|
-
|
|
5884
|
-
|
|
5885
|
-
|
|
5886
|
-
|
|
5887
|
-
|
|
5888
|
-
|
|
6383
|
+
x: -chartHeight / 2,
|
|
6384
|
+
y: -100,
|
|
6385
|
+
textAnchor: "middle",
|
|
6386
|
+
dominantBaseline: "middle",
|
|
6387
|
+
fontSize: axisLabelFontSize,
|
|
6388
|
+
fontWeight: "600",
|
|
6389
|
+
transform: "rotate(-90)",
|
|
6390
|
+
className: `fill-gray-700 dark:fill-gray-300 ${axisLabelClass}`,
|
|
6391
|
+
children: yAxisLabel
|
|
5889
6392
|
}
|
|
5890
6393
|
)
|
|
5891
|
-
] }
|
|
5892
|
-
}
|
|
5893
|
-
|
|
5894
|
-
|
|
5895
|
-
|
|
5896
|
-
|
|
5897
|
-
return series.data.map((point, pointIndex) => {
|
|
5898
|
-
const x = scaleX(point.x, pointIndex);
|
|
5899
|
-
const y = baselineYValues ? scaleY(baselineYValues[pointIndex] + point.y) : scaleY(point.y);
|
|
5900
|
-
const isHovered = hoveredPoint?.seriesIndex === seriesIndex && hoveredPoint?.pointIndex === pointIndex;
|
|
5901
|
-
return /* @__PURE__ */ (0, import_jsx_runtime110.jsx)(
|
|
5902
|
-
"circle",
|
|
5903
|
-
{
|
|
5904
|
-
cx: x,
|
|
5905
|
-
cy: y,
|
|
5906
|
-
r: isHovered ? 6 : 4,
|
|
5907
|
-
fill: series.color || defaultColors3[seriesIndex % defaultColors3.length],
|
|
5908
|
-
stroke: "white",
|
|
5909
|
-
strokeWidth: "2",
|
|
5910
|
-
className: "cursor-pointer transition-all",
|
|
5911
|
-
onMouseEnter: () => showTooltip && setHoveredPoint({ seriesIndex, pointIndex, x, y }),
|
|
5912
|
-
onMouseLeave: () => setHoveredPoint(null)
|
|
5913
|
-
},
|
|
5914
|
-
`point-${seriesIndex}-${pointIndex}`
|
|
5915
|
-
);
|
|
5916
|
-
});
|
|
5917
|
-
}),
|
|
5918
|
-
xAxisLabel && showXAxis && /* @__PURE__ */ (0, import_jsx_runtime110.jsx)(
|
|
5919
|
-
"text",
|
|
6394
|
+
] })
|
|
6395
|
+
}
|
|
6396
|
+
),
|
|
6397
|
+
showLegend && /* @__PURE__ */ (0, import_jsx_runtime110.jsx)("div", { className: "flex flex-wrap gap-3 justify-center px-4", children: data.map((series, index) => /* @__PURE__ */ (0, import_jsx_runtime110.jsxs)("div", { className: "flex items-center gap-2 text-sm", children: [
|
|
6398
|
+
/* @__PURE__ */ (0, import_jsx_runtime110.jsx)(
|
|
6399
|
+
"div",
|
|
5920
6400
|
{
|
|
5921
|
-
|
|
5922
|
-
|
|
5923
|
-
|
|
5924
|
-
|
|
5925
|
-
children: xAxisLabel
|
|
6401
|
+
className: "w-3 h-3 rounded-sm flex-shrink-0",
|
|
6402
|
+
style: {
|
|
6403
|
+
backgroundColor: series.color || defaultColors3[index % defaultColors3.length]
|
|
6404
|
+
}
|
|
5926
6405
|
}
|
|
5927
6406
|
),
|
|
5928
|
-
|
|
5929
|
-
|
|
5930
|
-
|
|
5931
|
-
|
|
5932
|
-
|
|
5933
|
-
|
|
5934
|
-
|
|
5935
|
-
|
|
5936
|
-
|
|
5937
|
-
|
|
5938
|
-
|
|
5939
|
-
|
|
5940
|
-
|
|
5941
|
-
|
|
5942
|
-
|
|
5943
|
-
|
|
5944
|
-
|
|
5945
|
-
|
|
5946
|
-
|
|
5947
|
-
|
|
5948
|
-
|
|
6407
|
+
/* @__PURE__ */ (0, import_jsx_runtime110.jsx)("span", { className: "text-gray-700 dark:text-gray-300", children: series.name })
|
|
6408
|
+
] }, `legend-${index}`)) }),
|
|
6409
|
+
showTooltip && hoveredPoint && tooltipPosition && /* @__PURE__ */ (0, import_jsx_runtime110.jsxs)(
|
|
6410
|
+
"div",
|
|
6411
|
+
{
|
|
6412
|
+
className: "absolute bg-gray-900 dark:bg-gray-700 text-white px-3 py-2 rounded shadow-lg pointer-events-none z-50 text-sm whitespace-nowrap",
|
|
6413
|
+
style: {
|
|
6414
|
+
left: `${tooltipPosition.x}px`,
|
|
6415
|
+
top: `${tooltipPosition.y}px`,
|
|
6416
|
+
transform: "translateZ(0)"
|
|
6417
|
+
},
|
|
6418
|
+
children: [
|
|
6419
|
+
/* @__PURE__ */ (0, import_jsx_runtime110.jsx)("div", { className: "font-semibold", children: data[hoveredPoint.seriesIndex].name }),
|
|
6420
|
+
/* @__PURE__ */ (0, import_jsx_runtime110.jsxs)("div", { className: "text-xs opacity-90", children: [
|
|
6421
|
+
"x: ",
|
|
6422
|
+
data[hoveredPoint.seriesIndex].data[hoveredPoint.pointIndex].x
|
|
6423
|
+
] }),
|
|
6424
|
+
/* @__PURE__ */ (0, import_jsx_runtime110.jsxs)("div", { className: "text-xs opacity-90", children: [
|
|
6425
|
+
"y: ",
|
|
6426
|
+
data[hoveredPoint.seriesIndex].data[hoveredPoint.pointIndex].y
|
|
6427
|
+
] })
|
|
6428
|
+
]
|
|
5949
6429
|
}
|
|
5950
|
-
|
|
5951
|
-
|
|
5952
|
-
|
|
5953
|
-
|
|
5954
|
-
showTooltip && hoveredPoint && /* @__PURE__ */ (0, import_jsx_runtime110.jsxs)(
|
|
5955
|
-
"div",
|
|
5956
|
-
{
|
|
5957
|
-
className: "absolute bg-gray-900 dark:bg-gray-700 text-white px-3 py-2 rounded shadow-lg text-sm pointer-events-none z-50",
|
|
5958
|
-
style: {
|
|
5959
|
-
left: `${padding.left + hoveredPoint.x + 10}px`,
|
|
5960
|
-
top: `${padding.top + hoveredPoint.y - 30}px`
|
|
5961
|
-
},
|
|
5962
|
-
children: [
|
|
5963
|
-
/* @__PURE__ */ (0, import_jsx_runtime110.jsx)("div", { className: "font-semibold", children: data[hoveredPoint.seriesIndex].name }),
|
|
5964
|
-
/* @__PURE__ */ (0, import_jsx_runtime110.jsxs)("div", { children: [
|
|
5965
|
-
"x: ",
|
|
5966
|
-
data[hoveredPoint.seriesIndex].data[hoveredPoint.pointIndex].x
|
|
5967
|
-
] }),
|
|
5968
|
-
/* @__PURE__ */ (0, import_jsx_runtime110.jsxs)("div", { children: [
|
|
5969
|
-
"y: ",
|
|
5970
|
-
data[hoveredPoint.seriesIndex].data[hoveredPoint.pointIndex].y
|
|
5971
|
-
] })
|
|
5972
|
-
]
|
|
5973
|
-
}
|
|
5974
|
-
)
|
|
5975
|
-
] });
|
|
6430
|
+
)
|
|
6431
|
+
]
|
|
6432
|
+
}
|
|
6433
|
+
);
|
|
5976
6434
|
};
|
|
5977
6435
|
|
|
5978
6436
|
// src/components/PieChart.tsx
|
|
@@ -5998,22 +6456,37 @@ var defaultColors4 = [
|
|
|
5998
6456
|
];
|
|
5999
6457
|
var PieChart = ({
|
|
6000
6458
|
data,
|
|
6001
|
-
width
|
|
6002
|
-
height
|
|
6459
|
+
width: providedWidth,
|
|
6460
|
+
height: providedHeight,
|
|
6461
|
+
aspectRatio = 1,
|
|
6462
|
+
responsive = true,
|
|
6003
6463
|
showLegend = true,
|
|
6004
6464
|
showLabels = true,
|
|
6005
6465
|
showValues = false,
|
|
6006
6466
|
showPercentages = false,
|
|
6007
6467
|
donut = false,
|
|
6008
6468
|
donutWidth = 60,
|
|
6009
|
-
className = ""
|
|
6469
|
+
className = "",
|
|
6470
|
+
baseFontSize = 14
|
|
6010
6471
|
}) => {
|
|
6472
|
+
const viewBoxWidth = 600;
|
|
6473
|
+
const viewBoxHeight = 600;
|
|
6474
|
+
const containerRef = import_react29.default.useRef(null);
|
|
6475
|
+
const [tooltipPosition, setTooltipPosition] = import_react29.default.useState(null);
|
|
6011
6476
|
const [hoveredSlice, setHoveredSlice] = import_react29.default.useState(null);
|
|
6477
|
+
const chartId = import_react29.default.useId();
|
|
6478
|
+
const wrapperClass = `chart-svg-wrapper-${chartId.replace(/:/g, "-")}`;
|
|
6479
|
+
const gridLabelClass = `chart-grid-label-${chartId.replace(/:/g, "-")}`;
|
|
6480
|
+
const axisLabelClass = `chart-axis-label-${chartId.replace(/:/g, "-")}`;
|
|
6481
|
+
const sliceClass = `chart-slice-${chartId.replace(/:/g, "-")}`;
|
|
6012
6482
|
const total = data.reduce((sum, item) => sum + item.value, 0);
|
|
6013
|
-
const centerX =
|
|
6014
|
-
const centerY =
|
|
6015
|
-
const radius = Math.min(
|
|
6483
|
+
const centerX = viewBoxWidth / 2;
|
|
6484
|
+
const centerY = viewBoxHeight / 2;
|
|
6485
|
+
const radius = Math.min(viewBoxWidth, viewBoxHeight) / 2 - 40;
|
|
6016
6486
|
const innerRadius = donut ? radius - donutWidth : 0;
|
|
6487
|
+
const gridLabelFontSize = viewBoxWidth * 0.045;
|
|
6488
|
+
const axisLabelFontSize = viewBoxWidth * 0.05;
|
|
6489
|
+
const titleFontSize = viewBoxWidth * 0.055;
|
|
6017
6490
|
let currentAngle = -90;
|
|
6018
6491
|
const slices = data.map((item, index) => {
|
|
6019
6492
|
const percentage = item.value / total * 100;
|
|
@@ -6065,112 +6538,194 @@ var PieChart = ({
|
|
|
6065
6538
|
index
|
|
6066
6539
|
};
|
|
6067
6540
|
});
|
|
6068
|
-
|
|
6069
|
-
|
|
6070
|
-
|
|
6071
|
-
|
|
6072
|
-
|
|
6073
|
-
|
|
6074
|
-
|
|
6075
|
-
|
|
6076
|
-
|
|
6077
|
-
|
|
6078
|
-
|
|
6541
|
+
const getTooltipPosition = import_react29.default.useCallback((x, y) => {
|
|
6542
|
+
if (!containerRef.current) return { x, y };
|
|
6543
|
+
const rect = containerRef.current.getBoundingClientRect();
|
|
6544
|
+
const tooltipWidth = 120;
|
|
6545
|
+
const tooltipHeight = 80;
|
|
6546
|
+
let tooltipX = x + 10;
|
|
6547
|
+
let tooltipY = y - 30;
|
|
6548
|
+
if (tooltipX + tooltipWidth > rect.width) {
|
|
6549
|
+
tooltipX = x - tooltipWidth - 10;
|
|
6550
|
+
}
|
|
6551
|
+
if (tooltipY + tooltipHeight > rect.height) {
|
|
6552
|
+
tooltipY = y + 30;
|
|
6553
|
+
}
|
|
6554
|
+
return { x: tooltipX, y: tooltipY };
|
|
6555
|
+
}, []);
|
|
6556
|
+
const containerStyle = {
|
|
6557
|
+
"--font-size-base": `${baseFontSize}px`,
|
|
6558
|
+
"--font-size-lg": `calc(var(--font-size-base) * 1.125)`,
|
|
6559
|
+
"--font-size-sm": `calc(var(--font-size-base) * 0.875)`,
|
|
6560
|
+
"--font-size-xs": `calc(var(--font-size-base) * 0.75)`,
|
|
6561
|
+
"--font-size-xl": `calc(var(--font-size-base) * 1.5)`,
|
|
6562
|
+
"--font-size-2xl": `calc(var(--font-size-base) * 2)`
|
|
6563
|
+
};
|
|
6564
|
+
if (responsive) {
|
|
6565
|
+
containerStyle["--font-size-base"] = `clamp(${baseFontSize * 0.8}px, 4vw, ${baseFontSize}px)`;
|
|
6566
|
+
}
|
|
6567
|
+
return /* @__PURE__ */ (0, import_jsx_runtime111.jsxs)(
|
|
6568
|
+
"div",
|
|
6569
|
+
{
|
|
6570
|
+
ref: containerRef,
|
|
6571
|
+
className: `relative flex flex-col gap-4 ${responsive ? "w-full" : "inline-block"} ${className}`,
|
|
6572
|
+
style: containerStyle,
|
|
6573
|
+
children: [
|
|
6574
|
+
/* @__PURE__ */ (0, import_jsx_runtime111.jsx)("style", { children: `
|
|
6575
|
+
/* Mobile: Large fonts (default) */
|
|
6576
|
+
.${gridLabelClass} { font-size: ${gridLabelFontSize}px !important; }
|
|
6577
|
+
.${axisLabelClass} { font-size: ${titleFontSize}px !important; }
|
|
6578
|
+
|
|
6579
|
+
/* Tablet: Medium fonts */
|
|
6580
|
+
@media (min-width: 640px) {
|
|
6581
|
+
.${gridLabelClass} { font-size: ${gridLabelFontSize * 0.7}px !important; }
|
|
6582
|
+
.${axisLabelClass} { font-size: ${titleFontSize * 0.7}px !important; }
|
|
6583
|
+
}
|
|
6584
|
+
|
|
6585
|
+
/* Desktop: Small fonts */
|
|
6586
|
+
@media (min-width: 1024px) {
|
|
6587
|
+
.${gridLabelClass} { font-size: ${gridLabelFontSize * 0.4}px !important; }
|
|
6588
|
+
.${axisLabelClass} { font-size: ${titleFontSize * 0.4}px !important; }
|
|
6589
|
+
}
|
|
6590
|
+
` }),
|
|
6591
|
+
/* @__PURE__ */ (0, import_jsx_runtime111.jsxs)(
|
|
6592
|
+
"svg",
|
|
6593
|
+
{
|
|
6594
|
+
width: "100%",
|
|
6595
|
+
viewBox: `0 0 ${viewBoxWidth} ${viewBoxHeight}`,
|
|
6596
|
+
preserveAspectRatio: "xMidYMid meet",
|
|
6597
|
+
className: "bg-white dark:bg-gray-800 block w-full",
|
|
6598
|
+
style: { height: "auto", overflow: "visible" },
|
|
6599
|
+
children: [
|
|
6600
|
+
slices.map((slice) => {
|
|
6601
|
+
const isHovered = hoveredSlice === slice.index;
|
|
6602
|
+
return /* @__PURE__ */ (0, import_jsx_runtime111.jsxs)("g", { children: [
|
|
6603
|
+
/* @__PURE__ */ (0, import_jsx_runtime111.jsx)(
|
|
6604
|
+
"path",
|
|
6605
|
+
{
|
|
6606
|
+
d: slice.path,
|
|
6607
|
+
fill: slice.color,
|
|
6608
|
+
stroke: "white",
|
|
6609
|
+
strokeWidth: "2",
|
|
6610
|
+
className: "cursor-pointer transition-opacity",
|
|
6611
|
+
opacity: isHovered ? 0.8 : 1,
|
|
6612
|
+
onMouseEnter: (e) => {
|
|
6613
|
+
setHoveredSlice(slice.index);
|
|
6614
|
+
if (containerRef.current) {
|
|
6615
|
+
const rect = containerRef.current.getBoundingClientRect();
|
|
6616
|
+
const svgX = e.clientX - rect.left;
|
|
6617
|
+
const svgY = e.clientY - rect.top;
|
|
6618
|
+
setTooltipPosition(getTooltipPosition(svgX, svgY));
|
|
6619
|
+
}
|
|
6620
|
+
},
|
|
6621
|
+
onMouseLeave: () => {
|
|
6622
|
+
setHoveredSlice(null);
|
|
6623
|
+
setTooltipPosition(null);
|
|
6624
|
+
}
|
|
6625
|
+
}
|
|
6626
|
+
),
|
|
6627
|
+
showLabels && slice.percentage > 5 && /* @__PURE__ */ (0, import_jsx_runtime111.jsxs)(
|
|
6628
|
+
"text",
|
|
6629
|
+
{
|
|
6630
|
+
x: slice.labelX,
|
|
6631
|
+
y: slice.labelY,
|
|
6632
|
+
textAnchor: "middle",
|
|
6633
|
+
dominantBaseline: "middle",
|
|
6634
|
+
fontSize: gridLabelFontSize,
|
|
6635
|
+
fontWeight: "600",
|
|
6636
|
+
className: `fill-gray-700 dark:fill-gray-200 pointer-events-none ${gridLabelClass}`,
|
|
6637
|
+
children: [
|
|
6638
|
+
showPercentages && `${slice.percentage.toFixed(1)}%`,
|
|
6639
|
+
showPercentages && showValues && " ",
|
|
6640
|
+
showValues && `(${slice.value})`,
|
|
6641
|
+
!showPercentages && !showValues && slice.label
|
|
6642
|
+
]
|
|
6643
|
+
}
|
|
6644
|
+
)
|
|
6645
|
+
] }, slice.index);
|
|
6646
|
+
}),
|
|
6647
|
+
donut && /* @__PURE__ */ (0, import_jsx_runtime111.jsxs)("g", { children: [
|
|
6648
|
+
/* @__PURE__ */ (0, import_jsx_runtime111.jsx)(
|
|
6649
|
+
"text",
|
|
6650
|
+
{
|
|
6651
|
+
x: centerX,
|
|
6652
|
+
y: centerY - 10,
|
|
6653
|
+
textAnchor: "middle",
|
|
6654
|
+
dominantBaseline: "middle",
|
|
6655
|
+
fontSize: titleFontSize,
|
|
6656
|
+
fontWeight: "700",
|
|
6657
|
+
className: `fill-gray-900 dark:fill-gray-100 ${axisLabelClass}`,
|
|
6658
|
+
children: total
|
|
6659
|
+
}
|
|
6660
|
+
),
|
|
6661
|
+
/* @__PURE__ */ (0, import_jsx_runtime111.jsx)(
|
|
6662
|
+
"text",
|
|
6663
|
+
{
|
|
6664
|
+
x: centerX,
|
|
6665
|
+
y: centerY + 15,
|
|
6666
|
+
textAnchor: "middle",
|
|
6667
|
+
dominantBaseline: "middle",
|
|
6668
|
+
fontSize: axisLabelFontSize,
|
|
6669
|
+
className: `fill-gray-600 dark:fill-gray-400 ${gridLabelClass}`,
|
|
6670
|
+
children: "Total"
|
|
6671
|
+
}
|
|
6672
|
+
)
|
|
6673
|
+
] })
|
|
6674
|
+
]
|
|
6675
|
+
}
|
|
6676
|
+
),
|
|
6677
|
+
showLegend && /* @__PURE__ */ (0, import_jsx_runtime111.jsx)("div", { className: "flex flex-wrap gap-3 justify-center px-4", children: data.map((item, index) => /* @__PURE__ */ (0, import_jsx_runtime111.jsxs)(
|
|
6678
|
+
"div",
|
|
6679
|
+
{
|
|
6680
|
+
className: "flex items-center gap-2 text-sm cursor-pointer",
|
|
6681
|
+
onMouseEnter: () => setHoveredSlice(index),
|
|
6682
|
+
onMouseLeave: () => setHoveredSlice(null),
|
|
6683
|
+
children: [
|
|
6079
6684
|
/* @__PURE__ */ (0, import_jsx_runtime111.jsx)(
|
|
6080
|
-
"
|
|
6685
|
+
"div",
|
|
6081
6686
|
{
|
|
6082
|
-
|
|
6083
|
-
|
|
6084
|
-
|
|
6085
|
-
|
|
6086
|
-
className: "cursor-pointer transition-opacity",
|
|
6087
|
-
opacity: isHovered ? 0.8 : 1,
|
|
6088
|
-
onMouseEnter: () => setHoveredSlice(slice.index),
|
|
6089
|
-
onMouseLeave: () => setHoveredSlice(null)
|
|
6687
|
+
className: "w-3 h-3 rounded-sm flex-shrink-0",
|
|
6688
|
+
style: {
|
|
6689
|
+
backgroundColor: item.color || defaultColors4[index % defaultColors4.length]
|
|
6690
|
+
}
|
|
6090
6691
|
}
|
|
6091
6692
|
),
|
|
6092
|
-
|
|
6093
|
-
|
|
6094
|
-
|
|
6095
|
-
|
|
6096
|
-
|
|
6097
|
-
|
|
6098
|
-
|
|
6099
|
-
|
|
6100
|
-
|
|
6101
|
-
|
|
6102
|
-
|
|
6103
|
-
|
|
6104
|
-
|
|
6105
|
-
|
|
6106
|
-
|
|
6107
|
-
|
|
6108
|
-
|
|
6109
|
-
|
|
6110
|
-
|
|
6111
|
-
|
|
6112
|
-
"
|
|
6113
|
-
{
|
|
6114
|
-
|
|
6115
|
-
|
|
6116
|
-
|
|
6117
|
-
|
|
6118
|
-
|
|
6119
|
-
|
|
6120
|
-
|
|
6121
|
-
|
|
6122
|
-
|
|
6123
|
-
|
|
6124
|
-
|
|
6125
|
-
|
|
6126
|
-
|
|
6127
|
-
|
|
6128
|
-
children: "Total"
|
|
6129
|
-
}
|
|
6130
|
-
)
|
|
6131
|
-
] })
|
|
6132
|
-
]
|
|
6133
|
-
}
|
|
6134
|
-
),
|
|
6135
|
-
showLegend && /* @__PURE__ */ (0, import_jsx_runtime111.jsx)("div", { className: "flex flex-wrap gap-4 mt-4 justify-center", children: data.map((item, index) => /* @__PURE__ */ (0, import_jsx_runtime111.jsxs)(
|
|
6136
|
-
"div",
|
|
6137
|
-
{
|
|
6138
|
-
className: "flex items-center gap-2 cursor-pointer",
|
|
6139
|
-
onMouseEnter: () => setHoveredSlice(index),
|
|
6140
|
-
onMouseLeave: () => setHoveredSlice(null),
|
|
6141
|
-
children: [
|
|
6142
|
-
/* @__PURE__ */ (0, import_jsx_runtime111.jsx)(
|
|
6143
|
-
"div",
|
|
6144
|
-
{
|
|
6145
|
-
className: "w-4 h-4 rounded-sm",
|
|
6146
|
-
style: {
|
|
6147
|
-
backgroundColor: item.color || defaultColors4[index % defaultColors4.length]
|
|
6148
|
-
}
|
|
6149
|
-
}
|
|
6150
|
-
),
|
|
6151
|
-
/* @__PURE__ */ (0, import_jsx_runtime111.jsxs)("span", { className: "text-sm text-gray-700 dark:text-gray-300", children: [
|
|
6152
|
-
item.label,
|
|
6153
|
-
": ",
|
|
6154
|
-
item.value,
|
|
6155
|
-
showPercentages && ` (${(item.value / total * 100).toFixed(1)}%)`
|
|
6156
|
-
] })
|
|
6157
|
-
]
|
|
6158
|
-
},
|
|
6159
|
-
`legend-${index}`
|
|
6160
|
-
)) }),
|
|
6161
|
-
hoveredSlice !== null && /* @__PURE__ */ (0, import_jsx_runtime111.jsxs)("div", { className: "absolute top-2 right-2 bg-gray-900 dark:bg-gray-700 text-white px-3 py-2 rounded shadow-lg text-sm pointer-events-none z-50", children: [
|
|
6162
|
-
/* @__PURE__ */ (0, import_jsx_runtime111.jsx)("div", { className: "font-semibold", children: data[hoveredSlice].label }),
|
|
6163
|
-
/* @__PURE__ */ (0, import_jsx_runtime111.jsxs)("div", { children: [
|
|
6164
|
-
"Value: ",
|
|
6165
|
-
data[hoveredSlice].value
|
|
6166
|
-
] }),
|
|
6167
|
-
/* @__PURE__ */ (0, import_jsx_runtime111.jsxs)("div", { children: [
|
|
6168
|
-
"Percentage: ",
|
|
6169
|
-
(data[hoveredSlice].value / total * 100).toFixed(1),
|
|
6170
|
-
"%"
|
|
6171
|
-
] })
|
|
6172
|
-
] })
|
|
6173
|
-
] });
|
|
6693
|
+
/* @__PURE__ */ (0, import_jsx_runtime111.jsxs)("span", { className: "text-gray-700 dark:text-gray-300", children: [
|
|
6694
|
+
item.label,
|
|
6695
|
+
": ",
|
|
6696
|
+
item.value,
|
|
6697
|
+
showPercentages && ` (${(item.value / total * 100).toFixed(1)}%)`
|
|
6698
|
+
] })
|
|
6699
|
+
]
|
|
6700
|
+
},
|
|
6701
|
+
`legend-${index}`
|
|
6702
|
+
)) }),
|
|
6703
|
+
hoveredSlice !== null && tooltipPosition && /* @__PURE__ */ (0, import_jsx_runtime111.jsxs)(
|
|
6704
|
+
"div",
|
|
6705
|
+
{
|
|
6706
|
+
className: "absolute bg-gray-900 dark:bg-gray-700 text-white px-3 py-2 rounded shadow-lg pointer-events-none z-50 text-sm whitespace-nowrap",
|
|
6707
|
+
style: {
|
|
6708
|
+
left: `${tooltipPosition.x}px`,
|
|
6709
|
+
top: `${tooltipPosition.y}px`,
|
|
6710
|
+
transform: "translateZ(0)"
|
|
6711
|
+
},
|
|
6712
|
+
children: [
|
|
6713
|
+
/* @__PURE__ */ (0, import_jsx_runtime111.jsx)("div", { className: "font-semibold", children: data[hoveredSlice].label }),
|
|
6714
|
+
/* @__PURE__ */ (0, import_jsx_runtime111.jsxs)("div", { className: "text-xs opacity-90", children: [
|
|
6715
|
+
"Value: ",
|
|
6716
|
+
data[hoveredSlice].value
|
|
6717
|
+
] }),
|
|
6718
|
+
/* @__PURE__ */ (0, import_jsx_runtime111.jsxs)("div", { className: "text-xs opacity-90", children: [
|
|
6719
|
+
"Percentage: ",
|
|
6720
|
+
(data[hoveredSlice].value / total * 100).toFixed(1),
|
|
6721
|
+
"%"
|
|
6722
|
+
] })
|
|
6723
|
+
]
|
|
6724
|
+
}
|
|
6725
|
+
)
|
|
6726
|
+
]
|
|
6727
|
+
}
|
|
6728
|
+
);
|
|
6174
6729
|
};
|
|
6175
6730
|
|
|
6176
6731
|
// src/utils/theme-script.ts
|