@nadicodeai/ui 7.1.4 → 8.0.1
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/AGENTS.md +0 -7
- package/dist/components/agent-card.js +1 -1
- package/dist/components/agent-visual.js +1 -1
- package/dist/components/alert-dialog.js +1 -1
- package/dist/components/automation-section.js +1 -1
- package/dist/components/card-marketing.d.ts +1 -1
- package/dist/components/card-marketing.js +4 -4
- package/dist/components/card.js +3 -3
- package/dist/components/chart-area-stacked.d.ts +4 -0
- package/dist/components/chart-area-stacked.d.ts.map +1 -0
- package/dist/components/chart-area-stacked.js +24 -0
- package/dist/components/chart-axis.d.ts +38 -0
- package/dist/components/chart-axis.d.ts.map +1 -0
- package/dist/components/chart-axis.js +83 -0
- package/dist/components/chart-bar-multiple.d.ts +9 -0
- package/dist/components/chart-bar-multiple.d.ts.map +1 -0
- package/dist/components/chart-bar-multiple.js +26 -0
- package/dist/components/chart-bar-stacked.d.ts +10 -0
- package/dist/components/chart-bar-stacked.d.ts.map +1 -0
- package/dist/components/chart-bar-stacked.js +41 -0
- package/dist/components/chart-card.d.ts +75 -0
- package/dist/components/chart-card.d.ts.map +1 -0
- package/dist/components/chart-card.js +71 -0
- package/dist/components/chart-date-range-control.d.ts +4 -6
- package/dist/components/chart-date-range-control.d.ts.map +1 -1
- package/dist/components/chart-date-range-control.js +43 -33
- package/dist/components/chart-sparkline.d.ts +6 -42
- package/dist/components/chart-sparkline.d.ts.map +1 -1
- package/dist/components/chart-sparkline.js +22 -35
- package/dist/components/chart.d.ts +3 -4
- package/dist/components/chart.d.ts.map +1 -1
- package/dist/components/chart.js +7 -16
- package/dist/components/combobox.js +1 -1
- package/dist/components/command.js +2 -2
- package/dist/components/context-menu.js +1 -1
- package/dist/components/dialog.js +1 -1
- package/dist/components/drawer.js +1 -1
- package/dist/components/dropdown-menu.js +1 -1
- package/dist/components/glass-control.js +1 -1
- package/dist/components/hover-card.js +1 -1
- package/dist/components/kpi-card.d.ts +37 -0
- package/dist/components/kpi-card.d.ts.map +1 -0
- package/dist/components/kpi-card.js +20 -0
- package/dist/components/live-demo.js +1 -1
- package/dist/components/menubar.js +1 -1
- package/dist/components/model-mark.js +1 -1
- package/dist/components/navigation-menu.js +2 -2
- package/dist/components/popover.d.ts +1 -1
- package/dist/components/popover.d.ts.map +1 -1
- package/dist/components/popover.js +2 -2
- package/dist/components/progress.d.ts +18 -2
- package/dist/components/progress.d.ts.map +1 -1
- package/dist/components/progress.js +40 -10
- package/dist/components/select.js +2 -2
- package/dist/components/sheet.js +1 -1
- package/dist/components/sidebar.js +1 -1
- package/dist/components/toggle.js +1 -1
- package/dist/components/trend-badge.d.ts +22 -0
- package/dist/components/trend-badge.d.ts.map +1 -0
- package/dist/components/trend-badge.js +31 -0
- package/dist/index.d.ts +13 -14
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +13 -14
- package/docs/component-classification.md +1 -1
- package/docs/contract.md +19 -21
- package/package.json +2 -2
- package/dist/components/chart-breakdown-table.d.ts +0 -69
- package/dist/components/chart-breakdown-table.d.ts.map +0 -1
- package/dist/components/chart-breakdown-table.js +0 -35
- package/dist/components/chart-bullet-bar.d.ts +0 -53
- package/dist/components/chart-bullet-bar.d.ts.map +0 -1
- package/dist/components/chart-bullet-bar.js +0 -63
- package/dist/components/chart-delta.d.ts +0 -30
- package/dist/components/chart-delta.d.ts.map +0 -1
- package/dist/components/chart-delta.js +0 -32
- package/dist/components/chart-kpi-stat.d.ts +0 -44
- package/dist/components/chart-kpi-stat.d.ts.map +0 -1
- package/dist/components/chart-kpi-stat.js +0 -51
- package/dist/components/chart-time-series-layout.d.ts +0 -6
- package/dist/components/chart-time-series-layout.d.ts.map +0 -1
- package/dist/components/chart-time-series-layout.js +0 -6
- package/dist/components/chart-time-series.d.ts +0 -103
- package/dist/components/chart-time-series.d.ts.map +0 -1
- package/dist/components/chart-time-series.js +0 -310
- package/dist/components/stat-block.d.ts +0 -24
- package/dist/components/stat-block.d.ts.map +0 -1
- package/dist/components/stat-block.js +0 -26
- package/dist/components/stats-band.d.ts +0 -22
- package/dist/components/stats-band.d.ts.map +0 -1
- 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 };
|