@misael703/ui 1.57.1 → 1.59.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/{chunk-CPMQ2DFS.js → chunk-2ET7IM4U.js} +77 -33
- package/dist/chunk-2ET7IM4U.js.map +1 -0
- package/dist/chunk-2RBVHLG4.mjs +305 -0
- package/dist/chunk-2RBVHLG4.mjs.map +1 -0
- package/dist/{chunk-HKHGODQE.js → chunk-4HHP3WT7.js} +4 -4
- package/dist/{chunk-HKHGODQE.js.map → chunk-4HHP3WT7.js.map} +1 -1
- package/dist/chunk-BULIPFOG.js +313 -0
- package/dist/chunk-BULIPFOG.js.map +1 -0
- package/dist/{chunk-S3DEFKS5.js → chunk-DLDTUVHR.js} +3 -3
- package/dist/{chunk-S3DEFKS5.js.map → chunk-DLDTUVHR.js.map} +1 -1
- package/dist/{chunk-O6FCBNWN.mjs → chunk-EJY4QWHT.mjs} +77 -33
- package/dist/chunk-EJY4QWHT.mjs.map +1 -0
- package/dist/{chunk-G4OOD6AR.mjs → chunk-F5EK5PIR.mjs} +3 -3
- package/dist/{chunk-G4OOD6AR.mjs.map → chunk-F5EK5PIR.mjs.map} +1 -1
- package/dist/{chunk-6OBFBETV.mjs → chunk-I7JH3OEO.mjs} +3 -3
- package/dist/{chunk-6OBFBETV.mjs.map → chunk-I7JH3OEO.mjs.map} +1 -1
- package/dist/{chunk-T46LLZHX.js → chunk-KNQOG43N.js} +4 -4
- package/dist/{chunk-T46LLZHX.js.map → chunk-KNQOG43N.js.map} +1 -1
- package/dist/{chunk-MSDFMVIF.js → chunk-NPUNAVWM.js} +3 -3
- package/dist/{chunk-MSDFMVIF.js.map → chunk-NPUNAVWM.js.map} +1 -1
- package/dist/{chunk-J2HHAZHV.mjs → chunk-X7BV2JFX.mjs} +3 -3
- package/dist/{chunk-J2HHAZHV.mjs.map → chunk-X7BV2JFX.mjs.map} +1 -1
- package/dist/{chunk-XJHK3RH5.mjs → chunk-ZAKJKBUX.mjs} +3 -3
- package/dist/{chunk-XJHK3RH5.mjs.map → chunk-ZAKJKBUX.mjs.map} +1 -1
- package/dist/components/AppShell.js +4 -4
- package/dist/components/AppShell.mjs +2 -2
- package/dist/components/Charts.d.mts +25 -15
- package/dist/components/Charts.d.ts +25 -15
- package/dist/components/Charts.js +6 -6
- package/dist/components/Charts.mjs +1 -1
- package/dist/components/Commerce.js +14 -14
- package/dist/components/Commerce.mjs +3 -3
- package/dist/components/Editing.js +9 -9
- package/dist/components/Editing.mjs +3 -3
- package/dist/components/Metrics.d.mts +130 -0
- package/dist/components/Metrics.d.ts +130 -0
- package/dist/components/Metrics.js +41 -0
- package/dist/components/Metrics.js.map +1 -0
- package/dist/components/Metrics.mjs +8 -0
- package/dist/components/Metrics.mjs.map +1 -0
- package/dist/components/Overlay.js +4 -4
- package/dist/components/Overlay.mjs +2 -2
- package/dist/hooks/index.js +5 -5
- package/dist/hooks/index.mjs +1 -1
- package/dist/index.d.mts +2 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +98 -69
- package/dist/index.mjs +12 -11
- package/dist/styles.css +1 -1
- package/package.json +1 -1
- package/dist/chunk-CPMQ2DFS.js.map +0 -1
- package/dist/chunk-O6FCBNWN.mjs.map +0 -1
|
@@ -14,6 +14,23 @@ var PALETTE = [
|
|
|
14
14
|
"#6b7e95",
|
|
15
15
|
"#d4a574"
|
|
16
16
|
];
|
|
17
|
+
var DEFAULT_X_INTERVAL = "preserveStartEnd";
|
|
18
|
+
function categoryTickProps(opts, rotate = true) {
|
|
19
|
+
const props = { interval: opts.xTickInterval ?? DEFAULT_X_INTERVAL };
|
|
20
|
+
if (opts.xTickFormatter) props.tickFormatter = opts.xTickFormatter;
|
|
21
|
+
if (rotate && opts.xTickAngle) {
|
|
22
|
+
props.angle = opts.xTickAngle;
|
|
23
|
+
props.textAnchor = "end";
|
|
24
|
+
props.height = 56;
|
|
25
|
+
}
|
|
26
|
+
return props;
|
|
27
|
+
}
|
|
28
|
+
function valueTickProps(valueFormatter) {
|
|
29
|
+
return valueFormatter ? { tickFormatter: (v) => valueFormatter(Number(v)) } : {};
|
|
30
|
+
}
|
|
31
|
+
function tooltipValueProps(valueFormatter) {
|
|
32
|
+
return valueFormatter ? { formatter: (v) => valueFormatter(Number(v)) } : {};
|
|
33
|
+
}
|
|
17
34
|
function LineChart({
|
|
18
35
|
recharts: R,
|
|
19
36
|
data,
|
|
@@ -24,18 +41,24 @@ function LineChart({
|
|
|
24
41
|
ariaLabel,
|
|
25
42
|
showGrid = true,
|
|
26
43
|
showLegend = true,
|
|
27
|
-
smooth = true
|
|
44
|
+
smooth = true,
|
|
45
|
+
curve,
|
|
46
|
+
xTickFormatter,
|
|
47
|
+
xTickInterval,
|
|
48
|
+
xTickAngle,
|
|
49
|
+
valueFormatter
|
|
28
50
|
}) {
|
|
51
|
+
const lineType = curve ?? (smooth ? "monotone" : "linear");
|
|
29
52
|
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: chunkPASF6T4H_js.cx("chart", className), role: "img", "aria-label": ariaLabel, children: /* @__PURE__ */ jsxRuntime.jsx(R.ResponsiveContainer, { width: "100%", height, children: /* @__PURE__ */ jsxRuntime.jsxs(R.LineChart, { data, margin: { top: 8, right: 16, bottom: 0, left: 0 }, children: [
|
|
30
53
|
showGrid && /* @__PURE__ */ jsxRuntime.jsx(R.CartesianGrid, { stroke: "var(--border-default)", strokeDasharray: "3 3", vertical: false }),
|
|
31
|
-
/* @__PURE__ */ jsxRuntime.jsx(R.XAxis, { dataKey: categoryKey, stroke: "var(--fg-subtle)", fontSize: 12, tickLine: false, axisLine: false }),
|
|
32
|
-
/* @__PURE__ */ jsxRuntime.jsx(R.YAxis, { stroke: "var(--fg-subtle)", fontSize: 12, tickLine: false, axisLine: false }),
|
|
33
|
-
/* @__PURE__ */ jsxRuntime.jsx(R.Tooltip, { contentStyle: { background: "var(--bg-surface)", border: "1px solid var(--border-default)", borderRadius: 8, fontSize: 12 } }),
|
|
54
|
+
/* @__PURE__ */ jsxRuntime.jsx(R.XAxis, { dataKey: categoryKey, stroke: "var(--fg-subtle)", fontSize: 12, tickLine: false, axisLine: false, ...categoryTickProps({ xTickFormatter, xTickInterval, xTickAngle }) }),
|
|
55
|
+
/* @__PURE__ */ jsxRuntime.jsx(R.YAxis, { stroke: "var(--fg-subtle)", fontSize: 12, tickLine: false, axisLine: false, ...valueTickProps(valueFormatter) }),
|
|
56
|
+
/* @__PURE__ */ jsxRuntime.jsx(R.Tooltip, { contentStyle: { background: "var(--bg-surface)", border: "1px solid var(--border-default)", borderRadius: 8, fontSize: 12 }, ...tooltipValueProps(valueFormatter) }),
|
|
34
57
|
showLegend && /* @__PURE__ */ jsxRuntime.jsx(R.Legend, { wrapperStyle: { fontSize: 12 } }),
|
|
35
58
|
series.map((s, i) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
36
59
|
R.Line,
|
|
37
60
|
{
|
|
38
|
-
type:
|
|
61
|
+
type: lineType,
|
|
39
62
|
dataKey: s.key,
|
|
40
63
|
name: s.label ?? s.key,
|
|
41
64
|
stroke: s.color ?? PALETTE[i % PALETTE.length],
|
|
@@ -58,18 +81,24 @@ function AreaChart({
|
|
|
58
81
|
showGrid = true,
|
|
59
82
|
showLegend = true,
|
|
60
83
|
smooth = true,
|
|
61
|
-
|
|
84
|
+
curve,
|
|
85
|
+
stacked,
|
|
86
|
+
xTickFormatter,
|
|
87
|
+
xTickInterval,
|
|
88
|
+
xTickAngle,
|
|
89
|
+
valueFormatter
|
|
62
90
|
}) {
|
|
91
|
+
const lineType = curve ?? (smooth ? "monotone" : "linear");
|
|
63
92
|
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: chunkPASF6T4H_js.cx("chart", className), role: "img", "aria-label": ariaLabel, children: /* @__PURE__ */ jsxRuntime.jsx(R.ResponsiveContainer, { width: "100%", height, children: /* @__PURE__ */ jsxRuntime.jsxs(R.AreaChart, { data, margin: { top: 8, right: 16, bottom: 0, left: 0 }, children: [
|
|
64
93
|
showGrid && /* @__PURE__ */ jsxRuntime.jsx(R.CartesianGrid, { stroke: "var(--border-default)", strokeDasharray: "3 3", vertical: false }),
|
|
65
|
-
/* @__PURE__ */ jsxRuntime.jsx(R.XAxis, { dataKey: categoryKey, stroke: "var(--fg-subtle)", fontSize: 12, tickLine: false, axisLine: false }),
|
|
66
|
-
/* @__PURE__ */ jsxRuntime.jsx(R.YAxis, { stroke: "var(--fg-subtle)", fontSize: 12, tickLine: false, axisLine: false }),
|
|
67
|
-
/* @__PURE__ */ jsxRuntime.jsx(R.Tooltip, { contentStyle: { background: "var(--bg-surface)", border: "1px solid var(--border-default)", borderRadius: 8, fontSize: 12 } }),
|
|
94
|
+
/* @__PURE__ */ jsxRuntime.jsx(R.XAxis, { dataKey: categoryKey, stroke: "var(--fg-subtle)", fontSize: 12, tickLine: false, axisLine: false, ...categoryTickProps({ xTickFormatter, xTickInterval, xTickAngle }) }),
|
|
95
|
+
/* @__PURE__ */ jsxRuntime.jsx(R.YAxis, { stroke: "var(--fg-subtle)", fontSize: 12, tickLine: false, axisLine: false, ...valueTickProps(valueFormatter) }),
|
|
96
|
+
/* @__PURE__ */ jsxRuntime.jsx(R.Tooltip, { contentStyle: { background: "var(--bg-surface)", border: "1px solid var(--border-default)", borderRadius: 8, fontSize: 12 }, ...tooltipValueProps(valueFormatter) }),
|
|
68
97
|
showLegend && /* @__PURE__ */ jsxRuntime.jsx(R.Legend, { wrapperStyle: { fontSize: 12 } }),
|
|
69
98
|
series.map((s, i) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
70
99
|
R.Area,
|
|
71
100
|
{
|
|
72
|
-
type:
|
|
101
|
+
type: lineType,
|
|
73
102
|
dataKey: s.key,
|
|
74
103
|
name: s.label ?? s.key,
|
|
75
104
|
stackId: stacked ? "1" : void 0,
|
|
@@ -82,6 +111,7 @@ function AreaChart({
|
|
|
82
111
|
))
|
|
83
112
|
] }) }) });
|
|
84
113
|
}
|
|
114
|
+
var BAR_RADIUS = 4;
|
|
85
115
|
function BarChart({
|
|
86
116
|
recharts: R,
|
|
87
117
|
data,
|
|
@@ -93,32 +123,43 @@ function BarChart({
|
|
|
93
123
|
layout = "vertical",
|
|
94
124
|
stacked,
|
|
95
125
|
showGrid = true,
|
|
96
|
-
showLegend = true
|
|
126
|
+
showLegend = true,
|
|
127
|
+
xTickFormatter,
|
|
128
|
+
xTickInterval,
|
|
129
|
+
xTickAngle,
|
|
130
|
+
valueFormatter
|
|
97
131
|
}) {
|
|
98
132
|
const isHorizontal = layout === "horizontal";
|
|
133
|
+
const endRadius = isHorizontal ? [0, BAR_RADIUS, BAR_RADIUS, 0] : [BAR_RADIUS, BAR_RADIUS, 0, 0];
|
|
134
|
+
const lastIdx = series.length - 1;
|
|
135
|
+
const catTicks = categoryTickProps({ xTickFormatter, xTickInterval, xTickAngle });
|
|
136
|
+
const catTicksNoRotate = categoryTickProps({ xTickFormatter, xTickInterval, xTickAngle }, false);
|
|
99
137
|
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: chunkPASF6T4H_js.cx("chart", className), role: "img", "aria-label": ariaLabel, children: /* @__PURE__ */ jsxRuntime.jsx(R.ResponsiveContainer, { width: "100%", height, children: /* @__PURE__ */ jsxRuntime.jsxs(R.BarChart, { data, layout: isHorizontal ? "vertical" : "horizontal", margin: { top: 8, right: 16, bottom: 0, left: 0 }, children: [
|
|
100
138
|
showGrid && /* @__PURE__ */ jsxRuntime.jsx(R.CartesianGrid, { stroke: "var(--border-default)", strokeDasharray: "3 3", vertical: isHorizontal, horizontal: !isHorizontal }),
|
|
101
139
|
isHorizontal ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
102
|
-
/* @__PURE__ */ jsxRuntime.jsx(R.XAxis, { type: "number", stroke: "var(--fg-subtle)", fontSize: 12, tickLine: false, axisLine: false }),
|
|
103
|
-
/* @__PURE__ */ jsxRuntime.jsx(R.YAxis, { dataKey: categoryKey, type: "category", stroke: "var(--fg-subtle)", fontSize: 12, tickLine: false, axisLine: false, width: 96 })
|
|
140
|
+
/* @__PURE__ */ jsxRuntime.jsx(R.XAxis, { type: "number", stroke: "var(--fg-subtle)", fontSize: 12, tickLine: false, axisLine: false, ...valueTickProps(valueFormatter) }),
|
|
141
|
+
/* @__PURE__ */ jsxRuntime.jsx(R.YAxis, { dataKey: categoryKey, type: "category", stroke: "var(--fg-subtle)", fontSize: 12, tickLine: false, axisLine: false, width: 96, ...catTicksNoRotate })
|
|
104
142
|
] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
105
|
-
/* @__PURE__ */ jsxRuntime.jsx(R.XAxis, { dataKey: categoryKey, stroke: "var(--fg-subtle)", fontSize: 12, tickLine: false, axisLine: false }),
|
|
106
|
-
/* @__PURE__ */ jsxRuntime.jsx(R.YAxis, { stroke: "var(--fg-subtle)", fontSize: 12, tickLine: false, axisLine: false })
|
|
143
|
+
/* @__PURE__ */ jsxRuntime.jsx(R.XAxis, { dataKey: categoryKey, stroke: "var(--fg-subtle)", fontSize: 12, tickLine: false, axisLine: false, ...catTicks }),
|
|
144
|
+
/* @__PURE__ */ jsxRuntime.jsx(R.YAxis, { stroke: "var(--fg-subtle)", fontSize: 12, tickLine: false, axisLine: false, ...valueTickProps(valueFormatter) })
|
|
107
145
|
] }),
|
|
108
|
-
/* @__PURE__ */ jsxRuntime.jsx(R.Tooltip, { contentStyle: { background: "var(--bg-surface)", border: "1px solid var(--border-default)", borderRadius: 8, fontSize: 12 }, cursor: { fill: "var(--bg-subtle)" } }),
|
|
146
|
+
/* @__PURE__ */ jsxRuntime.jsx(R.Tooltip, { contentStyle: { background: "var(--bg-surface)", border: "1px solid var(--border-default)", borderRadius: 8, fontSize: 12 }, cursor: { fill: "var(--bg-subtle)" }, ...tooltipValueProps(valueFormatter) }),
|
|
109
147
|
showLegend && /* @__PURE__ */ jsxRuntime.jsx(R.Legend, { wrapperStyle: { fontSize: 12 } }),
|
|
110
|
-
series.map((s, i) =>
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
148
|
+
series.map((s, i) => {
|
|
149
|
+
const barRadius = stacked ? i === lastIdx ? endRadius : [0, 0, 0, 0] : endRadius;
|
|
150
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
151
|
+
R.Bar,
|
|
152
|
+
{
|
|
153
|
+
dataKey: s.key,
|
|
154
|
+
name: s.label ?? s.key,
|
|
155
|
+
stackId: stacked ? "1" : void 0,
|
|
156
|
+
fill: s.color ?? PALETTE[i % PALETTE.length],
|
|
157
|
+
radius: barRadius,
|
|
158
|
+
maxBarSize: 48
|
|
159
|
+
},
|
|
160
|
+
s.key
|
|
161
|
+
);
|
|
162
|
+
})
|
|
122
163
|
] }) }) });
|
|
123
164
|
}
|
|
124
165
|
function DonutChart({
|
|
@@ -130,12 +171,15 @@ function DonutChart({
|
|
|
130
171
|
centerLabel,
|
|
131
172
|
showLegend = true,
|
|
132
173
|
innerRadius = 60,
|
|
133
|
-
outerRadius = 88
|
|
174
|
+
outerRadius = 88,
|
|
175
|
+
nameFormatter,
|
|
176
|
+
valueFormatter
|
|
134
177
|
}) {
|
|
178
|
+
const tooltipProps = nameFormatter || valueFormatter ? { formatter: (v, n) => [valueFormatter ? valueFormatter(Number(v)) : v, nameFormatter ? nameFormatter(String(n)) : n] } : {};
|
|
135
179
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: chunkPASF6T4H_js.cx("chart chart--donut", className), role: "img", "aria-label": ariaLabel, children: [
|
|
136
180
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "chart__donut-area", style: { height }, children: [
|
|
137
181
|
/* @__PURE__ */ jsxRuntime.jsx(R.ResponsiveContainer, { width: "100%", height: "100%", children: /* @__PURE__ */ jsxRuntime.jsxs(R.PieChart, { children: [
|
|
138
|
-
/* @__PURE__ */ jsxRuntime.jsx(R.Tooltip, { contentStyle: { background: "var(--bg-surface)", border: "1px solid var(--border-default)", borderRadius: 8, fontSize: 12 } }),
|
|
182
|
+
/* @__PURE__ */ jsxRuntime.jsx(R.Tooltip, { contentStyle: { background: "var(--bg-surface)", border: "1px solid var(--border-default)", borderRadius: 8, fontSize: 12 }, ...tooltipProps }),
|
|
139
183
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
140
184
|
R.Pie,
|
|
141
185
|
{
|
|
@@ -160,7 +204,7 @@ function DonutChart({
|
|
|
160
204
|
style: { background: d.color ?? PALETTE[i % PALETTE.length] }
|
|
161
205
|
}
|
|
162
206
|
),
|
|
163
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "chart__legend-label", children: d.name })
|
|
207
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "chart__legend-label", children: nameFormatter ? nameFormatter(d.name) : d.name })
|
|
164
208
|
] }, d.name)) })
|
|
165
209
|
] });
|
|
166
210
|
}
|
|
@@ -194,5 +238,5 @@ exports.BarChart = BarChart;
|
|
|
194
238
|
exports.DonutChart = DonutChart;
|
|
195
239
|
exports.LineChart = LineChart;
|
|
196
240
|
exports.Sparkline = Sparkline;
|
|
197
|
-
//# sourceMappingURL=chunk-
|
|
198
|
-
//# sourceMappingURL=chunk-
|
|
241
|
+
//# sourceMappingURL=chunk-2ET7IM4U.js.map
|
|
242
|
+
//# sourceMappingURL=chunk-2ET7IM4U.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/Charts.tsx"],"names":["jsx","cx","jsxs","Fragment"],"mappings":"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","file":"chunk-2ET7IM4U.js","sourcesContent":["'use client';\nimport * as React from 'react';\nimport { cx } from '../utils/cx';\n\n/**\n * Lightweight chart wrappers. We do NOT bundle Recharts — the host app provides it\n * and passes the modules in via the `recharts` prop so consumers only pay for what they use.\n *\n * Recharts is treated as an implicit peer dependency: install it in the host\n * app if you use any chart component.\n *\n * Usage in a Next.js client component:\n *\n * import * as Recharts from 'recharts';\n * import { LineChart } from '@misael703/elalba-ui';\n * <LineChart recharts={Recharts} data={...} dataKey=\"value\" categoryKey=\"month\" />\n */\n\n// Structural mirror of the Recharts public API. `any` is intentional here:\n// each Recharts component has a different prop shape, and typing them\n// exhaustively would mean depending on `recharts` types — which would force\n// consumers to install recharts even when they don't use any chart.\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype RechartsComp = React.ComponentType<any>;\n\nexport type RechartsLike = {\n ResponsiveContainer: RechartsComp;\n LineChart: RechartsComp;\n AreaChart: RechartsComp;\n BarChart: RechartsComp;\n PieChart: RechartsComp;\n Line: RechartsComp;\n Area: RechartsComp;\n Bar: RechartsComp;\n Pie: RechartsComp;\n Cell: RechartsComp;\n CartesianGrid: RechartsComp;\n XAxis: RechartsComp;\n YAxis: RechartsComp;\n Tooltip: RechartsComp;\n Legend: RechartsComp;\n};\n\nconst PALETTE = [\n 'var(--color-primary)',\n 'var(--color-secondary)',\n 'var(--color-primary-900)',\n 'var(--color-success)',\n 'var(--color-warning)',\n 'var(--color-danger)',\n '#6b7e95',\n '#d4a574',\n];\n\nexport interface BaseChartProps<D = any> {\n recharts: RechartsLike;\n data: D[];\n height?: number;\n className?: string;\n ariaLabel?: string;\n}\n\n// Mirror of Recharts' AxisInterval. Controls how category ticks thin on\n// collision so dense series (e.g. daily points) don't crowd the axis.\nexport type AxisInterval =\n | number\n | 'preserveStart'\n | 'preserveEnd'\n | 'preserveStartEnd'\n | 'equidistantPreserveStart'\n | 'equidistantPreserveEnd';\n\n// Shared cartesian controls (Line/Area/Bar). DonutChart/Sparkline don't have a\n// category axis, so they keep BaseChartProps.\nexport interface CartesianChartProps<D = any> extends BaseChartProps<D> {\n categoryKey: keyof D & string;\n series: Array<{ key: keyof D & string; label?: string; color?: string }>;\n showGrid?: boolean;\n showLegend?: boolean;\n /** Format each category-axis tick label, e.g. shorten `2026-05-23`. → XAxis.tickFormatter */\n xTickFormatter?: (value: string) => string;\n /** Thin out category ticks on collision. → XAxis.interval. Default `preserveStartEnd`. */\n xTickInterval?: AxisInterval;\n /** Rotate category-axis labels N degrees (anchors end + reserves height so they don't clip). */\n xTickAngle?: number;\n /** Format numeric values (value-axis ticks + tooltip). */\n valueFormatter?: (value: number) => string;\n}\n\nconst DEFAULT_X_INTERVAL: AxisInterval = 'preserveStartEnd';\n\n// Recharts XAxis/YAxis are `any`-typed (structural mirror), so tick controls go\n// in as a spread. `rotate` is off when the category axis is the Y axis\n// (horizontal bars: those labels are already horizontal).\nfunction categoryTickProps(\n opts: Pick<CartesianChartProps, 'xTickFormatter' | 'xTickInterval' | 'xTickAngle'>,\n rotate = true,\n): Record<string, unknown> {\n const props: Record<string, unknown> = { interval: opts.xTickInterval ?? DEFAULT_X_INTERVAL };\n if (opts.xTickFormatter) props.tickFormatter = opts.xTickFormatter;\n if (rotate && opts.xTickAngle) {\n props.angle = opts.xTickAngle;\n props.textAnchor = 'end';\n props.height = 56; // reserve room so the rotated label isn't clipped\n }\n return props;\n}\n\nfunction valueTickProps(valueFormatter?: (v: number) => string): Record<string, unknown> {\n return valueFormatter ? { tickFormatter: (v: number) => valueFormatter(Number(v)) } : {};\n}\n\nfunction tooltipValueProps(valueFormatter?: (v: number) => string): Record<string, unknown> {\n return valueFormatter ? { formatter: (v: unknown) => valueFormatter(Number(v)) } : {};\n}\n\n// ---------- LineChart ---------------------------------------------------\nexport interface LineChartProps<D = any> extends CartesianChartProps<D> {\n smooth?: boolean;\n /** Interpolation. `monotone` smooths (default, back-compat); `linear` draws honest\n * straight segments — recommended for counts/stepped series (no phantom humps over zeros). */\n curve?: 'linear' | 'monotone';\n}\n\nexport function LineChart<D = any>({\n recharts: R, data, categoryKey, series,\n height = 280, className, ariaLabel,\n showGrid = true, showLegend = true, smooth = true, curve,\n xTickFormatter, xTickInterval, xTickAngle, valueFormatter,\n}: LineChartProps<D>) {\n const lineType = curve ?? (smooth ? 'monotone' : 'linear');\n return (\n <div className={cx('chart', className)} role=\"img\" aria-label={ariaLabel}>\n <R.ResponsiveContainer width=\"100%\" height={height}>\n <R.LineChart data={data} margin={{ top: 8, right: 16, bottom: 0, left: 0 }}>\n {showGrid && <R.CartesianGrid stroke=\"var(--border-default)\" strokeDasharray=\"3 3\" vertical={false} />}\n <R.XAxis dataKey={categoryKey} stroke=\"var(--fg-subtle)\" fontSize={12} tickLine={false} axisLine={false} {...categoryTickProps({ xTickFormatter, xTickInterval, xTickAngle })} />\n <R.YAxis stroke=\"var(--fg-subtle)\" fontSize={12} tickLine={false} axisLine={false} {...valueTickProps(valueFormatter)} />\n <R.Tooltip contentStyle={{ background: 'var(--bg-surface)', border: '1px solid var(--border-default)', borderRadius: 8, fontSize: 12 }} {...tooltipValueProps(valueFormatter)} />\n {showLegend && <R.Legend wrapperStyle={{ fontSize: 12 }} />}\n {series.map((s, i) => (\n <R.Line\n key={s.key}\n type={lineType}\n dataKey={s.key}\n name={s.label ?? s.key}\n stroke={s.color ?? PALETTE[i % PALETTE.length]}\n strokeWidth={2}\n dot={false}\n activeDot={{ r: 4 }}\n />\n ))}\n </R.LineChart>\n </R.ResponsiveContainer>\n </div>\n );\n}\n\n// ---------- AreaChart ---------------------------------------------------\nexport interface AreaChartProps<D = any> extends LineChartProps<D> {\n stacked?: boolean;\n}\n\nexport function AreaChart<D = any>({\n recharts: R, data, categoryKey, series,\n height = 280, className, ariaLabel,\n showGrid = true, showLegend = true, smooth = true, curve, stacked,\n xTickFormatter, xTickInterval, xTickAngle, valueFormatter,\n}: AreaChartProps<D>) {\n const lineType = curve ?? (smooth ? 'monotone' : 'linear');\n return (\n <div className={cx('chart', className)} role=\"img\" aria-label={ariaLabel}>\n <R.ResponsiveContainer width=\"100%\" height={height}>\n <R.AreaChart data={data} margin={{ top: 8, right: 16, bottom: 0, left: 0 }}>\n {showGrid && <R.CartesianGrid stroke=\"var(--border-default)\" strokeDasharray=\"3 3\" vertical={false} />}\n <R.XAxis dataKey={categoryKey} stroke=\"var(--fg-subtle)\" fontSize={12} tickLine={false} axisLine={false} {...categoryTickProps({ xTickFormatter, xTickInterval, xTickAngle })} />\n <R.YAxis stroke=\"var(--fg-subtle)\" fontSize={12} tickLine={false} axisLine={false} {...valueTickProps(valueFormatter)} />\n <R.Tooltip contentStyle={{ background: 'var(--bg-surface)', border: '1px solid var(--border-default)', borderRadius: 8, fontSize: 12 }} {...tooltipValueProps(valueFormatter)} />\n {showLegend && <R.Legend wrapperStyle={{ fontSize: 12 }} />}\n {series.map((s, i) => (\n <R.Area\n key={s.key}\n type={lineType}\n dataKey={s.key}\n name={s.label ?? s.key}\n stackId={stacked ? '1' : undefined}\n stroke={s.color ?? PALETTE[i % PALETTE.length]}\n fill={s.color ?? PALETTE[i % PALETTE.length]}\n fillOpacity={0.18}\n strokeWidth={2}\n />\n ))}\n </R.AreaChart>\n </R.ResponsiveContainer>\n </div>\n );\n}\n\n// ---------- BarChart ----------------------------------------------------\nexport interface BarChartProps<D = any> extends CartesianChartProps<D> {\n layout?: 'vertical' | 'horizontal';\n stacked?: boolean;\n}\n\nconst BAR_RADIUS = 4;\n\nexport function BarChart<D = any>({\n recharts: R, data, categoryKey, series,\n height = 280, className, ariaLabel,\n layout = 'vertical', stacked, showGrid = true, showLegend = true,\n xTickFormatter, xTickInterval, xTickAngle, valueFormatter,\n}: BarChartProps<D>) {\n const isHorizontal = layout === 'horizontal';\n // Radius must round the VALUE end: top for columns ([tl,tr,br,bl] → [R,R,0,0]),\n // right for horizontal bars ([0,R,R,0]). The old hardcoded [4,4,0,0] left\n // horizontal bars rounded-top / pointed-bottom.\n const endRadius = isHorizontal ? [0, BAR_RADIUS, BAR_RADIUS, 0] : [BAR_RADIUS, BAR_RADIUS, 0, 0];\n const lastIdx = series.length - 1;\n const catTicks = categoryTickProps({ xTickFormatter, xTickInterval, xTickAngle });\n const catTicksNoRotate = categoryTickProps({ xTickFormatter, xTickInterval, xTickAngle }, false);\n return (\n <div className={cx('chart', className)} role=\"img\" aria-label={ariaLabel}>\n <R.ResponsiveContainer width=\"100%\" height={height}>\n <R.BarChart data={data} layout={isHorizontal ? 'vertical' : 'horizontal'} margin={{ top: 8, right: 16, bottom: 0, left: 0 }}>\n {showGrid && <R.CartesianGrid stroke=\"var(--border-default)\" strokeDasharray=\"3 3\" vertical={isHorizontal} horizontal={!isHorizontal} />}\n {isHorizontal ? (\n <>\n <R.XAxis type=\"number\" stroke=\"var(--fg-subtle)\" fontSize={12} tickLine={false} axisLine={false} {...valueTickProps(valueFormatter)} />\n <R.YAxis dataKey={categoryKey} type=\"category\" stroke=\"var(--fg-subtle)\" fontSize={12} tickLine={false} axisLine={false} width={96} {...catTicksNoRotate} />\n </>\n ) : (\n <>\n <R.XAxis dataKey={categoryKey} stroke=\"var(--fg-subtle)\" fontSize={12} tickLine={false} axisLine={false} {...catTicks} />\n <R.YAxis stroke=\"var(--fg-subtle)\" fontSize={12} tickLine={false} axisLine={false} {...valueTickProps(valueFormatter)} />\n </>\n )}\n <R.Tooltip contentStyle={{ background: 'var(--bg-surface)', border: '1px solid var(--border-default)', borderRadius: 8, fontSize: 12 }} cursor={{ fill: 'var(--bg-subtle)' }} {...tooltipValueProps(valueFormatter)} />\n {showLegend && <R.Legend wrapperStyle={{ fontSize: 12 }} />}\n {series.map((s, i) => {\n // Stacked: only the outermost (last) segment carries the end radius;\n // inner segments stay square so the stack reads as one bar.\n const barRadius = stacked ? (i === lastIdx ? endRadius : [0, 0, 0, 0]) : endRadius;\n return (\n <R.Bar\n key={s.key}\n dataKey={s.key}\n name={s.label ?? s.key}\n stackId={stacked ? '1' : undefined}\n fill={s.color ?? PALETTE[i % PALETTE.length]}\n radius={barRadius}\n maxBarSize={48}\n />\n );\n })}\n </R.BarChart>\n </R.ResponsiveContainer>\n </div>\n );\n}\n\n// ---------- DonutChart --------------------------------------------------\nexport interface DonutChartProps extends Omit<BaseChartProps, 'data'> {\n data: Array<{ name: string; value: number; color?: string }>;\n centerLabel?: React.ReactNode;\n showLegend?: boolean;\n innerRadius?: number;\n outerRadius?: number;\n /** Format slice names in tooltip + legend, e.g. `V_REGION` → `V Región` (no need to pre-map the data). */\n nameFormatter?: (name: string) => string;\n /** Format slice values in the tooltip. */\n valueFormatter?: (value: number) => string;\n}\n\nexport function DonutChart({\n recharts: R, data, height = 240, className, ariaLabel,\n centerLabel, showLegend = true, innerRadius = 60, outerRadius = 88,\n nameFormatter, valueFormatter,\n}: DonutChartProps) {\n // Recharts Tooltip formatter returns [formattedValue, formattedName].\n const tooltipProps = (nameFormatter || valueFormatter)\n ? { formatter: (v: unknown, n: unknown) => [valueFormatter ? valueFormatter(Number(v)) : v, nameFormatter ? nameFormatter(String(n)) : n] }\n : {};\n return (\n <div className={cx('chart chart--donut', className)} role=\"img\" aria-label={ariaLabel}>\n <div className=\"chart__donut-area\" style={{ height }}>\n <R.ResponsiveContainer width=\"100%\" height=\"100%\">\n <R.PieChart>\n <R.Tooltip contentStyle={{ background: 'var(--bg-surface)', border: '1px solid var(--border-default)', borderRadius: 8, fontSize: 12 }} {...tooltipProps} />\n <R.Pie\n data={data}\n dataKey=\"value\"\n nameKey=\"name\"\n innerRadius={innerRadius}\n outerRadius={outerRadius}\n paddingAngle={2}\n stroke=\"var(--bg-surface)\"\n >\n {data.map((d, i) => <R.Cell key={i} fill={d.color ?? PALETTE[i % PALETTE.length]} />)}\n </R.Pie>\n </R.PieChart>\n </R.ResponsiveContainer>\n {centerLabel && <div className=\"chart__center\">{centerLabel}</div>}\n </div>\n {showLegend && (\n <ul className=\"chart__legend\" aria-hidden=\"true\">\n {data.map((d, i) => (\n <li key={d.name} className=\"chart__legend-item\">\n <span\n className=\"chart__legend-swatch\"\n style={{ background: d.color ?? PALETTE[i % PALETTE.length] }}\n />\n <span className=\"chart__legend-label\">{nameFormatter ? nameFormatter(d.name) : d.name}</span>\n </li>\n ))}\n </ul>\n )}\n </div>\n );\n}\n\n// ---------- Sparkline ---------------------------------------------------\nexport interface SparklineProps<D = any> {\n recharts: RechartsLike;\n data: D[];\n dataKey: keyof D & string;\n width?: number;\n height?: number;\n color?: string;\n fill?: boolean;\n className?: string;\n ariaLabel?: string;\n}\n\nexport function Sparkline<D = any>({\n recharts: R, data, dataKey,\n width = 120, height = 32, color = 'var(--color-primary)',\n fill = true, className, ariaLabel,\n}: SparklineProps<D>) {\n return (\n <div className={cx('sparkline', className)} role=\"img\" aria-label={ariaLabel} style={{ width, height }}>\n <R.ResponsiveContainer width=\"100%\" height=\"100%\">\n <R.AreaChart data={data} margin={{ top: 2, right: 0, bottom: 2, left: 0 }}>\n <R.Area\n type=\"monotone\"\n dataKey={dataKey}\n stroke={color}\n strokeWidth={1.5}\n fill={fill ? color : 'none'}\n fillOpacity={fill ? 0.18 : 0}\n isAnimationActive={false}\n />\n </R.AreaChart>\n </R.ResponsiveContainer>\n </div>\n );\n}\n"]}
|
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { formatNumber } from './chunk-WYOJ7YRQ.mjs';
|
|
3
|
+
import { ArrowUp, ArrowDown, Minus } from './chunk-BJGMROKL.mjs';
|
|
4
|
+
import { cx } from './chunk-IEPCH3JB.mjs';
|
|
5
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
6
|
+
|
|
7
|
+
var SIGNED_PERCENT = (v) => {
|
|
8
|
+
const sign = v > 0 ? "+" : v < 0 ? "\u2212" : "";
|
|
9
|
+
return `${sign}${formatNumber(Math.abs(v), { maximumFractionDigits: 1 })}%`;
|
|
10
|
+
};
|
|
11
|
+
function DeltaBadge({
|
|
12
|
+
value,
|
|
13
|
+
format = SIGNED_PERCENT,
|
|
14
|
+
invert = false,
|
|
15
|
+
neutralThreshold = 0,
|
|
16
|
+
showIcon = true,
|
|
17
|
+
size = "md",
|
|
18
|
+
className,
|
|
19
|
+
...rest
|
|
20
|
+
}) {
|
|
21
|
+
const dir = value > neutralThreshold ? "up" : value < -neutralThreshold ? "down" : "flat";
|
|
22
|
+
const tone = dir === "flat" ? "flat" : dir === "up" ? invert ? "neg" : "pos" : invert ? "pos" : "neg";
|
|
23
|
+
const Icon = dir === "up" ? ArrowUp : dir === "down" ? ArrowDown : Minus;
|
|
24
|
+
const verb = dir === "up" ? "subi\xF3" : dir === "down" ? "baj\xF3" : "sin cambio";
|
|
25
|
+
const text = format(value);
|
|
26
|
+
return /* @__PURE__ */ jsxs(
|
|
27
|
+
"span",
|
|
28
|
+
{
|
|
29
|
+
className: cx("delta-badge", `delta-badge--${tone}`, size === "sm" && "delta-badge--sm", className),
|
|
30
|
+
"aria-label": `${verb} ${text}`,
|
|
31
|
+
...rest,
|
|
32
|
+
children: [
|
|
33
|
+
showIcon && /* @__PURE__ */ jsx(Icon, { size: size === "sm" ? 11 : 13, "aria-hidden": true }),
|
|
34
|
+
/* @__PURE__ */ jsx("span", { className: "delta-badge__text", children: text })
|
|
35
|
+
]
|
|
36
|
+
}
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
function StatCard({
|
|
40
|
+
label,
|
|
41
|
+
value,
|
|
42
|
+
delta,
|
|
43
|
+
deltaFormat,
|
|
44
|
+
deltaInvert,
|
|
45
|
+
deltaNode,
|
|
46
|
+
caption,
|
|
47
|
+
icon,
|
|
48
|
+
accent,
|
|
49
|
+
chart,
|
|
50
|
+
className,
|
|
51
|
+
...rest
|
|
52
|
+
}) {
|
|
53
|
+
const deltaEl = deltaNode ?? (delta !== void 0 ? /* @__PURE__ */ jsx(DeltaBadge, { value: delta, format: deltaFormat, invert: deltaInvert, size: "sm" }) : null);
|
|
54
|
+
return /* @__PURE__ */ jsxs(
|
|
55
|
+
"div",
|
|
56
|
+
{
|
|
57
|
+
className: cx("metric-card", accent && `metric-card--${accent}`, className),
|
|
58
|
+
"data-accent": accent,
|
|
59
|
+
...rest,
|
|
60
|
+
children: [
|
|
61
|
+
/* @__PURE__ */ jsxs("div", { className: "metric-card__head", children: [
|
|
62
|
+
icon && /* @__PURE__ */ jsx("span", { className: "metric-card__icon", "aria-hidden": true, children: icon }),
|
|
63
|
+
/* @__PURE__ */ jsx("span", { className: "metric-card__label", children: label })
|
|
64
|
+
] }),
|
|
65
|
+
/* @__PURE__ */ jsx("div", { className: "metric-card__value", children: value }),
|
|
66
|
+
(deltaEl || caption) && /* @__PURE__ */ jsxs("div", { className: "metric-card__foot", children: [
|
|
67
|
+
deltaEl,
|
|
68
|
+
caption && /* @__PURE__ */ jsx("span", { className: "metric-card__caption", children: caption })
|
|
69
|
+
] }),
|
|
70
|
+
chart && /* @__PURE__ */ jsx("div", { className: "metric-card__chart", children: chart })
|
|
71
|
+
]
|
|
72
|
+
}
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
function meterTone(value, low, high, optimum) {
|
|
76
|
+
if (low === void 0 && high === void 0) return "pos";
|
|
77
|
+
const lo = low ?? -Infinity;
|
|
78
|
+
const hi = high ?? Infinity;
|
|
79
|
+
if (optimum === "high") return value >= hi ? "pos" : value >= lo ? "warn" : "neg";
|
|
80
|
+
if (optimum === "low") return value <= lo ? "pos" : value <= hi ? "warn" : "neg";
|
|
81
|
+
return value >= lo && value <= hi ? "pos" : "warn";
|
|
82
|
+
}
|
|
83
|
+
function Meter({
|
|
84
|
+
value,
|
|
85
|
+
min = 0,
|
|
86
|
+
max = 100,
|
|
87
|
+
low,
|
|
88
|
+
high,
|
|
89
|
+
optimum = "high",
|
|
90
|
+
label,
|
|
91
|
+
valueLabel,
|
|
92
|
+
showValue = true,
|
|
93
|
+
size = "md",
|
|
94
|
+
className,
|
|
95
|
+
...rest
|
|
96
|
+
}) {
|
|
97
|
+
const clamped = Math.min(Math.max(value, min), max);
|
|
98
|
+
const pct = max > min ? (clamped - min) / (max - min) * 100 : 0;
|
|
99
|
+
const tone = meterTone(value, low, high, optimum);
|
|
100
|
+
const hasThresholds = low !== void 0 || high !== void 0;
|
|
101
|
+
const caption = typeof valueLabel === "function" ? valueLabel(value, max) : valueLabel !== void 0 ? valueLabel : `${formatNumber(value)} / ${formatNumber(max)}`;
|
|
102
|
+
return /* @__PURE__ */ jsxs("div", { className: cx("meter", size === "sm" && "meter--sm", className), ...rest, children: [
|
|
103
|
+
(label || showValue) && /* @__PURE__ */ jsxs("div", { className: "meter__head", children: [
|
|
104
|
+
label && /* @__PURE__ */ jsx("span", { className: "meter__label", children: label }),
|
|
105
|
+
showValue && /* @__PURE__ */ jsx("span", { className: "meter__value", children: caption })
|
|
106
|
+
] }),
|
|
107
|
+
/* @__PURE__ */ jsx(
|
|
108
|
+
"div",
|
|
109
|
+
{
|
|
110
|
+
className: "meter__track",
|
|
111
|
+
role: "meter",
|
|
112
|
+
"aria-valuenow": value,
|
|
113
|
+
"aria-valuemin": min,
|
|
114
|
+
"aria-valuemax": max,
|
|
115
|
+
"aria-label": typeof label === "string" ? label : void 0,
|
|
116
|
+
children: /* @__PURE__ */ jsx(
|
|
117
|
+
"div",
|
|
118
|
+
{
|
|
119
|
+
className: cx("meter__fill", hasThresholds ? `meter__fill--${tone}` : "meter__fill--primary"),
|
|
120
|
+
style: { width: `${pct}%` }
|
|
121
|
+
}
|
|
122
|
+
)
|
|
123
|
+
}
|
|
124
|
+
)
|
|
125
|
+
] });
|
|
126
|
+
}
|
|
127
|
+
function Sparkbar({
|
|
128
|
+
data,
|
|
129
|
+
height = 32,
|
|
130
|
+
max,
|
|
131
|
+
color = "var(--color-primary)",
|
|
132
|
+
highlightLast = false,
|
|
133
|
+
gap = 2,
|
|
134
|
+
ariaLabel,
|
|
135
|
+
className,
|
|
136
|
+
style,
|
|
137
|
+
...rest
|
|
138
|
+
}) {
|
|
139
|
+
const ceiling = max ?? Math.max(1, ...data.map((d) => Number.isFinite(d) ? d : 0));
|
|
140
|
+
return /* @__PURE__ */ jsx(
|
|
141
|
+
"div",
|
|
142
|
+
{
|
|
143
|
+
className: cx("sparkbar", className),
|
|
144
|
+
role: "img",
|
|
145
|
+
"aria-label": ariaLabel,
|
|
146
|
+
style: { height, gap, ...style },
|
|
147
|
+
...rest,
|
|
148
|
+
children: data.map((d, i) => {
|
|
149
|
+
const isLast = highlightLast && i === data.length - 1;
|
|
150
|
+
const h = ceiling > 0 ? Math.max(2, Math.max(0, d) / ceiling * 100) : 2;
|
|
151
|
+
return /* @__PURE__ */ jsx(
|
|
152
|
+
"span",
|
|
153
|
+
{
|
|
154
|
+
className: cx("sparkbar__bar", isLast && "sparkbar__bar--last"),
|
|
155
|
+
style: { height: `${h}%`, background: isLast ? "var(--color-secondary)" : color }
|
|
156
|
+
},
|
|
157
|
+
i
|
|
158
|
+
);
|
|
159
|
+
})
|
|
160
|
+
}
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
var CAT_HUES = ["var(--cat-1)", "var(--cat-2)", "var(--cat-4)", "var(--cat-5)", "var(--cat-3)", "var(--cat-6)"];
|
|
164
|
+
function ProportionBar({
|
|
165
|
+
segments,
|
|
166
|
+
total,
|
|
167
|
+
showLegend = true,
|
|
168
|
+
showPercent = true,
|
|
169
|
+
height = 10,
|
|
170
|
+
ariaLabel,
|
|
171
|
+
className,
|
|
172
|
+
...rest
|
|
173
|
+
}) {
|
|
174
|
+
const sum = total ?? segments.reduce((a, s) => a + Math.max(0, s.value), 0);
|
|
175
|
+
const pctOf = (v) => sum > 0 ? Math.max(0, v) / sum * 100 : 0;
|
|
176
|
+
return /* @__PURE__ */ jsxs("div", { className: cx("proportion", className), ...rest, children: [
|
|
177
|
+
/* @__PURE__ */ jsx("div", { className: "proportion__track", role: "img", "aria-label": ariaLabel, style: { height }, children: segments.map((s, i) => {
|
|
178
|
+
const pct = pctOf(s.value);
|
|
179
|
+
if (pct <= 0) return null;
|
|
180
|
+
return /* @__PURE__ */ jsx(
|
|
181
|
+
"span",
|
|
182
|
+
{
|
|
183
|
+
className: "proportion__seg",
|
|
184
|
+
style: { width: `${pct}%`, background: s.color ?? CAT_HUES[i % CAT_HUES.length] },
|
|
185
|
+
title: `${s.label}: ${formatNumber(s.value)}`
|
|
186
|
+
},
|
|
187
|
+
s.label
|
|
188
|
+
);
|
|
189
|
+
}) }),
|
|
190
|
+
showLegend && /* @__PURE__ */ jsx("ul", { className: "proportion__legend", children: segments.map((s, i) => /* @__PURE__ */ jsxs("li", { className: "proportion__legend-item", children: [
|
|
191
|
+
/* @__PURE__ */ jsx("span", { className: "proportion__swatch", style: { background: s.color ?? CAT_HUES[i % CAT_HUES.length] } }),
|
|
192
|
+
/* @__PURE__ */ jsx("span", { className: "proportion__legend-label", children: s.label }),
|
|
193
|
+
showPercent && /* @__PURE__ */ jsxs("span", { className: "proportion__legend-pct", children: [
|
|
194
|
+
formatNumber(pctOf(s.value), { maximumFractionDigits: 0 }),
|
|
195
|
+
"%"
|
|
196
|
+
] })
|
|
197
|
+
] }, s.label)) })
|
|
198
|
+
] });
|
|
199
|
+
}
|
|
200
|
+
function BulletChart({
|
|
201
|
+
value,
|
|
202
|
+
target,
|
|
203
|
+
min = 0,
|
|
204
|
+
max,
|
|
205
|
+
ranges = [],
|
|
206
|
+
label,
|
|
207
|
+
valueLabel,
|
|
208
|
+
tone = "primary",
|
|
209
|
+
height = 22,
|
|
210
|
+
ariaLabel,
|
|
211
|
+
className,
|
|
212
|
+
...rest
|
|
213
|
+
}) {
|
|
214
|
+
const scaleMax = max ?? Math.max(value, target ?? 0, ...ranges, 1);
|
|
215
|
+
const span = scaleMax - min || 1;
|
|
216
|
+
const toPct = (v) => Math.min(100, Math.max(0, (v - min) / span * 100));
|
|
217
|
+
const sortedRanges = [...ranges].sort((a, b) => a - b);
|
|
218
|
+
const bands = sortedRanges.map((r, i) => ({ from: i === 0 ? min : sortedRanges[i - 1], to: r, level: i }));
|
|
219
|
+
return /* @__PURE__ */ jsxs("div", { className: cx("bullet", className), ...rest, children: [
|
|
220
|
+
(label || valueLabel) && /* @__PURE__ */ jsxs("div", { className: "bullet__head", children: [
|
|
221
|
+
label && /* @__PURE__ */ jsx("span", { className: "bullet__label", children: label }),
|
|
222
|
+
valueLabel && /* @__PURE__ */ jsx("span", { className: "bullet__value", children: valueLabel })
|
|
223
|
+
] }),
|
|
224
|
+
/* @__PURE__ */ jsxs(
|
|
225
|
+
"div",
|
|
226
|
+
{
|
|
227
|
+
className: "bullet__track",
|
|
228
|
+
role: "img",
|
|
229
|
+
"aria-label": ariaLabel ?? (typeof label === "string" ? `${label}: ${value}${target !== void 0 ? ` de ${target}` : ""}` : void 0),
|
|
230
|
+
style: { height },
|
|
231
|
+
children: [
|
|
232
|
+
bands.map((b) => /* @__PURE__ */ jsx(
|
|
233
|
+
"span",
|
|
234
|
+
{
|
|
235
|
+
className: "bullet__band",
|
|
236
|
+
style: { left: `${toPct(b.from)}%`, width: `${toPct(b.to) - toPct(b.from)}%`, opacity: 0.12 + b.level * 0.1 }
|
|
237
|
+
},
|
|
238
|
+
b.level
|
|
239
|
+
)),
|
|
240
|
+
/* @__PURE__ */ jsx("span", { className: cx("bullet__measure", `bullet__measure--${tone}`), style: { width: `${toPct(value)}%` } }),
|
|
241
|
+
target !== void 0 && /* @__PURE__ */ jsx("span", { className: "bullet__target", style: { left: `${toPct(target)}%` }, "aria-hidden": true })
|
|
242
|
+
]
|
|
243
|
+
}
|
|
244
|
+
)
|
|
245
|
+
] });
|
|
246
|
+
}
|
|
247
|
+
var LEVELS = 4;
|
|
248
|
+
function CalendarHeatmap({
|
|
249
|
+
data,
|
|
250
|
+
rows = 7,
|
|
251
|
+
max,
|
|
252
|
+
color = "var(--color-primary)",
|
|
253
|
+
cellSize = 13,
|
|
254
|
+
gap = 3,
|
|
255
|
+
legend = true,
|
|
256
|
+
ariaLabel,
|
|
257
|
+
className,
|
|
258
|
+
style,
|
|
259
|
+
...rest
|
|
260
|
+
}) {
|
|
261
|
+
const ceiling = max ?? Math.max(1, ...data.map((d) => Number.isFinite(d.value) ? d.value : 0));
|
|
262
|
+
const bucket = (v) => {
|
|
263
|
+
if (v <= 0) return 0;
|
|
264
|
+
return Math.min(LEVELS, Math.ceil(v / ceiling * LEVELS));
|
|
265
|
+
};
|
|
266
|
+
const tint = (level) => level === 0 ? "var(--bg-muted)" : color;
|
|
267
|
+
const opacity = (level) => level === 0 ? 1 : 0.25 + level / LEVELS * 0.75;
|
|
268
|
+
return /* @__PURE__ */ jsxs("div", { className: cx("heatmap", className), ...rest, children: [
|
|
269
|
+
/* @__PURE__ */ jsx(
|
|
270
|
+
"div",
|
|
271
|
+
{
|
|
272
|
+
className: "heatmap__grid",
|
|
273
|
+
role: "img",
|
|
274
|
+
"aria-label": ariaLabel,
|
|
275
|
+
style: {
|
|
276
|
+
gridTemplateRows: `repeat(${rows}, ${cellSize}px)`,
|
|
277
|
+
gridAutoColumns: `${cellSize}px`,
|
|
278
|
+
gap,
|
|
279
|
+
...style
|
|
280
|
+
},
|
|
281
|
+
children: data.map((d, i) => {
|
|
282
|
+
const level = bucket(d.value);
|
|
283
|
+
return /* @__PURE__ */ jsx(
|
|
284
|
+
"span",
|
|
285
|
+
{
|
|
286
|
+
className: "heatmap__cell",
|
|
287
|
+
style: { background: tint(level), opacity: opacity(level), borderRadius: "var(--radius-sm)" },
|
|
288
|
+
title: d.label != null ? String(d.label) : d.date ? `${d.date}: ${formatNumber(d.value)}` : formatNumber(d.value)
|
|
289
|
+
},
|
|
290
|
+
d.date ?? i
|
|
291
|
+
);
|
|
292
|
+
})
|
|
293
|
+
}
|
|
294
|
+
),
|
|
295
|
+
legend && /* @__PURE__ */ jsxs("div", { className: "heatmap__legend", children: [
|
|
296
|
+
/* @__PURE__ */ jsx("span", { className: "heatmap__legend-text", children: "menos" }),
|
|
297
|
+
Array.from({ length: LEVELS + 1 }, (_, l) => /* @__PURE__ */ jsx("span", { className: "heatmap__cell heatmap__legend-cell", style: { background: tint(l), opacity: opacity(l), borderRadius: "var(--radius-sm)" } }, l)),
|
|
298
|
+
/* @__PURE__ */ jsx("span", { className: "heatmap__legend-text", children: "m\xE1s" })
|
|
299
|
+
] })
|
|
300
|
+
] });
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
export { BulletChart, CalendarHeatmap, DeltaBadge, Meter, ProportionBar, Sparkbar, StatCard };
|
|
304
|
+
//# sourceMappingURL=chunk-2RBVHLG4.mjs.map
|
|
305
|
+
//# sourceMappingURL=chunk-2RBVHLG4.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/Metrics.tsx"],"names":[],"mappings":"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","file":"chunk-2RBVHLG4.mjs","sourcesContent":["'use client';\nimport * as React from 'react';\nimport { cx } from '../utils/cx';\nimport { formatNumber } from '../utils/format';\nimport { ArrowUp, ArrowDown, Minus } from './Icons';\nimport type { CategoryAccent } from './Display';\n\n/**\n * Dashboard data-communication primitives. Everything here is CSS-only — no\n * recharts dependency — so consumers can drop dense micro-viz into cards and\n * table cells without paying for a chart library. The heavyweight charts\n * (Line/Area/Bar/Donut) still live in Charts.tsx behind the BYO-recharts seam.\n */\n\n// ---------- DeltaBadge --------------------------------------------------\n// A sign-driven variation pill: ▲ +12,4% (good) / ▼ −3,1% (bad) / – 0% (flat).\n// Pulls the trend logic that used to be locked inside Stat into a reusable\n// atom for table cells, cards, anywhere. `invert` flips the tone (not the\n// arrow) for \"higher is worse\" metrics like error rate or cost.\nexport type DeltaTone = 'pos' | 'neg' | 'flat';\n\nexport interface DeltaBadgeProps extends Omit<React.HTMLAttributes<HTMLSpanElement>, 'children'> {\n /** The variation. Sign drives direction + tone; magnitude is formatted. */\n value: number;\n /** Full-label formatter (incl. sign). Default: signed percent, e.g. `+12,4%`. */\n format?: (value: number) => string;\n /** Higher-is-worse: an increase shows ▲ but in red (cost, error rate, churn). */\n invert?: boolean;\n /** |value| ≤ this renders flat/neutral. Default 0. */\n neutralThreshold?: number;\n /** Hide the directional arrow, keep the colored number. */\n showIcon?: boolean;\n size?: 'sm' | 'md';\n}\n\nconst SIGNED_PERCENT = (v: number): string => {\n const sign = v > 0 ? '+' : v < 0 ? '−' : '';\n return `${sign}${formatNumber(Math.abs(v), { maximumFractionDigits: 1 })}%`;\n};\n\nexport function DeltaBadge({\n value, format = SIGNED_PERCENT, invert = false, neutralThreshold = 0,\n showIcon = true, size = 'md', className, ...rest\n}: DeltaBadgeProps) {\n const dir: 'up' | 'down' | 'flat' =\n value > neutralThreshold ? 'up' : value < -neutralThreshold ? 'down' : 'flat';\n const tone: DeltaTone =\n dir === 'flat' ? 'flat' : dir === 'up' ? (invert ? 'neg' : 'pos') : (invert ? 'pos' : 'neg');\n const Icon = dir === 'up' ? ArrowUp : dir === 'down' ? ArrowDown : Minus;\n const verb = dir === 'up' ? 'subió' : dir === 'down' ? 'bajó' : 'sin cambio';\n const text = format(value);\n\n return (\n <span\n className={cx('delta-badge', `delta-badge--${tone}`, size === 'sm' && 'delta-badge--sm', className)}\n aria-label={`${verb} ${text}`}\n {...rest}\n >\n {showIcon && <Icon size={size === 'sm' ? 11 : 13} aria-hidden />}\n <span className=\"delta-badge__text\">{text}</span>\n </span>\n );\n}\n\n// ---------- StatCard ----------------------------------------------------\n// The flagship KPI atom: leading icon + label, a large tabular value, a\n// DeltaBadge + comparison caption, and an optional chart slot (Sparkline /\n// Sparkbar). A standalone surface (does not depend on Card) so it composes in\n// any grid. `accent` tints the left edge with a category hue.\nexport interface StatCardProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'title'> {\n label: React.ReactNode;\n value: React.ReactNode;\n /** Convenience: renders a DeltaBadge. Omit and pass `deltaNode` for full control. */\n delta?: number;\n deltaFormat?: (value: number) => string;\n deltaInvert?: boolean;\n /** Escape hatch when `delta` isn't enough (custom node in the delta slot). */\n deltaNode?: React.ReactNode;\n /** Caption next to the delta, e.g. \"vs. mes anterior\". */\n caption?: React.ReactNode;\n /** Small leading glyph rendered in a tinted chip. */\n icon?: React.ReactNode;\n /** Left-edge accent hue. */\n accent?: CategoryAccent;\n /** Micro-viz slot (Sparkline / Sparkbar / ProportionBar). */\n chart?: React.ReactNode;\n}\n\nexport function StatCard({\n label, value, delta, deltaFormat, deltaInvert, deltaNode, caption, icon, accent, chart,\n className, ...rest\n}: StatCardProps) {\n const deltaEl =\n deltaNode ??\n (delta !== undefined ? <DeltaBadge value={delta} format={deltaFormat} invert={deltaInvert} size=\"sm\" /> : null);\n\n return (\n <div\n className={cx('metric-card', accent && `metric-card--${accent}`, className)}\n data-accent={accent}\n {...rest}\n >\n <div className=\"metric-card__head\">\n {icon && <span className=\"metric-card__icon\" aria-hidden>{icon}</span>}\n <span className=\"metric-card__label\">{label}</span>\n </div>\n <div className=\"metric-card__value\">{value}</div>\n {(deltaEl || caption) && (\n <div className=\"metric-card__foot\">\n {deltaEl}\n {caption && <span className=\"metric-card__caption\">{caption}</span>}\n </div>\n )}\n {chart && <div className=\"metric-card__chart\">{chart}</div>}\n </div>\n );\n}\n\n// ---------- Meter -------------------------------------------------------\n// A value within a range, with optional qualitative thresholds that drive the\n// fill tone. Distinct from Progress: `role=\"meter\"` is a static measurement\n// (stock level, budget used, capacity), not a task advancing to 100%.\nexport type MeterOptimum = 'low' | 'high' | 'middle';\n\nexport interface MeterProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'children'> {\n value: number;\n min?: number;\n max?: number;\n /** Lower threshold boundary (for tone zones). */\n low?: number;\n /** Upper threshold boundary (for tone zones). */\n high?: number;\n /** Where \"good\" lives. Default `high` (more is better). */\n optimum?: MeterOptimum;\n label?: React.ReactNode;\n /** Value caption. String or formatter; default shows `value/max`. */\n valueLabel?: React.ReactNode | ((value: number, max: number) => React.ReactNode);\n showValue?: boolean;\n size?: 'sm' | 'md';\n}\n\nfunction meterTone(value: number, low: number | undefined, high: number | undefined, optimum: MeterOptimum): DeltaTone | 'warn' {\n if (low === undefined && high === undefined) return 'pos'; // no thresholds → neutral-positive (primary)\n const lo = low ?? -Infinity;\n const hi = high ?? Infinity;\n if (optimum === 'high') return value >= hi ? 'pos' : value >= lo ? 'warn' : 'neg';\n if (optimum === 'low') return value <= lo ? 'pos' : value <= hi ? 'warn' : 'neg';\n return value >= lo && value <= hi ? 'pos' : 'warn'; // middle\n}\n\nexport function Meter({\n value, min = 0, max = 100, low, high, optimum = 'high',\n label, valueLabel, showValue = true, size = 'md', className, ...rest\n}: MeterProps) {\n const clamped = Math.min(Math.max(value, min), max);\n const pct = max > min ? ((clamped - min) / (max - min)) * 100 : 0;\n const tone = meterTone(value, low, high, optimum);\n const hasThresholds = low !== undefined || high !== undefined;\n const caption =\n typeof valueLabel === 'function' ? valueLabel(value, max)\n : valueLabel !== undefined ? valueLabel\n : `${formatNumber(value)} / ${formatNumber(max)}`;\n\n return (\n <div className={cx('meter', size === 'sm' && 'meter--sm', className)} {...rest}>\n {(label || showValue) && (\n <div className=\"meter__head\">\n {label && <span className=\"meter__label\">{label}</span>}\n {showValue && <span className=\"meter__value\">{caption}</span>}\n </div>\n )}\n <div\n className=\"meter__track\"\n role=\"meter\"\n aria-valuenow={value}\n aria-valuemin={min}\n aria-valuemax={max}\n aria-label={typeof label === 'string' ? label : undefined}\n >\n <div\n className={cx('meter__fill', hasThresholds ? `meter__fill--${tone}` : 'meter__fill--primary')}\n style={{ width: `${pct}%` }}\n />\n </div>\n </div>\n );\n}\n\n// ---------- Sparkbar ----------------------------------------------------\n// Inline mini bars, CSS-only. The bar counterpart to Sparkline — drop a\n// distribution into a table cell or StatCard without recharts.\nexport interface SparkbarProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'children'> {\n data: number[];\n height?: number;\n /** Scale ceiling. Default: max of `data`. */\n max?: number;\n color?: string;\n /** Emphasize the most recent bar (full opacity + secondary hue). */\n highlightLast?: boolean;\n /** Gap between bars in px. Default 2. */\n gap?: number;\n ariaLabel?: string;\n}\n\nexport function Sparkbar({\n data, height = 32, max, color = 'var(--color-primary)', highlightLast = false,\n gap = 2, ariaLabel, className, style, ...rest\n}: SparkbarProps) {\n const ceiling = max ?? Math.max(1, ...data.map((d) => (Number.isFinite(d) ? d : 0)));\n return (\n <div\n className={cx('sparkbar', className)}\n role=\"img\"\n aria-label={ariaLabel}\n style={{ height, gap, ...style }}\n {...rest}\n >\n {data.map((d, i) => {\n const isLast = highlightLast && i === data.length - 1;\n const h = ceiling > 0 ? Math.max(2, (Math.max(0, d) / ceiling) * 100) : 2;\n return (\n <span\n key={i}\n className={cx('sparkbar__bar', isLast && 'sparkbar__bar--last')}\n style={{ height: `${h}%`, background: isLast ? 'var(--color-secondary)' : color }}\n />\n );\n })}\n </div>\n );\n}\n\n// ---------- ProportionBar -----------------------------------------------\n// A single 100%-stacked bar for an inline category breakdown (paid / pending /\n// overdue) — the lightweight alternative to a donut when you just need shares.\nexport interface ProportionSegment {\n label: string;\n value: number;\n color?: string;\n}\n\nexport interface ProportionBarProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'children'> {\n segments: ProportionSegment[];\n /** Denominator override (e.g. show partial fill against a known total). */\n total?: number;\n showLegend?: boolean;\n showPercent?: boolean;\n height?: number;\n ariaLabel?: string;\n}\n\nconst CAT_HUES = ['var(--cat-1)', 'var(--cat-2)', 'var(--cat-4)', 'var(--cat-5)', 'var(--cat-3)', 'var(--cat-6)'];\n\nexport function ProportionBar({\n segments, total, showLegend = true, showPercent = true, height = 10,\n ariaLabel, className, ...rest\n}: ProportionBarProps) {\n const sum = total ?? segments.reduce((a, s) => a + Math.max(0, s.value), 0);\n const pctOf = (v: number) => (sum > 0 ? (Math.max(0, v) / sum) * 100 : 0);\n\n return (\n <div className={cx('proportion', className)} {...rest}>\n <div className=\"proportion__track\" role=\"img\" aria-label={ariaLabel} style={{ height }}>\n {segments.map((s, i) => {\n const pct = pctOf(s.value);\n if (pct <= 0) return null;\n return (\n <span\n key={s.label}\n className=\"proportion__seg\"\n style={{ width: `${pct}%`, background: s.color ?? CAT_HUES[i % CAT_HUES.length] }}\n title={`${s.label}: ${formatNumber(s.value)}`}\n />\n );\n })}\n </div>\n {showLegend && (\n <ul className=\"proportion__legend\">\n {segments.map((s, i) => (\n <li key={s.label} className=\"proportion__legend-item\">\n <span className=\"proportion__swatch\" style={{ background: s.color ?? CAT_HUES[i % CAT_HUES.length] }} />\n <span className=\"proportion__legend-label\">{s.label}</span>\n {showPercent && <span className=\"proportion__legend-pct\">{formatNumber(pctOf(s.value), { maximumFractionDigits: 0 })}%</span>}\n </li>\n ))}\n </ul>\n )}\n </div>\n );\n}\n\n// ---------- BulletChart -------------------------------------------------\n// Stephen Few's bullet graph (2006): a compact actual-vs-target bar over\n// qualitative ranges. Communicates more per pixel than a gauge — built for\n// dense KPI rows. CSS-only.\nexport type BulletTone = 'primary' | 'success' | 'warning' | 'danger';\n\nexport interface BulletChartProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'children'> {\n value: number;\n /** Comparative marker (the vertical tick). */\n target?: number;\n min?: number;\n /** Scale end. Default: max of value/target/ranges. */\n max?: number;\n /** Qualitative range boundaries (ascending). Shaded light→dark behind the bar. */\n ranges?: number[];\n label?: React.ReactNode;\n valueLabel?: React.ReactNode;\n tone?: BulletTone;\n height?: number;\n ariaLabel?: string;\n}\n\nexport function BulletChart({\n value, target, min = 0, max, ranges = [], label, valueLabel, tone = 'primary',\n height = 22, ariaLabel, className, ...rest\n}: BulletChartProps) {\n const scaleMax = max ?? Math.max(value, target ?? 0, ...ranges, 1);\n const span = scaleMax - min || 1;\n const toPct = (v: number) => Math.min(100, Math.max(0, ((v - min) / span) * 100));\n // Build cumulative band stops so each qualitative band has increasing tint.\n const sortedRanges = [...ranges].sort((a, b) => a - b);\n const bands = sortedRanges.map((r, i) => ({ from: i === 0 ? min : sortedRanges[i - 1], to: r, level: i }));\n\n return (\n <div className={cx('bullet', className)} {...rest}>\n {(label || valueLabel) && (\n <div className=\"bullet__head\">\n {label && <span className=\"bullet__label\">{label}</span>}\n {valueLabel && <span className=\"bullet__value\">{valueLabel}</span>}\n </div>\n )}\n <div\n className=\"bullet__track\"\n role=\"img\"\n aria-label={ariaLabel ?? (typeof label === 'string' ? `${label}: ${value}${target !== undefined ? ` de ${target}` : ''}` : undefined)}\n style={{ height }}\n >\n {bands.map((b) => (\n <span\n key={b.level}\n className=\"bullet__band\"\n style={{ left: `${toPct(b.from)}%`, width: `${toPct(b.to) - toPct(b.from)}%`, opacity: 0.12 + b.level * 0.1 }}\n />\n ))}\n <span className={cx('bullet__measure', `bullet__measure--${tone}`)} style={{ width: `${toPct(value)}%` }} />\n {target !== undefined && <span className=\"bullet__target\" style={{ left: `${toPct(target)}%` }} aria-hidden />}\n </div>\n </div>\n );\n}\n\n// ---------- CalendarHeatmap ---------------------------------------------\n// An intensity grid (GitHub-contributions style): cells tinted by value into\n// discrete buckets. Column-major fill (each column = a week, rows = weekdays).\nexport interface HeatmapCell {\n /** Optional key/date for accessibility + keys. */\n date?: string;\n label?: React.ReactNode;\n value: number;\n}\n\nexport interface CalendarHeatmapProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'children'> {\n data: HeatmapCell[];\n /** Rows in the grid (column-major flow). Default 7 (weekdays). */\n rows?: number;\n /** Intensity ceiling. Default: max of `data`. */\n max?: number;\n /** Base hue tinted by intensity. Default primary. */\n color?: string;\n cellSize?: number;\n gap?: number;\n legend?: boolean;\n ariaLabel?: string;\n}\n\nconst LEVELS = 4; // intensity buckets above zero\n\nexport function CalendarHeatmap({\n data, rows = 7, max, color = 'var(--color-primary)', cellSize = 13, gap = 3,\n legend = true, ariaLabel, className, style, ...rest\n}: CalendarHeatmapProps) {\n const ceiling = max ?? Math.max(1, ...data.map((d) => (Number.isFinite(d.value) ? d.value : 0)));\n const bucket = (v: number): number => {\n if (v <= 0) return 0;\n return Math.min(LEVELS, Math.ceil((v / ceiling) * LEVELS));\n };\n // opacity ramp per bucket so the base hue reads light→saturated.\n const tint = (level: number) => (level === 0 ? 'var(--bg-muted)' : color);\n const opacity = (level: number) => (level === 0 ? 1 : 0.25 + (level / LEVELS) * 0.75);\n\n return (\n <div className={cx('heatmap', className)} {...rest}>\n <div\n className=\"heatmap__grid\"\n role=\"img\"\n aria-label={ariaLabel}\n style={{\n gridTemplateRows: `repeat(${rows}, ${cellSize}px)`,\n gridAutoColumns: `${cellSize}px`,\n gap,\n ...style,\n }}\n >\n {data.map((d, i) => {\n const level = bucket(d.value);\n return (\n <span\n key={d.date ?? i}\n className=\"heatmap__cell\"\n style={{ background: tint(level), opacity: opacity(level), borderRadius: 'var(--radius-sm)' }}\n title={d.label != null ? String(d.label) : d.date ? `${d.date}: ${formatNumber(d.value)}` : formatNumber(d.value)}\n />\n );\n })}\n </div>\n {legend && (\n <div className=\"heatmap__legend\">\n <span className=\"heatmap__legend-text\">menos</span>\n {Array.from({ length: LEVELS + 1 }, (_, l) => (\n <span key={l} className=\"heatmap__cell heatmap__legend-cell\" style={{ background: tint(l), opacity: opacity(l), borderRadius: 'var(--radius-sm)' }} />\n ))}\n <span className=\"heatmap__legend-text\">más</span>\n </div>\n )}\n </div>\n );\n}\n"]}
|