@nadicodeai/ui 6.0.2 → 6.1.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/dist/components/action-menu.d.ts +8 -0
- package/dist/components/action-menu.d.ts.map +1 -0
- package/dist/components/action-menu.js +8 -0
- package/dist/components/card.d.ts +1 -1
- package/dist/components/card.d.ts.map +1 -1
- package/dist/components/card.js +5 -3
- package/dist/components/chart-breakdown-table.d.ts +3 -2
- package/dist/components/chart-breakdown-table.d.ts.map +1 -1
- package/dist/components/chart-breakdown-table.js +13 -9
- package/dist/components/chart-bullet-bar.d.ts +13 -12
- package/dist/components/chart-bullet-bar.d.ts.map +1 -1
- package/dist/components/chart-bullet-bar.js +16 -36
- package/dist/components/chart-kpi-stat.d.ts +26 -15
- package/dist/components/chart-kpi-stat.d.ts.map +1 -1
- package/dist/components/chart-kpi-stat.js +28 -4
- package/dist/components/chart-time-series-layout.d.ts +6 -0
- package/dist/components/chart-time-series-layout.d.ts.map +1 -0
- package/dist/components/chart-time-series-layout.js +6 -0
- package/dist/components/chart-time-series.d.ts +6 -2
- package/dist/components/chart-time-series.d.ts.map +1 -1
- package/dist/components/chart-time-series.js +27 -9
- package/dist/components/chart.js +1 -1
- package/dist/components/collection-filters.d.ts +28 -0
- package/dist/components/collection-filters.d.ts.map +1 -0
- package/dist/components/collection-filters.js +54 -0
- package/dist/components/collection-scope.d.ts +15 -0
- package/dist/components/collection-scope.d.ts.map +1 -0
- package/dist/components/collection-scope.js +7 -0
- package/dist/components/collection-toolbar.d.ts +10 -0
- package/dist/components/collection-toolbar.d.ts.map +1 -0
- package/dist/components/collection-toolbar.js +4 -0
- package/dist/components/settings-facts.d.ts +28 -0
- package/dist/components/settings-facts.d.ts.map +1 -0
- package/dist/components/settings-facts.js +7 -0
- package/dist/components/settings-section.d.ts +5 -5
- package/dist/components/settings-section.d.ts.map +1 -1
- package/dist/components/settings-section.js +6 -11
- package/dist/components/sidebar.d.ts +4 -1
- package/dist/components/sidebar.d.ts.map +1 -1
- package/dist/components/sidebar.js +3 -3
- package/dist/components/table-layouts.d.ts +22 -0
- package/dist/components/table-layouts.d.ts.map +1 -0
- package/dist/components/table-layouts.js +20 -0
- package/dist/components/table.d.ts +15 -2
- package/dist/components/table.d.ts.map +1 -1
- package/dist/components/table.js +8 -4
- package/dist/eslint/rules/jsx-class-utility.js +44 -12
- package/dist/eslint/rules/no-resting-settings-control.js +15 -13
- package/dist/eslint/rules/no-shadcn-appearance-override.js +189 -36
- package/dist/eslint/rules/shadcn-composition.js +48 -3
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -0
- package/docs/component-classification.md +3 -2
- package/docs/contract.md +78 -7
- package/docs/product-compositions.md +14 -11
- package/package.json +3 -2
|
@@ -6,8 +6,8 @@
|
|
|
6
6
|
* editor that changes them. The regression this catches is a form control
|
|
7
7
|
* rendered beside those facts rather than inside the editor they summon.
|
|
8
8
|
*
|
|
9
|
-
* "Inside the editor" means inside an `ActionDialogForm`'s
|
|
10
|
-
*
|
|
9
|
+
* "Inside the editor" means inside a `SettingsSection`, an `ActionDialogForm`'s
|
|
10
|
+
* `fields` prop, or a `Dialog`/`Drawer` subtree for a surface that owns its own. Being a
|
|
11
11
|
* CHILD of `ActionDialogForm` is the opposite: children are the resting surface
|
|
12
12
|
* the dialog is opened from, which is exactly where a control must not be.
|
|
13
13
|
*
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
* here. Replaces `restingSettingsControlViolations` in the retired portal
|
|
22
22
|
* page-grammar analyzers (#821).
|
|
23
23
|
*/
|
|
24
|
+
import { jsxComponentName } from "./jsx-class-utility.js";
|
|
24
25
|
|
|
25
26
|
// The controls a person types or picks into. Lowercase `input` is deliberately
|
|
26
27
|
// absent: a hidden field carries context a summoned editor already decided, and
|
|
@@ -37,15 +38,15 @@ const RESTING_CONTROL_TAGS = new Set([
|
|
|
37
38
|
|
|
38
39
|
const FACTS_SECTION_TAG = "SettingsFactsSection";
|
|
39
40
|
|
|
40
|
-
function
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
41
|
+
function summons(node, context) {
|
|
42
|
+
if (node.type === "JSXAttribute") {
|
|
43
|
+
return (
|
|
44
|
+
node.name.name === "fields" && jsxComponentName(node.parent, context) === "ActionDialogForm"
|
|
45
|
+
);
|
|
46
|
+
}
|
|
46
47
|
if (node.type !== "JSXElement") return false;
|
|
47
|
-
const name =
|
|
48
|
-
return
|
|
48
|
+
const name = jsxComponentName(node.openingElement, context);
|
|
49
|
+
return ["SettingsSection", "Dialog", "DialogContent", "Drawer", "DrawerContent"].includes(name);
|
|
49
50
|
}
|
|
50
51
|
|
|
51
52
|
export const noRestingSettingsControl = {
|
|
@@ -58,7 +59,7 @@ export const noRestingSettingsControl = {
|
|
|
58
59
|
schema: [],
|
|
59
60
|
messages: {
|
|
60
61
|
restingControl:
|
|
61
|
-
"<{{tag}}> rests beside its facts: put it in the editor
|
|
62
|
+
"<{{tag}}> rests beside its facts: use SettingsSection for a form, or put it in the summoned editor (ActionDialogForm `fields`, Dialog, or Drawer).",
|
|
62
63
|
},
|
|
63
64
|
},
|
|
64
65
|
create(context) {
|
|
@@ -67,13 +68,14 @@ export const noRestingSettingsControl = {
|
|
|
67
68
|
|
|
68
69
|
return {
|
|
69
70
|
JSXOpeningElement(node) {
|
|
70
|
-
const name =
|
|
71
|
+
const name = jsxComponentName(node, context);
|
|
71
72
|
if (name === FACTS_SECTION_TAG) {
|
|
72
73
|
declaresFacts = true;
|
|
73
74
|
return;
|
|
74
75
|
}
|
|
75
76
|
if (!name || !RESTING_CONTROL_TAGS.has(name)) return;
|
|
76
|
-
if (context.sourceCode.getAncestors(node).some(summons))
|
|
77
|
+
if (context.sourceCode.getAncestors(node).some((ancestor) => summons(ancestor, context)))
|
|
78
|
+
return;
|
|
77
79
|
resting.push({ node, name });
|
|
78
80
|
},
|
|
79
81
|
"Program:exit"() {
|
|
@@ -12,6 +12,8 @@
|
|
|
12
12
|
* of which still compile and still fork the primitive. Those are what survives
|
|
13
13
|
* here; the raw-palette names are gone from the sets below with the palette.
|
|
14
14
|
*/
|
|
15
|
+
import { posix } from "node:path";
|
|
16
|
+
|
|
15
17
|
import {
|
|
16
18
|
classUtilities,
|
|
17
19
|
openingElementClassUtilities,
|
|
@@ -19,6 +21,127 @@ import {
|
|
|
19
21
|
skipsPackageOwnedPrimitive,
|
|
20
22
|
} from "./jsx-class-utility.js";
|
|
21
23
|
|
|
24
|
+
// These exact Portal adapters forward props to package primitives. Card
|
|
25
|
+
// chooses its typed surface default; Table owns only structural geometry.
|
|
26
|
+
// Their other exports are direct reexports.
|
|
27
|
+
const portalCardExports = new Set([
|
|
28
|
+
"Card",
|
|
29
|
+
"CardHeader",
|
|
30
|
+
"CardTitle",
|
|
31
|
+
"CardDescription",
|
|
32
|
+
"CardAction",
|
|
33
|
+
"CardContent",
|
|
34
|
+
]);
|
|
35
|
+
const portalTableExports = new Set([
|
|
36
|
+
"Table",
|
|
37
|
+
"TableHead",
|
|
38
|
+
"TableCell",
|
|
39
|
+
"TableBody",
|
|
40
|
+
"TableCaption",
|
|
41
|
+
"TableFooter",
|
|
42
|
+
"TableHeader",
|
|
43
|
+
"TableRow",
|
|
44
|
+
]);
|
|
45
|
+
|
|
46
|
+
function primitiveModule(source, filename) {
|
|
47
|
+
if (source.startsWith("@nadicodeai/ui/components/")) {
|
|
48
|
+
return { card: source === "@nadicodeai/ui/components/card", exports: undefined };
|
|
49
|
+
}
|
|
50
|
+
const normalized = filename.replaceAll("\\", "/");
|
|
51
|
+
const portalStart = normalized.lastIndexOf("/apps/portal/src/");
|
|
52
|
+
if (portalStart === -1) return undefined;
|
|
53
|
+
const portalSource = `${normalized.slice(0, portalStart)}/apps/portal/src`;
|
|
54
|
+
const resolved = source.startsWith(".")
|
|
55
|
+
? posix.normalize(posix.join(posix.dirname(normalized), source)).replace(/\.[jt]sx?$/, "")
|
|
56
|
+
: undefined;
|
|
57
|
+
if (source === "@/components/kit/card" || resolved === `${portalSource}/components/kit/card`) {
|
|
58
|
+
return { card: true, exports: portalCardExports };
|
|
59
|
+
}
|
|
60
|
+
if (source === "@/components/kit/table" || resolved === `${portalSource}/components/kit/table`) {
|
|
61
|
+
return { card: false, exports: portalTableExports };
|
|
62
|
+
}
|
|
63
|
+
return undefined;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function primitiveExport(module, name) {
|
|
67
|
+
if (module.exports && !module.exports.has(name)) return undefined;
|
|
68
|
+
return { cardRoot: module.card && name === "Card" };
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const appearanceStyleProperties = new Set([
|
|
72
|
+
"appearance",
|
|
73
|
+
"WebkitAppearance",
|
|
74
|
+
"MozAppearance",
|
|
75
|
+
"color",
|
|
76
|
+
"background",
|
|
77
|
+
"backgroundColor",
|
|
78
|
+
"backgroundImage",
|
|
79
|
+
"boxShadow",
|
|
80
|
+
"textShadow",
|
|
81
|
+
"opacity",
|
|
82
|
+
"outlineColor",
|
|
83
|
+
"filter",
|
|
84
|
+
"backdropFilter",
|
|
85
|
+
"WebkitBackdropFilter",
|
|
86
|
+
"fill",
|
|
87
|
+
"fillOpacity",
|
|
88
|
+
"stroke",
|
|
89
|
+
"strokeOpacity",
|
|
90
|
+
...[
|
|
91
|
+
"",
|
|
92
|
+
"Top",
|
|
93
|
+
"Right",
|
|
94
|
+
"Bottom",
|
|
95
|
+
"Left",
|
|
96
|
+
"Block",
|
|
97
|
+
"Inline",
|
|
98
|
+
"BlockStart",
|
|
99
|
+
"BlockEnd",
|
|
100
|
+
"InlineStart",
|
|
101
|
+
"InlineEnd",
|
|
102
|
+
].map((side) => `border${side}Color`),
|
|
103
|
+
...[
|
|
104
|
+
"",
|
|
105
|
+
"TopLeft",
|
|
106
|
+
"TopRight",
|
|
107
|
+
"BottomLeft",
|
|
108
|
+
"BottomRight",
|
|
109
|
+
"StartStart",
|
|
110
|
+
"StartEnd",
|
|
111
|
+
"EndStart",
|
|
112
|
+
"EndEnd",
|
|
113
|
+
].map((corner) => `border${corner}Radius`),
|
|
114
|
+
]);
|
|
115
|
+
|
|
116
|
+
function staticStyleValue(node) {
|
|
117
|
+
if (node.type === "Literal") return node.value;
|
|
118
|
+
if (node.type === "TemplateLiteral" && node.expressions.length === 0) {
|
|
119
|
+
return node.quasis[0]?.value.cooked;
|
|
120
|
+
}
|
|
121
|
+
return undefined;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Inline object literals only. Unknown values, computed keys, imported style
|
|
125
|
+
// objects and spreads remain review work; layout properties are not paint.
|
|
126
|
+
function inlineAppearanceProperties(attribute) {
|
|
127
|
+
if (attribute.type !== "JSXAttribute" || attribute.name.name !== "style") return [];
|
|
128
|
+
const expression =
|
|
129
|
+
attribute.value?.type === "JSXExpressionContainer" ? attribute.value.expression : undefined;
|
|
130
|
+
if (expression?.type !== "ObjectExpression") return [];
|
|
131
|
+
return expression.properties.filter((property) => {
|
|
132
|
+
if (property.type !== "Property") return false;
|
|
133
|
+
const name =
|
|
134
|
+
property.key.type === "Literal"
|
|
135
|
+
? property.key.value
|
|
136
|
+
: !property.computed && property.key.type === "Identifier"
|
|
137
|
+
? property.key.name
|
|
138
|
+
: undefined;
|
|
139
|
+
if (!appearanceStyleProperties.has(name)) return false;
|
|
140
|
+
const value = staticStyleValue(property.value);
|
|
141
|
+
return typeof value === "number" || (typeof value === "string" && value.trim() !== "");
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
|
|
22
145
|
function withoutVariants(utility) {
|
|
23
146
|
return utility.slice(utility.lastIndexOf(":") + 1);
|
|
24
147
|
}
|
|
@@ -74,7 +197,10 @@ function isAppearanceUtility(utility) {
|
|
|
74
197
|
|
|
75
198
|
function isProhibitedAppearanceUtility(utility, { cardRoot = false } = {}) {
|
|
76
199
|
const base = withoutVariants(utility);
|
|
77
|
-
return
|
|
200
|
+
return (
|
|
201
|
+
targetsSharedSlot(utility) ||
|
|
202
|
+
(isAppearanceUtility(utility) && (!isBorderRemoval(base) || cardRoot))
|
|
203
|
+
);
|
|
78
204
|
}
|
|
79
205
|
|
|
80
206
|
function targetsSharedSlot(utility) {
|
|
@@ -92,15 +218,14 @@ export const noShadcnAppearanceOverride = {
|
|
|
92
218
|
meta: {
|
|
93
219
|
type: "problem",
|
|
94
220
|
docs: {
|
|
95
|
-
description:
|
|
96
|
-
"Disallow app callers repainting imported shadcn primitives; compose their structural layout only",
|
|
221
|
+
description: "Keep shared component appearance and internal slot layout at their owner",
|
|
97
222
|
},
|
|
98
223
|
// Default "exempt": inside @nadicodeai/ui the primitives ARE the appearance,
|
|
99
224
|
// so every utility here would be a false positive.
|
|
100
225
|
schema: [packageOwnedPrimitivesSchema],
|
|
101
226
|
messages: {
|
|
102
227
|
appearanceOverride:
|
|
103
|
-
"Do not
|
|
228
|
+
"Do not override shared component appearance or internal slot layout with {{utility}}; use a component prop or fix its shared owner.",
|
|
104
229
|
},
|
|
105
230
|
},
|
|
106
231
|
create(context) {
|
|
@@ -108,48 +233,76 @@ export const noShadcnAppearanceOverride = {
|
|
|
108
233
|
return {};
|
|
109
234
|
}
|
|
110
235
|
|
|
111
|
-
const
|
|
112
|
-
const
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
236
|
+
const sourceCode = context.sourceCode;
|
|
237
|
+
const sharedBindings = new Map();
|
|
238
|
+
|
|
239
|
+
function importedBinding(identifier) {
|
|
240
|
+
for (let scope = sourceCode.getScope(identifier); scope; scope = scope.upper) {
|
|
241
|
+
const variable = scope.set.get(identifier.name);
|
|
242
|
+
if (variable) return sharedBindings.get(variable);
|
|
243
|
+
}
|
|
244
|
+
return undefined;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
function sharedPrimitive(node) {
|
|
248
|
+
if (node?.type !== "JSXOpeningElement") return undefined;
|
|
249
|
+
if (node.name.type === "JSXIdentifier") {
|
|
250
|
+
const binding = importedBinding(node.name);
|
|
251
|
+
return binding && binding.name !== "*"
|
|
252
|
+
? primitiveExport(binding.module, binding.name)
|
|
253
|
+
: undefined;
|
|
254
|
+
}
|
|
255
|
+
if (node.name.type === "JSXMemberExpression" && node.name.object.type === "JSXIdentifier") {
|
|
256
|
+
const binding = importedBinding(node.name.object);
|
|
257
|
+
return binding?.name === "*"
|
|
258
|
+
? primitiveExport(binding.module, node.name.property.name)
|
|
259
|
+
: undefined;
|
|
260
|
+
}
|
|
261
|
+
return undefined;
|
|
262
|
+
}
|
|
263
|
+
|
|
121
264
|
return {
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
265
|
+
Program(node) {
|
|
266
|
+
// Imports are hoisted; collect their declared variables before visiting
|
|
267
|
+
// JSX. Binding identity also distinguishes shadowing parameters/locals.
|
|
268
|
+
for (const declaration of node.body) {
|
|
269
|
+
if (declaration.type !== "ImportDeclaration" || declaration.importKind === "type")
|
|
270
|
+
continue;
|
|
271
|
+
const module = primitiveModule(declaration.source.value, context.filename);
|
|
272
|
+
if (!module) continue;
|
|
273
|
+
for (const specifier of declaration.specifiers) {
|
|
274
|
+
if (specifier.importKind === "type") continue;
|
|
275
|
+
const name =
|
|
276
|
+
specifier.type === "ImportNamespaceSpecifier"
|
|
277
|
+
? "*"
|
|
278
|
+
: specifier.type === "ImportSpecifier"
|
|
279
|
+
? (specifier.imported.name ?? specifier.imported.value)
|
|
280
|
+
: "default";
|
|
281
|
+
for (const variable of sourceCode.getDeclaredVariables(specifier)) {
|
|
282
|
+
sharedBindings.set(variable, { module, name });
|
|
283
|
+
}
|
|
137
284
|
}
|
|
138
285
|
}
|
|
139
286
|
},
|
|
140
287
|
JSXOpeningElement(node) {
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
isProhibitedAppearanceUtility(utility, { cardRoot: isSharedCardRoot(node) })
|
|
146
|
-
) {
|
|
288
|
+
const primitive = sharedPrimitive(node);
|
|
289
|
+
if (!primitive) return;
|
|
290
|
+
for (const utility of openingElementClassUtilities(node, context, true)) {
|
|
291
|
+
if (utility && isProhibitedAppearanceUtility(utility, primitive)) {
|
|
147
292
|
context.report({ node, messageId: "appearanceOverride", data: { utility } });
|
|
148
293
|
}
|
|
149
294
|
}
|
|
295
|
+
for (const property of node.attributes.flatMap(inlineAppearanceProperties)) {
|
|
296
|
+
const name = property.key.name ?? property.key.value;
|
|
297
|
+
context.report({
|
|
298
|
+
node: property,
|
|
299
|
+
messageId: "appearanceOverride",
|
|
300
|
+
data: { utility: `style.${name}` },
|
|
301
|
+
});
|
|
302
|
+
}
|
|
150
303
|
},
|
|
151
304
|
JSXAttribute(node) {
|
|
152
|
-
if (
|
|
305
|
+
if (sharedPrimitive(node.parent)) return;
|
|
153
306
|
for (const utility of classUtilities(node, context)) {
|
|
154
307
|
if (
|
|
155
308
|
!targetsSharedSlot(utility) ||
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* The
|
|
2
|
+
* The composition conventions the shared components are built to, checked
|
|
3
3
|
* where they are composed:
|
|
4
4
|
*
|
|
5
5
|
* - an overlay carries its title, so a screen reader announces what opened
|
|
@@ -9,7 +9,9 @@
|
|
|
9
9
|
* - an `AvatarImage` has an `AvatarFallback` beside it, or the avatar is blank
|
|
10
10
|
* until the image loads;
|
|
11
11
|
* - a lucide icon inside a `Button` carries `data-icon`, the hook the button's
|
|
12
|
-
* own spacing rules key on
|
|
12
|
+
* own spacing rules key on;
|
|
13
|
+
* - a settings submit action uses SettingsSection's shared footer, so each
|
|
14
|
+
* caller cannot rebuild its own divider, padding, and action position.
|
|
13
15
|
*
|
|
14
16
|
* Standard tool checked first: `jsx-a11y` covers roles and ARIA on
|
|
15
17
|
* intrinsic elements and says nothing about these composed components;
|
|
@@ -18,7 +20,11 @@
|
|
|
18
20
|
* of the retired @nadicodeai/ui composition-conventions scanner (#822); stock
|
|
19
21
|
* upstream shapes carry an inline disable with the reason at the site.
|
|
20
22
|
*/
|
|
21
|
-
import {
|
|
23
|
+
import {
|
|
24
|
+
jsxComponentName,
|
|
25
|
+
packageOwnedPrimitivesSchema,
|
|
26
|
+
skipsPackageOwnedPrimitive,
|
|
27
|
+
} from "./jsx-class-utility.js";
|
|
22
28
|
|
|
23
29
|
const OVERLAY_TITLES = {
|
|
24
30
|
AlertDialogContent: "AlertDialogTitle",
|
|
@@ -75,6 +81,39 @@ function isIconTag(tagName) {
|
|
|
75
81
|
return tagName === "Icon" || tagName.endsWith("Icon");
|
|
76
82
|
}
|
|
77
83
|
|
|
84
|
+
function misplacedSettingsSubmit(node, context) {
|
|
85
|
+
if (jsxComponentName(node, context) !== "Button") return false;
|
|
86
|
+
const attribute = node.attributes.find(
|
|
87
|
+
(entry) => entry.type === "JSXAttribute" && entry.name.name === "type",
|
|
88
|
+
);
|
|
89
|
+
const value =
|
|
90
|
+
attribute?.value?.type === "JSXExpressionContainer"
|
|
91
|
+
? attribute.value.expression
|
|
92
|
+
: attribute?.value;
|
|
93
|
+
const type =
|
|
94
|
+
value?.type === "TemplateLiteral" && value.expressions.length === 0
|
|
95
|
+
? value.quasis[0]?.value.cooked
|
|
96
|
+
: value?.value;
|
|
97
|
+
if (type !== "submit") return false;
|
|
98
|
+
const ancestors = context.sourceCode.getAncestors(node);
|
|
99
|
+
const section = ancestors.findLast(
|
|
100
|
+
(ancestor) =>
|
|
101
|
+
ancestor.type === "JSXElement" &&
|
|
102
|
+
jsxComponentName(ancestor.openingElement, context) === "SettingsSection",
|
|
103
|
+
);
|
|
104
|
+
if (!section) return false;
|
|
105
|
+
return !ancestors.slice(ancestors.indexOf(section) + 1).some((ancestor) => {
|
|
106
|
+
if (ancestor.type === "JSXAttribute")
|
|
107
|
+
return ancestor.name.name === "footer" && ancestor.parent === section.openingElement;
|
|
108
|
+
return (
|
|
109
|
+
ancestor.type === "JSXElement" &&
|
|
110
|
+
["DialogContent", "DrawerContent", "AlertDialogContent"].includes(
|
|
111
|
+
jsxComponentName(ancestor.openingElement, context),
|
|
112
|
+
)
|
|
113
|
+
);
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
|
|
78
117
|
export const shadcnComposition = {
|
|
79
118
|
meta: {
|
|
80
119
|
type: "problem",
|
|
@@ -88,6 +127,8 @@ export const shadcnComposition = {
|
|
|
88
127
|
selectGroup: "SelectItem must be rendered inside SelectGroup.",
|
|
89
128
|
avatarFallback: "AvatarImage requires an AvatarFallback in the same Avatar.",
|
|
90
129
|
buttonIcon: "An icon inside Button must carry data-icon.",
|
|
130
|
+
settingsFooter:
|
|
131
|
+
"Put a SettingsSection submit action in its footer prop; the shared component owns the divider, spacing, and action position.",
|
|
91
132
|
},
|
|
92
133
|
},
|
|
93
134
|
create(context) {
|
|
@@ -101,6 +142,10 @@ export const shadcnComposition = {
|
|
|
101
142
|
if (!tag) return;
|
|
102
143
|
const element = node.parent;
|
|
103
144
|
|
|
145
|
+
if (misplacedSettingsSubmit(node, context)) {
|
|
146
|
+
context.report({ node, messageId: "settingsFooter" });
|
|
147
|
+
}
|
|
148
|
+
|
|
104
149
|
const title = OVERLAY_TITLES[tag];
|
|
105
150
|
if (title && !hasDescendant(element, title)) {
|
|
106
151
|
context.report({ node, messageId: "overlayTitle", data: { tag, title } });
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export * from "./components/accordion.js";
|
|
2
2
|
export * from "./components/action-band.js";
|
|
3
|
+
export * from "./components/action-menu.js";
|
|
3
4
|
export * from "./components/action-status.js";
|
|
4
5
|
export * from "./components/activity-orb.js";
|
|
5
6
|
export * from "./components/alert.js";
|
|
@@ -20,6 +21,7 @@ export * from "./components/button.js";
|
|
|
20
21
|
export * from "./components/button-group.js";
|
|
21
22
|
export * from "./components/calendar.js";
|
|
22
23
|
export * from "./components/card.js";
|
|
24
|
+
export * from "./components/collection-scope.js";
|
|
23
25
|
export * from "./components/card-marketing.js";
|
|
24
26
|
export * from "./components/carousel.js";
|
|
25
27
|
export * from "./components/chart.js";
|
|
@@ -105,6 +107,9 @@ export * from "./components/security-grid.js";
|
|
|
105
107
|
export * from "./components/select.js";
|
|
106
108
|
export * from "./components/separator.js";
|
|
107
109
|
export * from "./components/settings-section.js";
|
|
110
|
+
export * from "./components/settings-facts.js";
|
|
111
|
+
export * from "./components/collection-toolbar.js";
|
|
112
|
+
export * from "./components/collection-filters.js";
|
|
108
113
|
export * from "./components/sheet.js";
|
|
109
114
|
export * from "./components/shift-band.js";
|
|
110
115
|
export * from "./components/sidebar.js";
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export * from "./components/accordion.js";
|
|
2
2
|
export * from "./components/action-band.js";
|
|
3
|
+
export * from "./components/action-menu.js";
|
|
3
4
|
export * from "./components/action-status.js";
|
|
4
5
|
export * from "./components/activity-orb.js";
|
|
5
6
|
export * from "./components/alert.js";
|
|
@@ -20,6 +21,7 @@ export * from "./components/button.js";
|
|
|
20
21
|
export * from "./components/button-group.js";
|
|
21
22
|
export * from "./components/calendar.js";
|
|
22
23
|
export * from "./components/card.js";
|
|
24
|
+
export * from "./components/collection-scope.js";
|
|
23
25
|
export * from "./components/card-marketing.js";
|
|
24
26
|
export * from "./components/carousel.js";
|
|
25
27
|
export * from "./components/chart.js";
|
|
@@ -105,6 +107,9 @@ export * from "./components/security-grid.js";
|
|
|
105
107
|
export * from "./components/select.js";
|
|
106
108
|
export * from "./components/separator.js";
|
|
107
109
|
export * from "./components/settings-section.js";
|
|
110
|
+
export * from "./components/settings-facts.js";
|
|
111
|
+
export * from "./components/collection-toolbar.js";
|
|
112
|
+
export * from "./components/collection-filters.js";
|
|
108
113
|
export * from "./components/sheet.js";
|
|
109
114
|
export * from "./components/shift-band.js";
|
|
110
115
|
export * from "./components/sidebar.js";
|
|
@@ -19,8 +19,9 @@ copy, and behavior.
|
|
|
19
19
|
|
|
20
20
|
| Component | Classification | Built on | Required states | Package-owned accessibility | Fixture |
|
|
21
21
|
| --- | --- | --- | --- | --- | --- |
|
|
22
|
-
| `
|
|
23
|
-
| `
|
|
22
|
+
| `ActionMenu` | Canonical record-action composition. | shadcn `Button` and `DropdownMenu`. | closed, open, keyboard selection, focus return. | Required accessible trigger name; inherited menu mechanics. | `../src/components/action-menu.test.tsx` |
|
|
23
|
+
| `SettingsSection` | NadicodeAI product composition. | shadcn Card slots, Separator, and FieldGroup. | header, field group, and divided action footer. | Section structure; consumer owns the form transaction. | `../src/components/settings-section.test.tsx` |
|
|
24
|
+
| `SettingsField` | NadicodeAI product composition. | shadcn Field, FieldLabel, FieldDescription, and FieldError. | valid, invalid, required, and disabled field rows. | Render callback supplies the control's label and message associations in server and client components. | `../src/components/settings-section.test.tsx` |
|
|
24
25
|
| `FeedbackState` | Net-new central product state composition adapted from shadcn empty-state patterns. | shadcn `Empty` and lucide status icons. | `empty`, `loading`, `saving`, `error`, `permission`, `offline`, `no-results`, `fatal`. | `role="status"` for loading/saving; `role="alert"` for error, permission, offline, and fatal. | `../tests/guards/product-compositions.test.ts` |
|
|
25
26
|
|
|
26
27
|
## App-Owned Boundaries
|
package/docs/contract.md
CHANGED
|
@@ -27,6 +27,9 @@ Physical role, not content importance, chooses depth:
|
|
|
27
27
|
work chooses it explicitly.
|
|
28
28
|
- `Card surface="plain"` is the explicit structural/auth/website escape and
|
|
29
29
|
carries no elevation.
|
|
30
|
+
- `Card surface="outlined"` is a flat panel with a single semantic border and
|
|
31
|
+
compact corners; it carries no elevation ring or shadow. Bounded fact cells
|
|
32
|
+
use it inside resting record sections. Portal's kit keeps the resting default.
|
|
30
33
|
- Tooltip, menu, popover, select, combobox, navigation popup, hover card,
|
|
31
34
|
chart tooltip, and toast roots own `nc-elevation-floating` internally.
|
|
32
35
|
- Dialog, alert dialog, drawer, and sheet roots own `nc-elevation-modal`
|
|
@@ -64,11 +67,36 @@ transparent and unframed because the Card already owns containment.
|
|
|
64
67
|
|
|
65
68
|
### Tables
|
|
66
69
|
|
|
67
|
-
`Table` owns the native table structure, solid semantic background,
|
|
68
|
-
|
|
70
|
+
`Table` owns the native table structure, solid semantic background, an overflow
|
|
71
|
+
safety boundary, and one compact density: 36px header rows and 40px body rows. Consumers
|
|
69
72
|
compose `TableHeader`, `TableBody`, `TableRow`, `TableHead`, and `TableCell`; they
|
|
70
73
|
do not replace table layout with grid or flex, hide the header to create another
|
|
71
|
-
presentation, or reconstruct the native semantics with ARIA.
|
|
74
|
+
presentation, or reconstruct the native semantics with ARIA. Portal collections
|
|
75
|
+
must fit their available width without horizontal scrolling: preserve identity
|
|
76
|
+
and actions in columns and place supporting facts in native detail rows before
|
|
77
|
+
the full schema becomes cramped. Test the table container itself, not only the
|
|
78
|
+
document width; retaining the overflow safety boundary must not conceal a
|
|
79
|
+
layout defect.
|
|
80
|
+
|
|
81
|
+
`CollectionScope` chooses the parent of one record collection. It places the
|
|
82
|
+
scope selector and optional detail action above the collection toolbar. Empty
|
|
83
|
+
parents stay in the selector. Apps bind authorized options and URL state; clearing
|
|
84
|
+
record filters preserves the parent scope. Parent details belong on their detail
|
|
85
|
+
page, not in repeated panels surrounding identical child tables.
|
|
86
|
+
|
|
87
|
+
`BreakdownTable` owns its responsive comparison layout: identity and value stay
|
|
88
|
+
in the main row; share moves into a labeled detail row when space is tight.
|
|
89
|
+
Its caption names the table. Callers supply data and labels, not column-hiding CSS.
|
|
90
|
+
|
|
91
|
+
### Record action menus
|
|
92
|
+
|
|
93
|
+
`ActionMenu` is the shared three-dot composition of `Button` and `DropdownMenu`:
|
|
94
|
+
a ghost icon trigger, horizontal ellipsis, and end-aligned popup. It inherits the
|
|
95
|
+
menu's glass, elevation, motion, keyboard behavior, and focus return. Supply
|
|
96
|
+
`label` as a localized, record-specific accessible name; `triggerId` supports
|
|
97
|
+
focus return from an app-owned confirmation, and `children` supplies menu items.
|
|
98
|
+
Apps own action selection, permission, placement, and confirmation. Reuse this
|
|
99
|
+
composition instead of drawing another trigger or popup.
|
|
72
100
|
|
|
73
101
|
## Status Vocabulary
|
|
74
102
|
|
|
@@ -121,12 +149,15 @@ fundamental `Card` with explicit `visual`, `name`, `job`, `status`, `meta`,
|
|
|
121
149
|
and `action` slots and owns presentation only; when it is the page identity,
|
|
122
150
|
the `name` slot carries the page's `h1`. The app owns Agent data, recipe
|
|
123
151
|
assignment, status grammar, routing, and actions; it assigns a recipe
|
|
124
|
-
explicitly and never infers or randomizes one from a free-form role
|
|
125
|
-
|
|
152
|
+
explicitly and never infers or randomizes one from a free-form role.
|
|
153
|
+
When the product chooses a card presentation, use `AgentCard`; registry cells,
|
|
154
|
+
membership rows, and compact detail headers compose the shared `AgentVisual`
|
|
155
|
+
within the structure their reading task requires. An Agent's presence does not
|
|
156
|
+
by itself choose a card layout. There is no `AgentGlyph`,
|
|
126
157
|
`AgentRow`, `KpiCard`, or product-domain `agent` object prop in the shared
|
|
127
158
|
package.
|
|
128
159
|
|
|
129
|
-
- **Standard components, not app-owned product features.** This package ships primitives, standard UI components, assistant-ui compositions, brand adapters, React adapters, reusable sections, and presentational product components. It must not ship app-specific feature/use-case screens or names such as operator-login, portal activation, customer credential control, or customer management.
|
|
160
|
+
- **Standard components, not app-owned product features.** This package ships primitives, standard UI components, assistant-ui compositions, brand adapters, React adapters, reusable sections, and presentational product components. It must not ship app-specific feature/use-case screens or names such as operator-login, portal activation, customer credential control, or customer management. Domain registries, command search providers, and chart dashboards bind app data and routes in the app; that binding is not a reason to copy reusable presentation. Portal's DataTable owns its Next.js query and registry protocol once, while consuming the shared UI foundation. Product-independent compositions belong here with a real consumer. The source-backed `chart-*` package surface is the plan-approved exception per issue #166; apps select charts and bind their titles, data, and navigation. Every data-shaped chart exposes an equivalent non-visual data alternative. A component whose complete value is already available in visible text or `aria-valuetext` does not add a redundant table.
|
|
130
161
|
- **Auth routes are consumer-owned shadcn block work.** Login, signup, OTP, two-factor, magic-link, and provider-button flows start from the selected shadcn block/example in the consuming app, with app-owned Better Auth behavior. Do not ship generic auth, signup, OTP, federated-login, or provider abstractions from this package unless a later explicit boundary decision replaces the selected shadcn block.
|
|
131
162
|
- **Reusable sections and compositions live here after proof.** Bento grids, media mounts, demo surfaces, page frames, section intros, CTA bands, proof slots, benefits grids, generic shells, and homepage/use-case sections belong in `@nadicodeai/ui` when a second concrete consumer, a plan-approved shared component, or a named validation slice proves the package boundary. Website, portal, and sibling Nadia routes pass data and media children into existing compositions instead of rebuilding layout locally.
|
|
132
163
|
- **Brand renderers live here for React consumers.** Logo geometry, asset generation, and usage semantics are owned by [`packages/design-system/DESIGN.md`, "Brand media authority"](../../design-system/DESIGN.md#brand-media-authority). `BrandLockup` supplies the approved horizontal lockup. `BrandMark` serves tight spaces and `BrandWordmark` serves placements where the name must read by itself. All three render the package's public neutral geometry seam with React-scoped SVG ids, dynamic theme paint, and accessible names. Consuming routes use these adapters; they never import raw logo assets, draw brand SVG inline, mask or recolor assets, or create another lockup rule.
|
|
@@ -141,11 +172,51 @@ package.
|
|
|
141
172
|
|
|
142
173
|
The source tree, barrels, and package exports own the current reusable section vocabulary. When a page needs a variation, extend the owning source component with a small typed prop or slot; do not fork the layout in a consumer or document a second component inventory here.
|
|
143
174
|
|
|
175
|
+
`KpiStatGroup` owns the metric strip, responsive tiles, and link treatment; `KpiStat` owns its value, note, delta, and decorative trend. Apps supply metrics and bind links through `render`. Time-series charts expose `size`; their lightweight layout module also sizes deferred placeholders, so consumers never select internal chart elements.
|
|
176
|
+
|
|
177
|
+
`CollectionToolbar` owns the collection-control layout: compact filters at the
|
|
178
|
+
leading edge, a flexible search field, and trailing actions. Its slot order is
|
|
179
|
+
also the reading and focus order, including when controls wrap. Consumers supply
|
|
180
|
+
existing controls and own their query state; they do not set independent search
|
|
181
|
+
widths or align filters to table columns. `CollectionFilters` owns the controlled
|
|
182
|
+
field/value picker and applied-filter chips; callers supply options, localized
|
|
183
|
+
copy, and callbacks. The component owns discovery, edit/remove, keyboard focus,
|
|
184
|
+
and empty search results. It does not own routing or fetch state.
|
|
185
|
+
|
|
186
|
+
`SettingsFactsSection` owns a named record or settings reading section: its
|
|
187
|
+
heading and optional action, header separator, and bounded responsive fact
|
|
188
|
+
cells in two equal columns, stacked on compact screens. Consumers supply labels,
|
|
189
|
+
values, an optional contextual footer, and a Card surface; paragraph facts take a full row. The stable heading remains a programmatic focus target
|
|
190
|
+
when an editor closes. Keep fact labels, order, and cells stable when data is
|
|
191
|
+
missing: null and undefined values use the required localized
|
|
192
|
+
`missingValueLabel`, while zero remains a value. Consumers do not filter out
|
|
193
|
+
unknown facts or replace them with indefinite loading skeletons. Editable
|
|
194
|
+
controls remain in `SettingsSection`.
|
|
195
|
+
|
|
196
|
+
`SettingsSection` composes Card's named header, divider, FieldGroup body, and
|
|
197
|
+
optional divided `footer` for local save actions and feedback. The caller owns
|
|
198
|
+
the form transaction and controls; the section owns their shared geometry.
|
|
199
|
+
Header `actions` are optional supporting commands, distinct from footer submission.
|
|
200
|
+
`SettingsField` uses one render callback in server and client components. Spread
|
|
201
|
+
its `controlProps` onto the control to retain the visible label, descriptions,
|
|
202
|
+
error association, and disabled/required state; do not wire another copy by hand.
|
|
203
|
+
|
|
204
|
+
`TableDetailRow` keeps supporting collection facts readable at compact widths
|
|
205
|
+
inside a native full-width table cell. It shares the owning column labels and
|
|
206
|
+
values rather than requiring another responsive cell implementation. Identity
|
|
207
|
+
and status retain their column headers. Missing values retain their place.
|
|
208
|
+
`Table compactDetails` and `TableDetailRow layout` share `tableCompactLayouts`:
|
|
209
|
+
`standard` retains full columns at 768px of container width, `wide` at 1024px,
|
|
210
|
+
and `spacious` at 1152px for schemas with additional operational columns.
|
|
211
|
+
The same owner switches column visibility, detail visibility, and row boundaries.
|
|
212
|
+
Two supporting facts use two columns; additional facts can use three when they fit.
|
|
213
|
+
The cell renderer supplies the value, leaving the label to the detail row.
|
|
214
|
+
|
|
144
215
|
## Consumer Boundary
|
|
145
216
|
|
|
146
217
|
Consumers own routing, auth, app data, server actions, runtime construction and adapters, permission decisions, analytics, and app-specific copy. The package owns reusable React presentation and the public Agent UI dependency boundary. Agent state and behavior come through the Agents interface; non-React and TUI contracts belong to their consuming products.
|
|
147
218
|
|
|
148
|
-
App source composes layout, data, behavior, and semantic content around package components. It does not restyle an imported component root: appearance changes enter through the component's typed variant or are added to the owning package component. App `className` values on package components are
|
|
219
|
+
App source composes layout, data, behavior, and semantic content around package components. It does not restyle an imported component root: appearance changes enter through the component's typed variant or are added to the owning package component. App `className` values on package components arrange and size the component root. Descendant `data-slot` selectors are not a public styling API; internal spacing, type, and sizing belong to the shared component, enforced by `nadicodeai/no-shadcn-appearance-override`. App source uses semantic roles such as `bg-background` and `text-foreground`; canonical `nc-*` color utilities stay behind package-owned primitives such as `BrandField`. Raw Tailwind palette colors are not available to anyone: the generated theme clears the `--color-*` namespace, so `bg-red-500` produces no CSS and `better-tailwindcss/no-unknown-classes` reports it. Visible controls come from `@nadicodeai/ui`, while hidden inputs remain valid form transport. The root ESLint rules `nadicodeai/no-shadcn-appearance-override` and `nadicodeai/no-native-visible-control` enforce the rest of this boundary; each takes a `packageOwnedPrimitives` option (`"exempt"` by default, `"lint"` to hold this package's own source to the same rule).
|
|
149
220
|
|
|
150
221
|
When this package is read from an installed npm package, follow the [consumer setup guide](consuming-cross-repo.md) for setup and public import paths. The styling invariant above remains the authority for component implementation.
|
|
151
222
|
|