@nadicodeai/ui 0.5.2 → 0.6.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 (92) hide show
  1. package/AGENTS.md +12 -3
  2. package/README.md +18 -1
  3. package/dist/components/accordion.js +1 -1
  4. package/dist/components/alert-dialog.js +5 -5
  5. package/dist/components/alert.js +1 -1
  6. package/dist/components/badge.js +4 -4
  7. package/dist/components/button-group.js +4 -4
  8. package/dist/components/button.d.ts.map +1 -1
  9. package/dist/components/button.js +12 -12
  10. package/dist/components/calendar.js +2 -2
  11. package/dist/components/card.js +3 -3
  12. package/dist/components/chart-breakdown-table.d.ts +39 -0
  13. package/dist/components/chart-breakdown-table.d.ts.map +1 -0
  14. package/dist/components/chart-breakdown-table.js +29 -0
  15. package/dist/components/chart-bullet-bar.d.ts +36 -0
  16. package/dist/components/chart-bullet-bar.d.ts.map +1 -0
  17. package/dist/components/chart-bullet-bar.js +30 -0
  18. package/dist/components/chart-date-range-control.d.ts +12 -0
  19. package/dist/components/chart-date-range-control.d.ts.map +1 -0
  20. package/dist/components/chart-date-range-control.js +59 -0
  21. package/dist/components/chart-date-range.d.ts +38 -0
  22. package/dist/components/chart-date-range.d.ts.map +1 -0
  23. package/dist/components/chart-date-range.js +63 -0
  24. package/dist/components/chart-delta.d.ts +28 -0
  25. package/dist/components/chart-delta.d.ts.map +1 -0
  26. package/dist/components/chart-delta.js +32 -0
  27. package/dist/components/chart-entity-colors.d.ts +37 -0
  28. package/dist/components/chart-entity-colors.d.ts.map +1 -0
  29. package/dist/components/chart-entity-colors.js +68 -0
  30. package/dist/components/chart-kpi-stat.d.ts +33 -0
  31. package/dist/components/chart-kpi-stat.d.ts.map +1 -0
  32. package/dist/components/chart-kpi-stat.js +7 -0
  33. package/dist/components/chart-sparkline.d.ts +45 -0
  34. package/dist/components/chart-sparkline.d.ts.map +1 -0
  35. package/dist/components/chart-sparkline.js +44 -0
  36. package/dist/components/chart-time-series.d.ts +33 -0
  37. package/dist/components/chart-time-series.d.ts.map +1 -0
  38. package/dist/components/chart-time-series.js +136 -0
  39. package/dist/components/chart.js +1 -1
  40. package/dist/components/checkbox.js +1 -1
  41. package/dist/components/combobox.js +5 -5
  42. package/dist/components/command.js +4 -4
  43. package/dist/components/context-menu.js +6 -6
  44. package/dist/components/dialog.d.ts +8 -4
  45. package/dist/components/dialog.d.ts.map +1 -1
  46. package/dist/components/dialog.js +6 -6
  47. package/dist/components/drawer.d.ts.map +1 -1
  48. package/dist/components/drawer.js +4 -4
  49. package/dist/components/dropdown-menu.js +7 -7
  50. package/dist/components/empty.js +2 -2
  51. package/dist/components/field.js +3 -3
  52. package/dist/components/hover-card.js +1 -1
  53. package/dist/components/input-group.js +5 -5
  54. package/dist/components/input-otp.d.ts.map +1 -1
  55. package/dist/components/input-otp.js +8 -3
  56. package/dist/components/input.js +1 -1
  57. package/dist/components/item.d.ts.map +1 -1
  58. package/dist/components/item.js +5 -5
  59. package/dist/components/menubar.js +8 -8
  60. package/dist/components/native-select.js +1 -1
  61. package/dist/components/navigation-menu.js +5 -5
  62. package/dist/components/popover.js +1 -1
  63. package/dist/components/radio-group.js +1 -1
  64. package/dist/components/resizable.js +1 -1
  65. package/dist/components/scroll-area.js +1 -1
  66. package/dist/components/select.js +4 -4
  67. package/dist/components/sheet.d.ts +4 -2
  68. package/dist/components/sheet.d.ts.map +1 -1
  69. package/dist/components/sheet.js +5 -5
  70. package/dist/components/sidebar.d.ts +7 -3
  71. package/dist/components/sidebar.d.ts.map +1 -1
  72. package/dist/components/sidebar.js +16 -16
  73. package/dist/components/slider.js +1 -1
  74. package/dist/components/spinner.js +1 -1
  75. package/dist/components/switch.js +1 -1
  76. package/dist/components/tabs.js +2 -2
  77. package/dist/components/textarea.js +1 -1
  78. package/dist/components/theme-mode-switcher.js +1 -1
  79. package/dist/components/toggle-group.js +2 -2
  80. package/dist/components/toggle.js +5 -5
  81. package/dist/components/tooltip.js +1 -1
  82. package/dist/index.d.ts +9 -0
  83. package/dist/index.d.ts.map +1 -1
  84. package/dist/index.js +9 -0
  85. package/dist/styles/globals.css +25 -3
  86. package/dist/styles/shadcn.tailwind.css +629 -0
  87. package/docs/agents/nadicodeai-ui.md +24 -1
  88. package/docs/component-classification.md +3 -3
  89. package/docs/product-compositions.md +2 -2
  90. package/docs/product-sections.md +2 -2
  91. package/llms.txt +11 -1
  92. package/package.json +5 -3
@@ -0,0 +1,63 @@
1
+ /**
2
+ * chart-date-range — the date-range value model and URL-state codec for the
3
+ * console chart kit (issue #166).
4
+ *
5
+ * This module is intentionally server-safe (no "use client" directive): the
6
+ * package owns no routing, so consumers wire `encodeDateRange` /
7
+ * `decodeDateRange` to their own searchParams — often in a Server Component,
8
+ * where importing a pure function from a client module would break. The
9
+ * interactive picker lives in `chart-date-range-control`.
10
+ */
11
+ const DATE_RANGE_PRESETS = ["7d", "14d", "30d", "cycle"];
12
+ const ISO_DAY = /^\d{4}-\d{2}-\d{2}$/;
13
+ const CUSTOM_SEPARATOR = "..";
14
+ /**
15
+ * Encodes a date-range value for URL state: a preset encodes as its token
16
+ * ("7d", "cycle", …) and a custom range as "yyyy-mm-dd..yyyy-mm-dd".
17
+ */
18
+ function encodeDateRange(value) {
19
+ return value.kind === "preset"
20
+ ? value.preset
21
+ : `${value.from}${CUSTOM_SEPARATOR}${value.to}`;
22
+ }
23
+ /**
24
+ * Decodes a URL-state string back into a date-range value. Returns null for
25
+ * anything malformed (unknown preset, bad date shape, inverted range) so
26
+ * consumers fall back to their own default.
27
+ */
28
+ function decodeDateRange(raw) {
29
+ if (!raw) {
30
+ return null;
31
+ }
32
+ if (DATE_RANGE_PRESETS.includes(raw)) {
33
+ return { kind: "preset", preset: raw };
34
+ }
35
+ const parts = raw.split(CUSTOM_SEPARATOR);
36
+ if (parts.length !== 2) {
37
+ return null;
38
+ }
39
+ const [from, to] = parts;
40
+ if (!ISO_DAY.test(from) || !ISO_DAY.test(to) || from > to) {
41
+ return null;
42
+ }
43
+ if (!isRealIsoDay(from) || !isRealIsoDay(to)) {
44
+ return null;
45
+ }
46
+ return { kind: "custom", from, to };
47
+ }
48
+ /** Parses a "yyyy-mm-dd" day as a LOCAL date (no timezone shifting). */
49
+ function parseIsoDay(iso) {
50
+ const [year, month, day] = iso.split("-").map(Number);
51
+ return new Date(year, month - 1, day);
52
+ }
53
+ /** Formats a local date as "yyyy-mm-dd". */
54
+ function formatIsoDay(date) {
55
+ const year = String(date.getFullYear()).padStart(4, "0");
56
+ const month = String(date.getMonth() + 1).padStart(2, "0");
57
+ const day = String(date.getDate()).padStart(2, "0");
58
+ return `${year}-${month}-${day}`;
59
+ }
60
+ function isRealIsoDay(iso) {
61
+ return formatIsoDay(parseIsoDay(iso)) === iso;
62
+ }
63
+ export { DATE_RANGE_PRESETS, decodeDateRange, encodeDateRange, formatIsoDay, parseIsoDay, };
@@ -0,0 +1,28 @@
1
+ import * as React from "react";
2
+ /**
3
+ * chart-delta — shared delta/trend encoding for the console chart kit
4
+ * (issue #166; DESIGN.md "Delta and trend encoding").
5
+ *
6
+ * A change reads as a direction glyph (▲ ▼) plus a tone token, never bare
7
+ * signed text and never color alone. Tone follows direction crossed with
8
+ * whether up is good in the surrounding context (`upIsGood`).
9
+ */
10
+ type DeltaDirection = "up" | "down" | "flat";
11
+ type DeltaTone = "positive" | "negative" | "neutral";
12
+ /**
13
+ * Maps a delta direction onto its tone: direction crossed with whether up is
14
+ * good in this context. A flat delta is always neutral.
15
+ */
16
+ declare function deltaTone(direction: DeltaDirection, upIsGood?: boolean): DeltaTone;
17
+ declare function Delta({ value, direction, upIsGood, vsLabel, className, ...props }: Omit<React.ComponentProps<"span">, "children"> & {
18
+ /** The formatted change, e.g. "+12%" or "3.4k". */
19
+ value: React.ReactNode;
20
+ direction: DeltaDirection;
21
+ /** Whether an upward move is good in this context. Defaults to true. */
22
+ upIsGood?: boolean;
23
+ /** The named comparison period, rendered as "vs {vsLabel}". */
24
+ vsLabel?: string;
25
+ }): React.JSX.Element;
26
+ export { Delta, deltaTone };
27
+ export type { DeltaDirection, DeltaTone };
28
+ //# sourceMappingURL=chart-delta.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chart-delta.d.ts","sourceRoot":"","sources":["../../src/components/chart-delta.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAI9B;;;;;;;GAOG;AAEH,KAAK,cAAc,GAAG,IAAI,GAAG,MAAM,GAAG,MAAM,CAAA;AAC5C,KAAK,SAAS,GAAG,UAAU,GAAG,UAAU,GAAG,SAAS,CAAA;AAoBpD;;;GAGG;AACH,iBAAS,SAAS,CAAC,SAAS,EAAE,cAAc,EAAE,QAAQ,UAAO,GAAG,SAAS,CAMxE;AAED,iBAAS,KAAK,CAAC,EACb,KAAK,EACL,SAAS,EACT,QAAe,EACf,OAAO,EACP,SAAS,EACT,GAAG,KAAK,EACT,EAAE,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,MAAM,CAAC,EAAE,UAAU,CAAC,GAAG;IAClD,mDAAmD;IACnD,KAAK,EAAE,KAAK,CAAC,SAAS,CAAA;IACtB,SAAS,EAAE,cAAc,CAAA;IACzB,wEAAwE;IACxE,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,+DAA+D;IAC/D,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB,qBAuBA;AAED,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,CAAA;AAC3B,YAAY,EAAE,cAAc,EAAE,SAAS,EAAE,CAAA"}
@@ -0,0 +1,32 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { cn } from "../lib/utils.js";
3
+ const DELTA_GLYPHS = {
4
+ up: "▲",
5
+ down: "▼",
6
+ flat: "–",
7
+ };
8
+ const DELTA_DIRECTION_TEXT = {
9
+ up: "up",
10
+ down: "down",
11
+ flat: "unchanged",
12
+ };
13
+ const DELTA_TONE_CLASSES = {
14
+ positive: "text-(--nc-state-complete)",
15
+ negative: "text-(--nc-state-blocked)",
16
+ neutral: "text-muted-foreground",
17
+ };
18
+ /**
19
+ * Maps a delta direction onto its tone: direction crossed with whether up is
20
+ * good in this context. A flat delta is always neutral.
21
+ */
22
+ function deltaTone(direction, upIsGood = true) {
23
+ if (direction === "flat") {
24
+ return "neutral";
25
+ }
26
+ return (direction === "up") === upIsGood ? "positive" : "negative";
27
+ }
28
+ function Delta({ value, direction, upIsGood = true, vsLabel, className, ...props }) {
29
+ const tone = deltaTone(direction, upIsGood);
30
+ return (_jsxs("span", { "data-slot": "delta", "data-direction": direction, "data-tone": tone, className: cn("inline-flex items-baseline gap-1 text-sm font-medium", DELTA_TONE_CLASSES[tone], className), ...props, children: [_jsx("span", { "aria-hidden": "true", children: DELTA_GLYPHS[direction] }), _jsx("span", { className: "sr-only", children: DELTA_DIRECTION_TEXT[direction] }), _jsx("span", { children: value }), vsLabel ? (_jsxs("span", { className: "font-normal text-muted-foreground", children: ["vs ", vsLabel] })) : null] }));
31
+ }
32
+ export { Delta, deltaTone };
@@ -0,0 +1,37 @@
1
+ /**
2
+ * chart-entity-colors — the entity-color assignment helper for the console
3
+ * chart kit (issue #166; DESIGN.md "Entity-color assignment").
4
+ *
5
+ * Categorical hues assign in fixed order to entities per page context, and the
6
+ * assignment follows the entity identity, never rank, sort position, or array
7
+ * index. Callers build ONE map from the full page-context entity list (not a
8
+ * filtered or re-sorted view) and pass it to every chart widget on that
9
+ * surface, so a filter or re-sort never repaints the entities that remain on
10
+ * screen. Beyond six entities the tail folds into a single neutral "other"
11
+ * register, never a seventh hue.
12
+ *
13
+ * The map is a plain serializable record so a Server Component can compute it
14
+ * and pass it across the RSC boundary to client chart widgets.
15
+ */
16
+ declare const CHART_ENTITY_COLOR_TOKENS: readonly ["var(--nc-chart-1)", "var(--nc-chart-2)", "var(--nc-chart-3)", "var(--nc-chart-4)", "var(--nc-chart-5)", "var(--nc-chart-6)"];
17
+ /**
18
+ * The neutral register for folded "everything else" entities. A muted ink
19
+ * token, never an extra categorical hue.
20
+ */
21
+ declare const CHART_ENTITY_OTHER_COLOR = "var(--muted-foreground)";
22
+ type EntityColorMap = Readonly<Record<string, string>>;
23
+ /**
24
+ * Assigns `var(--nc-chart-1..6)` in fixed order by FIRST APPEARANCE of each
25
+ * stable entity id. Duplicate ids keep their first assignment. Entities past
26
+ * the sixth unique id fold to the neutral "other" register.
27
+ */
28
+ declare function createEntityColorMap(entityIds: readonly string[]): EntityColorMap;
29
+ /**
30
+ * Resolves an entity's assigned color. Ids missing from the map (an entity the
31
+ * page context never registered) read as the neutral "other" register instead
32
+ * of silently minting a new hue.
33
+ */
34
+ declare function entityColor(map: EntityColorMap, entityId: string): string;
35
+ export { CHART_ENTITY_COLOR_TOKENS, CHART_ENTITY_OTHER_COLOR, createEntityColorMap, entityColor, };
36
+ export type { EntityColorMap };
37
+ //# sourceMappingURL=chart-entity-colors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chart-entity-colors.d.ts","sourceRoot":"","sources":["../../src/components/chart-entity-colors.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,QAAA,MAAM,yBAAyB,yIAOrB,CAAA;AAEV;;;GAGG;AACH,QAAA,MAAM,wBAAwB,4BAA4B,CAAA;AAE1D,KAAK,cAAc,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;AAEtD;;;;GAIG;AACH,iBAAS,oBAAoB,CAAC,SAAS,EAAE,SAAS,MAAM,EAAE,GAAG,cAAc,CA2B1E;AAED;;;;GAIG;AACH,iBAAS,WAAW,CAAC,GAAG,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAMlE;AAED,OAAO,EACL,yBAAyB,EACzB,wBAAwB,EACxB,oBAAoB,EACpB,WAAW,GACZ,CAAA;AACD,YAAY,EAAE,cAAc,EAAE,CAAA"}
@@ -0,0 +1,68 @@
1
+ /**
2
+ * chart-entity-colors — the entity-color assignment helper for the console
3
+ * chart kit (issue #166; DESIGN.md "Entity-color assignment").
4
+ *
5
+ * Categorical hues assign in fixed order to entities per page context, and the
6
+ * assignment follows the entity identity, never rank, sort position, or array
7
+ * index. Callers build ONE map from the full page-context entity list (not a
8
+ * filtered or re-sorted view) and pass it to every chart widget on that
9
+ * surface, so a filter or re-sort never repaints the entities that remain on
10
+ * screen. Beyond six entities the tail folds into a single neutral "other"
11
+ * register, never a seventh hue.
12
+ *
13
+ * The map is a plain serializable record so a Server Component can compute it
14
+ * and pass it across the RSC boundary to client chart widgets.
15
+ */
16
+ const CHART_ENTITY_COLOR_TOKENS = [
17
+ "var(--nc-chart-1)",
18
+ "var(--nc-chart-2)",
19
+ "var(--nc-chart-3)",
20
+ "var(--nc-chart-4)",
21
+ "var(--nc-chart-5)",
22
+ "var(--nc-chart-6)",
23
+ ];
24
+ /**
25
+ * The neutral register for folded "everything else" entities. A muted ink
26
+ * token, never an extra categorical hue.
27
+ */
28
+ const CHART_ENTITY_OTHER_COLOR = "var(--muted-foreground)";
29
+ /**
30
+ * Assigns `var(--nc-chart-1..6)` in fixed order by FIRST APPEARANCE of each
31
+ * stable entity id. Duplicate ids keep their first assignment. Entities past
32
+ * the sixth unique id fold to the neutral "other" register.
33
+ */
34
+ function createEntityColorMap(entityIds) {
35
+ // Assign through a Map so ids that collide with Object.prototype members
36
+ // ("toString", "__proto__", "constructor") key and dedupe safely during the
37
+ // build — a Map never confuses an entity id with an inherited prototype
38
+ // method, and never triggers the `__proto__` accessor.
39
+ const assignments = new Map();
40
+ for (const entityId of entityIds) {
41
+ if (assignments.has(entityId)) {
42
+ continue;
43
+ }
44
+ assignments.set(entityId, assignments.size < CHART_ENTITY_COLOR_TOKENS.length
45
+ ? CHART_ENTITY_COLOR_TOKENS[assignments.size]
46
+ : CHART_ENTITY_OTHER_COLOR);
47
+ }
48
+ // Return a PLAIN, serializable object so a Server Component can compute the
49
+ // map and pass it across the RSC boundary to client chart widgets (React
50
+ // rejects a null-prototype object). `Object.fromEntries` writes OWN data
51
+ // properties, so an id like "__proto__" becomes an own key with its hue
52
+ // instead of mutating the object's prototype — and `entityColor` reads
53
+ // membership with `Object.hasOwn`, so inherited members never leak.
54
+ return Object.fromEntries(assignments);
55
+ }
56
+ /**
57
+ * Resolves an entity's assigned color. Ids missing from the map (an entity the
58
+ * page context never registered) read as the neutral "other" register instead
59
+ * of silently minting a new hue.
60
+ */
61
+ function entityColor(map, entityId) {
62
+ // Object.hasOwn, not `map[entityId] ??`: the map is serialized across the RSC
63
+ // boundary and rehydrates with Object.prototype, so a bare index on an id like
64
+ // "toString" or "constructor" would return an inherited method instead of
65
+ // folding to the neutral register.
66
+ return Object.hasOwn(map, entityId) ? map[entityId] : CHART_ENTITY_OTHER_COLOR;
67
+ }
68
+ export { CHART_ENTITY_COLOR_TOKENS, CHART_ENTITY_OTHER_COLOR, createEntityColorMap, entityColor, };
@@ -0,0 +1,33 @@
1
+ import * as React from "react";
2
+ import { type DeltaDirection } from "./chart-delta";
3
+ /**
4
+ * chart-kpi-stat — the package KPI stat for the console chart kit
5
+ * (issue #166). The portal's KpiTile stays app-owned; this is the package
6
+ * sibling that adds the contracted delta and sparkline behavior.
7
+ *
8
+ * The value renders semibold with proportional figures (no tabular-nums —
9
+ * a single stat is read, not columnar-scanned). The delta reads as a
10
+ * direction glyph plus tone token with a named comparison period
11
+ * ("vs {vsLabel}"), per DESIGN.md "Delta and trend encoding". The sparkline
12
+ * slot is decorative here: the stat carries the accessible name, so the slot
13
+ * wrapper is `aria-hidden`.
14
+ */
15
+ type KpiStatDelta = {
16
+ /** The formatted change, e.g. "+12%". */
17
+ value: React.ReactNode;
18
+ direction: DeltaDirection;
19
+ /** Whether an upward move is good for this KPI. Defaults to true. */
20
+ upIsGood?: boolean;
21
+ /** The named comparison period, e.g. "previous 30 days". */
22
+ vsLabel: string;
23
+ };
24
+ declare function KpiStat({ label, value, delta, sparkline, className, ...props }: Omit<React.ComponentProps<"div">, "children"> & {
25
+ label: React.ReactNode;
26
+ value: React.ReactNode;
27
+ delta?: KpiStatDelta;
28
+ /** Optional decorative sparkline slot (e.g. a `Sparkline` element). */
29
+ sparkline?: React.ReactNode;
30
+ }): React.JSX.Element;
31
+ export { KpiStat };
32
+ export type { KpiStatDelta };
33
+ //# sourceMappingURL=chart-kpi-stat.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chart-kpi-stat.d.ts","sourceRoot":"","sources":["../../src/components/chart-kpi-stat.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAG9B,OAAO,EAAS,KAAK,cAAc,EAAE,MAAM,0BAA0B,CAAA;AAErE;;;;;;;;;;;GAWG;AAEH,KAAK,YAAY,GAAG;IAClB,yCAAyC;IACzC,KAAK,EAAE,KAAK,CAAC,SAAS,CAAA;IACtB,SAAS,EAAE,cAAc,CAAA;IACzB,qEAAqE;IACrE,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,4DAA4D;IAC5D,OAAO,EAAE,MAAM,CAAA;CAChB,CAAA;AAED,iBAAS,OAAO,CAAC,EACf,KAAK,EACL,KAAK,EACL,KAAK,EACL,SAAS,EACT,SAAS,EACT,GAAG,KAAK,EACT,EAAE,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,EAAE,UAAU,CAAC,GAAG;IACjD,KAAK,EAAE,KAAK,CAAC,SAAS,CAAA;IACtB,KAAK,EAAE,KAAK,CAAC,SAAS,CAAA;IACtB,KAAK,CAAC,EAAE,YAAY,CAAA;IACpB,uEAAuE;IACvE,SAAS,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;CAC5B,qBAoCA;AAED,OAAO,EAAE,OAAO,EAAE,CAAA;AAClB,YAAY,EAAE,YAAY,EAAE,CAAA"}
@@ -0,0 +1,7 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { cn } from "../lib/utils.js";
3
+ import { Delta } from "./chart-delta.js";
4
+ function KpiStat({ label, value, delta, sparkline, className, ...props }) {
5
+ return (_jsxs("div", { "data-slot": "kpi-stat", className: cn("flex flex-col gap-1.5", className), ...props, children: [_jsx("div", { "data-slot": "kpi-stat-label", className: "font-mono text-xs text-muted-foreground uppercase", children: label }), _jsxs("div", { className: "flex items-end justify-between gap-3", children: [_jsx("div", { "data-slot": "kpi-stat-value", className: "text-3xl leading-none font-semibold text-foreground", children: value }), sparkline ? (_jsx("div", { "data-slot": "kpi-stat-sparkline", "aria-hidden": "true", children: sparkline })) : null] }), delta ? (_jsx(Delta, { value: delta.value, direction: delta.direction, upIsGood: delta.upIsGood, vsLabel: delta.vsLabel })) : null] }));
6
+ }
7
+ export { KpiStat };
@@ -0,0 +1,45 @@
1
+ import * as React from "react";
2
+ /**
3
+ * chart-sparkline — hand-rolled SVG sparkline for the console chart kit
4
+ * (issue #166; ADR 0010 D1 sparkline carve-out; DESIGN.md "Delta and trend
5
+ * encoding", "Mark specs").
6
+ *
7
+ * Sparklines normalize against a meaningful baseline, never the visible
8
+ * min–max span alone: the vertical domain always includes the provided
9
+ * baseline, so a flat-ish series near a distant baseline reads as flat, not
10
+ * as dramatic noise. Marks render in real pixel space (uniform viewBox, no
11
+ * non-uniform scaling), single stroke of at least 2px with round joins/caps.
12
+ *
13
+ * When rendered inside a named stat (KpiStat), the sparkline is decorative
14
+ * and stays `aria-hidden`; when standalone, pass `label` so it carries its
15
+ * own accessible name. A standalone sparkline also renders a visually hidden
16
+ * data alternative next to the `role="img"` svg — the point values in order —
17
+ * so the trend is not vision-only. The decorative (aria-hidden) variant emits
18
+ * no such list.
19
+ */
20
+ type SparklinePoint = {
21
+ x: number;
22
+ y: number;
23
+ };
24
+ /**
25
+ * Baseline-anchored normalization: the vertical domain is
26
+ * [min(baseline, data), max(baseline, data)] — never the visible data
27
+ * min–max alone. Points are inset by half the stroke width so the stroke
28
+ * never clips at the edges. A zero-span domain centers the line vertically.
29
+ */
30
+ declare function normalizeSparklinePoints(data: readonly number[], baseline: number, width: number, height: number, strokeWidth?: number): SparklinePoint[];
31
+ declare function Sparkline({ data, baseline, label, width, height, stroke, strokeWidth, className, ...props }: Omit<React.ComponentProps<"svg">, "children" | "height" | "viewBox" | "width"> & {
32
+ data: readonly number[];
33
+ /** The meaningful comparison baseline the vertical domain anchors to. */
34
+ baseline: number;
35
+ /** Accessible name for standalone use; omit inside a named stat. */
36
+ label?: string;
37
+ width?: number;
38
+ height?: number;
39
+ /** Stroke token: ink or one chart token, never a raw color. */
40
+ stroke?: string;
41
+ strokeWidth?: number;
42
+ }): React.JSX.Element;
43
+ export { Sparkline, normalizeSparklinePoints };
44
+ export type { SparklinePoint };
45
+ //# sourceMappingURL=chart-sparkline.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chart-sparkline.d.ts","sourceRoot":"","sources":["../../src/components/chart-sparkline.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAI9B;;;;;;;;;;;;;;;;;GAiBG;AAEH,KAAK,cAAc,GAAG;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,CAAA;AAE9C;;;;;GAKG;AACH,iBAAS,wBAAwB,CAC/B,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,WAAW,SAAI,GACd,cAAc,EAAE,CAqBlB;AAED,iBAAS,SAAS,CAAC,EACjB,IAAI,EACJ,QAAQ,EACR,KAAK,EACL,KAAW,EACX,MAAW,EACX,MAA4B,EAC5B,WAAe,EACf,SAAS,EACT,GAAG,KAAK,EACT,EAAE,IAAI,CACL,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,EAC3B,UAAU,GAAG,QAAQ,GAAG,SAAS,GAAG,OAAO,CAC5C,GAAG;IACF,IAAI,EAAE,SAAS,MAAM,EAAE,CAAA;IACvB,yEAAyE;IACzE,QAAQ,EAAE,MAAM,CAAA;IAChB,oEAAoE;IACpE,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,+DAA+D;IAC/D,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB,qBAqDA;AAED,OAAO,EAAE,SAAS,EAAE,wBAAwB,EAAE,CAAA;AAC9C,YAAY,EAAE,cAAc,EAAE,CAAA"}
@@ -0,0 +1,44 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { cn } from "../lib/utils.js";
3
+ /**
4
+ * Baseline-anchored normalization: the vertical domain is
5
+ * [min(baseline, data), max(baseline, data)] — never the visible data
6
+ * min–max alone. Points are inset by half the stroke width so the stroke
7
+ * never clips at the edges. A zero-span domain centers the line vertically.
8
+ */
9
+ function normalizeSparklinePoints(data, baseline, width, height, strokeWidth = 2) {
10
+ if (data.length === 0) {
11
+ return [];
12
+ }
13
+ const min = Math.min(baseline, ...data);
14
+ const max = Math.max(baseline, ...data);
15
+ const span = max - min;
16
+ const pad = strokeWidth / 2;
17
+ const usableWidth = Math.max(width - strokeWidth, 0);
18
+ const usableHeight = Math.max(height - strokeWidth, 0);
19
+ return data.map((value, index) => ({
20
+ x: pad +
21
+ (data.length === 1
22
+ ? usableWidth / 2
23
+ : (index / (data.length - 1)) * usableWidth),
24
+ y: span === 0 ? height / 2 : pad + (1 - (value - min) / span) * usableHeight,
25
+ }));
26
+ }
27
+ function Sparkline({ data, baseline, label, width = 120, height = 32, stroke = "var(--nc-chart-1)", strokeWidth = 2, className, ...props }) {
28
+ // The 2px minimum is a DESIGN.md contract floor ("single stroke of at least
29
+ // 2px"); clamp the public prop so a caller passing a thinner stroke still
30
+ // renders at the floor and the normalization inset matches the painted line.
31
+ const effectiveStrokeWidth = Math.max(strokeWidth, 2);
32
+ const points = normalizeSparklinePoints(data, baseline, width, height, effectiveStrokeWidth);
33
+ const svg = (_jsx("svg", { "data-slot": "sparkline", width: width, height: height, viewBox: `0 0 ${width} ${height}`, className: cn("shrink-0", className), ...(label
34
+ ? { role: "img", "aria-label": label }
35
+ : { "aria-hidden": true }), ...props, children: points.length >= 2 ? (_jsx("polyline", { fill: "none", stroke: stroke, strokeWidth: effectiveStrokeWidth, strokeLinejoin: "round", strokeLinecap: "round", points: points.map((point) => `${point.x},${point.y}`).join(" ") })) : null }));
36
+ // Decorative inside a named stat: emit the bare svg, no data twin.
37
+ if (!label) {
38
+ return svg;
39
+ }
40
+ // Standalone: pair the graphic with a visually hidden ordered value list so
41
+ // the trend is available to assistive tech, not vision-only.
42
+ return (_jsxs("span", { "data-slot": "sparkline-standalone", className: "inline-flex", children: [svg, _jsx("span", { "data-slot": "sparkline-values", className: "sr-only", children: `${label}: ${data.join(", ")}` })] }));
43
+ }
44
+ export { Sparkline, normalizeSparklinePoints };
@@ -0,0 +1,33 @@
1
+ import * as React from "react";
2
+ import { type EntityColorMap } from "./chart-entity-colors";
3
+ type TimeSeriesSeries = {
4
+ /** Stable entity id — the data key in each datum and the color-map key. */
5
+ id: string;
6
+ label: string;
7
+ };
8
+ type TimeSeriesDatum = Record<string, number | string>;
9
+ type TimeSeriesProps = Omit<React.ComponentProps<"figure">, "children"> & {
10
+ data: readonly TimeSeriesDatum[];
11
+ /** The datum key carrying the period (e.g. the day). */
12
+ xKey: string;
13
+ series: readonly TimeSeriesSeries[];
14
+ /** The shared page-context entity-color assignment map. */
15
+ entityColorMap: EntityColorMap;
16
+ /** REQUIRED accessible name for the chart and its table alternative. */
17
+ label: string;
18
+ /** Column header for the period column in the table alternative. */
19
+ xLabel?: string;
20
+ valueFormatter?: (value: number) => string;
21
+ /**
22
+ * Y-axis width. Defaults to `"auto"` (recharts 3.8 sizes the gutter to the
23
+ * formatted ticks); pass a number to pin it.
24
+ */
25
+ yAxisWidth?: number;
26
+ /** Label for the folded neutral "Other" series (entities past the sixth). */
27
+ otherLabel?: string;
28
+ };
29
+ declare function TimeSeriesBars({ data, xKey, series, entityColorMap, label, xLabel, valueFormatter, yAxisWidth, otherLabel, className, ...props }: TimeSeriesProps): React.JSX.Element;
30
+ declare function TimeSeriesArea({ data, xKey, series, entityColorMap, label, xLabel, valueFormatter, yAxisWidth, otherLabel, className, ...props }: TimeSeriesProps): React.JSX.Element;
31
+ export { TimeSeriesArea, TimeSeriesBars };
32
+ export type { TimeSeriesDatum, TimeSeriesProps, TimeSeriesSeries };
33
+ //# sourceMappingURL=chart-time-series.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chart-time-series.d.ts","sourceRoot":"","sources":["../../src/components/chart-time-series.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAqB9B,OAAO,EAGL,KAAK,cAAc,EACpB,MAAM,kCAAkC,CAAA;AAsDzC,KAAK,gBAAgB,GAAG;IACtB,2EAA2E;IAC3E,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;CACd,CAAA;AAED,KAAK,eAAe,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,CAAA;AAEtD,KAAK,eAAe,GAAG,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,QAAQ,CAAC,EAAE,UAAU,CAAC,GAAG;IACxE,IAAI,EAAE,SAAS,eAAe,EAAE,CAAA;IAChC,wDAAwD;IACxD,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,SAAS,gBAAgB,EAAE,CAAA;IACnC,2DAA2D;IAC3D,cAAc,EAAE,cAAc,CAAA;IAC9B,wEAAwE;IACxE,KAAK,EAAE,MAAM,CAAA;IACb,oEAAoE;IACpE,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAA;IAC1C;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,6EAA6E;IAC7E,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB,CAAA;AAiKD,iBAAS,cAAc,CAAC,EACtB,IAAI,EACJ,IAAI,EACJ,MAAM,EACN,cAAc,EACd,KAAK,EACL,MAAiB,EACjB,cAA8B,EAC9B,UAAU,EACV,UAAoB,EACpB,SAAS,EACT,GAAG,KAAK,EACT,EAAE,eAAe,qBAuDjB;AAED,iBAAS,cAAc,CAAC,EACtB,IAAI,EACJ,IAAI,EACJ,MAAM,EACN,cAAc,EACd,KAAK,EACL,MAAiB,EACjB,cAA8B,EAC9B,UAAU,EACV,UAAoB,EACpB,SAAS,EACT,GAAG,KAAK,EACT,EAAE,eAAe,qBAgEjB;AAED,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,CAAA;AACzC,YAAY,EAAE,eAAe,EAAE,eAAe,EAAE,gBAAgB,EAAE,CAAA"}
@@ -0,0 +1,136 @@
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, XAxis, YAxis, } from "recharts";
4
+ import { cn } from "../lib/utils.js";
5
+ import { ChartContainer, ChartLegend, ChartLegendContent, ChartTooltip, ChartTooltipContent, } from "./chart.js";
6
+ import { CHART_ENTITY_OTHER_COLOR, entityColor, } from "./chart-entity-colors.js";
7
+ /**
8
+ * chart-time-series — daily stacked time-series charts for the console chart
9
+ * kit (issue #166), built on the ADR 0010 Recharts foundation
10
+ * (ChartContainer / ChartTooltip / ChartLegend; chart.tsx untouched).
11
+ *
12
+ * Contract highlights (DESIGN.md "Mark specs", "Legends and labels"):
13
+ * - A y-axis is ALWAYS present — never axisless stacks.
14
+ * - Stacked segments keep a surface gap; the whole stack is wrapped in a
15
+ * Recharts `BarStack` so the 4px rounded data-ends sit on the visible top of
16
+ * the stack (anchored square at the baseline) even when the last series is
17
+ * zero-valued — the radius rounds the stack, not one hard-coded series.
18
+ * - The public `valueFormatter` reaches every value surface — the table twin,
19
+ * the y-axis ticks, and the tooltip value — not only the hidden table cells.
20
+ * - Series colors resolve from the shared page-context entity-color map,
21
+ * never from array index.
22
+ * - Direct labels are not feasible on stacked dailies, so at two or more
23
+ * series a legend identifies every series; a single series is titled by
24
+ * the accessible name, not legended.
25
+ * - Every chart names a built-in table alternative: a visually hidden
26
+ * `<table>` twin renders the same series, and the chart root carries the
27
+ * accessible name. Recharts' `accessibilityLayer` (ARIA + arrow-key data
28
+ * navigation) stays enabled, and the focusable chart svg carries its own
29
+ * `title` so the accessibility tree names the graphic, not just the figure.
30
+ */
31
+ /**
32
+ * The stock `ChartContainer` suppresses the Recharts svg outline
33
+ * (`[&_.recharts-surface]:outline-hidden`) so the default browser focus ring
34
+ * never shows, which would leave the `accessibilityLayer` keyboard path with
35
+ * no visible focus. chart.tsx is untouched, so we restore a tokenized ring
36
+ * through the `className` we pass to our own `ChartContainer` instances: the
37
+ * base rule sets `outline: 2px solid transparent` on `.recharts-surface`, and
38
+ * these `:focus-visible` overrides sit on a distinct arbitrary-variant selector
39
+ * (a different tailwind-merge group AND modifier, so `cn()` keeps both) with
40
+ * higher CSS specificity, flipping the outline color to the `--ring` role only
41
+ * while the svg is keyboard-focused.
42
+ */
43
+ const CHART_SURFACE_FOCUS_CLASS = "[&_.recharts-surface:focus-visible]:outline-2 " +
44
+ "[&_.recharts-surface:focus-visible]:outline-ring " +
45
+ "[&_.recharts-surface:focus-visible]:outline-offset-2";
46
+ /**
47
+ * Synthetic id/key for the single aggregated neutral series. Entities past the
48
+ * sixth resolve to the neutral register, and rendering each as its own
49
+ * indistinguishable neutral mark is unreadable, so they fold into ONE "Other"
50
+ * series (values summed per bucket) in the marks, the legend, and the table
51
+ * twin. Unknown to any entity-color map, so `entityColor` resolves it to the
52
+ * neutral register.
53
+ */
54
+ const OTHER_SERIES_ID = "__nc-other-series__";
55
+ function defaultFormat(value) {
56
+ return value.toLocaleString("en-US");
57
+ }
58
+ /**
59
+ * Route the public `valueFormatter` through the visible tooltip, not only the
60
+ * table twin. The stock shadcn tooltip takes a full-row `formatter`, so this
61
+ * re-renders the stock row shape — entity swatch, series label, formatted
62
+ * value — with the formatter applied to the number.
63
+ */
64
+ function tooltipRowFormatter(series, valueFormatter) {
65
+ const labelById = new Map(series.map((entry) => [entry.id, entry.label]));
66
+ return (value, name, item) => (_jsxs(_Fragment, { children: [_jsx("span", { "data-slot": "time-series-tooltip-swatch", className: "size-2.5 shrink-0 rounded-[2px]", style: { backgroundColor: item.color ?? item.payload?.fill } }), _jsxs("span", { className: "flex flex-1 items-center justify-between gap-2 leading-none", children: [_jsx("span", { className: "text-muted-foreground", children: labelById.get(String(name)) ?? name }), _jsx("span", { className: "font-mono font-medium text-foreground tabular-nums", children: typeof value === "number" ? valueFormatter(value) : String(value) })] })] }));
67
+ }
68
+ function seriesChartConfig(series, entityColorMap) {
69
+ return Object.fromEntries(series.map((entry) => [
70
+ entry.id,
71
+ { label: entry.label, color: entityColor(entityColorMap, entry.id) },
72
+ ]));
73
+ }
74
+ /**
75
+ * Fold every series whose entity color resolves to the neutral "other"
76
+ * register into ONE aggregated "Other" series (values summed per bucket).
77
+ * Beyond six entities the color map returns the neutral register, and drawing
78
+ * each tail entity as its own indistinguishable neutral mark is unreadable —
79
+ * so the marks, legend, and table twin all show at most one neutral series.
80
+ * Series resolving to a hue pass through unchanged; with no neutral series the
81
+ * inputs return untouched (so ≤6 series are never rewritten).
82
+ */
83
+ function foldNeutralSeries(series, entityColorMap, data, otherLabel) {
84
+ const isNeutral = (entry) => entityColor(entityColorMap, entry.id) === CHART_ENTITY_OTHER_COLOR;
85
+ const neutral = series.filter(isNeutral);
86
+ if (neutral.length === 0) {
87
+ return { series: [...series], data: [...data] };
88
+ }
89
+ const hued = series.filter((entry) => !isNeutral(entry));
90
+ const foldedSeries = [
91
+ ...hued,
92
+ { id: OTHER_SERIES_ID, label: otherLabel },
93
+ ];
94
+ const foldedData = data.map((datum) => {
95
+ const otherSum = neutral.reduce((sum, entry) => {
96
+ const value = datum[entry.id];
97
+ return sum + (typeof value === "number" ? value : 0);
98
+ }, 0);
99
+ const next = { ...datum };
100
+ for (const entry of neutral) {
101
+ delete next[entry.id];
102
+ }
103
+ next[OTHER_SERIES_ID] = otherSum;
104
+ return next;
105
+ });
106
+ return { series: foldedSeries, data: foldedData };
107
+ }
108
+ function TimeSeriesTableAlternative({ data, xKey, xLabel, series, label, valueFormatter, }) {
109
+ 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) => {
110
+ const value = datum[entry.id];
111
+ return (_jsx("td", { children: typeof value === "number"
112
+ ? valueFormatter(value)
113
+ : String(value ?? "") }, entry.id));
114
+ })] }, index))) })] }));
115
+ }
116
+ function TimeSeriesFrame({ label, className, children, ...props }) {
117
+ return (_jsx("figure", { "aria-label": label, className: cn("m-0 flex w-full flex-col", className), ...props, children: children }));
118
+ }
119
+ function TimeSeriesBars({ data, xKey, series, entityColorMap, label, xLabel = "Period", valueFormatter = defaultFormat, yAxisWidth, otherLabel = "Other", className, ...props }) {
120
+ const folded = foldNeutralSeries(series, entityColorMap, data, otherLabel);
121
+ const config = seriesChartConfig(folded.series, entityColorMap);
122
+ return (_jsxs(TimeSeriesFrame, { "data-slot": "time-series-bars", label: label, className: className, ...props, children: [_jsx(ChartContainer, { config: config, className: cn("w-full", 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 }), _jsx(YAxis, { tickLine: false, axisLine: false, width: yAxisWidth ?? "auto", tickFormatter: (value) => valueFormatter(Number(value)) }), _jsx(ChartTooltip, { content: _jsx(ChartTooltipContent, { formatter: tooltipRowFormatter(folded.series, valueFormatter) }) }), folded.series.length > 1 ? (_jsx(ChartLegend, { content: _jsx(ChartLegendContent, {}) })) : null, _jsx(BarStack, { stackId: "stacked", radius: [4, 4, 0, 0], children: folded.series.map((entry) => (_jsx(Bar, { dataKey: entry.id, 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 })] }));
123
+ }
124
+ function TimeSeriesArea({ data, xKey, series, entityColorMap, label, xLabel = "Period", valueFormatter = defaultFormat, yAxisWidth, otherLabel = "Other", className, ...props }) {
125
+ const folded = foldNeutralSeries(series, entityColorMap, data, otherLabel);
126
+ const config = seriesChartConfig(folded.series, entityColorMap);
127
+ return (_jsxs(TimeSeriesFrame, { "data-slot": "time-series-area", label: label, className: className, ...props, children: [_jsx(ChartContainer, { config: config, className: cn("w-full", CHART_SURFACE_FOCUS_CLASS), children: _jsxs(AreaChart, { accessibilityLayer: true, title: label, data: folded.data, children: [_jsx(CartesianGrid, { vertical: false }), _jsx(XAxis, { dataKey: xKey, tickLine: false, axisLine: false, tickMargin: 8 }), _jsx(YAxis, { tickLine: false, axisLine: false, width: yAxisWidth ?? "auto", tickFormatter: (value) => valueFormatter(Number(value)) }), _jsx(ChartTooltip, { content: _jsx(ChartTooltipContent, { formatter: tooltipRowFormatter(folded.series, valueFormatter) }) }), folded.series.length > 1 ? (_jsx(ChartLegend, { content: _jsx(ChartLegendContent, {}) })) : null, folded.series.map((entry) => {
128
+ const color = entityColor(entityColorMap, entry.id);
129
+ return (_jsx(Area, { type: "monotone", dataKey: entry.id, stackId: "stacked", stroke: color, strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round", fill: color, fillOpacity: 0.1,
130
+ // Recharts' default activeDot ring is stroke #fff, which the
131
+ // chart foundation remaps to transparent — an explicit surface
132
+ // ring keeps the DESIGN.md 2px marker-ring spec on hover/focus.
133
+ activeDot: { r: 4, stroke: "var(--background)", strokeWidth: 2 } }, entry.id));
134
+ })] }) }), _jsx(TimeSeriesTableAlternative, { data: folded.data, xKey: xKey, xLabel: xLabel, series: folded.series, label: label, valueFormatter: valueFormatter })] }));
135
+ }
136
+ export { TimeSeriesArea, TimeSeriesBars };
@@ -94,7 +94,7 @@ function ChartTooltipContent({ active, payload, className, indicator = "dot", hi
94
94
  return null;
95
95
  }
96
96
  const nestLabel = payload.length === 1 && indicator !== "dot";
97
- return (_jsxs("div", { className: cn("grid min-w-32 items-start gap-1.5 rounded-lg border border-border/50 bg-background px-2.5 py-1.5 text-xs shadow-xl", className), children: [!nestLabel ? tooltipLabel : null, _jsx("div", { className: "grid gap-1.5", children: payload
97
+ return (_jsxs("div", { className: cn("grid min-w-32 items-start gap-1.5 rounded-md border border-border bg-popover px-nadicode-sm py-nadicode-filter-chip-gap text-xs text-popover-foreground shadow-tooltip", className), children: [!nestLabel ? tooltipLabel : null, _jsx("div", { className: "grid gap-1.5", children: payload
98
98
  .filter((item) => item.type !== "none")
99
99
  .map((item, index) => {
100
100
  const key = `${nameKey ?? item.name ?? item.dataKey ?? "value"}`;
@@ -4,6 +4,6 @@ import { Checkbox as CheckboxPrimitive } from "@base-ui/react/checkbox";
4
4
  import { cn } from "../lib/utils.js";
5
5
  import { CheckIcon } from "lucide-react";
6
6
  function Checkbox({ className, ...props }) {
7
- return (_jsx(CheckboxPrimitive.Root, { "data-slot": "checkbox", className: cn("peer relative flex size-4 shrink-0 items-center justify-center rounded-[4px] border border-input transition-colors outline-none group-has-disabled/field:opacity-50 after:absolute after:-inset-x-3 after:-inset-y-2 focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 aria-invalid:aria-checked:border-primary data-checked:border-primary data-checked:bg-primary data-checked:text-primary-foreground", className), ...props, children: _jsx(CheckboxPrimitive.Indicator, { "data-slot": "checkbox-indicator", className: "grid place-content-center text-current transition-none [&>svg]:size-3.5", children: _jsx(CheckIcon, {}) }) }));
7
+ return (_jsx(CheckboxPrimitive.Root, { "data-slot": "checkbox", className: cn("peer relative flex size-4 shrink-0 items-center justify-center rounded-md border border-input transition-colors outline-none group-has-disabled/field:opacity-50 after:absolute after:top-1/2 after:left-1/2 after:size-touch-target after:-translate-x-1/2 after:-translate-y-1/2 focus-visible:border-ring focus-visible:ring-focus focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-focus aria-invalid:ring-destructive aria-invalid:aria-checked:border-primary data-checked:border-primary data-checked:bg-primary data-checked:text-primary-foreground [@media(hover:hover)_and_(pointer:fine)]:after:hidden", className), ...props, children: _jsx(CheckboxPrimitive.Indicator, { "data-slot": "checkbox-indicator", className: "grid place-content-center text-current transition-none [&>svg]:size-3.5", children: _jsx(CheckIcon, {}) }) }));
8
8
  }
9
9
  export { Checkbox };