@hexclave/dashboard-ui-components 1.0.0 → 1.0.2
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/alert.js +4 -4
- package/dist/components/alert.js.map +1 -1
- package/dist/components/analytics-chart/analytics-chart-pie.js +33 -11
- package/dist/components/analytics-chart/analytics-chart-pie.js.map +1 -1
- package/dist/components/analytics-chart/analytics-chart.js +3 -3
- package/dist/components/analytics-chart/analytics-chart.js.map +1 -1
- package/dist/components/analytics-chart/default-analytics-chart-tooltip.js +4 -4
- package/dist/components/analytics-chart/default-analytics-chart-tooltip.js.map +1 -1
- package/dist/components/analytics-chart/types.d.ts +1 -0
- package/dist/components/analytics-chart/types.d.ts.map +1 -1
- package/dist/components/analytics-chart/types.js.map +1 -1
- package/dist/components/badge.js +2 -2
- package/dist/components/badge.js.map +1 -1
- package/dist/components/button.d.ts +4 -4
- package/dist/components/button.js +11 -11
- package/dist/components/button.js.map +1 -1
- package/dist/components/card.js +7 -7
- package/dist/components/card.js.map +1 -1
- package/dist/components/chart-card.js +3 -3
- package/dist/components/chart-card.js.map +1 -1
- package/dist/components/chart-container.js +2 -2
- package/dist/components/chart-container.js.map +1 -1
- package/dist/components/chart-legend.js +3 -3
- package/dist/components/chart-legend.js.map +1 -1
- package/dist/components/chart-tooltip.js +7 -7
- package/dist/components/chart-tooltip.js.map +1 -1
- package/dist/components/data-grid/data-grid-toolbar.js +7 -7
- package/dist/components/data-grid/data-grid-toolbar.js.map +1 -1
- package/dist/components/data-grid/data-grid.js +16 -16
- package/dist/components/data-grid/data-grid.js.map +1 -1
- package/dist/components/data-grid/state.js +2 -2
- package/dist/components/data-grid/state.js.map +1 -1
- package/dist/components/dialog.js +22 -22
- package/dist/components/dialog.js.map +1 -1
- package/dist/components/empty-state.js +2 -2
- package/dist/components/empty-state.js.map +1 -1
- package/dist/components/input.js +8 -8
- package/dist/components/input.js.map +1 -1
- package/dist/components/metric-card.js +5 -5
- package/dist/components/metric-card.js.map +1 -1
- package/dist/components/pill-toggle.js +11 -10
- package/dist/components/pill-toggle.js.map +1 -1
- package/dist/components/progress-bar.js +4 -4
- package/dist/components/progress-bar.js.map +1 -1
- package/dist/components/separator.js +2 -2
- package/dist/components/separator.js.map +1 -1
- package/dist/components/skeleton.js +2 -2
- package/dist/components/skeleton.js.map +1 -1
- package/dist/components/table.js +14 -14
- package/dist/components/table.js.map +1 -1
- package/dist/components/tabs.js +10 -10
- package/dist/components/tabs.js.map +1 -1
- package/dist/dashboard-ui-components.global.js +1720 -1678
- package/dist/dashboard-ui-components.global.js.map +4 -4
- package/dist/esm/components/alert.js.map +1 -1
- package/dist/esm/components/analytics-chart/analytics-chart-pie.js +33 -11
- package/dist/esm/components/analytics-chart/analytics-chart-pie.js.map +1 -1
- package/dist/esm/components/analytics-chart/analytics-chart.js.map +1 -1
- package/dist/esm/components/analytics-chart/default-analytics-chart-tooltip.js.map +1 -1
- package/dist/esm/components/analytics-chart/types.d.ts +1 -0
- package/dist/esm/components/analytics-chart/types.d.ts.map +1 -1
- package/dist/esm/components/analytics-chart/types.js.map +1 -1
- package/dist/esm/components/badge.js.map +1 -1
- package/dist/esm/components/button.d.ts +4 -4
- package/dist/esm/components/button.js.map +1 -1
- package/dist/esm/components/card.js.map +1 -1
- package/dist/esm/components/chart-card.js.map +1 -1
- package/dist/esm/components/chart-container.js.map +1 -1
- package/dist/esm/components/chart-legend.js.map +1 -1
- package/dist/esm/components/chart-tooltip.js.map +1 -1
- package/dist/esm/components/data-grid/data-grid-toolbar.js.map +1 -1
- package/dist/esm/components/data-grid/data-grid.js.map +1 -1
- package/dist/esm/components/data-grid/state.js.map +1 -1
- package/dist/esm/components/dialog.js.map +1 -1
- package/dist/esm/components/empty-state.js.map +1 -1
- package/dist/esm/components/input.js.map +1 -1
- package/dist/esm/components/metric-card.js.map +1 -1
- package/dist/esm/components/pill-toggle.js +1 -0
- package/dist/esm/components/pill-toggle.js.map +1 -1
- package/dist/esm/components/progress-bar.js.map +1 -1
- package/dist/esm/components/separator.js.map +1 -1
- package/dist/esm/components/skeleton.js.map +1 -1
- package/dist/esm/components/table.js.map +1 -1
- package/dist/esm/components/tabs.js +1 -1
- package/dist/esm/components/tabs.js.map +1 -1
- package/package.json +4 -4
package/dist/components/alert.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
4
4
|
const require_chunk = require('../chunk-BE-pF4vm.js');
|
|
5
5
|
let _phosphor_icons_react = require("@phosphor-icons/react");
|
|
6
|
-
let
|
|
6
|
+
let _hexclave_ui = require("@hexclave/ui");
|
|
7
7
|
let react_jsx_runtime = require("react/jsx-runtime");
|
|
8
8
|
|
|
9
9
|
//#region src/components/alert.tsx
|
|
@@ -51,13 +51,13 @@ function DesignAlert({ variant = "default", title, description, glassmorphic = f
|
|
|
51
51
|
const Icon = getMapValueOrThrow(variantIconMap, variant, "variantIconMap");
|
|
52
52
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
53
53
|
role: "alert",
|
|
54
|
-
className: (0,
|
|
54
|
+
className: (0, _hexclave_ui.cn)("relative w-full rounded-2xl border p-4 text-sm", "flex gap-3 items-start", styles.container, glassmorphic && "backdrop-blur-xl", className),
|
|
55
55
|
...props,
|
|
56
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Icon, { className: (0,
|
|
56
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Icon, { className: (0, _hexclave_ui.cn)("h-4 w-4 mt-0.5 flex-shrink-0", styles.icon) }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
57
57
|
className: "min-w-0",
|
|
58
58
|
children: [
|
|
59
59
|
title && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h5", {
|
|
60
|
-
className: (0,
|
|
60
|
+
className: (0, _hexclave_ui.cn)("mb-1 font-medium leading-none tracking-tight", styles.title),
|
|
61
61
|
children: title
|
|
62
62
|
}),
|
|
63
63
|
description && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"alert.js","names":["Info","CheckCircle","XCircle","WarningCircle"],"sources":["../../src/components/alert.tsx"],"sourcesContent":["\"use client\";\n\nimport { CheckCircle, Info, WarningCircle, XCircle } from \"@phosphor-icons/react\";\nimport { cn } from \"@
|
|
1
|
+
{"version":3,"file":"alert.js","names":["Info","CheckCircle","XCircle","WarningCircle"],"sources":["../../src/components/alert.tsx"],"sourcesContent":["\"use client\";\n\nimport { CheckCircle, Info, WarningCircle, XCircle } from \"@phosphor-icons/react\";\nimport { cn } from \"@hexclave/ui\";\n\ntype DesignAlertVariant = \"default\" | \"success\" | \"error\" | \"warning\" | \"info\";\n\nconst variantIconMap = new Map<DesignAlertVariant, React.ElementType>([\n [\"default\", Info],\n [\"success\", CheckCircle],\n [\"error\", XCircle],\n [\"warning\", WarningCircle],\n [\"info\", Info],\n]);\n\ntype VariantStyles = {\n container: string,\n icon: string,\n title: string,\n};\n\nconst variantStyles = new Map<DesignAlertVariant, VariantStyles>([\n [\n \"default\",\n {\n container: \"bg-background border-border\",\n icon: \"text-foreground\",\n title: \"text-foreground\",\n },\n ],\n [\n \"success\",\n {\n container: \"bg-green-500/[0.06] border-green-500/30\",\n icon: \"text-green-500\",\n title: \"text-green-600 dark:text-green-400\",\n },\n ],\n [\n \"error\",\n {\n container: \"bg-red-500/[0.06] border-red-500/30\",\n icon: \"text-red-500\",\n title: \"text-red-600 dark:text-red-400\",\n },\n ],\n [\n \"warning\",\n {\n container: \"bg-amber-500/[0.08] border-amber-500/40\",\n icon: \"text-amber-600 dark:text-amber-400\",\n title: \"text-amber-700 dark:text-amber-300\",\n },\n ],\n [\n \"info\",\n {\n container: \"bg-blue-500/[0.06] border-blue-500/30\",\n icon: \"text-blue-500\",\n title: \"text-blue-600 dark:text-blue-400\",\n },\n ],\n]);\n\nfunction getMapValueOrThrow<TKey, TValue>(map: Map<TKey, TValue>, key: TKey, mapName: string) {\n const value = map.get(key);\n if (!value) {\n throw new Error(`Missing ${mapName} entry for key \"${String(key)}\"`);\n }\n return value;\n}\n\nexport type DesignAlertProps = React.HTMLAttributes<HTMLDivElement> & {\n variant?: DesignAlertVariant,\n title?: React.ReactNode,\n description?: React.ReactNode,\n glassmorphic?: boolean,\n};\n\nexport function DesignAlert({\n variant = \"default\",\n title,\n description,\n glassmorphic = false,\n className,\n children,\n ...props\n}: DesignAlertProps) {\n const styles = getMapValueOrThrow(variantStyles, variant, \"variantStyles\");\n const Icon = getMapValueOrThrow(variantIconMap, variant, \"variantIconMap\");\n\n return (\n <div\n role=\"alert\"\n className={cn(\n \"relative w-full rounded-2xl border p-4 text-sm\",\n \"flex gap-3 items-start\",\n styles.container,\n glassmorphic && \"backdrop-blur-xl\",\n className\n )}\n {...props}\n >\n <Icon className={cn(\"h-4 w-4 mt-0.5 flex-shrink-0\", styles.icon)} />\n <div className=\"min-w-0\">\n {title && (\n <h5 className={cn(\"mb-1 font-medium leading-none tracking-tight\", styles.title)}>\n {title}\n </h5>\n )}\n {description && (\n <div className=\"text-sm text-foreground/80 dark:text-muted-foreground [&_p]:leading-relaxed\">\n {description}\n </div>\n )}\n {children}\n </div>\n </div>\n );\n}\n"],"mappings":";;;;;;;;;AAOA,MAAM,iBAAiB,IAAI,IAA2C;CACpE,CAAC,WAAWA,2BAAK;CACjB,CAAC,WAAWC,kCAAY;CACxB,CAAC,SAASC,8BAAQ;CAClB,CAAC,WAAWC,oCAAc;CAC1B,CAAC,QAAQH,2BAAK;CACf,CAAC;AAQF,MAAM,gBAAgB,IAAI,IAAuC;CAC/D,CACE,WACA;EACE,WAAW;EACX,MAAM;EACN,OAAO;EACR,CACF;CACD,CACE,WACA;EACE,WAAW;EACX,MAAM;EACN,OAAO;EACR,CACF;CACD,CACE,SACA;EACE,WAAW;EACX,MAAM;EACN,OAAO;EACR,CACF;CACD,CACE,WACA;EACE,WAAW;EACX,MAAM;EACN,OAAO;EACR,CACF;CACD,CACE,QACA;EACE,WAAW;EACX,MAAM;EACN,OAAO;EACR,CACF;CACF,CAAC;AAEF,SAAS,mBAAiC,KAAwB,KAAW,SAAiB;CAC5F,MAAM,QAAQ,IAAI,IAAI,IAAI;AAC1B,KAAI,CAAC,MACH,OAAM,IAAI,MAAM,WAAW,QAAQ,kBAAkB,OAAO,IAAI,CAAC,GAAG;AAEtE,QAAO;;AAUT,SAAgB,YAAY,EAC1B,UAAU,WACV,OACA,aACA,eAAe,OACf,WACA,UACA,GAAG,SACgB;CACnB,MAAM,SAAS,mBAAmB,eAAe,SAAS,gBAAgB;CAC1E,MAAM,OAAO,mBAAmB,gBAAgB,SAAS,iBAAiB;AAE1E,QACE,4CAAC;EACC,MAAK;EACL,gCACE,kDACA,0BACA,OAAO,WACP,gBAAgB,oBAChB,UACD;EACD,GAAI;aAEJ,2CAAC,QAAK,gCAAc,gCAAgC,OAAO,KAAK,GAAI,EACpE,4CAAC;GAAI,WAAU;;IACZ,SACC,2CAAC;KAAG,gCAAc,gDAAgD,OAAO,MAAM;eAC5E;MACE;IAEN,eACC,2CAAC;KAAI,WAAU;eACZ;MACG;IAEP;;IACG;GACF"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
2
|
const require_chunk = require('../../chunk-BE-pF4vm.js');
|
|
3
3
|
let _phosphor_icons_react = require("@phosphor-icons/react");
|
|
4
|
-
let
|
|
4
|
+
let _hexclave_ui = require("@hexclave/ui");
|
|
5
5
|
let react_jsx_runtime = require("react/jsx-runtime");
|
|
6
6
|
let react = require("react");
|
|
7
7
|
let recharts = require("recharts");
|
|
@@ -18,6 +18,9 @@ function AnalyticsChartPie({ wrapperRef, primarySegmentSeries, compareSegmentSer
|
|
|
18
18
|
const compareInnerRadius = pie?.compareInnerRadius ?? 36;
|
|
19
19
|
const compareOuterRadius = pie?.compareOuterRadius ?? 52;
|
|
20
20
|
const containerClassName = pie?.className ?? DEFAULT_PIE_CLASSNAME;
|
|
21
|
+
const showDateRange = pie?.showDateRange ?? true;
|
|
22
|
+
const centerTextMaxWidth = Math.max(64, innerRadius * 2 - 28);
|
|
23
|
+
const segmentTransitionStyle = { transition: "opacity 180ms ease-out" };
|
|
21
24
|
const canonicalSeries = primarySegmentSeries.length > 0 ? primarySegmentSeries : compareSegmentSeries;
|
|
22
25
|
const usePrimaryForCanonical = primarySegmentSeries.length > 0;
|
|
23
26
|
const compareValueByKey = (0, react.useMemo)(() => {
|
|
@@ -80,7 +83,22 @@ function AnalyticsChartPie({ wrapperRef, primarySegmentSeries, compareSegmentSer
|
|
|
80
83
|
});
|
|
81
84
|
return config;
|
|
82
85
|
}, [canonicalSeries, segmentColors]);
|
|
83
|
-
const activeRow = hoverKey ? legendRows.find((r) => r.key === hoverKey) ?? null : null;
|
|
86
|
+
const activeRow = hoverKey != null ? legendRows.find((r) => r.key === hoverKey) ?? null : null;
|
|
87
|
+
const [centerDisplayKey, setCenterDisplayKey] = (0, react.useState)(null);
|
|
88
|
+
const [centerDisplayVisible, setCenterDisplayVisible] = (0, react.useState)(true);
|
|
89
|
+
const centerDisplayRow = centerDisplayKey != null ? legendRows.find((r) => r.key === centerDisplayKey) ?? null : null;
|
|
90
|
+
(0, react.useEffect)(() => {
|
|
91
|
+
const nextKey = activeRow?.key ?? null;
|
|
92
|
+
if (nextKey === centerDisplayKey) return;
|
|
93
|
+
setCenterDisplayVisible(false);
|
|
94
|
+
const timeoutId = window.setTimeout(() => {
|
|
95
|
+
setCenterDisplayKey(nextKey);
|
|
96
|
+
setCenterDisplayVisible(true);
|
|
97
|
+
}, 120);
|
|
98
|
+
return () => {
|
|
99
|
+
window.clearTimeout(timeoutId);
|
|
100
|
+
};
|
|
101
|
+
}, [activeRow?.key, centerDisplayKey]);
|
|
84
102
|
const activeDelta = activeRow ? (0, __format_js.formatDelta)(activeRow.value, activeRow.prevValue) : (0, __format_js.formatDelta)(aggregatedPrimaryTotal, aggregatedCompareTotal);
|
|
85
103
|
const windowDays = visibleEnd - visibleStart + 1;
|
|
86
104
|
const startLabel = fmtValue(fullData[visibleStart].ts, xFormatKind);
|
|
@@ -97,7 +115,7 @@ function AnalyticsChartPie({ wrapperRef, primarySegmentSeries, compareSegmentSer
|
|
|
97
115
|
value: r.prevValue,
|
|
98
116
|
fill: r.fillCompare
|
|
99
117
|
}));
|
|
100
|
-
const activeIdx = hoverKey ? legendRows.findIndex((r) => r.key === hoverKey) : -1;
|
|
118
|
+
const activeIdx = hoverKey != null ? legendRows.findIndex((r) => r.key === hoverKey) : -1;
|
|
101
119
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
102
120
|
ref: wrapperRef,
|
|
103
121
|
className: "relative flex h-full min-h-0 w-full min-w-0 flex-1 flex-col select-none",
|
|
@@ -147,6 +165,7 @@ function AnalyticsChartPie({ wrapperRef, primarySegmentSeries, compareSegmentSer
|
|
|
147
165
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(recharts.Cell, {
|
|
148
166
|
fill: `var(--color-${d.name})`,
|
|
149
167
|
opacity: inactive ? .22 : 1,
|
|
168
|
+
style: segmentTransitionStyle,
|
|
150
169
|
onMouseEnter: () => setHoverKey(d.hoverKey),
|
|
151
170
|
onMouseLeave: () => setHoverKey(null)
|
|
152
171
|
}, `outer-${d.name}`);
|
|
@@ -169,6 +188,7 @@ function AnalyticsChartPie({ wrapperRef, primarySegmentSeries, compareSegmentSer
|
|
|
169
188
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(recharts.Cell, {
|
|
170
189
|
fill: `var(--color-compare-${d.name})`,
|
|
171
190
|
opacity: inactive ? .22 : .95,
|
|
191
|
+
style: segmentTransitionStyle,
|
|
172
192
|
onMouseEnter: () => setHoverKey(d.hoverKey),
|
|
173
193
|
onMouseLeave: () => setHoverKey(null)
|
|
174
194
|
}, `inner-${d.name}`);
|
|
@@ -176,18 +196,20 @@ function AnalyticsChartPie({ wrapperRef, primarySegmentSeries, compareSegmentSer
|
|
|
176
196
|
})]
|
|
177
197
|
})
|
|
178
198
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
179
|
-
className: "pointer-events-none absolute inset-0 flex flex-col items-center justify-center text-center",
|
|
199
|
+
className: (0, _hexclave_ui.cn)("pointer-events-none absolute inset-0 flex flex-col items-center justify-center text-center transition-opacity duration-200 ease-out", centerDisplayVisible ? "opacity-100" : "opacity-35"),
|
|
180
200
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
181
|
-
className: "block
|
|
182
|
-
|
|
201
|
+
className: "block truncate font-mono text-[9px] uppercase tracking-wider text-muted-foreground",
|
|
202
|
+
style: { maxWidth: centerTextMaxWidth },
|
|
203
|
+
children: centerDisplayRow ? centerDisplayRow.label : strings.pieTotalCenter
|
|
183
204
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
184
|
-
className: "mt-0.5 block
|
|
185
|
-
|
|
205
|
+
className: "mt-0.5 block truncate font-mono text-xl font-semibold leading-none tabular-nums text-foreground",
|
|
206
|
+
style: { maxWidth: centerTextMaxWidth },
|
|
207
|
+
children: fmtValue(centerDisplayRow ? centerDisplayRow.value : canonicalTotal, yFormatKind)
|
|
186
208
|
})]
|
|
187
209
|
})]
|
|
188
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
210
|
+
}), (showDateRange || showCompare) && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
189
211
|
className: "flex items-center gap-2 text-center",
|
|
190
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
212
|
+
children: [showDateRange && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
191
213
|
className: "font-mono text-[10px] uppercase tracking-wider text-muted-foreground/80",
|
|
192
214
|
children: [
|
|
193
215
|
startLabel,
|
|
@@ -212,7 +234,7 @@ function AnalyticsChartPie({ wrapperRef, primarySegmentSeries, compareSegmentSer
|
|
|
212
234
|
onMouseLeave: () => setHoverKey(null),
|
|
213
235
|
onFocus: () => setHoverKey(r.key),
|
|
214
236
|
onBlur: () => setHoverKey(null),
|
|
215
|
-
className: (0,
|
|
237
|
+
className: (0, _hexclave_ui.cn)("flex w-full items-center gap-2.5 rounded-md px-2 py-1.5 text-left transition-[background-color,opacity] duration-200 ease-out hover:bg-foreground/[0.04] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-foreground/20", isActive && "bg-foreground/[0.05]", dimmed && "opacity-50"),
|
|
216
238
|
children: [
|
|
217
239
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
218
240
|
className: "size-2.5 shrink-0 rounded-[3px] bg-[var(--c-l)] dark:bg-[var(--c-d)]",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"analytics-chart-pie.js","names":["MagnifyingGlassMinusIcon","DesignChartContainer","PieChart","Pie","Cell","TrendPill"],"sources":["../../../src/components/analytics-chart/analytics-chart-pie.tsx"],"sourcesContent":["import { MagnifyingGlassMinusIcon } from \"@phosphor-icons/react\";\nimport { cn } from \"@stackframe/stack-ui\";\nimport { type DesignChartConfig, DesignChartContainer } from \"../chart-container\";\nimport { type CSSProperties, type Ref, useMemo } from \"react\";\nimport { Cell, Pie, PieChart } from \"recharts\";\nimport { TrendPill } from \"./default-analytics-chart-tooltip\";\nimport { formatDelta } from \"./format\";\nimport type { AnalyticsChartStrings } from \"./strings\";\nimport {\n cssIdent,\n type AnalyticsChartPieProps,\n type AnalyticsChartSeries,\n type FormatKind,\n type Point,\n} from \"./types\";\n\ntype SegmentColors = {\n primary: { light: string[], dark: string[] },\n compare: { light: string[], dark: string[] },\n};\n\ntype AnalyticsChartPieBodyProps = {\n wrapperRef: Ref<HTMLDivElement>,\n primarySegmentSeries: readonly AnalyticsChartSeries[],\n compareSegmentSeries: readonly AnalyticsChartSeries[],\n aggregatedPrimarySegments: number[],\n aggregatedCompareSegments: number[],\n aggregatedPrimaryTotal: number,\n aggregatedCompareTotal: number,\n segmentColors: SegmentColors,\n showPrimary: boolean,\n showCompare: boolean,\n xFormatKind: FormatKind,\n yFormatKind: FormatKind,\n hoverKey: string | null,\n setHoverKey: (k: string | null) => void,\n zoomRange: [number, number] | null,\n onResetZoom: () => void,\n visibleStart: number,\n visibleEnd: number,\n fullData: Point[],\n strings: AnalyticsChartStrings,\n fmtValue: (value: number, kind: FormatKind) => string,\n pie: AnalyticsChartPieProps | undefined,\n};\n\nconst DEFAULT_PIE_CLASSNAME = \"aspect-square h-[220px] w-[220px] sm:h-[240px] sm:w-[240px]\";\n\nexport function AnalyticsChartPie({\n wrapperRef,\n primarySegmentSeries,\n compareSegmentSeries,\n aggregatedPrimarySegments,\n aggregatedCompareSegments,\n aggregatedPrimaryTotal,\n aggregatedCompareTotal,\n segmentColors,\n showPrimary,\n showCompare,\n xFormatKind,\n yFormatKind,\n hoverKey,\n setHoverKey,\n zoomRange,\n onResetZoom,\n visibleStart,\n visibleEnd,\n fullData,\n strings,\n fmtValue,\n pie,\n}: AnalyticsChartPieBodyProps) {\n const innerRadius = pie?.innerRadius ?? 60;\n const outerRadius = pie?.outerRadius ?? 84;\n const compareInnerRadius = pie?.compareInnerRadius ?? 36;\n const compareOuterRadius = pie?.compareOuterRadius ?? 52;\n const containerClassName = pie?.className ?? DEFAULT_PIE_CLASSNAME;\n\n const canonicalSeries = primarySegmentSeries.length > 0\n ? primarySegmentSeries\n : compareSegmentSeries;\n const usePrimaryForCanonical = primarySegmentSeries.length > 0;\n\n const compareValueByKey = useMemo(() => {\n const m = new Map<string, number>();\n compareSegmentSeries.forEach((s, sIdx) => {\n m.set(s.key, aggregatedCompareSegments[sIdx] ?? 0);\n });\n return m;\n }, [compareSegmentSeries, aggregatedCompareSegments]);\n const primaryValueByKey = useMemo(() => {\n const m = new Map<string, number>();\n primarySegmentSeries.forEach((s, sIdx) => {\n m.set(s.key, aggregatedPrimarySegments[sIdx] ?? 0);\n });\n return m;\n }, [primarySegmentSeries, aggregatedPrimarySegments]);\n\n const canonicalTotal = usePrimaryForCanonical\n ? aggregatedPrimaryTotal\n : aggregatedCompareTotal;\n\n const legendRows = useMemo(\n () =>\n canonicalSeries\n .map((s, sIdx) => {\n const value = usePrimaryForCanonical\n ? (aggregatedPrimarySegments[sIdx] ?? 0)\n : (aggregatedCompareSegments[sIdx] ?? 0);\n const prevValue = usePrimaryForCanonical\n ? (compareValueByKey.get(s.key) ?? 0)\n : (primaryValueByKey.get(s.key) ?? 0);\n return {\n key: s.key,\n label: s.label,\n sIdx,\n value,\n prevValue,\n pct: canonicalTotal > 0 ? value / canonicalTotal : 0,\n fill: segmentColors.primary.light[sIdx],\n fillDark: segmentColors.primary.dark[sIdx],\n fillCompare: segmentColors.compare.light[sIdx],\n fillCompareDark: segmentColors.compare.dark[sIdx],\n };\n })\n .sort((a, b) => b.value - a.value),\n [\n canonicalSeries,\n usePrimaryForCanonical,\n aggregatedPrimarySegments,\n aggregatedCompareSegments,\n compareValueByKey,\n primaryValueByKey,\n canonicalTotal,\n segmentColors,\n ],\n );\n\n const chartConfig = useMemo<DesignChartConfig>(() => {\n const config: DesignChartConfig = {};\n canonicalSeries.forEach((s, sIdx) => {\n config[cssIdent(s.key)] = {\n label: s.label,\n theme: {\n light: segmentColors.primary.light[sIdx],\n dark: segmentColors.primary.dark[sIdx],\n },\n };\n config[`compare-${cssIdent(s.key)}`] = {\n label: s.label,\n theme: {\n light: segmentColors.compare.light[sIdx],\n dark: segmentColors.compare.dark[sIdx],\n },\n };\n });\n return config;\n }, [canonicalSeries, segmentColors]);\n\n const activeRow = hoverKey\n ? legendRows.find((r) => r.key === hoverKey) ?? null\n : null;\n const activeDelta = activeRow\n ? formatDelta(activeRow.value, activeRow.prevValue)\n : formatDelta(aggregatedPrimaryTotal, aggregatedCompareTotal);\n\n const windowDays = visibleEnd - visibleStart + 1;\n const startLabel = fmtValue(fullData[visibleStart]!.ts, xFormatKind);\n const endLabel = fmtValue(fullData[visibleEnd]!.ts, xFormatKind);\n\n const outerData = legendRows.map((r) => ({ name: cssIdent(r.key), hoverKey: r.key, value: r.value, fill: r.fill }));\n const innerData = legendRows.map((r) => ({ name: cssIdent(r.key), hoverKey: r.key, value: r.prevValue, fill: r.fillCompare }));\n const activeIdx = hoverKey ? legendRows.findIndex((r) => r.key === hoverKey) : -1;\n\n return (\n <div\n ref={wrapperRef}\n className=\"relative flex h-full min-h-0 w-full min-w-0 flex-1 flex-col select-none\"\n onClick={(e) => e.stopPropagation()}\n >\n {zoomRange && (\n <div className=\"absolute right-2 top-2 z-20\">\n <button\n type=\"button\"\n onClick={onResetZoom}\n className=\"inline-flex items-center gap-1.5 rounded-full bg-blue-500/10 px-2.5 py-1 font-mono text-[10px] font-semibold uppercase tracking-wider text-blue-600 ring-1 ring-blue-500/30 transition-colors duration-150 hover:bg-blue-500/15 hover:transition-none focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-500/50 dark:text-blue-300 dark:ring-blue-400/30\"\n >\n <MagnifyingGlassMinusIcon weight=\"bold\" className=\"size-3\" aria-hidden=\"true\" />\n <span>{strings.resetZoom}</span>\n </button>\n </div>\n )}\n\n <div className=\"flex h-full min-h-0 w-full flex-1 flex-wrap content-center items-center justify-center gap-x-10 gap-y-6\">\n <div className=\"flex shrink-0 flex-col items-center gap-3\">\n <div className=\"relative\">\n <DesignChartContainer config={chartConfig} className={containerClassName}>\n <PieChart\n role=\"img\"\n aria-label={strings.pieAriaLabel({\n segmentCount: canonicalSeries.length,\n windowDays,\n })}\n >\n {showPrimary && (\n <Pie\n data={outerData}\n dataKey=\"value\"\n nameKey=\"name\"\n cx=\"50%\"\n cy=\"50%\"\n innerRadius={innerRadius}\n outerRadius={outerRadius}\n paddingAngle={1}\n startAngle={90}\n endAngle={-270}\n isAnimationActive={false}\n stroke=\"none\"\n >\n {outerData.map((d, i) => {\n const inactive = activeIdx >= 0 && activeIdx !== i;\n return (\n <Cell\n key={`outer-${d.name}`}\n fill={`var(--color-${d.name})`}\n opacity={inactive ? 0.22 : 1}\n onMouseEnter={() => setHoverKey(d.hoverKey)}\n onMouseLeave={() => setHoverKey(null)}\n />\n );\n })}\n </Pie>\n )}\n {showCompare && (\n <Pie\n data={innerData}\n dataKey=\"value\"\n nameKey=\"name\"\n cx=\"50%\"\n cy=\"50%\"\n innerRadius={compareInnerRadius}\n outerRadius={compareOuterRadius}\n paddingAngle={1}\n startAngle={90}\n endAngle={-270}\n isAnimationActive={false}\n stroke=\"none\"\n >\n {innerData.map((d, i) => {\n const inactive = activeIdx >= 0 && activeIdx !== i;\n return (\n <Cell\n key={`inner-${d.name}`}\n fill={`var(--color-compare-${d.name})`}\n opacity={inactive ? 0.22 : 0.95}\n onMouseEnter={() => setHoverKey(d.hoverKey)}\n onMouseLeave={() => setHoverKey(null)}\n />\n );\n })}\n </Pie>\n )}\n </PieChart>\n </DesignChartContainer>\n\n <div className=\"pointer-events-none absolute inset-0 flex flex-col items-center justify-center text-center\">\n <span className=\"block max-w-[68px] truncate font-mono text-[9px] uppercase tracking-wider text-muted-foreground\">\n {activeRow ? activeRow.label : strings.pieTotalCenter}\n </span>\n <span className=\"mt-0.5 block max-w-[72px] truncate font-mono text-xl font-semibold leading-none tabular-nums text-foreground\">\n {fmtValue(activeRow ? activeRow.value : canonicalTotal, yFormatKind)}\n </span>\n </div>\n </div>\n\n <div className=\"flex items-center gap-2 text-center\">\n <span className=\"font-mono text-[10px] uppercase tracking-wider text-muted-foreground/80\">\n {startLabel} – {endLabel}\n </span>\n {showCompare && (\n <TrendPill delta={activeDelta} size=\"sm\" label={strings.pieVsPrev} />\n )}\n </div>\n </div>\n\n <ul className=\"flex min-w-[200px] max-w-[300px] flex-col gap-1\">\n {legendRows.map((r) => {\n const isActive = hoverKey === r.key;\n const dimmed = hoverKey != null && !isActive;\n const rowDelta = formatDelta(r.value, r.prevValue);\n return (\n <li key={r.key}>\n <button\n type=\"button\"\n onMouseEnter={() => setHoverKey(r.key)}\n onMouseLeave={() => setHoverKey(null)}\n onFocus={() => setHoverKey(r.key)}\n onBlur={() => setHoverKey(null)}\n className={cn(\n \"flex w-full items-center gap-2.5 rounded-md px-2 py-1.5 text-left transition-[background-color,opacity] duration-150 hover:bg-foreground/[0.04] hover:transition-none focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-foreground/20\",\n isActive && \"bg-foreground/[0.05]\",\n dimmed && \"opacity-50\",\n )}\n >\n <span\n className=\"size-2.5 shrink-0 rounded-[3px] bg-[var(--c-l)] dark:bg-[var(--c-d)]\"\n style={{ \"--c-l\": r.fill, \"--c-d\": r.fillDark } as CSSProperties}\n />\n <span className=\"min-w-0 flex-1 truncate text-[12px] font-medium text-foreground\">\n {r.label}\n </span>\n <span className=\"shrink-0 font-mono text-[11px] tabular-nums text-muted-foreground\">\n {(r.pct * 100).toFixed(1)}%\n </span>\n <span className=\"shrink-0 min-w-[48px] text-right font-mono text-[11px] font-semibold tabular-nums text-foreground\">\n {fmtValue(r.value, yFormatKind)}\n </span>\n {showCompare && (\n <span className=\"shrink-0\">\n <TrendPill delta={rowDelta} size=\"sm\" />\n </span>\n )}\n </button>\n </li>\n );\n })}\n </ul>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"analytics-chart-pie.js","names":["MagnifyingGlassMinusIcon","DesignChartContainer","PieChart","Pie","Cell","TrendPill"],"sources":["../../../src/components/analytics-chart/analytics-chart-pie.tsx"],"sourcesContent":["import { MagnifyingGlassMinusIcon } from \"@phosphor-icons/react\";\nimport { cn } from \"@hexclave/ui\";\nimport { type DesignChartConfig, DesignChartContainer } from \"../chart-container\";\nimport { type CSSProperties, type Ref, useEffect, useMemo, useState } from \"react\";\nimport { Cell, Pie, PieChart } from \"recharts\";\nimport { TrendPill } from \"./default-analytics-chart-tooltip\";\nimport { formatDelta } from \"./format\";\nimport type { AnalyticsChartStrings } from \"./strings\";\nimport {\n cssIdent,\n type AnalyticsChartPieProps,\n type AnalyticsChartSeries,\n type FormatKind,\n type Point,\n} from \"./types\";\n\ntype SegmentColors = {\n primary: { light: string[], dark: string[] },\n compare: { light: string[], dark: string[] },\n};\n\ntype AnalyticsChartPieBodyProps = {\n wrapperRef: Ref<HTMLDivElement>,\n primarySegmentSeries: readonly AnalyticsChartSeries[],\n compareSegmentSeries: readonly AnalyticsChartSeries[],\n aggregatedPrimarySegments: number[],\n aggregatedCompareSegments: number[],\n aggregatedPrimaryTotal: number,\n aggregatedCompareTotal: number,\n segmentColors: SegmentColors,\n showPrimary: boolean,\n showCompare: boolean,\n xFormatKind: FormatKind,\n yFormatKind: FormatKind,\n hoverKey: string | null,\n setHoverKey: (k: string | null) => void,\n zoomRange: [number, number] | null,\n onResetZoom: () => void,\n visibleStart: number,\n visibleEnd: number,\n fullData: Point[],\n strings: AnalyticsChartStrings,\n fmtValue: (value: number, kind: FormatKind) => string,\n pie: AnalyticsChartPieProps | undefined,\n};\n\nconst DEFAULT_PIE_CLASSNAME = \"aspect-square h-[220px] w-[220px] sm:h-[240px] sm:w-[240px]\";\n\nexport function AnalyticsChartPie({\n wrapperRef,\n primarySegmentSeries,\n compareSegmentSeries,\n aggregatedPrimarySegments,\n aggregatedCompareSegments,\n aggregatedPrimaryTotal,\n aggregatedCompareTotal,\n segmentColors,\n showPrimary,\n showCompare,\n xFormatKind,\n yFormatKind,\n hoverKey,\n setHoverKey,\n zoomRange,\n onResetZoom,\n visibleStart,\n visibleEnd,\n fullData,\n strings,\n fmtValue,\n pie,\n}: AnalyticsChartPieBodyProps) {\n const innerRadius = pie?.innerRadius ?? 60;\n const outerRadius = pie?.outerRadius ?? 84;\n const compareInnerRadius = pie?.compareInnerRadius ?? 36;\n const compareOuterRadius = pie?.compareOuterRadius ?? 52;\n const containerClassName = pie?.className ?? DEFAULT_PIE_CLASSNAME;\n const showDateRange = pie?.showDateRange ?? true;\n const centerTextMaxWidth = Math.max(64, innerRadius * 2 - 28);\n const segmentTransitionStyle: CSSProperties = {\n transition: \"opacity 180ms ease-out\",\n };\n\n const canonicalSeries = primarySegmentSeries.length > 0\n ? primarySegmentSeries\n : compareSegmentSeries;\n const usePrimaryForCanonical = primarySegmentSeries.length > 0;\n\n const compareValueByKey = useMemo(() => {\n const m = new Map<string, number>();\n compareSegmentSeries.forEach((s, sIdx) => {\n m.set(s.key, aggregatedCompareSegments[sIdx] ?? 0);\n });\n return m;\n }, [compareSegmentSeries, aggregatedCompareSegments]);\n const primaryValueByKey = useMemo(() => {\n const m = new Map<string, number>();\n primarySegmentSeries.forEach((s, sIdx) => {\n m.set(s.key, aggregatedPrimarySegments[sIdx] ?? 0);\n });\n return m;\n }, [primarySegmentSeries, aggregatedPrimarySegments]);\n\n const canonicalTotal = usePrimaryForCanonical\n ? aggregatedPrimaryTotal\n : aggregatedCompareTotal;\n\n const legendRows = useMemo(\n () =>\n canonicalSeries\n .map((s, sIdx) => {\n const value = usePrimaryForCanonical\n ? (aggregatedPrimarySegments[sIdx] ?? 0)\n : (aggregatedCompareSegments[sIdx] ?? 0);\n const prevValue = usePrimaryForCanonical\n ? (compareValueByKey.get(s.key) ?? 0)\n : (primaryValueByKey.get(s.key) ?? 0);\n return {\n key: s.key,\n label: s.label,\n sIdx,\n value,\n prevValue,\n pct: canonicalTotal > 0 ? value / canonicalTotal : 0,\n fill: segmentColors.primary.light[sIdx],\n fillDark: segmentColors.primary.dark[sIdx],\n fillCompare: segmentColors.compare.light[sIdx],\n fillCompareDark: segmentColors.compare.dark[sIdx],\n };\n })\n .sort((a, b) => b.value - a.value),\n [\n canonicalSeries,\n usePrimaryForCanonical,\n aggregatedPrimarySegments,\n aggregatedCompareSegments,\n compareValueByKey,\n primaryValueByKey,\n canonicalTotal,\n segmentColors,\n ],\n );\n\n const chartConfig = useMemo<DesignChartConfig>(() => {\n const config: DesignChartConfig = {};\n canonicalSeries.forEach((s, sIdx) => {\n config[cssIdent(s.key)] = {\n label: s.label,\n theme: {\n light: segmentColors.primary.light[sIdx],\n dark: segmentColors.primary.dark[sIdx],\n },\n };\n config[`compare-${cssIdent(s.key)}`] = {\n label: s.label,\n theme: {\n light: segmentColors.compare.light[sIdx],\n dark: segmentColors.compare.dark[sIdx],\n },\n };\n });\n return config;\n }, [canonicalSeries, segmentColors]);\n\n const activeRow = hoverKey != null\n ? legendRows.find((r) => r.key === hoverKey) ?? null\n : null;\n const [centerDisplayKey, setCenterDisplayKey] = useState<string | null>(null);\n const [centerDisplayVisible, setCenterDisplayVisible] = useState(true);\n const centerDisplayRow = centerDisplayKey != null\n ? legendRows.find((r) => r.key === centerDisplayKey) ?? null\n : null;\n useEffect(() => {\n const nextKey = activeRow?.key ?? null;\n if (nextKey === centerDisplayKey) {\n return;\n }\n\n setCenterDisplayVisible(false);\n const timeoutId = window.setTimeout(() => {\n setCenterDisplayKey(nextKey);\n setCenterDisplayVisible(true);\n }, 120);\n\n return () => {\n window.clearTimeout(timeoutId);\n };\n }, [activeRow?.key, centerDisplayKey]);\n\n const activeDelta = activeRow\n ? formatDelta(activeRow.value, activeRow.prevValue)\n : formatDelta(aggregatedPrimaryTotal, aggregatedCompareTotal);\n\n const windowDays = visibleEnd - visibleStart + 1;\n const startLabel = fmtValue(fullData[visibleStart]!.ts, xFormatKind);\n const endLabel = fmtValue(fullData[visibleEnd]!.ts, xFormatKind);\n\n const outerData = legendRows.map((r) => ({ name: cssIdent(r.key), hoverKey: r.key, value: r.value, fill: r.fill }));\n const innerData = legendRows.map((r) => ({ name: cssIdent(r.key), hoverKey: r.key, value: r.prevValue, fill: r.fillCompare }));\n const activeIdx = hoverKey != null ? legendRows.findIndex((r) => r.key === hoverKey) : -1;\n\n return (\n <div\n ref={wrapperRef}\n className=\"relative flex h-full min-h-0 w-full min-w-0 flex-1 flex-col select-none\"\n onClick={(e) => e.stopPropagation()}\n >\n {zoomRange && (\n <div className=\"absolute right-2 top-2 z-20\">\n <button\n type=\"button\"\n onClick={onResetZoom}\n className=\"inline-flex items-center gap-1.5 rounded-full bg-blue-500/10 px-2.5 py-1 font-mono text-[10px] font-semibold uppercase tracking-wider text-blue-600 ring-1 ring-blue-500/30 transition-colors duration-150 hover:bg-blue-500/15 hover:transition-none focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-500/50 dark:text-blue-300 dark:ring-blue-400/30\"\n >\n <MagnifyingGlassMinusIcon weight=\"bold\" className=\"size-3\" aria-hidden=\"true\" />\n <span>{strings.resetZoom}</span>\n </button>\n </div>\n )}\n\n <div className=\"flex h-full min-h-0 w-full flex-1 flex-wrap content-center items-center justify-center gap-x-10 gap-y-6\">\n <div className=\"flex shrink-0 flex-col items-center gap-3\">\n <div className=\"relative\">\n <DesignChartContainer config={chartConfig} className={containerClassName}>\n <PieChart\n role=\"img\"\n aria-label={strings.pieAriaLabel({\n segmentCount: canonicalSeries.length,\n windowDays,\n })}\n >\n {showPrimary && (\n <Pie\n data={outerData}\n dataKey=\"value\"\n nameKey=\"name\"\n cx=\"50%\"\n cy=\"50%\"\n innerRadius={innerRadius}\n outerRadius={outerRadius}\n paddingAngle={1}\n startAngle={90}\n endAngle={-270}\n isAnimationActive={false}\n stroke=\"none\"\n >\n {outerData.map((d, i) => {\n const inactive = activeIdx >= 0 && activeIdx !== i;\n return (\n <Cell\n key={`outer-${d.name}`}\n fill={`var(--color-${d.name})`}\n opacity={inactive ? 0.22 : 1}\n style={segmentTransitionStyle}\n onMouseEnter={() => setHoverKey(d.hoverKey)}\n onMouseLeave={() => setHoverKey(null)}\n />\n );\n })}\n </Pie>\n )}\n {showCompare && (\n <Pie\n data={innerData}\n dataKey=\"value\"\n nameKey=\"name\"\n cx=\"50%\"\n cy=\"50%\"\n innerRadius={compareInnerRadius}\n outerRadius={compareOuterRadius}\n paddingAngle={1}\n startAngle={90}\n endAngle={-270}\n isAnimationActive={false}\n stroke=\"none\"\n >\n {innerData.map((d, i) => {\n const inactive = activeIdx >= 0 && activeIdx !== i;\n return (\n <Cell\n key={`inner-${d.name}`}\n fill={`var(--color-compare-${d.name})`}\n opacity={inactive ? 0.22 : 0.95}\n style={segmentTransitionStyle}\n onMouseEnter={() => setHoverKey(d.hoverKey)}\n onMouseLeave={() => setHoverKey(null)}\n />\n );\n })}\n </Pie>\n )}\n </PieChart>\n </DesignChartContainer>\n\n <div\n className={cn(\n \"pointer-events-none absolute inset-0 flex flex-col items-center justify-center text-center transition-opacity duration-200 ease-out\",\n centerDisplayVisible ? \"opacity-100\" : \"opacity-35\",\n )}\n >\n <span className=\"block truncate font-mono text-[9px] uppercase tracking-wider text-muted-foreground\" style={{ maxWidth: centerTextMaxWidth }}>\n {centerDisplayRow ? centerDisplayRow.label : strings.pieTotalCenter}\n </span>\n <span className=\"mt-0.5 block truncate font-mono text-xl font-semibold leading-none tabular-nums text-foreground\" style={{ maxWidth: centerTextMaxWidth }}>\n {fmtValue(centerDisplayRow ? centerDisplayRow.value : canonicalTotal, yFormatKind)}\n </span>\n </div>\n </div>\n\n {(showDateRange || showCompare) && (\n <div className=\"flex items-center gap-2 text-center\">\n {showDateRange && (\n <span className=\"font-mono text-[10px] uppercase tracking-wider text-muted-foreground/80\">\n {startLabel} – {endLabel}\n </span>\n )}\n {showCompare && (\n <TrendPill delta={activeDelta} size=\"sm\" label={strings.pieVsPrev} />\n )}\n </div>\n )}\n </div>\n\n <ul className=\"flex min-w-[200px] max-w-[300px] flex-col gap-1\">\n {legendRows.map((r) => {\n const isActive = hoverKey === r.key;\n const dimmed = hoverKey != null && !isActive;\n const rowDelta = formatDelta(r.value, r.prevValue);\n return (\n <li key={r.key}>\n <button\n type=\"button\"\n onMouseEnter={() => setHoverKey(r.key)}\n onMouseLeave={() => setHoverKey(null)}\n onFocus={() => setHoverKey(r.key)}\n onBlur={() => setHoverKey(null)}\n className={cn(\n \"flex w-full items-center gap-2.5 rounded-md px-2 py-1.5 text-left transition-[background-color,opacity] duration-200 ease-out hover:bg-foreground/[0.04] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-foreground/20\",\n isActive && \"bg-foreground/[0.05]\",\n dimmed && \"opacity-50\",\n )}\n >\n <span\n className=\"size-2.5 shrink-0 rounded-[3px] bg-[var(--c-l)] dark:bg-[var(--c-d)]\"\n style={{ \"--c-l\": r.fill, \"--c-d\": r.fillDark } as CSSProperties}\n />\n <span className=\"min-w-0 flex-1 truncate text-[12px] font-medium text-foreground\">\n {r.label}\n </span>\n <span className=\"shrink-0 font-mono text-[11px] tabular-nums text-muted-foreground\">\n {(r.pct * 100).toFixed(1)}%\n </span>\n <span className=\"shrink-0 min-w-[48px] text-right font-mono text-[11px] font-semibold tabular-nums text-foreground\">\n {fmtValue(r.value, yFormatKind)}\n </span>\n {showCompare && (\n <span className=\"shrink-0\">\n <TrendPill delta={rowDelta} size=\"sm\" />\n </span>\n )}\n </button>\n </li>\n );\n })}\n </ul>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
4
4
|
const require_chunk = require('../../chunk-BE-pF4vm.js');
|
|
5
5
|
let _phosphor_icons_react = require("@phosphor-icons/react");
|
|
6
|
-
let
|
|
6
|
+
let _hexclave_ui = require("@hexclave/ui");
|
|
7
7
|
let react_jsx_runtime = require("react/jsx-runtime");
|
|
8
8
|
let react = require("react");
|
|
9
9
|
let recharts = require("recharts");
|
|
@@ -633,7 +633,7 @@ function AnalyticsChart({ data: fullData, annotations: fullAnnotations = [], sta
|
|
|
633
633
|
});
|
|
634
634
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
635
635
|
ref: wrapperRef,
|
|
636
|
-
className: (0,
|
|
636
|
+
className: (0, _hexclave_ui.cn)("relative w-full select-none rounded-lg focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-500/40 dark:focus-visible:ring-blue-400/40", brushing && "[&_.recharts-wrapper]:cursor-ew-resize", !brushing && "[&_.recharts-wrapper]:cursor-crosshair"),
|
|
637
637
|
onClick: (e) => {
|
|
638
638
|
e.stopPropagation();
|
|
639
639
|
},
|
|
@@ -1004,7 +1004,7 @@ function AnalyticsChart({ data: fullData, annotations: fullAnnotations = [], sta
|
|
|
1004
1004
|
strings
|
|
1005
1005
|
};
|
|
1006
1006
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1007
|
-
className: (0,
|
|
1007
|
+
className: (0, _hexclave_ui.cn)("pointer-events-none absolute top-10 z-20", "transition-[transform,opacity] duration-150"),
|
|
1008
1008
|
style: {
|
|
1009
1009
|
left: indexToCss(activeIndex),
|
|
1010
1010
|
transform: shouldFlip ? "translateX(calc(-100% - 16px))" : "translateX(16px)"
|