@ai-matrx/design-system 0.60.1 → 0.60.6
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/CHANGELOG.md +41 -0
- package/dist/desktop-lint.mjs +218 -0
- package/dist/desktop.cjs +3565 -0
- package/dist/desktop.cjs.map +1 -0
- package/dist/desktop.css +234 -0
- package/dist/desktop.d.cts +484 -0
- package/dist/desktop.d.ts +484 -0
- package/dist/desktop.js +3533 -0
- package/dist/desktop.js.map +1 -0
- package/dist/index.d.cts +14 -876
- package/dist/index.d.ts +14 -876
- package/dist/organization-picker-mLqIcesx.d.cts +875 -0
- package/dist/organization-picker-mLqIcesx.d.ts +875 -0
- package/package.json +13 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,46 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.60.6
|
|
4
|
+
|
|
5
|
+
Automatic changed-only republish (docs/metadata drift since the last tag — see
|
|
6
|
+
`git diff npm/design-system/v0.60.1..npm/design-system/v0.60.6 -- apps/shared/design-system`).
|
|
7
|
+
No source changes intended and no consumer action required.
|
|
8
|
+
|
|
9
|
+
## 0.60.5
|
|
10
|
+
|
|
11
|
+
Automatic changed-only republish (docs/metadata drift since the last tag — see
|
|
12
|
+
`git diff npm/design-system/v0.60.1..npm/design-system/v0.60.5 -- apps/shared/design-system`).
|
|
13
|
+
No source changes intended and no consumer action required.
|
|
14
|
+
|
|
15
|
+
## 0.60.4
|
|
16
|
+
|
|
17
|
+
Automatic changed-only republish (docs/metadata drift since the last tag — see
|
|
18
|
+
`git diff npm/design-system/v0.60.1..npm/design-system/v0.60.4 -- apps/shared/design-system`).
|
|
19
|
+
No source changes intended and no consumer action required.
|
|
20
|
+
|
|
21
|
+
## 0.60.3
|
|
22
|
+
|
|
23
|
+
Automatic changed-only republish (docs/metadata drift since the last tag — see
|
|
24
|
+
`git diff npm/design-system/v0.60.1..npm/design-system/v0.60.3 -- apps/shared/design-system`).
|
|
25
|
+
No source changes intended and no consumer action required.
|
|
26
|
+
|
|
27
|
+
## 0.60.2
|
|
28
|
+
### Unreleased
|
|
29
|
+
|
|
30
|
+
- **THE DESKTOP STANDARD (`./desktop`, `./desktop.css`, `./desktop/lint`).** An opt-in macOS-class
|
|
31
|
+
layer for desktop apps (Matrx 2): the named scale (text styles, label levels, surfaces, fills,
|
|
32
|
+
tint, system colours, 22/28/32 controls, 24/28/36/48 rows, 16 px icons, 6/10/12 radii) and the
|
|
33
|
+
components built only from it — Text, Button, IconButton, TextField, SearchField, TextArea,
|
|
34
|
+
Select, Segmented, Page, PageHeader, Toolbar, Section, Row, DataTable, SourceList, StateView,
|
|
35
|
+
Banner, Meter, Tag, StatusLight, Kbd, Spinner, plus display formatters. `./desktop/lint` is the
|
|
36
|
+
ESLint plugin that holds desktop apps to it. Rules: `src/desktop/DESKTOP.md`. Guard:
|
|
37
|
+
`src/desktop/desktop-law.test.ts`.
|
|
38
|
+
|
|
39
|
+
### Consumer action
|
|
40
|
+
|
|
41
|
+
- None for the web: nothing loads `desktop.css` unless a host imports it; no existing component
|
|
42
|
+
changed.
|
|
43
|
+
|
|
3
44
|
## 0.60.1
|
|
4
45
|
|
|
5
46
|
- **A declared actions column is as wide as its controls.** A `customActions` column with no
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* THE DESKTOP CLASS LAW — the one checker behind the package test and the desktop app's ESLint rule.
|
|
3
|
+
*
|
|
4
|
+
* Desktop UI uses the named scale from desktop.css and nothing else. A class is refused when it:
|
|
5
|
+
* - carries an arbitrary value (`h-[39px]`, `text-[12px]`, `bg-[#fff]`, `w-[var(--x)]`, `z-[9]`)
|
|
6
|
+
* - uses a Tailwind font size, line height or letter spacing (the text styles carry them)
|
|
7
|
+
* - uses Tailwind's colour palette or black/white (semantic colours only)
|
|
8
|
+
* - uses the web token vocabulary (`text-muted-foreground`, `bg-primary` …) instead of the
|
|
9
|
+
* desktop one (`text-label-secondary`, `bg-tint` …)
|
|
10
|
+
* - uses a radius or shadow outside the named set
|
|
11
|
+
* - sets a font weight other than medium/normal (the text styles carry their weight)
|
|
12
|
+
*
|
|
13
|
+
* Variant prefixes (`hover:`, `data-[state=on]:`, `[&_svg]:`) are allowed; only the utility after
|
|
14
|
+
* the last top-level `:` is judged. Plain JavaScript so ESLint configs and tests can both load it.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
const PALETTE =
|
|
18
|
+
"slate|gray|zinc|neutral|stone|red|orange|amber|yellow|lime|green|emerald|teal|cyan|sky|blue|indigo|violet|purple|fuchsia|pink|rose|black|white";
|
|
19
|
+
const COLOR_UTILS = "bg|text|border|border-[trblxy]|ring|ring-offset|outline|fill|stroke|from|via|to|divide|decoration|accent|caret|placeholder|shadow";
|
|
20
|
+
const WEB_TOKENS =
|
|
21
|
+
"foreground|background|muted|muted-foreground|primary|primary-foreground|secondary|secondary-foreground|accent|accent-foreground|destructive|destructive-foreground|card|card-foreground|popover|popover-foreground|input|ring|border|info|warning|success|chrome";
|
|
22
|
+
|
|
23
|
+
const RULES = [
|
|
24
|
+
[/\[/, "arbitrary value: use a named size or colour from desktop.css"],
|
|
25
|
+
[/^text-(xs|sm|base|lg|xl|[2-9]xl)$/, "Tailwind font size: use a text style (text-body, text-callout, text-headline …) or <Text variant>"],
|
|
26
|
+
[/^(leading|tracking)-/, "line height / letter spacing: the text styles carry them"],
|
|
27
|
+
[/^font-(thin|extralight|light|semibold|bold|extrabold|black)$/, "font weight: titles and headline carry their weight; use font-medium for emphasis"],
|
|
28
|
+
[new RegExp(`^(${COLOR_UTILS})-(${PALETTE})(-\\d+)?(\\/\\d+)?$`), "palette colour: use a semantic colour (label, fill, separator, tint, system-red …)"],
|
|
29
|
+
[new RegExp(`^(${COLOR_UTILS})-(${WEB_TOKENS})(\\/\\d+)?$`), "web token: use the desktop vocabulary (text-label-secondary, bg-fill, border-separator, bg-tint …)"],
|
|
30
|
+
[/^rounded(-[trblse]{1,2})?-(xs|sm|md|lg|xl|[2-4]xl)$/, "radius: use rounded-control, rounded-group, rounded-panel or rounded-full"],
|
|
31
|
+
[/^rounded(-[trblse]{1,2})?$/, "radius: use rounded-control, rounded-group, rounded-panel or rounded-full"],
|
|
32
|
+
[/^shadow-(2xs|xs|sm|md|lg|xl|2xl|inner)$/, "shadow: use shadow-control, shadow-raised or shadow-popover"],
|
|
33
|
+
[/^shadow$/, "shadow: use shadow-control, shadow-raised or shadow-popover"],
|
|
34
|
+
[/^z-\d+$/, "z-index: layering belongs to the overlay primitives"],
|
|
35
|
+
];
|
|
36
|
+
|
|
37
|
+
/** The utility a class token applies, with variant prefixes and the `!`/`-` modifiers removed. */
|
|
38
|
+
export function utilityOf(token) {
|
|
39
|
+
let depth = 0;
|
|
40
|
+
let cut = 0;
|
|
41
|
+
for (let i = 0; i < token.length; i += 1) {
|
|
42
|
+
const ch = token[i];
|
|
43
|
+
if (ch === "[" || ch === "(") depth += 1;
|
|
44
|
+
else if (ch === "]" || ch === ")") depth -= 1;
|
|
45
|
+
else if (ch === ":" && depth === 0) cut = i + 1;
|
|
46
|
+
}
|
|
47
|
+
return token.slice(cut).replace(/^!/, "").replace(/!$/, "").replace(/^-/, "");
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** Returns the reason a class token breaks the law, or null when it is lawful. */
|
|
51
|
+
export function classViolation(token) {
|
|
52
|
+
const utility = utilityOf(token);
|
|
53
|
+
if (!utility) return null;
|
|
54
|
+
for (const [pattern, reason] of RULES) {
|
|
55
|
+
if (pattern.test(utility)) return reason;
|
|
56
|
+
}
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** Every unlawful token in a class string, with its reason. */
|
|
61
|
+
export function classViolations(classString) {
|
|
62
|
+
return classString
|
|
63
|
+
.split(/\s+/)
|
|
64
|
+
.filter(Boolean)
|
|
65
|
+
.map((token) => ({ token, reason: classViolation(token) }))
|
|
66
|
+
.filter((v) => v.reason !== null);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const CLASS_CALLEES = new Set(["cn", "cva", "clsx", "twMerge"]);
|
|
70
|
+
|
|
71
|
+
function stringsUnder(node, out) {
|
|
72
|
+
if (!node || typeof node !== "object") return out;
|
|
73
|
+
if (node.type === "Literal" && typeof node.value === "string") out.push(node);
|
|
74
|
+
else if (node.type === "TemplateElement") out.push(node);
|
|
75
|
+
for (const key of Object.keys(node)) {
|
|
76
|
+
if (key === "parent" || key === "loc" || key === "range") continue;
|
|
77
|
+
const child = node[key];
|
|
78
|
+
if (Array.isArray(child)) child.forEach((c) => stringsUnder(c, out));
|
|
79
|
+
else if (child && typeof child === "object" && typeof child.type === "string") stringsUnder(child, out);
|
|
80
|
+
}
|
|
81
|
+
return out;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function report(context, node) {
|
|
85
|
+
const text = node.type === "TemplateElement" ? node.value.cooked ?? node.value.raw : node.value;
|
|
86
|
+
for (const { token, reason } of classViolations(text)) {
|
|
87
|
+
context.report({ node, message: `"${token}" — ${reason}.` });
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/** ESLint rule: class strings in className / class attributes and cn()/cva()/clsx() calls. */
|
|
92
|
+
export const desktopClassRule = {
|
|
93
|
+
meta: {
|
|
94
|
+
type: "problem",
|
|
95
|
+
docs: { description: "Desktop UI uses only the named scale of @ai-matrx/design-system/desktop.css" },
|
|
96
|
+
schema: [],
|
|
97
|
+
},
|
|
98
|
+
create(context) {
|
|
99
|
+
return {
|
|
100
|
+
JSXAttribute(node) {
|
|
101
|
+
const name = node.name && node.name.name;
|
|
102
|
+
if ((name === "className" || name === "class") && node.value) stringsUnder(node.value, []).forEach((s) => report(context, s));
|
|
103
|
+
},
|
|
104
|
+
CallExpression(node) {
|
|
105
|
+
if (node.callee.type === "Identifier" && CLASS_CALLEES.has(node.callee.name)) {
|
|
106
|
+
// cva's second argument is a config object; its keys are variant names, its values classes.
|
|
107
|
+
node.arguments.forEach((arg) => stringsUnder(arg, []).forEach((s) => report(context, s)));
|
|
108
|
+
}
|
|
109
|
+
},
|
|
110
|
+
};
|
|
111
|
+
},
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
/** ESLint rule: no inline style values (colours, pixel sizes); computed widths/positions pass. */
|
|
115
|
+
export const desktopInlineStyleRule = {
|
|
116
|
+
meta: {
|
|
117
|
+
type: "problem",
|
|
118
|
+
docs: { description: "Desktop UI takes no literal colours or pixel sizes in style={{}}" },
|
|
119
|
+
schema: [],
|
|
120
|
+
},
|
|
121
|
+
create(context) {
|
|
122
|
+
return {
|
|
123
|
+
JSXAttribute(node) {
|
|
124
|
+
if (node.name?.name !== "style" || node.value?.type !== "JSXExpressionContainer") return;
|
|
125
|
+
const expr = node.value.expression;
|
|
126
|
+
if (expr.type !== "ObjectExpression") return;
|
|
127
|
+
for (const prop of expr.properties) {
|
|
128
|
+
if (prop.type !== "Property") continue;
|
|
129
|
+
const v = prop.value;
|
|
130
|
+
const literal = v.type === "Literal" ? v.value : v.type === "TemplateLiteral" && v.expressions.length === 0 ? v.quasis[0].value.cooked : undefined;
|
|
131
|
+
if (literal === undefined) continue;
|
|
132
|
+
if (typeof literal === "number" || /(#[0-9a-f]{3,8}\b|\brgba?\(|\bhsla?\(|\d+px)/i.test(String(literal))) {
|
|
133
|
+
context.report({ node: prop, message: "Literal style value: use a named size or colour from desktop.css." });
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
},
|
|
137
|
+
};
|
|
138
|
+
},
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
/** The raw elements a desktop screen never draws itself, and the component that replaces each. */
|
|
142
|
+
export const RAW_ELEMENTS = {
|
|
143
|
+
button: "Button, IconButton, Row (onSelect) or SourceListItem",
|
|
144
|
+
input: "TextField, SearchField, Switch or Checkbox",
|
|
145
|
+
select: "Select",
|
|
146
|
+
textarea: "TextArea",
|
|
147
|
+
table: "DataTable",
|
|
148
|
+
progress: "Meter",
|
|
149
|
+
h1: "PageHeader or <Text as=\"h1\">",
|
|
150
|
+
h2: "Section title or <Text as=\"h2\">",
|
|
151
|
+
h3: "<Text as=\"h3\">",
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
/** ESLint rule: no raw controls, tables or headings in desktop screens. */
|
|
155
|
+
export const desktopRawElementRule = {
|
|
156
|
+
meta: {
|
|
157
|
+
type: "problem",
|
|
158
|
+
docs: { description: "Desktop screens compose design-system components, never raw controls" },
|
|
159
|
+
schema: [],
|
|
160
|
+
},
|
|
161
|
+
create(context) {
|
|
162
|
+
return {
|
|
163
|
+
JSXOpeningElement(node) {
|
|
164
|
+
const name = node.name.type === "JSXIdentifier" ? node.name.name : null;
|
|
165
|
+
if (name && Object.hasOwn(RAW_ELEMENTS, name)) {
|
|
166
|
+
context.report({ node, message: `Raw <${name}>: use ${RAW_ELEMENTS[name]} from @ai-matrx/design-system/desktop.` });
|
|
167
|
+
}
|
|
168
|
+
},
|
|
169
|
+
};
|
|
170
|
+
},
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
const CELL_CONTROLS = new Set(["Button", "IconButton", "DropdownMenu", "button"]);
|
|
174
|
+
|
|
175
|
+
function jsxNamesUnder(node, out) {
|
|
176
|
+
if (!node || typeof node !== "object") return out;
|
|
177
|
+
if (node.type === "JSXOpeningElement" && node.name.type === "JSXIdentifier") out.push(node);
|
|
178
|
+
for (const key of Object.keys(node)) {
|
|
179
|
+
if (key === "parent" || key === "loc" || key === "range") continue;
|
|
180
|
+
const child = node[key];
|
|
181
|
+
if (Array.isArray(child)) child.forEach((c) => jsxNamesUnder(c, out));
|
|
182
|
+
else if (child && typeof child === "object" && typeof child.type === "string") jsxNamesUnder(child, out);
|
|
183
|
+
}
|
|
184
|
+
return out;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/** ESLint rule: a DataTable column's `cell` never draws a control — row actions go through the
|
|
188
|
+
* table's `rowAction` / `rowMenu` slots, so every table's actions look and behave the same. */
|
|
189
|
+
export const desktopCellControlRule = {
|
|
190
|
+
meta: {
|
|
191
|
+
type: "problem",
|
|
192
|
+
docs: { description: "Table row actions go through DataTable rowAction / rowMenu, never a control in a cell" },
|
|
193
|
+
schema: [],
|
|
194
|
+
},
|
|
195
|
+
create(context) {
|
|
196
|
+
return {
|
|
197
|
+
Property(node) {
|
|
198
|
+
if (node.key?.type !== "Identifier" || node.key.name !== "cell") return;
|
|
199
|
+
for (const el of jsxNamesUnder(node.value, [])) {
|
|
200
|
+
if (CELL_CONTROLS.has(el.name.name)) {
|
|
201
|
+
context.report({ node: el, message: `<${el.name.name}> in a table cell: use DataTable rowAction (one small button) or rowMenu (everything else).` });
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
},
|
|
205
|
+
};
|
|
206
|
+
},
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
/** The ESLint plugin a desktop app registers: `plugins: { desktop: desktopLintPlugin }`. */
|
|
210
|
+
export const desktopLintPlugin = {
|
|
211
|
+
meta: { name: "@ai-matrx/design-system/desktop-lint" },
|
|
212
|
+
rules: {
|
|
213
|
+
"class-law": desktopClassRule,
|
|
214
|
+
"no-literal-style": desktopInlineStyleRule,
|
|
215
|
+
"no-raw-elements": desktopRawElementRule,
|
|
216
|
+
"no-cell-controls": desktopCellControlRule,
|
|
217
|
+
},
|
|
218
|
+
};
|