@nadicodeai/ui 0.19.2 → 0.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +3 -1
- package/README.md +0 -2
- package/dist/components/brand-icons/apple.d.ts +18 -0
- package/dist/components/brand-icons/apple.d.ts.map +1 -0
- package/dist/components/brand-icons/apple.js +22 -0
- package/dist/components/brand-icons/brand-icon.d.ts +22 -0
- package/dist/components/brand-icons/brand-icon.d.ts.map +1 -0
- package/dist/components/brand-icons/brand-icon.js +26 -0
- package/dist/components/brand-icons/linux.d.ts +18 -0
- package/dist/components/brand-icons/linux.d.ts.map +1 -0
- package/dist/components/brand-icons/linux.js +25 -0
- package/dist/components/brand-icons/windows.d.ts +18 -0
- package/dist/components/brand-icons/windows.d.ts.map +1 -0
- package/dist/components/brand-icons/windows.js +22 -0
- package/dist/components/brand-icons.d.ts +5 -0
- package/dist/components/brand-icons.d.ts.map +1 -0
- package/dist/components/brand-icons.js +10 -0
- package/dist/components/button.js +1 -1
- package/dist/components/card-marketing.js +2 -2
- package/dist/components/card.d.ts +4 -3
- package/dist/components/card.d.ts.map +1 -1
- package/dist/components/card.js +12 -2
- package/dist/components/chart-breakdown-table.js +1 -1
- package/dist/components/chart-bullet-bar.d.ts +18 -2
- package/dist/components/chart-bullet-bar.d.ts.map +1 -1
- package/dist/components/chart-bullet-bar.js +62 -9
- package/dist/components/chart-date-range-control.d.ts +11 -1
- package/dist/components/chart-date-range-control.d.ts.map +1 -1
- package/dist/components/chart-date-range-control.js +13 -3
- package/dist/components/chart-entity-colors.d.ts +7 -1
- package/dist/components/chart-entity-colors.d.ts.map +1 -1
- package/dist/components/chart-entity-colors.js +7 -1
- package/dist/components/chart-kpi-stat.d.ts.map +1 -1
- package/dist/components/chart-kpi-stat.js +1 -1
- package/dist/components/chart-time-series.d.ts.map +1 -1
- package/dist/components/chart-time-series.js +62 -4
- package/dist/components/chart.js +2 -2
- package/dist/components/code-editor-mockup.js +1 -1
- package/dist/components/nav-bar.js +1 -1
- package/dist/components/radio-group.d.ts +1 -1
- package/dist/components/radio-group.d.ts.map +1 -1
- package/dist/components/radio-group.js +2 -2
- package/dist/components/theme-mode-switcher.d.ts +10 -3
- package/dist/components/theme-mode-switcher.d.ts.map +1 -1
- package/dist/components/theme-mode-switcher.js +14 -7
- package/dist/eslint/index.js +91 -0
- package/dist/eslint/rules/jsx-class-utility.js +231 -0
- package/dist/eslint/rules/no-deprecated-zod-api.js +156 -0
- package/dist/eslint/rules/no-design-system-src-import.js +11 -0
- package/dist/eslint/rules/no-invalid-token-utility.js +46 -0
- package/dist/eslint/rules/no-native-visible-control.js +86 -0
- package/dist/eslint/rules/no-raw-color-utility.js +109 -0
- package/dist/eslint/rules/no-raw-focus-ring-width.js +30 -0
- package/dist/eslint/rules/no-raw-logo-import.js +41 -0
- package/dist/eslint/rules/no-shadcn-appearance-override.js +191 -0
- package/dist/eslint/rules/no-token-category-bypass.js +53 -0
- package/dist/eslint/rules/no-ui-src-import.js +10 -0
- package/dist/eslint/rules/no-unpinned-vercel-functions-import.js +100 -0
- package/dist/eslint/rules/src-import-boundary.js +207 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/docs/agents/nadicodeai-ui.md +0 -2
- package/docs/consuming-cross-repo.md +31 -3
- package/docs/contract.md +11 -1
- package/llms.txt +0 -1
- package/package.json +9 -3
- package/skills/nadicodeai-ui/SKILL.md +1 -1
- package/docs/migration/nadia-consumer-readiness.md +0 -180
|
@@ -17,8 +17,14 @@ declare const CHART_ENTITY_COLOR_TOKENS: readonly ["var(--nc-chart-1)", "var(--n
|
|
|
17
17
|
/**
|
|
18
18
|
* The neutral register for folded "everything else" entities. A muted ink
|
|
19
19
|
* token, never an extra categorical hue.
|
|
20
|
+
*
|
|
21
|
+
* `--nc-muted` and not the `--muted-foreground` role: the role resolves to the
|
|
22
|
+
* body-text tone, which in dark is near-white (L 0.83) — as a mark it becomes
|
|
23
|
+
* the loudest thing in the chart, so the tail outshouts every named entity.
|
|
24
|
+
* `--nc-muted` is authored as a mid grey in both modes (L 0.53 light / 0.65
|
|
25
|
+
* dark), which is what a folded tail should read as either way.
|
|
20
26
|
*/
|
|
21
|
-
declare const CHART_ENTITY_OTHER_COLOR = "var(--muted
|
|
27
|
+
declare const CHART_ENTITY_OTHER_COLOR = "var(--nc-muted)";
|
|
22
28
|
type EntityColorMap = Readonly<Record<string, string>>;
|
|
23
29
|
/**
|
|
24
30
|
* Assigns `var(--nc-chart-1..6)` in fixed order by FIRST APPEARANCE of each
|
|
@@ -1 +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,CAAC;AAEX
|
|
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,CAAC;AAEX;;;;;;;;;GASG;AACH,QAAA,MAAM,wBAAwB,oBAAoB,CAAC;AAEnD,KAAK,cAAc,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;AAEvD;;;;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,EAAE,yBAAyB,EAAE,wBAAwB,EAAE,oBAAoB,EAAE,WAAW,EAAE,CAAC;AAClG,YAAY,EAAE,cAAc,EAAE,CAAC"}
|
|
@@ -24,8 +24,14 @@ const CHART_ENTITY_COLOR_TOKENS = [
|
|
|
24
24
|
/**
|
|
25
25
|
* The neutral register for folded "everything else" entities. A muted ink
|
|
26
26
|
* token, never an extra categorical hue.
|
|
27
|
+
*
|
|
28
|
+
* `--nc-muted` and not the `--muted-foreground` role: the role resolves to the
|
|
29
|
+
* body-text tone, which in dark is near-white (L 0.83) — as a mark it becomes
|
|
30
|
+
* the loudest thing in the chart, so the tail outshouts every named entity.
|
|
31
|
+
* `--nc-muted` is authored as a mid grey in both modes (L 0.53 light / 0.65
|
|
32
|
+
* dark), which is what a folded tail should read as either way.
|
|
27
33
|
*/
|
|
28
|
-
const CHART_ENTITY_OTHER_COLOR = "var(--muted
|
|
34
|
+
const CHART_ENTITY_OTHER_COLOR = "var(--nc-muted)";
|
|
29
35
|
/**
|
|
30
36
|
* Assigns `var(--nc-chart-1..6)` in fixed order by FIRST APPEARANCE of each
|
|
31
37
|
* stable entity id. Duplicate ids keep their first assignment. Entities past
|
|
@@ -1 +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,CAAC;AAG/B,OAAO,EAAS,KAAK,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAEtE;;;;;;;;;;;GAWG;AAEH,KAAK,YAAY,GAAG;IAClB,yCAAyC;IACzC,KAAK,EAAE,KAAK,CAAC,SAAS,CAAC;IACvB,SAAS,EAAE,cAAc,CAAC;IAC1B,qEAAqE;IACrE,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,4DAA4D;IAC5D,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,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,CAAC;IACvB,KAAK,EAAE,KAAK,CAAC,SAAS,CAAC;IACvB,KAAK,CAAC,EAAE,YAAY,CAAC;IACrB,uEAAuE;IACvE,SAAS,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CAC7B,
|
|
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,CAAC;AAG/B,OAAO,EAAS,KAAK,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAEtE;;;;;;;;;;;GAWG;AAEH,KAAK,YAAY,GAAG;IAClB,yCAAyC;IACzC,KAAK,EAAE,KAAK,CAAC,SAAS,CAAC;IACvB,SAAS,EAAE,cAAc,CAAC;IAC1B,qEAAqE;IACrE,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,4DAA4D;IAC5D,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,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,CAAC;IACvB,KAAK,EAAE,KAAK,CAAC,SAAS,CAAC;IACvB,KAAK,CAAC,EAAE,YAAY,CAAC;IACrB,uEAAuE;IACvE,SAAS,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CAC7B,qBAkCA;AAED,OAAO,EAAE,OAAO,EAAE,CAAC;AACnB,YAAY,EAAE,YAAY,EAAE,CAAC"}
|
|
@@ -2,6 +2,6 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import { cn } from "../lib/utils.js";
|
|
3
3
|
import { Delta } from "./chart-delta.js";
|
|
4
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: "
|
|
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: "text-xs font-medium text-muted-foreground", 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
6
|
}
|
|
7
7
|
export { KpiStat };
|
|
@@ -1 +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,CAAC;AAY/B,OAAO,EAGL,KAAK,cAAc,EACpB,MAAM,kCAAkC,CAAC;
|
|
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,CAAC;AAY/B,OAAO,EAGL,KAAK,cAAc,EACpB,MAAM,kCAAkC,CAAC;AA+G1C,KAAK,gBAAgB,GAAG;IACtB,2EAA2E;IAC3E,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,KAAK,eAAe,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,CAAC;AAEvD,KAAK,eAAe,GAAG,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,QAAQ,CAAC,EAAE,UAAU,CAAC,GAAG;IACxE,IAAI,EAAE,SAAS,eAAe,EAAE,CAAC;IACjC,wDAAwD;IACxD,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,SAAS,gBAAgB,EAAE,CAAC;IACpC,2DAA2D;IAC3D,cAAc,EAAE,cAAc,CAAC;IAC/B,wEAAwE;IACxE,KAAK,EAAE,MAAM,CAAC;IACd,oEAAoE;IACpE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;IAC3C;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,6EAA6E;IAC7E,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAsJF,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,qBAyDjB;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,qBA6EjB;AAED,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,CAAC;AAC1C,YAAY,EAAE,eAAe,EAAE,eAAe,EAAE,gBAAgB,EAAE,CAAC"}
|
|
@@ -43,6 +43,59 @@ import { CHART_ENTITY_OTHER_COLOR, entityColor, } from "./chart-entity-colors.js
|
|
|
43
43
|
const CHART_SURFACE_FOCUS_CLASS = "[&_.recharts-surface:focus-visible]:outline-2 " +
|
|
44
44
|
"[&_.recharts-surface:focus-visible]:outline-ring " +
|
|
45
45
|
"[&_.recharts-surface:focus-visible]:outline-offset-2";
|
|
46
|
+
/**
|
|
47
|
+
* The x ticks land at ONE constant stride across the range, and the stride is
|
|
48
|
+
* the smallest one whose labels still clear each other at the width the chart
|
|
49
|
+
* actually has. Recharts' default `preserveEnd` fitting is greedy on label
|
|
50
|
+
* width, so a 30-day axis draws the narrow early labels daily and then switches
|
|
51
|
+
* to every other day once "Jul 10" is wider than "Jul 1" — the rhythm breaks
|
|
52
|
+
* mid-chart, differently per locale. `equidistantPreserveStart` evaluates one
|
|
53
|
+
* stride against every label and raises it until the whole run fits, so the
|
|
54
|
+
* axis is even by construction AND survives a narrow viewport: a count fixed at
|
|
55
|
+
* the wide case superimposed eight labels into an unreadable smear on a phone,
|
|
56
|
+
* and worse in locales whose date labels run longer.
|
|
57
|
+
*/
|
|
58
|
+
const X_AXIS_MIN_TICK_GAP = 48;
|
|
59
|
+
/**
|
|
60
|
+
* How many ticks the y axis draws, ends included. Recharts divides the data's
|
|
61
|
+
* own maximum by this count when it is left to itself, which produces steps of
|
|
62
|
+
* whatever the data happens to reach: one console chart read $0/$5/$10 while the
|
|
63
|
+
* chart beside it read EUR0/9/18/27/36, and the two stopped sharing a numeric
|
|
64
|
+
* register. The domain is rounded up to a step a person counts in instead.
|
|
65
|
+
*/
|
|
66
|
+
const Y_AXIS_TICK_COUNT = 5;
|
|
67
|
+
/** The steps a reader counts in, per order of magnitude. */
|
|
68
|
+
const NICE_STEP_MULTIPLES = [1, 2, 2.5, 5, 10];
|
|
69
|
+
function niceStep(rawStep) {
|
|
70
|
+
if (!Number.isFinite(rawStep) || rawStep <= 0)
|
|
71
|
+
return 1;
|
|
72
|
+
const magnitude = 10 ** Math.floor(Math.log10(rawStep));
|
|
73
|
+
const normalized = rawStep / magnitude;
|
|
74
|
+
const multiple = NICE_STEP_MULTIPLES.find((candidate) => normalized <= candidate) ?? 10;
|
|
75
|
+
return multiple * magnitude;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* The stacked maximum across the data, rounded up to a whole number of nice
|
|
79
|
+
* steps. Returns `undefined` when there is nothing to bound, so the axis falls
|
|
80
|
+
* back to recharts' own domain rather than pinning itself to zero.
|
|
81
|
+
*/
|
|
82
|
+
function niceYDomainMax(data, series) {
|
|
83
|
+
let max = 0;
|
|
84
|
+
for (const datum of data) {
|
|
85
|
+
let total = 0;
|
|
86
|
+
for (const entry of series) {
|
|
87
|
+
const value = datum[entry.id];
|
|
88
|
+
if (typeof value === "number" && Number.isFinite(value))
|
|
89
|
+
total += value;
|
|
90
|
+
}
|
|
91
|
+
if (total > max)
|
|
92
|
+
max = total;
|
|
93
|
+
}
|
|
94
|
+
if (max <= 0)
|
|
95
|
+
return undefined;
|
|
96
|
+
const step = niceStep(max / (Y_AXIS_TICK_COUNT - 1));
|
|
97
|
+
return step * (Y_AXIS_TICK_COUNT - 1);
|
|
98
|
+
}
|
|
46
99
|
/**
|
|
47
100
|
* Synthetic id/key for the single aggregated neutral series. Entities past the
|
|
48
101
|
* sixth resolve to the neutral register, and rendering each as its own
|
|
@@ -63,7 +116,7 @@ function defaultFormat(value) {
|
|
|
63
116
|
*/
|
|
64
117
|
function tooltipRowFormatter(series, valueFormatter) {
|
|
65
118
|
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-
|
|
119
|
+
return (value, name, item) => (_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 } }), _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
120
|
}
|
|
68
121
|
function seriesChartConfig(series, entityColorMap) {
|
|
69
122
|
return Object.fromEntries(series.map((entry) => [
|
|
@@ -114,14 +167,19 @@ function TimeSeriesFrame({ label, className, children, ...props }) {
|
|
|
114
167
|
function TimeSeriesBars({ data, xKey, series, entityColorMap, label, xLabel = "Period", valueFormatter = defaultFormat, yAxisWidth, otherLabel = "Other", className, ...props }) {
|
|
115
168
|
const folded = foldNeutralSeries(series, entityColorMap, data, otherLabel);
|
|
116
169
|
const config = seriesChartConfig(folded.series, entityColorMap);
|
|
117
|
-
|
|
170
|
+
const yDomainMax = niceYDomainMax(folded.data, folded.series);
|
|
171
|
+
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, 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, {}) }) : null, _jsx(BarStack, { stackId: "stacked", 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 })] }));
|
|
118
172
|
}
|
|
119
173
|
function TimeSeriesArea({ data, xKey, series, entityColorMap, label, xLabel = "Period", valueFormatter = defaultFormat, yAxisWidth, otherLabel = "Other", className, ...props }) {
|
|
120
174
|
const folded = foldNeutralSeries(series, entityColorMap, data, otherLabel);
|
|
121
175
|
const config = seriesChartConfig(folded.series, entityColorMap);
|
|
122
|
-
|
|
176
|
+
const yDomainMax = niceYDomainMax(folded.data, folded.series);
|
|
177
|
+
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, 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, {}) }) : null, folded.series.map((entry) => {
|
|
123
178
|
const color = entityColor(entityColorMap, entry.id);
|
|
124
|
-
return (_jsx(Area, { type: "monotone", dataKey: entry.id, isAnimationActive: false, stackId: "stacked", stroke: color, strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round", fill: color,
|
|
179
|
+
return (_jsx(Area, { type: "monotone", dataKey: entry.id, isAnimationActive: false, stackId: "stacked", stroke: color, strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round", fill: color,
|
|
180
|
+
// DESIGN.md mark specs: an area fill reads as a light wash
|
|
181
|
+
// (roughly 10% opacity) under a full-strength line.
|
|
182
|
+
fillOpacity: 0.1,
|
|
125
183
|
// Recharts' default activeDot ring is stroke #fff, which the
|
|
126
184
|
// chart foundation remaps to transparent — an explicit surface
|
|
127
185
|
// ring keeps the DESIGN.md 2px marker-ring spec on hover/focus.
|
package/dist/components/chart.js
CHANGED
|
@@ -89,7 +89,7 @@ function ChartTooltipContent({ active, payload, className, indicator = "dot", hi
|
|
|
89
89
|
const key = `${nameKey ?? item.name ?? item.dataKey ?? "value"}`;
|
|
90
90
|
const itemConfig = getPayloadConfigFromPayload(config, item, key);
|
|
91
91
|
const indicatorColor = color ?? item.payload?.fill ?? item.color;
|
|
92
|
-
return (_jsx("div", { className: cn("flex w-full flex-wrap items-stretch gap-2 [&>svg]:h-2.5 [&>svg]:w-2.5 [&>svg]:text-muted-foreground", indicator === "dot" && "items-center"), children: formatter && item?.value !== undefined && item.name ? (formatter(item.value, item.name, item, index, item.payload)) : (_jsxs(_Fragment, { children: [itemConfig?.icon ? (_jsx(itemConfig.icon, {})) : (!hideIndicator && (_jsx("div", { className: cn("shrink-0 rounded-
|
|
92
|
+
return (_jsx("div", { className: cn("flex w-full flex-wrap items-stretch gap-2 [&>svg]:h-2.5 [&>svg]:w-2.5 [&>svg]:text-muted-foreground", indicator === "dot" && "items-center"), children: formatter && item?.value !== undefined && item.name ? (formatter(item.value, item.name, item, index, item.payload)) : (_jsxs(_Fragment, { children: [itemConfig?.icon ? (_jsx(itemConfig.icon, {})) : (!hideIndicator && (_jsx("div", { className: cn("shrink-0 rounded-xs border-(--color-border) bg-(--color-bg)", {
|
|
93
93
|
"h-2.5 w-2.5": indicator === "dot",
|
|
94
94
|
"w-1": indicator === "line",
|
|
95
95
|
"w-0 border-[1.5px] border-dashed bg-transparent": indicator === "dashed",
|
|
@@ -113,7 +113,7 @@ function ChartLegendContent({ className, hideIcon = false, payload, verticalAlig
|
|
|
113
113
|
.map((item, index) => {
|
|
114
114
|
const key = `${nameKey ?? item.dataKey ?? "value"}`;
|
|
115
115
|
const itemConfig = getPayloadConfigFromPayload(config, item, key);
|
|
116
|
-
return (_jsxs("div", { className: cn("flex items-center gap-1.5 [&>svg]:h-3 [&>svg]:w-3 [&>svg]:text-muted-foreground"), children: [itemConfig?.icon && !hideIcon ? (_jsx(itemConfig.icon, {})) : (_jsx("div", { className: "h-2 w-2 shrink-0 rounded-
|
|
116
|
+
return (_jsxs("div", { className: cn("flex items-center gap-1.5 [&>svg]:h-3 [&>svg]:w-3 [&>svg]:text-muted-foreground"), children: [itemConfig?.icon && !hideIcon ? (_jsx(itemConfig.icon, {})) : (_jsx("div", { className: "h-2 w-2 shrink-0 rounded-xs", style: {
|
|
117
117
|
backgroundColor: item.color,
|
|
118
118
|
} })), itemConfig?.label] }, index));
|
|
119
119
|
}) }));
|
|
@@ -24,7 +24,7 @@ import { cn } from "../lib/utils.js";
|
|
|
24
24
|
* dots -> bg-[color-mix(in_srgb,var(--nc-line)_24%,var(--nc-canvas)_22%)]
|
|
25
25
|
*/
|
|
26
26
|
function CodeEditorMockup({ className, ...props }) {
|
|
27
|
-
return (_jsx("div", { "data-slot": "code-editor-mockup", className: cn("grid content-start overflow-hidden rounded-
|
|
27
|
+
return (_jsx("div", { "data-slot": "code-editor-mockup", className: cn("grid content-start overflow-hidden rounded-md bg-primary text-primary-foreground", className), ...props }));
|
|
28
28
|
}
|
|
29
29
|
function CodeEditorMockupChrome({ className, children, ...props }) {
|
|
30
30
|
return (_jsxs("div", { "data-slot": "code-editor-mockup-chrome", className: cn("flex items-center gap-2 border-b border-b-[color-mix(in_srgb,var(--nc-line)_24%,var(--nc-canvas)_22%)] px-6 py-3", className), ...props, children: [_jsxs("span", { "data-slot": "code-editor-mockup-dots", "aria-hidden": "true", className: "flex flex-none gap-2", children: [_jsx("span", { className: "size-[11px] rounded-full bg-[color-mix(in_srgb,var(--nc-line)_24%,var(--nc-canvas)_22%)]" }), _jsx("span", { className: "size-[11px] rounded-full bg-[color-mix(in_srgb,var(--nc-line)_24%,var(--nc-canvas)_22%)]" }), _jsx("span", { className: "size-[11px] rounded-full bg-[color-mix(in_srgb,var(--nc-line)_24%,var(--nc-canvas)_22%)]" })] }), children] }));
|
|
@@ -28,7 +28,7 @@ function NavBarLogo({ className, ...props }) {
|
|
|
28
28
|
function NavBarLinks({ className, ...props }) {
|
|
29
29
|
return (_jsx("div", { "data-slot": "nav-bar-links", className: cn("hidden items-center justify-center gap-1 text-sm text-muted-foreground lg:inline-flex",
|
|
30
30
|
// each link reads as a flat pill with hover/focus affordance against the glass.
|
|
31
|
-
"[&_a]:rounded-
|
|
31
|
+
"[&_a]:rounded-sm [&_a]:px-3 [&_a]:py-2 [&_a]:text-inherit [&_a]:no-underline [&_a]:transition-colors", "[&_a:hover]:bg-accent [&_a:hover]:text-foreground", "[&_a[data-active]]:text-foreground", "[&_a:focus-visible]:outline [&_a:focus-visible]:outline-1 [&_a:focus-visible]:[outline-color:var(--ring)] [&_a:focus-visible]:outline-offset-2", className), ...props }));
|
|
32
32
|
}
|
|
33
33
|
function NavBarCta({ className, ...props }) {
|
|
34
34
|
return (_jsx("div", { "data-slot": "nav-bar-cta", className: cn("inline-flex items-center gap-3 justify-self-end", className), ...props }));
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Radio as RadioPrimitive } from "@base-ui/react/radio";
|
|
2
2
|
import { RadioGroup as RadioGroupPrimitive } from "@base-ui/react/radio-group";
|
|
3
3
|
declare function RadioGroup({ className, ...props }: RadioGroupPrimitive.Props): import("react").JSX.Element;
|
|
4
|
-
declare function RadioGroupItem({ className, ...props }: RadioPrimitive.Root.Props): import("react").JSX.Element;
|
|
4
|
+
declare function RadioGroupItem({ children, className, ...props }: RadioPrimitive.Root.Props): import("react").JSX.Element;
|
|
5
5
|
export { RadioGroup, RadioGroupItem };
|
|
6
6
|
//# sourceMappingURL=radio-group.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"radio-group.d.ts","sourceRoot":"","sources":["../../src/components/radio-group.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,IAAI,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC/D,OAAO,EAAE,UAAU,IAAI,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AAI/E,iBAAS,UAAU,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,mBAAmB,CAAC,KAAK,+BAQrE;AAED,iBAAS,cAAc,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,cAAc,CAAC,IAAI,CAAC,KAAK,+
|
|
1
|
+
{"version":3,"file":"radio-group.d.ts","sourceRoot":"","sources":["../../src/components/radio-group.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,IAAI,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC/D,OAAO,EAAE,UAAU,IAAI,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AAI/E,iBAAS,UAAU,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,mBAAmB,CAAC,KAAK,+BAQrE;AAED,iBAAS,cAAc,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,cAAc,CAAC,IAAI,CAAC,KAAK,+BAoBnF;AAED,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,CAAC"}
|
|
@@ -5,7 +5,7 @@ import { cn } from "../lib/utils.js";
|
|
|
5
5
|
function RadioGroup({ className, ...props }) {
|
|
6
6
|
return (_jsx(RadioGroupPrimitive, { "data-slot": "radio-group", className: cn("grid w-full gap-2", className), ...props }));
|
|
7
7
|
}
|
|
8
|
-
function RadioGroupItem({ className, ...props }) {
|
|
9
|
-
return (_jsx(RadioPrimitive.Root, { "data-slot": "radio-group-item", className: cn("group/radio-group-item peer relative flex aspect-square size-4 shrink-0 rounded-full border border-input outline-none 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-[length:var(--ring-width-focus)] focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-[length:var(--ring-width-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(RadioPrimitive.Indicator, { "data-slot": "radio-group-indicator", className: "flex size-4 items-center justify-center", children: _jsx("span", { className: "absolute top-1/2 left-1/2 size-2 -translate-x-1/2 -translate-y-1/2 rounded-full bg-primary-foreground" }) }) }));
|
|
8
|
+
function RadioGroupItem({ children, className, ...props }) {
|
|
9
|
+
return (_jsx(RadioPrimitive.Root, { "data-slot": "radio-group-item", className: cn("group/radio-group-item peer relative flex aspect-square size-4 shrink-0 rounded-full border border-input outline-none 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-[length:var(--ring-width-focus)] focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-[length:var(--ring-width-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: children ?? (_jsx(RadioPrimitive.Indicator, { "data-slot": "radio-group-indicator", className: "flex size-4 items-center justify-center", children: _jsx("span", { className: "absolute top-1/2 left-1/2 size-2 -translate-x-1/2 -translate-y-1/2 rounded-full bg-primary-foreground" }) })) }));
|
|
10
10
|
}
|
|
11
11
|
export { RadioGroup, RadioGroupItem };
|
|
@@ -1,17 +1,24 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
-
import {
|
|
2
|
+
import { RadioGroup } from "./radio-group";
|
|
3
3
|
import { isThemeMode, themeModes, type ThemeMode } from "../lib/theme-mode";
|
|
4
4
|
export { isThemeMode, themeModes };
|
|
5
5
|
export type { ThemeMode };
|
|
6
6
|
export type ThemeModeOptionLabels = Record<ThemeMode, string>;
|
|
7
|
-
export type
|
|
7
|
+
export type ThemeModeOption = {
|
|
8
|
+
Icon: React.ComponentType<React.SVGProps<SVGSVGElement>>;
|
|
9
|
+
label: string;
|
|
10
|
+
mode: ThemeMode;
|
|
11
|
+
selected: boolean;
|
|
12
|
+
};
|
|
13
|
+
export type ThemeModeSwitcherProps = Omit<React.ComponentProps<typeof RadioGroup>, "children" | "defaultValue" | "onValueChange" | "value"> & {
|
|
8
14
|
"aria-label": string;
|
|
9
15
|
compact?: boolean;
|
|
10
16
|
itemClassName?: string;
|
|
11
17
|
labels: ThemeModeOptionLabels;
|
|
12
18
|
onValueChange: (value: ThemeMode) => void;
|
|
19
|
+
renderItem?: (option: ThemeModeOption) => React.ReactNode;
|
|
13
20
|
value: ThemeMode;
|
|
14
21
|
};
|
|
15
|
-
declare function ThemeModeSwitcher({ "aria-label": ariaLabel, className, compact, disabled, itemClassName, labels, onValueChange, value, ...props }: ThemeModeSwitcherProps): React.JSX.Element;
|
|
22
|
+
declare function ThemeModeSwitcher({ "aria-label": ariaLabel, className, compact, disabled, itemClassName, labels, onValueChange, renderItem, value, ...props }: ThemeModeSwitcherProps): React.JSX.Element;
|
|
16
23
|
export { ThemeModeSwitcher };
|
|
17
24
|
//# sourceMappingURL=theme-mode-switcher.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"theme-mode-switcher.d.ts","sourceRoot":"","sources":["../../src/components/theme-mode-switcher.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAG/B,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"theme-mode-switcher.d.ts","sourceRoot":"","sources":["../../src/components/theme-mode-switcher.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAG/B,OAAO,EAAE,UAAU,EAAkB,MAAM,0BAA0B,CAAC;AAEtE,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,KAAK,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAG3E,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,CAAC;AACnC,YAAY,EAAE,SAAS,EAAE,CAAC;AAE1B,MAAM,MAAM,qBAAqB,GAAG,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;AAE9D,MAAM,MAAM,eAAe,GAAG;IAC5B,IAAI,EAAE,KAAK,CAAC,aAAa,CAAC,KAAK,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAC,CAAC;IACzD,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,SAAS,CAAC;IAChB,QAAQ,EAAE,OAAO,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,sBAAsB,GAAG,IAAI,CACvC,KAAK,CAAC,cAAc,CAAC,OAAO,UAAU,CAAC,EACvC,UAAU,GAAG,cAAc,GAAG,eAAe,GAAG,OAAO,CACxD,GAAG;IACF,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,qBAAqB,CAAC;IAC9B,aAAa,EAAE,CAAC,KAAK,EAAE,SAAS,KAAK,IAAI,CAAC;IAC1C,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE,eAAe,KAAK,KAAK,CAAC,SAAS,CAAC;IAC1D,KAAK,EAAE,SAAS,CAAC;CAClB,CAAC;AAQF,iBAAS,iBAAiB,CAAC,EACzB,YAAY,EAAE,SAAS,EACvB,SAAS,EACT,OAAe,EACf,QAAQ,EACR,aAAa,EACb,MAAM,EACN,aAAa,EACb,UAAU,EACV,KAAK,EACL,GAAG,KAAK,EACT,EAAE,sBAAsB,qBAkDxB;AAED,OAAO,EAAE,iBAAiB,EAAE,CAAC"}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { MonitorIcon, MoonIcon, SunIcon } from "lucide-react";
|
|
4
|
-
import {
|
|
4
|
+
import { RadioGroup, RadioGroupItem } from "./radio-group.js";
|
|
5
|
+
import { toggleVariants } from "./toggle.js";
|
|
5
6
|
import { isThemeMode, themeModes } from "../lib/theme-mode.js";
|
|
6
7
|
import { cn } from "../lib/utils.js";
|
|
7
8
|
export { isThemeMode, themeModes };
|
|
@@ -10,16 +11,22 @@ const themeModeIcons = {
|
|
|
10
11
|
dark: MoonIcon,
|
|
11
12
|
system: MonitorIcon,
|
|
12
13
|
};
|
|
13
|
-
function ThemeModeSwitcher({ "aria-label": ariaLabel, className, compact = false, disabled, itemClassName, labels, onValueChange, value, ...props }) {
|
|
14
|
-
return (_jsx(
|
|
15
|
-
const nextMode = nextValue[0];
|
|
14
|
+
function ThemeModeSwitcher({ "aria-label": ariaLabel, className, compact = false, disabled, itemClassName, labels, onValueChange, renderItem, value, ...props }) {
|
|
15
|
+
return (_jsx(RadioGroup, { "aria-label": ariaLabel, className: cn("max-w-full", className), disabled: disabled, onValueChange: (nextMode) => {
|
|
16
16
|
if (isThemeMode(nextMode)) {
|
|
17
17
|
onValueChange(nextMode);
|
|
18
18
|
}
|
|
19
|
-
}, value:
|
|
19
|
+
}, value: value, ...props, children: themeModes.map((mode) => {
|
|
20
20
|
const Icon = themeModeIcons[mode];
|
|
21
21
|
const label = labels[mode];
|
|
22
|
-
|
|
22
|
+
const selected = value === mode;
|
|
23
|
+
return (_jsx(RadioGroupItem, { "aria-label": label, className: cn(toggleVariants({ variant: "outline" }),
|
|
24
|
+
// The selected tile takes the muted interaction surface the
|
|
25
|
+
// toggle uses for its own on-state, so it takes that surface's
|
|
26
|
+
// foreground with it. Left on the radio primitive's on-primary
|
|
27
|
+
// foreground, the selected label reads white on near-white in
|
|
28
|
+
// light and black on black in dark.
|
|
29
|
+
"aspect-auto w-auto min-w-0 gap-1.5 px-2.5 data-checked:bg-muted data-checked:text-foreground", compact ? "h-8 text-xs" : "h-9", itemClassName), disabled: disabled, value: mode, children: renderItem ? (renderItem({ Icon, label, mode, selected })) : (_jsxs(_Fragment, { children: [_jsx(Icon, { "aria-hidden": "true", "data-icon": true }), _jsx("span", { className: "min-w-0 truncate", children: label })] })) }, mode));
|
|
23
30
|
}) }));
|
|
24
31
|
}
|
|
25
32
|
export { ThemeModeSwitcher };
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
|
|
3
|
+
import { noDesignSystemSrcImport } from "./rules/no-design-system-src-import.js";
|
|
4
|
+
import { noDeprecatedZodApi } from "./rules/no-deprecated-zod-api.js";
|
|
5
|
+
import { noRawLogoImport } from "./rules/no-raw-logo-import.js";
|
|
6
|
+
import { noRawColorUtility } from "./rules/no-raw-color-utility.js";
|
|
7
|
+
import { noShadcnAppearanceOverride } from "./rules/no-shadcn-appearance-override.js";
|
|
8
|
+
import { noUnpinnedVercelFunctionsImport } from "./rules/no-unpinned-vercel-functions-import.js";
|
|
9
|
+
import { noUiSrcImport } from "./rules/no-ui-src-import.js";
|
|
10
|
+
import { noNativeVisibleControl } from "./rules/no-native-visible-control.js";
|
|
11
|
+
import { noRawFocusRingWidth } from "./rules/no-raw-focus-ring-width.js";
|
|
12
|
+
import { noInvalidTokenUtility } from "./rules/no-invalid-token-utility.js";
|
|
13
|
+
import { noTokenCategoryBypass } from "./rules/no-token-category-bypass.js";
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* The plugin carries no version of its own. Its bytes are published inside
|
|
17
|
+
* `@nadicodeai/ui`, whose version the release gate already forces to move in
|
|
18
|
+
* lockstep, so that manifest is the one home of the number and this reads it.
|
|
19
|
+
* The published copy of this file sits at `<ui>/dist/eslint/index.js` and the
|
|
20
|
+
* source sits at `tools/eslint-plugin-nadicodeai/index.js`, which puts the
|
|
21
|
+
* manifest at one of two places relative to here; the `name` field says which
|
|
22
|
+
* candidate is the right one, so the repository root's own manifest — also two
|
|
23
|
+
* levels up — cannot answer by accident.
|
|
24
|
+
*/
|
|
25
|
+
const PUBLISHING_PACKAGE = "@nadicodeai/ui";
|
|
26
|
+
const MANIFEST_CANDIDATES = ["../../package.json", `../../${PUBLISHING_PACKAGE}/package.json`];
|
|
27
|
+
|
|
28
|
+
const version = readPublishedVersion();
|
|
29
|
+
|
|
30
|
+
function readPublishedVersion() {
|
|
31
|
+
for (const candidate of MANIFEST_CANDIDATES) {
|
|
32
|
+
let manifest;
|
|
33
|
+
try {
|
|
34
|
+
manifest = JSON.parse(readFileSync(new URL(candidate, import.meta.url), "utf8"));
|
|
35
|
+
} catch {
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
if (manifest?.name === PUBLISHING_PACKAGE && typeof manifest.version === "string") {
|
|
39
|
+
return manifest.version;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
throw new Error(
|
|
43
|
+
`@nadicodeai/eslint-plugin: no ${PUBLISHING_PACKAGE} manifest at ${MANIFEST_CANDIDATES.join(" or ")}, so the plugin cannot report the version it ships under.`,
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export const strictDesignSystemRules = {
|
|
48
|
+
"nadicodeai/no-design-system-src-import": "error",
|
|
49
|
+
"nadicodeai/no-invalid-token-utility": "error",
|
|
50
|
+
"nadicodeai/no-native-visible-control": "error",
|
|
51
|
+
"nadicodeai/no-raw-color-utility": "error",
|
|
52
|
+
"nadicodeai/no-raw-focus-ring-width": "error",
|
|
53
|
+
"nadicodeai/no-raw-logo-import": "error",
|
|
54
|
+
"nadicodeai/no-shadcn-appearance-override": "error",
|
|
55
|
+
"nadicodeai/no-token-category-bypass": "error",
|
|
56
|
+
"nadicodeai/no-ui-src-import": "error",
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
/** @type {import('eslint').ESLint.Plugin} */
|
|
60
|
+
const plugin = {
|
|
61
|
+
meta: {
|
|
62
|
+
name: "@nadicodeai/eslint-plugin",
|
|
63
|
+
version,
|
|
64
|
+
},
|
|
65
|
+
configs: {},
|
|
66
|
+
rules: {
|
|
67
|
+
"no-deprecated-zod-api": noDeprecatedZodApi,
|
|
68
|
+
"no-raw-logo-import": noRawLogoImport,
|
|
69
|
+
"no-raw-color-utility": noRawColorUtility,
|
|
70
|
+
"no-shadcn-appearance-override": noShadcnAppearanceOverride,
|
|
71
|
+
"no-native-visible-control": noNativeVisibleControl,
|
|
72
|
+
"no-raw-focus-ring-width": noRawFocusRingWidth,
|
|
73
|
+
"no-unpinned-vercel-functions-import": noUnpinnedVercelFunctionsImport,
|
|
74
|
+
"no-ui-src-import": noUiSrcImport,
|
|
75
|
+
"no-design-system-src-import": noDesignSystemSrcImport,
|
|
76
|
+
"no-invalid-token-utility": noInvalidTokenUtility,
|
|
77
|
+
"no-token-category-bypass": noTokenCategoryBypass,
|
|
78
|
+
},
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
export const strictDesignSystemConfig = [
|
|
82
|
+
{
|
|
83
|
+
name: "nadicodeai/design-system-strict",
|
|
84
|
+
plugins: { nadicodeai: plugin },
|
|
85
|
+
rules: strictDesignSystemRules,
|
|
86
|
+
},
|
|
87
|
+
];
|
|
88
|
+
|
|
89
|
+
plugin.configs["flat/strict"] = strictDesignSystemConfig;
|
|
90
|
+
|
|
91
|
+
export default plugin;
|