@nadicodeai/ui 7.1.3 → 8.0.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.
Files changed (90) hide show
  1. package/AGENTS.md +0 -7
  2. package/dist/components/agent-card.js +1 -1
  3. package/dist/components/agent-visual.js +1 -1
  4. package/dist/components/alert-dialog.js +1 -1
  5. package/dist/components/automation-section.js +1 -1
  6. package/dist/components/card-marketing.d.ts +1 -1
  7. package/dist/components/card-marketing.js +4 -4
  8. package/dist/components/card.js +3 -3
  9. package/dist/components/chart-area-stacked.d.ts +4 -0
  10. package/dist/components/chart-area-stacked.d.ts.map +1 -0
  11. package/dist/components/chart-area-stacked.js +24 -0
  12. package/dist/components/chart-axis.d.ts +38 -0
  13. package/dist/components/chart-axis.d.ts.map +1 -0
  14. package/dist/components/chart-axis.js +83 -0
  15. package/dist/components/chart-bar-multiple.d.ts +9 -0
  16. package/dist/components/chart-bar-multiple.d.ts.map +1 -0
  17. package/dist/components/chart-bar-multiple.js +26 -0
  18. package/dist/components/chart-bar-stacked.d.ts +10 -0
  19. package/dist/components/chart-bar-stacked.d.ts.map +1 -0
  20. package/dist/components/chart-bar-stacked.js +41 -0
  21. package/dist/components/chart-card.d.ts +75 -0
  22. package/dist/components/chart-card.d.ts.map +1 -0
  23. package/dist/components/chart-card.js +71 -0
  24. package/dist/components/chart-date-range-control.d.ts +4 -6
  25. package/dist/components/chart-date-range-control.d.ts.map +1 -1
  26. package/dist/components/chart-date-range-control.js +34 -32
  27. package/dist/components/chart-sparkline.d.ts +6 -42
  28. package/dist/components/chart-sparkline.d.ts.map +1 -1
  29. package/dist/components/chart-sparkline.js +22 -35
  30. package/dist/components/chart.d.ts +3 -4
  31. package/dist/components/chart.d.ts.map +1 -1
  32. package/dist/components/chart.js +7 -16
  33. package/dist/components/combobox.js +1 -1
  34. package/dist/components/command.js +2 -2
  35. package/dist/components/context-menu.js +1 -1
  36. package/dist/components/dialog.js +1 -1
  37. package/dist/components/drawer.js +1 -1
  38. package/dist/components/dropdown-menu.js +1 -1
  39. package/dist/components/glass-control.js +1 -1
  40. package/dist/components/hover-card.js +1 -1
  41. package/dist/components/kpi-card.d.ts +37 -0
  42. package/dist/components/kpi-card.d.ts.map +1 -0
  43. package/dist/components/kpi-card.js +20 -0
  44. package/dist/components/live-demo.js +1 -1
  45. package/dist/components/menubar.js +1 -1
  46. package/dist/components/model-mark.js +1 -1
  47. package/dist/components/navigation-menu.js +2 -2
  48. package/dist/components/popover.d.ts +1 -1
  49. package/dist/components/popover.d.ts.map +1 -1
  50. package/dist/components/popover.js +2 -2
  51. package/dist/components/progress.d.ts +18 -2
  52. package/dist/components/progress.d.ts.map +1 -1
  53. package/dist/components/progress.js +40 -10
  54. package/dist/components/select.js +2 -2
  55. package/dist/components/sheet.js +1 -1
  56. package/dist/components/sidebar.js +1 -1
  57. package/dist/components/toggle.js +1 -1
  58. package/dist/components/trend-badge.d.ts +22 -0
  59. package/dist/components/trend-badge.d.ts.map +1 -0
  60. package/dist/components/trend-badge.js +31 -0
  61. package/dist/index.d.ts +13 -14
  62. package/dist/index.d.ts.map +1 -1
  63. package/dist/index.js +13 -14
  64. package/docs/component-classification.md +1 -1
  65. package/docs/contract.md +19 -21
  66. package/package.json +9 -9
  67. package/dist/components/chart-breakdown-table.d.ts +0 -69
  68. package/dist/components/chart-breakdown-table.d.ts.map +0 -1
  69. package/dist/components/chart-breakdown-table.js +0 -35
  70. package/dist/components/chart-bullet-bar.d.ts +0 -53
  71. package/dist/components/chart-bullet-bar.d.ts.map +0 -1
  72. package/dist/components/chart-bullet-bar.js +0 -63
  73. package/dist/components/chart-delta.d.ts +0 -30
  74. package/dist/components/chart-delta.d.ts.map +0 -1
  75. package/dist/components/chart-delta.js +0 -32
  76. package/dist/components/chart-kpi-stat.d.ts +0 -44
  77. package/dist/components/chart-kpi-stat.d.ts.map +0 -1
  78. package/dist/components/chart-kpi-stat.js +0 -51
  79. package/dist/components/chart-time-series-layout.d.ts +0 -6
  80. package/dist/components/chart-time-series-layout.d.ts.map +0 -1
  81. package/dist/components/chart-time-series-layout.js +0 -6
  82. package/dist/components/chart-time-series.d.ts +0 -103
  83. package/dist/components/chart-time-series.d.ts.map +0 -1
  84. package/dist/components/chart-time-series.js +0 -310
  85. package/dist/components/stat-block.d.ts +0 -24
  86. package/dist/components/stat-block.d.ts.map +0 -1
  87. package/dist/components/stat-block.js +0 -26
  88. package/dist/components/stats-band.d.ts +0 -22
  89. package/dist/components/stats-band.d.ts.map +0 -1
  90. package/dist/components/stats-band.js +0 -32
@@ -1,310 +0,0 @@
1
- "use client";
2
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
- import { Area, AreaChart, Bar, BarChart, BarStack, CartesianGrid, ComposedChart, Line, Rectangle, Text, XAxis, YAxis, } from "recharts";
4
- import { cn } from "../lib/utils.js";
5
- import { timeSeriesLayouts } from "./chart-time-series-layout.js";
6
- import { ChartContainer, ChartLegend, ChartLegendContent, ChartTooltip, ChartTooltipContent, } from "./chart.js";
7
- import { CHART_ENTITY_OTHER_COLOR, entityColor, } from "./chart-entity-colors.js";
8
- /**
9
- * chart-time-series — stacked time-series, categorical bars, and a
10
- * bars-plus-line composition for the console chart kit (issue #166), built on
11
- * the Recharts foundation (ChartContainer / ChartTooltip / ChartLegend; the
12
- * legend's own marks path is the one seam chart.tsx carries for this kit).
13
- *
14
- * Contract highlights (DESIGN.md "Mark specs", "Legends and labels"):
15
- * - A y-axis is ALWAYS present — never axisless stacks.
16
- * - Stacked segments keep a surface gap; the whole stack is wrapped in a
17
- * Recharts `BarStack` so the 4px rounded data-ends sit on the visible top of
18
- * the stack (anchored square at the baseline) even when the last series is
19
- * zero-valued — the radius rounds the stack, not one hard-coded series.
20
- * - The public `valueFormatter` reaches every value surface — the table twin,
21
- * the y-axis ticks, and the tooltip value — not only the hidden table cells.
22
- * - Series and categorical bar colors resolve from the shared page-context
23
- * entity-color map, never from array index. Categorical bars run horizontally
24
- * with every entity label visible; time-series axes keep their even stride.
25
- * - Direct labels are not feasible on stacked dailies, so at two or more
26
- * series a legend identifies every series; a single series is titled by
27
- * the accessible name, not legended.
28
- * - Every chart names a built-in table alternative: a visually hidden
29
- * `<table>` twin renders the same series, and the chart root carries the
30
- * accessible name. Recharts' `accessibilityLayer` (ARIA + arrow-key data
31
- * navigation) stays enabled, and the focusable chart svg carries its own
32
- * `title` so the accessibility tree names the graphic, not just the figure.
33
- * - `TimeSeriesComposed` draws measures with a different unit against a second
34
- * axis: bars for the sums, a line for the rate over them. The shadcn registry
35
- * was searched first (`npx shadcn@latest search @shadcn -q chart`, and
36
- * `view @shadcn/chart-example`) and holds no composed item, so the composition
37
- * is Recharts' `ComposedChart` through this same foundation.
38
- * - A series may carry a `mark`, printed beside its name in the legend. A model
39
- * series uses it for its maker's mark, so color never identifies a model
40
- * alone (rule `model-shown-with-logo`); the mark is decorative there, because
41
- * the legend's own text names the series.
42
- */
43
- /**
44
- * The stock `ChartContainer` suppresses the Recharts svg outline
45
- * (`[&_.recharts-surface]:outline-hidden`) so the default browser focus ring
46
- * never shows, which would leave the `accessibilityLayer` keyboard path with
47
- * no visible focus — and that utility also beats the global base-layer focus
48
- * outline. chart.tsx is untouched, so we restore the canonical treatment
49
- * through the `className` we pass to our own `ChartContainer` instances: this
50
- * `:focus-visible` override sits on a distinct arbitrary-variant selector (a
51
- * different tailwind-merge group AND modifier, so `cn()` keeps both) with
52
- * higher CSS specificity, re-enabling the offset ink outline only while the
53
- * svg is keyboard-focused. `nc-focus-outline` writes `outline-style: solid`
54
- * outright, which is what gets past `outline-hidden` — that utility parks
55
- * `none` in the `--tw-outline-style` variable every Tailwind outline utility
56
- * reads, so a plain `focus-visible:outline` restates a suppression.
57
- */
58
- const CHART_SURFACE_FOCUS_CLASS = "[&_.recharts-surface:focus-visible]:nc-focus-outline " +
59
- "[&_.recharts-surface:focus-visible]:outline-ring";
60
- /**
61
- * The x ticks land at ONE constant stride across the range, and the stride is
62
- * the smallest one whose labels still clear each other at the width the chart
63
- * actually has. Recharts' default `preserveEnd` fitting is greedy on label
64
- * width, so a 30-day axis draws the narrow early labels daily and then switches
65
- * to every other day once "Jul 10" is wider than "Jul 1" — the rhythm breaks
66
- * mid-chart, differently per locale. `equidistantPreserveStart` evaluates one
67
- * stride against every label and raises it until the whole run fits, so the
68
- * axis is even by construction AND survives a narrow viewport: a count fixed at
69
- * the wide case superimposed eight labels into an unreadable smear on a phone,
70
- * and worse in locales whose date labels run longer.
71
- */
72
- const X_AXIS_MIN_TICK_GAP = 48;
73
- /**
74
- * How many ticks the y axis draws, ends included. Recharts divides the data's
75
- * own maximum by this count when it is left to itself, which produces steps of
76
- * whatever the data happens to reach: one console chart read $0/$5/$10 while the
77
- * chart beside it read EUR0/9/18/27/36, and the two stopped sharing a numeric
78
- * register. The domain is rounded up to a step a person counts in instead.
79
- */
80
- const Y_AXIS_TICK_COUNT = 5;
81
- /** The steps a reader counts in, per order of magnitude. */
82
- const NICE_STEP_MULTIPLES = [1, 2, 2.5, 5, 10];
83
- function niceStep(rawStep) {
84
- if (!Number.isFinite(rawStep) || rawStep <= 0)
85
- return 1;
86
- const magnitude = 10 ** Math.floor(Math.log10(rawStep));
87
- const normalized = rawStep / magnitude;
88
- const multiple = NICE_STEP_MULTIPLES.find((candidate) => normalized <= candidate) ?? 10;
89
- return multiple * magnitude;
90
- }
91
- /**
92
- * The stacked maximum across the data, rounded up to a whole number of nice
93
- * steps. Returns `undefined` when there is nothing to bound, so the axis falls
94
- * back to recharts' own domain rather than pinning itself to zero.
95
- */
96
- function niceYDomainMax(data, series) {
97
- let max = 0;
98
- for (const datum of data) {
99
- let total = 0;
100
- for (const entry of series) {
101
- const value = datum[entry.id];
102
- if (typeof value === "number" && Number.isFinite(value))
103
- total += value;
104
- }
105
- if (total > max)
106
- max = total;
107
- }
108
- if (max <= 0)
109
- return undefined;
110
- const step = niceStep(max / (Y_AXIS_TICK_COUNT - 1));
111
- return step * (Y_AXIS_TICK_COUNT - 1);
112
- }
113
- /**
114
- * The largest single value across `series`, rounded up to a whole number of
115
- * nice steps. A composed plot groups its bars instead of stacking them, so its
116
- * axis bounds one value rather than a column's sum. `undefined` when there is
117
- * nothing to bound, or when any value is negative: a diverging read fits its
118
- * own domain instead of being clipped to a zero-based one.
119
- */
120
- function niceSeriesDomainMax(data, series) {
121
- let max = 0;
122
- for (const datum of data) {
123
- for (const entry of series) {
124
- const value = datum[entry.id];
125
- if (typeof value !== "number" || !Number.isFinite(value))
126
- continue;
127
- if (value < 0)
128
- return undefined;
129
- if (value > max)
130
- max = value;
131
- }
132
- }
133
- if (max <= 0)
134
- return undefined;
135
- const step = niceStep(max / (Y_AXIS_TICK_COUNT - 1));
136
- return step * (Y_AXIS_TICK_COUNT - 1);
137
- }
138
- /**
139
- * Synthetic id/key for the single aggregated neutral series. Entities past the
140
- * sixth resolve to the neutral register, and rendering each as its own
141
- * indistinguishable neutral mark is unreadable, so they fold into ONE "Other"
142
- * series (values summed per bucket) in the marks, the legend, and the table
143
- * twin. Unknown to any entity-color map, so `entityColor` resolves it to the
144
- * neutral register.
145
- */
146
- const OTHER_SERIES_ID = "__nc-other-series__";
147
- function defaultFormat(value) {
148
- return value.toLocaleString("en-US");
149
- }
150
- /**
151
- * Route the public `valueFormatter` through the visible tooltip, not only the
152
- * table twin. The stock shadcn tooltip takes a full-row `formatter`, so this
153
- * re-renders the stock row shape — entity swatch, series label, formatted
154
- * value — with each series' own formatter when it declares one and the chart's
155
- * otherwise, so a rate and a sum in one tooltip each read in their own terms. A
156
- * marked series prints its mark here too: rule `model-shown-with-logo` names
157
- * tooltips among the places a model appears, so the row reads swatch, mark,
158
- * name, value — the legend's order.
159
- */
160
- function tooltipRowFormatter(series, valueFormatter) {
161
- const byId = new Map(series.map((entry) => [entry.id, entry]));
162
- return (value, name, item) => {
163
- const entry = byId.get(String(name));
164
- const format = entry?.valueFormatter ?? valueFormatter;
165
- return (_jsxs(_Fragment, { children: [_jsx("span", { "data-slot": "time-series-tooltip-swatch", className: "size-2.5 shrink-0 rounded-xs", style: { backgroundColor: item.color ?? item.payload?.fill } }), entry?.mark, _jsxs("span", { className: "flex flex-1 items-center justify-between gap-2 leading-none", children: [_jsx("span", { className: "text-muted-foreground", children: entry?.label ?? name }), _jsx("span", { className: "font-mono font-medium text-foreground tabular-nums", children: typeof value === "number" ? format(value) : String(value) })] })] }));
166
- };
167
- }
168
- /**
169
- * The chart config the legend and tooltip read: a series' label and its color.
170
- * A series' mark is identity, not part of this record — both surfaces read the
171
- * config's label, so an identity placed here would reach both unasked. The
172
- * legend takes marks through `ChartLegendContent`'s own path instead.
173
- */
174
- function seriesChartConfig(series, entityColorMap) {
175
- return Object.fromEntries(series.map((entry) => [
176
- entry.id,
177
- { label: entry.label, color: entityColor(entityColorMap, entry.id) },
178
- ]));
179
- }
180
- /**
181
- * Each series' mark by data key, for the legend's marks path. `undefined` when
182
- * no series carries one, which leaves a chart of customers or accounts on the
183
- * stock legend.
184
- */
185
- function seriesMarks(series) {
186
- const marked = series.filter((entry) => entry.mark);
187
- return marked.length === 0
188
- ? undefined
189
- : Object.fromEntries(marked.map((entry) => [entry.id, entry.mark]));
190
- }
191
- /**
192
- * Fold every series whose entity color resolves to the neutral "other"
193
- * register into ONE aggregated "Other" series (values summed per bucket).
194
- * Beyond six entities the color map returns the neutral register, and drawing
195
- * each tail entity as its own indistinguishable neutral mark is unreadable —
196
- * so the marks, legend, and table twin all show at most one neutral series.
197
- * Series resolving to a hue pass through unchanged; with no neutral series the
198
- * inputs return untouched (so ≤6 series are never rewritten).
199
- */
200
- function foldNeutralSeries(series, entityColorMap, data, otherLabel) {
201
- const isNeutral = (entry) => entityColor(entityColorMap, entry.id) === CHART_ENTITY_OTHER_COLOR;
202
- const neutral = series.filter(isNeutral);
203
- if (neutral.length === 0) {
204
- return { series: [...series], data: [...data] };
205
- }
206
- const hued = series.filter((entry) => !isNeutral(entry));
207
- const foldedSeries = [...hued, { id: OTHER_SERIES_ID, label: otherLabel }];
208
- const foldedData = data.map((datum) => {
209
- const otherSum = neutral.reduce((sum, entry) => {
210
- const value = datum[entry.id];
211
- return sum + (typeof value === "number" ? value : 0);
212
- }, 0);
213
- const next = { ...datum };
214
- for (const entry of neutral) {
215
- delete next[entry.id];
216
- }
217
- next[OTHER_SERIES_ID] = otherSum;
218
- return next;
219
- });
220
- return { series: foldedSeries, data: foldedData };
221
- }
222
- function TimeSeriesTableAlternative({ data, xKey, xLabel, series, label, valueFormatter, }) {
223
- return (_jsxs("table", { className: "sr-only", children: [_jsx("caption", { className: "sr-only", children: label }), _jsx("thead", { children: _jsxs("tr", { children: [_jsx("th", { scope: "col", children: xLabel }), series.map((entry) => (_jsx("th", { scope: "col", children: entry.label }, entry.id)))] }) }), _jsx("tbody", { children: data.map((datum, index) => (_jsxs("tr", { children: [_jsx("th", { scope: "row", children: String(datum[xKey] ?? "") }), series.map((entry) => {
224
- const value = datum[entry.id];
225
- return (_jsx("td", { children: typeof value === "number"
226
- ? (entry.valueFormatter ?? valueFormatter)(value)
227
- : String(value ?? "") }, entry.id));
228
- })] }, index))) })] }));
229
- }
230
- function TimeSeriesFrame({ label, className, children, ...props }) {
231
- return (_jsx("figure", { "aria-label": label, className: cn("m-0 flex w-full flex-col", className), ...props, children: children }));
232
- }
233
- function TimeSeriesBars({ data, xKey, series, entityColorMap, label, xLabel = "Period", valueFormatter = defaultFormat, yAxisWidth, otherLabel = "Other", barCategoryKey, size = "default", className, ...props }) {
234
- if (barCategoryKey) {
235
- const config = seriesChartConfig(series, entityColorMap);
236
- const coloredData = data.map((datum) => ({
237
- ...datum,
238
- fill: entityColor(entityColorMap, String(datum[barCategoryKey] ?? "")),
239
- }));
240
- const domainMax = niceYDomainMax(data, series);
241
- return (_jsxs(TimeSeriesFrame, { "data-slot": "categorical-bars", label: label, className: className, ...props, children: [_jsx(ChartContainer, { config: config, className: cn("w-full aspect-auto", CHART_SURFACE_FOCUS_CLASS), style: { height: Math.max(256, data.length * 64 + 40) }, children: _jsxs(BarChart, { accessibilityLayer: true, title: label, data: coloredData, layout: "vertical", children: [_jsx(CartesianGrid, { horizontal: false }), _jsx(XAxis, { type: "number", tickLine: false, axisLine: false, tickCount: 3, ...(domainMax === undefined ? {} : { domain: [0, domainMax] }), tickFormatter: (value) => valueFormatter(Number(value)) }), _jsx(YAxis, { type: "category", dataKey: xKey, width: 120, interval: 0, tickLine: false, axisLine: false, tick: ({ x, y, payload }) => (_jsx(Text, { x: x, y: y, width: 108, breakAll: true, maxLines: 2, "aria-label": String(payload.value), textAnchor: "end", verticalAnchor: "middle", className: "fill-muted-foreground", children: String(payload.value) })) }), _jsx(ChartTooltip, { content: _jsx(ChartTooltipContent, { formatter: (value, name, item) => tooltipRowFormatter(series, valueFormatter)(value, name, {
242
- ...item,
243
- color: item.payload?.fill,
244
- }, 0, []) }) }), series.map((entry) => (_jsx(Bar, { dataKey: entry.id, isAnimationActive: false, maxBarSize: 24, shape: ({ x, y, width, height, originalDataIndex }) => (_jsx(Rectangle, { x: x, y: y, width: width, height: height, radius: [0, 4, 4, 0], fill: coloredData[originalDataIndex]?.fill })) }, entry.id)))] }) }), _jsx(TimeSeriesTableAlternative, { data: data, xKey: xKey, xLabel: xLabel, series: series, label: label, valueFormatter: valueFormatter })] }));
245
- }
246
- const folded = foldNeutralSeries(series, entityColorMap, data, otherLabel);
247
- const config = seriesChartConfig(folded.series, entityColorMap);
248
- const marks = seriesMarks(folded.series);
249
- const yDomainMax = niceYDomainMax(folded.data, folded.series);
250
- return (_jsxs(TimeSeriesFrame, { "data-slot": "time-series-bars", label: label, className: cn(timeSeriesLayouts[size].frame, className), ...props, children: [_jsx(ChartContainer, { config: config, className: cn("w-full aspect-auto", timeSeriesLayouts[size].chart, CHART_SURFACE_FOCUS_CLASS), children: _jsxs(BarChart, { accessibilityLayer: true, title: label, data: folded.data, children: [_jsx(CartesianGrid, { vertical: false }), _jsx(XAxis, { dataKey: xKey, tickLine: false, axisLine: false, tickMargin: 8, interval: "equidistantPreserveStart", minTickGap: X_AXIS_MIN_TICK_GAP }), _jsx(YAxis, { tickLine: false, axisLine: false, width: yAxisWidth ?? "auto", tickCount: Y_AXIS_TICK_COUNT, ...(yDomainMax === undefined ? {} : { domain: [0, yDomainMax] }), tickFormatter: (value) => valueFormatter(Number(value)) }), _jsx(ChartTooltip, { content: _jsx(ChartTooltipContent, { formatter: tooltipRowFormatter(folded.series, valueFormatter) }) }), folded.series.length > 1 ? (_jsx(ChartLegend, { content: _jsx(ChartLegendContent, { marks: marks }) })) : null, _jsx(BarStack, { radius: [4, 4, 0, 0], children: folded.series.map((entry) => (_jsx(Bar, { dataKey: entry.id, isAnimationActive: false, maxBarSize: 24, fill: entityColor(entityColorMap, entry.id), stroke: "var(--background)", strokeWidth: 2 }, entry.id))) })] }) }), _jsx(TimeSeriesTableAlternative, { data: folded.data, xKey: xKey, xLabel: xLabel, series: folded.series, label: label, valueFormatter: valueFormatter })] }));
251
- }
252
- function TimeSeriesArea({ data, xKey, series, entityColorMap, label, xLabel = "Period", valueFormatter = defaultFormat, yAxisWidth, otherLabel = "Other", size = "default", className, ...props }) {
253
- const folded = foldNeutralSeries(series, entityColorMap, data, otherLabel);
254
- const config = seriesChartConfig(folded.series, entityColorMap);
255
- const marks = seriesMarks(folded.series);
256
- const yDomainMax = niceYDomainMax(folded.data, folded.series);
257
- return (_jsxs(TimeSeriesFrame, { "data-slot": "time-series-area", label: label, className: cn(timeSeriesLayouts[size].frame, className), ...props, children: [_jsx(ChartContainer, { config: config, className: cn("w-full aspect-auto", timeSeriesLayouts[size].chart, CHART_SURFACE_FOCUS_CLASS), children: _jsxs(AreaChart, { accessibilityLayer: true, title: label, data: folded.data, margin: { top: 5, right: 20, bottom: 5, left: 20 }, children: [_jsx(CartesianGrid, { vertical: false }), _jsx(XAxis, { dataKey: xKey, tickLine: false, axisLine: false, tickMargin: 8, interval: "equidistantPreserveStart", minTickGap: X_AXIS_MIN_TICK_GAP }), _jsx(YAxis, { tickLine: false, axisLine: false, width: yAxisWidth ?? "auto", tickCount: Y_AXIS_TICK_COUNT, ...(yDomainMax === undefined ? {} : { domain: [0, yDomainMax] }), tickFormatter: (value) => valueFormatter(Number(value)) }), _jsx(ChartTooltip, { content: _jsx(ChartTooltipContent, { formatter: tooltipRowFormatter(folded.series, valueFormatter) }) }), folded.series.length > 1 ? (_jsx(ChartLegend, { content: _jsx(ChartLegendContent, { marks: marks }) })) : null, folded.series.map((entry) => {
258
- const color = entityColor(entityColorMap, entry.id);
259
- return (_jsx(Area, { type: "monotone", dataKey: entry.id, isAnimationActive: false, stackId: "stacked", stroke: color, strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round", fill: color,
260
- // DESIGN.md mark specs: an area fill reads as a light wash
261
- // (roughly 10% opacity) under a full-strength line.
262
- fillOpacity: 0.1,
263
- // Recharts' default activeDot ring is stroke #fff, which the
264
- // chart foundation remaps to transparent — an explicit surface
265
- // ring keeps the DESIGN.md 2px marker-ring spec on hover/focus.
266
- activeDot: {
267
- r: 4,
268
- stroke: "var(--background)",
269
- strokeWidth: 2,
270
- } }, entry.id));
271
- })] }) }), _jsx(TimeSeriesTableAlternative, { data: folded.data, xKey: xKey, xLabel: xLabel, series: folded.series, label: label, valueFormatter: valueFormatter })] }));
272
- }
273
- /**
274
- * Bars for the measures, a line over them for the rate, and a second axis for
275
- * the rate's own unit. The shadcn registry holds no composed chart item (see the
276
- * file header), so this is Recharts' `ComposedChart` through the same
277
- * foundation, and it keeps the kit's whole contract: a named figure, a
278
- * `accessibilityLayer` graphic with its own title, a legend at two or more
279
- * series, an entity-colored pair of marks, and a table twin carrying each
280
- * series in its own terms.
281
- */
282
- function TimeSeriesComposed({ data, xKey, series, entityColorMap, label, xLabel = "Period", valueFormatter = defaultFormat, yAxisWidth, size = "default", className, ...props }) {
283
- const config = seriesChartConfig(series, entityColorMap);
284
- const marks = seriesMarks(series);
285
- const leftSeries = series.filter((entry) => entry.axis !== "right");
286
- const rightSeries = series.filter((entry) => entry.axis === "right");
287
- const leftDomainMax = niceSeriesDomainMax(data, leftSeries);
288
- const rightDomainMax = niceSeriesDomainMax(data, rightSeries);
289
- const leftFormatter = leftSeries[0]?.valueFormatter ?? valueFormatter;
290
- const rightFormatter = rightSeries[0]?.valueFormatter ?? valueFormatter;
291
- return (_jsxs(TimeSeriesFrame, { "data-slot": "time-series-composed", label: label, className: cn(timeSeriesLayouts[size].frame, className), ...props, children: [_jsx(ChartContainer, { config: config, className: cn("w-full aspect-auto", timeSeriesLayouts[size].chart, CHART_SURFACE_FOCUS_CLASS), children: _jsxs(ComposedChart, { accessibilityLayer: true, title: label, data: data, children: [_jsx(CartesianGrid, { vertical: false }), _jsx(XAxis, { dataKey: xKey, tickLine: false, axisLine: false, tickMargin: 8, interval: "equidistantPreserveStart", minTickGap: X_AXIS_MIN_TICK_GAP }), _jsx(YAxis, { yAxisId: "left", tickLine: false, axisLine: false, width: yAxisWidth ?? "auto", tickCount: Y_AXIS_TICK_COUNT, ...(leftDomainMax === undefined
292
- ? {}
293
- : { domain: [0, leftDomainMax] }), tickFormatter: (value) => leftFormatter(Number(value)) }), rightSeries.length > 0 ? (_jsx(YAxis, { yAxisId: "right", orientation: "right", tickLine: false, axisLine: false, width: yAxisWidth ?? "auto", tickCount: Y_AXIS_TICK_COUNT, ...(rightDomainMax === undefined
294
- ? {}
295
- : { domain: [0, rightDomainMax] }), tickFormatter: (value) => rightFormatter(Number(value)) })) : null, _jsx(ChartTooltip, { content: _jsx(ChartTooltipContent, { formatter: tooltipRowFormatter(series, valueFormatter) }) }), series.length > 1 ? (_jsx(ChartLegend, { content: _jsx(ChartLegendContent, { marks: marks }) })) : null, series.map((entry) => {
296
- const color = entityColor(entityColorMap, entry.id);
297
- const yAxisId = entry.axis === "right" ? "right" : "left";
298
- return entry.kind === "bar" ? (_jsx(Bar, { yAxisId: yAxisId, dataKey: entry.id, isAnimationActive: false, maxBarSize: 24, radius: [4, 4, 0, 0], fill: color, fillOpacity: entry.fillOpacity }, entry.id)) : (_jsx(Line, { yAxisId: yAxisId, type: "monotone", dataKey: entry.id, isAnimationActive: false, stroke: color, strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round",
299
- // DESIGN.md mark specs: a point marker runs at least 8px with a
300
- // 2px surface ring, and the marker itself carries the series'
301
- // hue — Recharts fills a dot white, which reads as a hole on the
302
- // light surface and stays white in dark mode. Both markers are
303
- // configured: the active one arrives ringless, because the chart
304
- // foundation maps Recharts' white active stroke to transparent.
305
- dot: entry.dot
306
- ? { r: 4, fill: color, stroke: "var(--background)", strokeWidth: 2 }
307
- : false, activeDot: { r: 4, stroke: "var(--background)", strokeWidth: 2 } }, entry.id));
308
- })] }) }), _jsx(TimeSeriesTableAlternative, { data: data, xKey: xKey, xLabel: xLabel, series: series, label: label, valueFormatter: valueFormatter })] }));
309
- }
310
- export { TimeSeriesArea, TimeSeriesBars, TimeSeriesComposed };
@@ -1,24 +0,0 @@
1
- import * as React from "react";
2
- /**
3
- * stat-block — single statistic fill (replaces the retired `.nc-stat-block`).
4
- *
5
- * A large tabular numeral over a mono label. Seam-free canvas fill: it drops into
6
- * the `StatsBand` grid which owns the hairline dividers (gap-as-seam), so it draws
7
- * NO border and is square-cornered. `surface="ink"` inverts onto the ink role for
8
- * a dark band; inside a `StatsBand surface="night"` the ancestor `.dark` class
9
- * flips the default canvas roles, so a plain `StatBlock` reads dark there.
10
- * `StatBlockFigureAccent` marks the verde substring of a figure (`text-primary`).
11
- * Token-only; no `dark:` styling.
12
- */
13
- declare function StatBlock({ className, surface, ...props }: React.ComponentProps<"div"> & {
14
- surface?: "canvas" | "ink";
15
- }): React.JSX.Element;
16
- declare function StatBlockFigure({ className, surface, ...props }: React.ComponentProps<"div"> & {
17
- surface?: "canvas" | "ink";
18
- }): React.JSX.Element;
19
- declare function StatBlockFigureAccent({ className, ...props }: React.ComponentProps<"span">): React.JSX.Element;
20
- declare function StatBlockLabel({ className, surface, ...props }: React.ComponentProps<"div"> & {
21
- surface?: "canvas" | "ink";
22
- }): React.JSX.Element;
23
- export { StatBlock, StatBlockFigure, StatBlockFigureAccent, StatBlockLabel };
24
- //# sourceMappingURL=stat-block.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"stat-block.d.ts","sourceRoot":"","sources":["../../src/components/stat-block.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAI/B;;;;;;;;;;GAUG;AAEH,iBAAS,SAAS,CAAC,EACjB,SAAS,EACT,OAAkB,EAClB,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,GAAG;IAAE,OAAO,CAAC,EAAE,QAAQ,GAAG,KAAK,CAAA;CAAE,qBAa9D;AAED,iBAAS,eAAe,CAAC,EACvB,SAAS,EACT,OAAkB,EAClB,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,GAAG;IAAE,OAAO,CAAC,EAAE,QAAQ,GAAG,KAAK,CAAA;CAAE,qBAY9D;AAED,iBAAS,qBAAqB,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,KAAK,CAAC,cAAc,CAAC,MAAM,CAAC,qBAQnF;AAED,iBAAS,cAAc,CAAC,EACtB,SAAS,EACT,OAAkB,EAClB,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,GAAG;IAAE,OAAO,CAAC,EAAE,QAAQ,GAAG,KAAK,CAAA;CAAE,qBAY9D;AAED,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,qBAAqB,EAAE,cAAc,EAAE,CAAC"}
@@ -1,26 +0,0 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
2
- import { cn } from "../lib/utils.js";
3
- /**
4
- * stat-block — single statistic fill (replaces the retired `.nc-stat-block`).
5
- *
6
- * A large tabular numeral over a mono label. Seam-free canvas fill: it drops into
7
- * the `StatsBand` grid which owns the hairline dividers (gap-as-seam), so it draws
8
- * NO border and is square-cornered. `surface="ink"` inverts onto the ink role for
9
- * a dark band; inside a `StatsBand surface="night"` the ancestor `.dark` class
10
- * flips the default canvas roles, so a plain `StatBlock` reads dark there.
11
- * `StatBlockFigureAccent` marks the verde substring of a figure (`text-primary`).
12
- * Token-only; no `dark:` styling.
13
- */
14
- function StatBlock({ className, surface = "canvas", ...props }) {
15
- return (_jsx("div", { "data-slot": "stat-block", "data-surface": surface, className: cn("grid min-h-24 content-center gap-1.5 p-6", surface === "ink" ? "bg-primary" : "bg-card", className), ...props }));
16
- }
17
- function StatBlockFigure({ className, surface = "canvas", ...props }) {
18
- return (_jsx("div", { "data-slot": "stat-block-figure", className: cn("text-[clamp(2rem,3.2vw,2.875rem)] leading-none font-bold tabular-nums", surface === "ink" ? "text-primary-foreground" : "text-foreground", className), ...props }));
19
- }
20
- function StatBlockFigureAccent({ className, ...props }) {
21
- return (_jsx("span", { "data-slot": "stat-block-figure-accent", className: cn("text-primary", className), ...props }));
22
- }
23
- function StatBlockLabel({ className, surface = "canvas", ...props }) {
24
- return (_jsx("div", { "data-slot": "stat-block-label", className: cn("font-mono text-xs uppercase", surface === "ink" ? "text-primary-foreground" : "text-muted-foreground", className), ...props }));
25
- }
26
- export { StatBlock, StatBlockFigure, StatBlockFigureAccent, StatBlockLabel };
@@ -1,22 +0,0 @@
1
- import * as React from "react";
2
- /**
3
- * stats-band — proof row of headline statistics (replaces the retired
4
- * `sections/stats-band.css` + its coupled `stat-block` cells).
5
- *
6
- * SINGLE-OWNER SEAM: the dividers between blocks are owned once by the grid
7
- * (`gap-px bg-border` = the retired gap-as-seam). Each `StatBlock` is a borderless,
8
- * square-cornered fill, so no coordinate carries two lines. `surface="ink"` flips
9
- * the band onto the ink role; only the seam COLOUR changes (the grid background
10
- * mixes toward the surface), never its weight, so the grid keeps sole ownership.
11
- *
12
- * `surface="night"` is the always-dark V5 stats band (B12): rather than a `dark:`
13
- * utility (forbidden) it sets the `dark` ancestor class on the grid root, so the
14
- * seam (`bg-border`) and every `StatBlock` role resolve to their dark value while
15
- * the grid keeps its plain canvas seam. Token-only; no `dark:` styling.
16
- */
17
- declare function StatsBand({ className, columns, surface, ...props }: React.ComponentProps<"div"> & {
18
- columns?: 2 | 3 | 4 | 5 | 6;
19
- surface?: "canvas" | "ink" | "night";
20
- }): React.JSX.Element;
21
- export { StatsBand };
22
- //# sourceMappingURL=stats-band.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"stats-band.d.ts","sourceRoot":"","sources":["../../src/components/stats-band.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAa/B;;;;;;;;;;;;;;GAcG;AAEH,iBAAS,SAAS,CAAC,EACjB,SAAS,EACT,OAAW,EACX,OAAkB,EAClB,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,GAAG;IAC/B,OAAO,CAAC,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAC5B,OAAO,CAAC,EAAE,QAAQ,GAAG,KAAK,GAAG,OAAO,CAAC;CACtC,qBAmBA;AAED,OAAO,EAAE,SAAS,EAAE,CAAC"}
@@ -1,32 +0,0 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
2
- import { cn } from "../lib/utils.js";
3
- import { SeamGrid } from "./seam-grid.js";
4
- const STATS_BAND_COLUMNS = {
5
- 2: "grid-cols-1 sm:grid-cols-2",
6
- 3: "grid-cols-1 sm:grid-cols-3",
7
- 4: "grid-cols-2 lg:grid-cols-4",
8
- 5: "grid-cols-2 lg:grid-cols-5",
9
- 6: "grid-cols-2 lg:grid-cols-6",
10
- };
11
- /**
12
- * stats-band — proof row of headline statistics (replaces the retired
13
- * `sections/stats-band.css` + its coupled `stat-block` cells).
14
- *
15
- * SINGLE-OWNER SEAM: the dividers between blocks are owned once by the grid
16
- * (`gap-px bg-border` = the retired gap-as-seam). Each `StatBlock` is a borderless,
17
- * square-cornered fill, so no coordinate carries two lines. `surface="ink"` flips
18
- * the band onto the ink role; only the seam COLOUR changes (the grid background
19
- * mixes toward the surface), never its weight, so the grid keeps sole ownership.
20
- *
21
- * `surface="night"` is the always-dark V5 stats band (B12): rather than a `dark:`
22
- * utility (forbidden) it sets the `dark` ancestor class on the grid root, so the
23
- * seam (`bg-border`) and every `StatBlock` role resolve to their dark value while
24
- * the grid keeps its plain canvas seam. Token-only; no `dark:` styling.
25
- */
26
- function StatsBand({ className, columns = 4, surface = "canvas", ...props }) {
27
- // night carries the plain canvas seam and lets the `dark` root flip its colour;
28
- // ink/canvas pass straight through to the surface-driven seam colour.
29
- const seamSurface = surface === "night" ? "canvas" : surface;
30
- return (_jsx(SeamGrid, { "data-slot": "stats-band", "data-surface": surface, surface: seamSurface, className: cn("h-full", surface === "night" && "dark", STATS_BAND_COLUMNS[columns], className), ...props }));
31
- }
32
- export { StatsBand };