@assure-one/design-system 1.36.0 → 1.38.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/README.md +45 -16
- package/codemods/README.md +93 -0
- package/codemods/lib/registry.mjs +3 -0
- package/codemods/transforms/cm-09-side-drawer-to-sheet.mjs +348 -0
- package/codemods/transforms/cm-13-tooltip-delay.mjs +99 -0
- package/codemods/transforms/cm-18-data-table-parts.mjs +234 -0
- package/dist/css/components.css +1 -1
- package/dist/css/legacy-aliases.css +33 -1
- package/dist/css/shadcn.css +2 -2
- package/dist/css/tailwind.css +9 -0
- package/dist/css/tokens.css +72 -38
- package/dist/design-system-provider-B5MbvbRc.d.ts +609 -0
- package/dist/icons/index.d.ts +2 -2
- package/dist/icons/index.js +6 -2
- package/dist/icons/index.js.map +1 -1
- package/dist/{index-CQwzTm0v.d.ts → index-vztxMSfl.d.ts} +7 -4
- package/dist/index.d.ts +1757 -303
- package/dist/index.js +4125 -1956
- package/dist/index.js.map +1 -1
- package/dist/next/index.d.ts +1 -1
- package/dist/styles.css +1 -1
- package/dist/{system-BDU18fVg.d.ts → system-CyAVbHhM.d.ts} +16 -16
- package/dist/tokens/index.d.ts +1 -1
- package/dist/tokens/index.js +8 -8
- package/dist/tokens/index.js.map +1 -1
- package/docs/components.md +47 -14
- package/docs/components.registry.json +2415 -461
- package/docs/for-ai-agents.md +2 -0
- package/eslint-config/index.mjs +210 -0
- package/eslint-config/stylelint-rules.mjs +54 -0
- package/eslint-config/stylelint.mjs +73 -0
- package/package.json +8 -1
- package/dist/design-system-provider-cPUklDJv.d.ts +0 -220
package/docs/for-ai-agents.md
CHANGED
|
@@ -55,6 +55,8 @@ If a UI element exists in the design system, use it. Do not write `<button>`, `<
|
|
|
55
55
|
| `flex gap-*` wrappers | `Stack`, `Inline`; `Surface` / `Card` for boxes |
|
|
56
56
|
| `title="…"`, `window.confirm()`, `alert()` | `Tooltip`, `ConfirmActionButton` / `AlertDialog`, `useToast` |
|
|
57
57
|
|
|
58
|
+
Two shareable lint configs report the first three rows for you: `@assure-one/design-system/eslint-config` and `@assure-one/design-system/stylelint-config` (warn-level, experimental — see the README section "Lint configs for consumers").
|
|
59
|
+
|
|
58
60
|
Before writing any element, look it up in the ["Pick a component"](./components.md#pick-a-component) table. The only raw elements that are fine are structural ones the design system does not model (`<main>`, `<article>`, `<ul>` for plain lists, `<img>` for content images, `<form>` itself).
|
|
59
61
|
|
|
60
62
|
## 3. Forms: the `Field` recipe
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@assure-one/design-system/eslint-config` (W7-18).
|
|
3
|
+
*
|
|
4
|
+
* The consumer-facing half of the in-repo ratchets: the four findings the
|
|
5
|
+
* design system ratchets on its own source (`scripts/ratchet/*` — raw platform
|
|
6
|
+
* controls, direct Radix imports, deep imports past the public entries,
|
|
7
|
+
* `--ds-*` redefinition outside a preset) reported in a product's own lint run.
|
|
8
|
+
*
|
|
9
|
+
* Every rule is **warn** by default, deliberately: a product adopts this on a
|
|
10
|
+
* codebase that already has hundreds of findings, records the count, and
|
|
11
|
+
* ratchets it down (W7-19). Raise the ones you have cleaned to "error" in your
|
|
12
|
+
* own config.
|
|
13
|
+
*
|
|
14
|
+
* Flat config only:
|
|
15
|
+
*
|
|
16
|
+
* import ds from "@assure-one/design-system/eslint-config";
|
|
17
|
+
* export default [...ds.configs.recommended];
|
|
18
|
+
*
|
|
19
|
+
* Or wire the plugin yourself: `plugins: { ds }` plus the rules you want.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
const ENTRY_SUBPATHS = new Set([
|
|
23
|
+
"tokens",
|
|
24
|
+
"icons",
|
|
25
|
+
"next",
|
|
26
|
+
"testing",
|
|
27
|
+
"styles.css",
|
|
28
|
+
"package.json",
|
|
29
|
+
"eslint-config",
|
|
30
|
+
"stylelint-config",
|
|
31
|
+
]);
|
|
32
|
+
|
|
33
|
+
/** The DS component(s) that own each raw element (scripts/ratchet/lib/raw-controls-owners.mjs). */
|
|
34
|
+
const RAW_CONTROLS = {
|
|
35
|
+
button: "Button, IconButton, LinkButton, SubmitButton or Pressable",
|
|
36
|
+
input: "Input, NumberInput, SearchInput, Checkbox, RadioGroup, Switch, Slider, DatePicker…",
|
|
37
|
+
select: "Select, Combobox or MultiSelect",
|
|
38
|
+
textarea: "Textarea",
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const DS_PROPERTY = /^--ds-/;
|
|
42
|
+
const DEFAULT_PRESET_PATH = "(^|/)presets/";
|
|
43
|
+
|
|
44
|
+
const isStringLiteral = (node) => node?.type === "Literal" && typeof node.value === "string";
|
|
45
|
+
|
|
46
|
+
/** The `--ds-*` name a property key or JSX attribute declares, or undefined. */
|
|
47
|
+
function dsPropertyName(key, computed) {
|
|
48
|
+
if (computed) return undefined;
|
|
49
|
+
const name = isStringLiteral(key) ? key.value : key?.type === "Identifier" ? key.name : undefined;
|
|
50
|
+
return name && DS_PROPERTY.test(name) ? name : undefined;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** Import-like source string of a node, or undefined. */
|
|
54
|
+
function importSource(node) {
|
|
55
|
+
if (node.type === "ImportDeclaration" || node.type === "ExportNamedDeclaration") {
|
|
56
|
+
return isStringLiteral(node.source) ? node.source.value : undefined;
|
|
57
|
+
}
|
|
58
|
+
if (node.type === "ExportAllDeclaration") {
|
|
59
|
+
return isStringLiteral(node.source) ? node.source.value : undefined;
|
|
60
|
+
}
|
|
61
|
+
return undefined;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const importVisitors = (check) => ({
|
|
65
|
+
ImportDeclaration: (node) => check(node, importSource(node)),
|
|
66
|
+
ExportNamedDeclaration: (node) => check(node, importSource(node)),
|
|
67
|
+
ExportAllDeclaration: (node) => check(node, importSource(node)),
|
|
68
|
+
ImportExpression: (node) =>
|
|
69
|
+
check(node, isStringLiteral(node.source) ? node.source.value : undefined),
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
/** @type {import("eslint").Rule.RuleModule} */
|
|
73
|
+
const noRawControls = {
|
|
74
|
+
meta: {
|
|
75
|
+
type: "problem",
|
|
76
|
+
docs: {
|
|
77
|
+
description:
|
|
78
|
+
"Use the design system's control instead of a raw <button>, <input>, <select> or <textarea>.",
|
|
79
|
+
},
|
|
80
|
+
schema: [],
|
|
81
|
+
messages: {
|
|
82
|
+
raw: "<{{ element }}> is owned by the design system. Use {{ use }} (docs/components.md → “Never hand-roll”).",
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
create(context) {
|
|
86
|
+
return {
|
|
87
|
+
JSXOpeningElement(node) {
|
|
88
|
+
if (node.name?.type !== "JSXIdentifier") return;
|
|
89
|
+
const element = node.name.name;
|
|
90
|
+
const use = RAW_CONTROLS[element];
|
|
91
|
+
if (!use) return;
|
|
92
|
+
context.report({ node, messageId: "raw", data: { element, use } });
|
|
93
|
+
},
|
|
94
|
+
};
|
|
95
|
+
},
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
/** @type {import("eslint").Rule.RuleModule} */
|
|
99
|
+
const noRadixImports = {
|
|
100
|
+
meta: {
|
|
101
|
+
type: "problem",
|
|
102
|
+
docs: {
|
|
103
|
+
description: "Import the design system's primitive instead of the Radix package it wraps.",
|
|
104
|
+
},
|
|
105
|
+
schema: [],
|
|
106
|
+
messages: {
|
|
107
|
+
radix:
|
|
108
|
+
'"{{ source }}" is wrapped by the design system. Import the primitive from "@assure-one/design-system" so behaviour, tokens and the Radix version stay in one place.',
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
create(context) {
|
|
112
|
+
return importVisitors((node, source) => {
|
|
113
|
+
if (!source?.startsWith("@radix-ui/")) return;
|
|
114
|
+
context.report({ node, messageId: "radix", data: { source } });
|
|
115
|
+
});
|
|
116
|
+
},
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
/** @type {import("eslint").Rule.RuleModule} */
|
|
120
|
+
const noDeepImports = {
|
|
121
|
+
meta: {
|
|
122
|
+
type: "problem",
|
|
123
|
+
docs: {
|
|
124
|
+
description:
|
|
125
|
+
"Import from a published design-system entry, never from dist/ or src/ (ADR-011).",
|
|
126
|
+
},
|
|
127
|
+
schema: [],
|
|
128
|
+
messages: {
|
|
129
|
+
deep: '"{{ source }}" is not a published entry. Import from "@assure-one/design-system" or one of {{ entries }} — anything else may change without a major release (ADR-011).',
|
|
130
|
+
},
|
|
131
|
+
},
|
|
132
|
+
create(context) {
|
|
133
|
+
const entries = [...ENTRY_SUBPATHS].map((s) => `/${s}`).join(", ");
|
|
134
|
+
return importVisitors((node, source) => {
|
|
135
|
+
if (!source?.startsWith("@assure-one/design-system/")) return;
|
|
136
|
+
const subpath = source.slice("@assure-one/design-system/".length);
|
|
137
|
+
if (ENTRY_SUBPATHS.has(subpath) || subpath.startsWith("css/")) return;
|
|
138
|
+
context.report({ node, messageId: "deep", data: { source, entries } });
|
|
139
|
+
});
|
|
140
|
+
},
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
/** @type {import("eslint").Rule.RuleModule} */
|
|
144
|
+
const noDsTokenRedefinition = {
|
|
145
|
+
meta: {
|
|
146
|
+
type: "problem",
|
|
147
|
+
docs: {
|
|
148
|
+
description:
|
|
149
|
+
"Set a `--ds-*` custom property only in a theme preset, never inline in a component.",
|
|
150
|
+
},
|
|
151
|
+
schema: [
|
|
152
|
+
{
|
|
153
|
+
type: "object",
|
|
154
|
+
properties: { presetPath: { type: "string" } },
|
|
155
|
+
additionalProperties: false,
|
|
156
|
+
},
|
|
157
|
+
],
|
|
158
|
+
messages: {
|
|
159
|
+
redefined:
|
|
160
|
+
"{{ name }} is a design-system token. Override it in a theme preset (a file under presets/, or ThemeScope/BrandScope), not inline: an inline value escapes the theme and the token contract.",
|
|
161
|
+
},
|
|
162
|
+
},
|
|
163
|
+
create(context) {
|
|
164
|
+
const presetPath = new RegExp(context.options?.[0]?.presetPath ?? DEFAULT_PRESET_PATH);
|
|
165
|
+
const filename = (context.filename ?? context.getFilename?.() ?? "").replace(/\\/g, "/");
|
|
166
|
+
if (presetPath.test(filename)) return {};
|
|
167
|
+
const report = (node, name) => context.report({ node, messageId: "redefined", data: { name } });
|
|
168
|
+
return {
|
|
169
|
+
Property(node) {
|
|
170
|
+
const name = dsPropertyName(node.key, node.computed);
|
|
171
|
+
if (name) report(node, name);
|
|
172
|
+
},
|
|
173
|
+
JSXAttribute(node) {
|
|
174
|
+
if (node.name?.type !== "JSXNamespacedName") return;
|
|
175
|
+
const name = `${node.name.namespace?.name}:${node.name.name?.name}`;
|
|
176
|
+
if (DS_PROPERTY.test(name)) report(node, name);
|
|
177
|
+
},
|
|
178
|
+
};
|
|
179
|
+
},
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
/** The `ds` plugin: the rules alone, for a config that wires them itself. */
|
|
183
|
+
export const plugin = {
|
|
184
|
+
meta: { name: "@assure-one/design-system/eslint-config" },
|
|
185
|
+
rules: {
|
|
186
|
+
"no-raw-controls": noRawControls,
|
|
187
|
+
"no-radix-imports": noRadixImports,
|
|
188
|
+
"no-deep-imports": noDeepImports,
|
|
189
|
+
"no-ds-token-redefinition": noDsTokenRedefinition,
|
|
190
|
+
},
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
/** Warn-level rule table (W7-18): every rule this config ships. */
|
|
194
|
+
export const rules = Object.fromEntries(
|
|
195
|
+
Object.keys(plugin.rules).map((name) => [`ds/${name}`, "warn"]),
|
|
196
|
+
);
|
|
197
|
+
|
|
198
|
+
/** Flat config: `export default [...ds.configs.recommended]`. */
|
|
199
|
+
export const configs = {
|
|
200
|
+
recommended: [
|
|
201
|
+
{
|
|
202
|
+
name: "@assure-one/design-system/recommended",
|
|
203
|
+
files: ["**/*.{js,jsx,mjs,cjs,ts,tsx,mts,cts}"],
|
|
204
|
+
plugins: { ds: plugin },
|
|
205
|
+
rules,
|
|
206
|
+
},
|
|
207
|
+
],
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
export default { plugin, rules, configs };
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The two stylelint checks of W7-18, as plain PostCSS finders.
|
|
3
|
+
*
|
|
4
|
+
* Kept free of any `stylelint` import so they can be exercised directly (and
|
|
5
|
+
* reused by the repo's own CSS tooling): `stylelint.mjs` wraps them into the
|
|
6
|
+
* published plugin. They mirror the `css-hygiene` ratchet family's concerns
|
|
7
|
+
* (`scripts/ratchet/lib/css-hygiene-analyse.mjs`) from the consumer side.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/** A declaration whose property is a design-system token. */
|
|
11
|
+
const DS_PROPERTY = /^--ds-/;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Selectors that reach design-system DOM: a `ds:`-prefixed utility class
|
|
15
|
+
* (written `.ds\:flex` in CSS), the `ds-` component classes, and the
|
|
16
|
+
* `data-slot` / `data-ds-*` attributes components expose (ADR-008).
|
|
17
|
+
*/
|
|
18
|
+
const DS_SELECTOR = /\.ds\\?:|\.ds-|\[data-slot|\[data-ds-/;
|
|
19
|
+
|
|
20
|
+
/** The nearest enclosing rule's selector, or undefined at the top level. */
|
|
21
|
+
function enclosingSelector(node) {
|
|
22
|
+
for (let parent = node.parent; parent; parent = parent.parent) {
|
|
23
|
+
if (parent.type === "rule") return parent.selector;
|
|
24
|
+
}
|
|
25
|
+
return undefined;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** Every `--ds-*` declaration in a stylesheet: `[{ prop, decl }]`. */
|
|
29
|
+
export function dsTokenDeclarations(root) {
|
|
30
|
+
const found = [];
|
|
31
|
+
root.walkDecls((decl) => {
|
|
32
|
+
if (DS_PROPERTY.test(decl.prop)) found.push({ prop: decl.prop, decl });
|
|
33
|
+
});
|
|
34
|
+
return found;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** Every `!important` declaration under a design-system selector: `[{ prop, selector, decl }]`. */
|
|
38
|
+
export function importantOnDsSelectors(root) {
|
|
39
|
+
const found = [];
|
|
40
|
+
root.walkDecls((decl) => {
|
|
41
|
+
if (!decl.important) return;
|
|
42
|
+
const selector = enclosingSelector(decl);
|
|
43
|
+
if (!selector || !DS_SELECTOR.test(selector)) return;
|
|
44
|
+
found.push({ prop: decl.prop, selector, decl });
|
|
45
|
+
});
|
|
46
|
+
return found;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export const messages = {
|
|
50
|
+
dsToken: (prop) =>
|
|
51
|
+
`${prop} is a design-system token. Set it in a theme preset (the block \`pnpm derive-consumer-preset\` writes, or ThemeScope/BrandScope), not in app CSS — an override here escapes the theme and the token contract.`,
|
|
52
|
+
important: (prop, selector) =>
|
|
53
|
+
`Avoid \`!important\` on a design-system selector (\`${selector}\` → ${prop}). Pass \`className\`/\`classNames\` instead: consumer classes already win by cascade (ADR-004).`,
|
|
54
|
+
};
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@assure-one/design-system/stylelint-config` (W7-18).
|
|
3
|
+
*
|
|
4
|
+
* The CSS half of the consumer lint surface: the two things a product's own
|
|
5
|
+
* stylesheet does to the design system that the `css-hygiene` ratchet catches
|
|
6
|
+
* inside this repository — redefining a `--ds-*` token outside a theme preset,
|
|
7
|
+
* and `!important` on a design-system selector.
|
|
8
|
+
*
|
|
9
|
+
* Both are **warnings** (the `severity: "warning"` below), for the same reason
|
|
10
|
+
* the ESLint rules are: a product adopts this on CSS that already has
|
|
11
|
+
* findings, records the count, and ratchets it down (W7-19).
|
|
12
|
+
*
|
|
13
|
+
* // stylelint.config.mjs
|
|
14
|
+
* import ds from "@assure-one/design-system/stylelint-config";
|
|
15
|
+
* export default { extends: [], ...ds };
|
|
16
|
+
*
|
|
17
|
+
* `stylelint` itself is the consumer's (an optional peer dependency here).
|
|
18
|
+
* Preset files are exempt: override `ignoreFiles`/`overrides` to move them.
|
|
19
|
+
*/
|
|
20
|
+
import stylelint from "stylelint";
|
|
21
|
+
|
|
22
|
+
import { dsTokenDeclarations, importantOnDsSelectors, messages } from "./stylelint-rules.mjs";
|
|
23
|
+
|
|
24
|
+
const NAMESPACE = "assure-ds";
|
|
25
|
+
|
|
26
|
+
const plugin = (ruleName, find, message) =>
|
|
27
|
+
stylelint.createPlugin(`${NAMESPACE}/${ruleName}`, (primary, secondary, context) => {
|
|
28
|
+
const name = `${NAMESPACE}/${ruleName}`;
|
|
29
|
+
return (root, result) => {
|
|
30
|
+
if (!primary) return;
|
|
31
|
+
for (const found of find(root)) {
|
|
32
|
+
stylelint.utils.report({
|
|
33
|
+
result,
|
|
34
|
+
ruleName: name,
|
|
35
|
+
node: found.decl,
|
|
36
|
+
word: found.prop,
|
|
37
|
+
message: message(found),
|
|
38
|
+
severity: secondary?.severity ?? context?.severity ?? "warning",
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
/** The two plugin rules, for a config that wires them itself. */
|
|
45
|
+
export const plugins = [
|
|
46
|
+
plugin("no-ds-token-redefinition", dsTokenDeclarations, ({ prop }) => messages.dsToken(prop)),
|
|
47
|
+
plugin("no-important-on-ds-selectors", importantOnDsSelectors, ({ prop, selector }) =>
|
|
48
|
+
messages.important(prop, selector),
|
|
49
|
+
),
|
|
50
|
+
];
|
|
51
|
+
|
|
52
|
+
/** Warn-level rule table (W7-18). */
|
|
53
|
+
export const rules = {
|
|
54
|
+
[`${NAMESPACE}/no-ds-token-redefinition`]: [true, { severity: "warning" }],
|
|
55
|
+
[`${NAMESPACE}/no-important-on-ds-selectors`]: [true, { severity: "warning" }],
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* The shareable config. Theme presets are where `--ds-*` belongs, so any file
|
|
60
|
+
* under a `presets/` directory (or named `*.preset.css`) is exempt.
|
|
61
|
+
*/
|
|
62
|
+
export const config = {
|
|
63
|
+
plugins,
|
|
64
|
+
rules,
|
|
65
|
+
overrides: [
|
|
66
|
+
{
|
|
67
|
+
files: ["**/presets/**/*.css", "**/*.preset.css"],
|
|
68
|
+
rules: { [`${NAMESPACE}/no-ds-token-redefinition`]: null },
|
|
69
|
+
},
|
|
70
|
+
],
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
export default config;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@assure-one/design-system",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.38.0",
|
|
4
4
|
"description": "Assure One design system — tokens, primitives, and patterns for the Assure Suite.",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"private": false,
|
|
@@ -45,12 +45,15 @@
|
|
|
45
45
|
"default": "./dist/testing/index.cjs"
|
|
46
46
|
}
|
|
47
47
|
},
|
|
48
|
+
"./eslint-config": "./eslint-config/index.mjs",
|
|
49
|
+
"./stylelint-config": "./eslint-config/stylelint.mjs",
|
|
48
50
|
"./package.json": "./package.json"
|
|
49
51
|
},
|
|
50
52
|
"files": [
|
|
51
53
|
"dist",
|
|
52
54
|
"codemods",
|
|
53
55
|
"!codemods/__test__",
|
|
56
|
+
"eslint-config",
|
|
54
57
|
"docs/components.md",
|
|
55
58
|
"docs/components.registry.json",
|
|
56
59
|
"docs/for-ai-agents.md",
|
|
@@ -65,12 +68,16 @@
|
|
|
65
68
|
},
|
|
66
69
|
"peerDependencies": {
|
|
67
70
|
"next": ">=15",
|
|
71
|
+
"stylelint": ">=16",
|
|
68
72
|
"react": ">=19",
|
|
69
73
|
"react-dom": ">=19"
|
|
70
74
|
},
|
|
71
75
|
"peerDependenciesMeta": {
|
|
72
76
|
"next": {
|
|
73
77
|
"optional": true
|
|
78
|
+
},
|
|
79
|
+
"stylelint": {
|
|
80
|
+
"optional": true
|
|
74
81
|
}
|
|
75
82
|
},
|
|
76
83
|
"dependencies": {
|
|
@@ -1,220 +0,0 @@
|
|
|
1
|
-
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
-
import { ComponentType, AnchorHTMLAttributes, Ref, ReactNode, ImgHTMLAttributes } from 'react';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* L0 foundation (W5-20, ADR-010). The typed messages catalogue every
|
|
6
|
-
* design-system component reads its generic strings from (target architecture
|
|
7
|
-
* §38): accessibility and micro-copy with a universal meaning — Clear, No
|
|
8
|
-
* results, Loading, optional — never product copy, which is always the
|
|
9
|
-
* consumer's (`children`, `label`, `placeholder`).
|
|
10
|
-
*
|
|
11
|
-
* The catalogue is keyed by component namespace; each namespace is the
|
|
12
|
-
* `*Messages` interface that component already accepted through its
|
|
13
|
-
* `messages` prop before the provider existed, so the resolution order is one
|
|
14
|
-
* rule everywhere: **prop > provider > English default**. A value is a string
|
|
15
|
-
* or, where a parameter is involved, a function of that parameter (no string
|
|
16
|
-
* concatenation or pluralisation inside components).
|
|
17
|
-
*
|
|
18
|
-
* The English defaults live in `./en.ts`; `./en.json` is generated from it
|
|
19
|
-
* (`pnpm messages:generate`) so translators start from the full list.
|
|
20
|
-
*/
|
|
21
|
-
/** The copy a `Select` renders. @experimental */
|
|
22
|
-
interface SelectMessages {
|
|
23
|
-
/** The accessible name of the `clearable` button. Default `"Clear selection"`. */
|
|
24
|
-
clear: string;
|
|
25
|
-
}
|
|
26
|
-
/** The copy a `SearchInput` renders. @experimental */
|
|
27
|
-
interface SearchInputMessages {
|
|
28
|
-
/** The accessible name of the clear button. Default `"Clear search"`. */
|
|
29
|
-
clear: string;
|
|
30
|
-
}
|
|
31
|
-
/** The copy a `Combobox` (and `MultiSelect`) renders. @experimental */
|
|
32
|
-
interface ComboboxMessages {
|
|
33
|
-
/** The empty state when the query matches nothing. */
|
|
34
|
-
empty: string;
|
|
35
|
-
/** The loading state while `loading`. */
|
|
36
|
-
loading: string;
|
|
37
|
-
/** The label of the create row for a query. */
|
|
38
|
-
create: (query: string) => string;
|
|
39
|
-
/** The accessible name of the clear button. */
|
|
40
|
-
clear: string;
|
|
41
|
-
/** The accessible name of the toggle button. */
|
|
42
|
-
toggle: string;
|
|
43
|
-
/** The accessible name of a chip's remove button (`multiple`). */
|
|
44
|
-
remove: (label: string) => string;
|
|
45
|
-
}
|
|
46
|
-
/**
|
|
47
|
-
* The copy a `Field` renders (target architecture §24.5).
|
|
48
|
-
*
|
|
49
|
-
* @experimental
|
|
50
|
-
*/
|
|
51
|
-
interface FieldMessages {
|
|
52
|
-
/** The indicator after the label of an optional field. Default `"optional"`, rendered in parentheses. */
|
|
53
|
-
optional: string;
|
|
54
|
-
}
|
|
55
|
-
/** The default dropzone copy a `FileUpload` renders when it has no `children`. @experimental */
|
|
56
|
-
interface FileUploadMessages {
|
|
57
|
-
/** The emphasised call to action. Default `"Click to upload"`. */
|
|
58
|
-
prompt: string;
|
|
59
|
-
/** The rest of the sentence after the prompt. Default `" or drag and drop"`. */
|
|
60
|
-
dragHint: string;
|
|
61
|
-
/** The accepted-types line; receives the `accept` list. */
|
|
62
|
-
accepted: (accept: string) => string;
|
|
63
|
-
/** The size-limit line; receives the formatted limit (e.g. `"10 MB"`). */
|
|
64
|
-
maxSize: (size: string) => string;
|
|
65
|
-
}
|
|
66
|
-
/**
|
|
67
|
-
* The whole catalogue, one namespace per component. This is the type a
|
|
68
|
-
* consumer's i18n lint can check for completeness.
|
|
69
|
-
*
|
|
70
|
-
* @experimental
|
|
71
|
-
*/
|
|
72
|
-
interface DsMessages {
|
|
73
|
-
select: SelectMessages;
|
|
74
|
-
searchInput: SearchInputMessages;
|
|
75
|
-
combobox: ComboboxMessages;
|
|
76
|
-
field: FieldMessages;
|
|
77
|
-
fileUpload: FileUploadMessages;
|
|
78
|
-
}
|
|
79
|
-
/** A component namespace of the catalogue. @experimental */
|
|
80
|
-
type DsMessageNamespace = keyof DsMessages;
|
|
81
|
-
/**
|
|
82
|
-
* What `DesignSystemProvider messages` accepts: any subset of namespaces, each
|
|
83
|
-
* any subset of its keys. Every omitted key keeps its English default.
|
|
84
|
-
*
|
|
85
|
-
* @experimental
|
|
86
|
-
*/
|
|
87
|
-
type DsMessagesOverride = {
|
|
88
|
-
[N in DsMessageNamespace]?: Partial<DsMessages[N]>;
|
|
89
|
-
};
|
|
90
|
-
|
|
91
|
-
/**
|
|
92
|
-
* L0 foundation (W5-21, ADR-010). The adapters through which the design
|
|
93
|
-
* system renders an application's router link and image component without
|
|
94
|
-
* importing them itself.
|
|
95
|
-
*
|
|
96
|
-
* `LinkButton` and `Logo` read these from `DesignSystemProvider`. Without a
|
|
97
|
-
* provider they keep their static `next/link` / `next/image` imports (C-NEXT-PEER)
|
|
98
|
-
* until 2.0, so nothing changes for a Next.js app that renders no provider;
|
|
99
|
-
* `@assure-one/design-system/next` exports a provider that passes exactly
|
|
100
|
-
* those two, and a non-Next app passes its own.
|
|
101
|
-
*/
|
|
102
|
-
/** The props a link adapter receives: a plain anchor with an `href`. @experimental */
|
|
103
|
-
interface LinkComponentProps extends AnchorHTMLAttributes<HTMLAnchorElement> {
|
|
104
|
-
href: string;
|
|
105
|
-
ref?: Ref<HTMLAnchorElement>;
|
|
106
|
-
children?: ReactNode;
|
|
107
|
-
}
|
|
108
|
-
/**
|
|
109
|
-
* A component that renders a link — `next/link`, React Router's `Link`, or a
|
|
110
|
-
* plain `<a>`. It must forward every anchor attribute and the ref.
|
|
111
|
-
*
|
|
112
|
-
* @experimental
|
|
113
|
-
*/
|
|
114
|
-
type LinkComponent = ComponentType<LinkComponentProps>;
|
|
115
|
-
/** The props an image adapter receives: `src`, `alt` and the intrinsic size. @experimental */
|
|
116
|
-
interface ImageComponentProps extends Omit<ImgHTMLAttributes<HTMLImageElement>, "src" | "width" | "height"> {
|
|
117
|
-
src: string;
|
|
118
|
-
alt: string;
|
|
119
|
-
width: number;
|
|
120
|
-
height: number;
|
|
121
|
-
/** Load eagerly, above the fold (`next/image` `priority`). */
|
|
122
|
-
priority?: boolean;
|
|
123
|
-
}
|
|
124
|
-
/**
|
|
125
|
-
* A component that renders an image — `next/image` or a plain `<img>`.
|
|
126
|
-
*
|
|
127
|
-
* @experimental
|
|
128
|
-
*/
|
|
129
|
-
type ImageComponent = ComponentType<ImageComponentProps>;
|
|
130
|
-
|
|
131
|
-
/** Text direction the provider announces. Context only until components adopt logical properties (W5-22). @experimental */
|
|
132
|
-
type DsDirection = "ltr" | "rtl";
|
|
133
|
-
/**
|
|
134
|
-
* The resolved messages of one component namespace: the component's own
|
|
135
|
-
* `messages` prop wins over the provider's catalogue, which wins over the
|
|
136
|
-
* English default. Works without a provider (English). Stable while the three
|
|
137
|
-
* inputs are.
|
|
138
|
-
*
|
|
139
|
-
* @experimental
|
|
140
|
-
*/
|
|
141
|
-
declare function useDsMessages<N extends DsMessageNamespace>(namespace: N, override?: Partial<DsMessages[N]> | undefined): DsMessages[N];
|
|
142
|
-
|
|
143
|
-
/**
|
|
144
|
-
* Props of `DesignSystemProvider` (W5-20, ADR-010, target architecture §43).
|
|
145
|
-
*
|
|
146
|
-
* @experimental
|
|
147
|
-
*/
|
|
148
|
-
interface DesignSystemProviderProps {
|
|
149
|
-
/**
|
|
150
|
-
* Overrides for the generic strings components render (Clear, No results,
|
|
151
|
-
* optional…), by component namespace. Any subset; omitted keys keep their
|
|
152
|
-
* English default. A component's own `messages` prop still wins. The full
|
|
153
|
-
* list is `DsMessages` (and `messages/en.json` in the package source).
|
|
154
|
-
*/
|
|
155
|
-
messages?: DsMessagesOverride;
|
|
156
|
-
/**
|
|
157
|
-
* The BCP 47 tag locale-aware components format with (dates, numbers).
|
|
158
|
-
* Default `"en-US"` on the server and on the client (amendment A3): pass the
|
|
159
|
-
* locale your app already knows server-side; never `navigator.language`
|
|
160
|
-
* during render.
|
|
161
|
-
*/
|
|
162
|
-
locale?: string;
|
|
163
|
-
/** Text direction. Context only for now — components adopt logical properties in W5-22. */
|
|
164
|
-
dir?: DsDirection;
|
|
165
|
-
/**
|
|
166
|
-
* Where portalled content (dialogs, menus, tooltips) mounts. `undefined`
|
|
167
|
-
* (default) keeps Radix's `document.body`. Read by the shared Portal of the
|
|
168
|
-
* overlay primitives as they adopt it.
|
|
169
|
-
*/
|
|
170
|
-
portalContainer?: HTMLElement | null;
|
|
171
|
-
/**
|
|
172
|
-
* The application's router link, rendered by `LinkButton` and `Logo`
|
|
173
|
-
* instead of their static `next/link` import (W5-21). A Next.js app uses
|
|
174
|
-
* `NextDesignSystemProvider` from `@assure-one/design-system/next`.
|
|
175
|
-
*/
|
|
176
|
-
linkComponent?: LinkComponent;
|
|
177
|
-
/** The application's image component, rendered by `Logo` instead of its static `next/image` import. */
|
|
178
|
-
imageComponent?: ImageComponent;
|
|
179
|
-
/**
|
|
180
|
-
* Shared tooltip defaults (`delayDuration`, `skipDelayDuration`,
|
|
181
|
-
* `disableHoverableContent`) for tooltips built directly on the Radix
|
|
182
|
-
* root. `false` mounts no `TooltipProvider`. The design system's own
|
|
183
|
-
* `Tooltip` still wraps its own provider today, so it is unaffected.
|
|
184
|
-
*/
|
|
185
|
-
tooltip?: TooltipDefaults | false;
|
|
186
|
-
/**
|
|
187
|
-
* Mount the toast region (`ToastProvider`) so `useToast()` works anywhere
|
|
188
|
-
* below. Off by default: an app that already renders `ToastProvider` keeps
|
|
189
|
-
* its single region (the `aria-label="Notifications"` region consumers
|
|
190
|
-
* select on, C-TOAST-ARIALABEL, must exist exactly once).
|
|
191
|
-
*/
|
|
192
|
-
toasts?: boolean;
|
|
193
|
-
children?: ReactNode;
|
|
194
|
-
}
|
|
195
|
-
/** The Radix `TooltipProvider` props the provider forwards. @experimental */
|
|
196
|
-
interface TooltipDefaults {
|
|
197
|
-
delayDuration?: number;
|
|
198
|
-
skipDelayDuration?: number;
|
|
199
|
-
disableHoverableContent?: boolean;
|
|
200
|
-
}
|
|
201
|
-
/**
|
|
202
|
-
* The optional application-level provider of the design system (W5-20,
|
|
203
|
-
* ADR-010). Thin by design: it owns only what CSS cannot — the messages
|
|
204
|
-
* catalogue, the locale, the text direction, the portal container, the link
|
|
205
|
-
* and image adapters, shared tooltip defaults and (opt-in) the toast region.
|
|
206
|
-
* Colour scheme, brand and density stay attributes + CSS on `<html>`.
|
|
207
|
-
*
|
|
208
|
-
* Everything works identically without it: every component falls back to
|
|
209
|
-
* the English catalogue, `"en-US"`, `document.body` and its static `next/*`
|
|
210
|
-
* import. The provider renders **no DOM wrapper** (only the toast region when
|
|
211
|
-
* `toasts` is set), so it cannot cause a hydration mismatch.
|
|
212
|
-
*
|
|
213
|
-
* Reserved: the `useConfirm` / `usePrompt` host (W5-18) mounts here once it
|
|
214
|
-
* exists — see the comment in the render tree.
|
|
215
|
-
*
|
|
216
|
-
* @experimental
|
|
217
|
-
*/
|
|
218
|
-
declare function DesignSystemProvider({ messages, locale, dir, portalContainer, linkComponent, imageComponent, tooltip, toasts, children, }: DesignSystemProviderProps): react_jsx_runtime.JSX.Element;
|
|
219
|
-
|
|
220
|
-
export { type ComboboxMessages as C, type DesignSystemProviderProps as D, type FileUploadMessages as F, type ImageComponent as I, type LinkComponent as L, type SearchInputMessages as S, type TooltipDefaults as T, type SelectMessages as a, type FieldMessages as b, DesignSystemProvider as c, type DsDirection as d, type DsMessageNamespace as e, type DsMessages as f, type DsMessagesOverride as g, type ImageComponentProps as h, type LinkComponentProps as i, useDsMessages as u };
|