@neatui/css 4.2.1 → 4.2.3
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 +35 -6
- package/components/anim/package.json +3 -3
- package/components/badge/package.json +6 -6
- package/components/btns/README.md +2 -1
- package/components/btns/base.scss +1 -1
- package/components/btns/btns@a.scss +3 -3
- package/components/btns/btns@b.scss +3 -3
- package/components/btns/package.json +2 -2
- package/components/card/package.json +1 -1
- package/components/chat/package.json +1 -1
- package/components/form/README.md +9 -9
- package/components/form/package.json +5 -5
- package/components/grid/package.json +2 -2
- package/components/icon/package.json +1 -1
- package/components/imgs/README.md +1 -1
- package/components/imgs/package.json +2 -2
- package/components/line/package.json +2 -2
- package/components/list/package.json +3 -3
- package/components/load/README.md +1 -1
- package/components/load/package.json +2 -2
- package/components/menu/package.json +4 -4
- package/components/navs/README.md +1 -1
- package/components/navs/package.json +2 -2
- package/components/path/README.md +9 -9
- package/components/path/package.json +5 -5
- package/components/steps/package.json +1 -1
- package/components/table/package.json +5 -5
- package/components/tabs/README.md +5 -5
- package/components/tabs/package.json +5 -5
- package/components/tags/README.md +4 -4
- package/components/tags/package.json +5 -5
- package/components/tips/README.md +5 -5
- package/components/tips/package.json +5 -5
- package/components/title/package.json +1 -1
- package/core/theme.scss +35 -25
- package/dist/neatui.css +28248 -0
- package/dist/neatui.min.css +1 -0
- package/dist/neatui.preview.css +36300 -0
- package/dist/neatui.preview.min.css +1 -0
- package/package.json +57 -5
- package/preview.scss +10 -0
- package/tokens/grammar.cjs +310 -0
- package/tokens/neatui.tokens.json +489 -0
- package/components/user/mcui@user.scss +0 -0
- package/postcss.config.js +0 -20
package/package.json
CHANGED
|
@@ -1,18 +1,70 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@neatui/css",
|
|
3
|
-
"version": "4.2.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "4.2.3",
|
|
4
|
+
"description": "NeatUI constrained atomic design system — SCSS source for production themes, plus preview CSS with default light/dark",
|
|
5
5
|
"main": "./neatui.scss",
|
|
6
|
+
"sass": "./neatui.scss",
|
|
7
|
+
"style": "./dist/neatui.preview.min.css",
|
|
6
8
|
"license": "MIT",
|
|
7
9
|
"publishConfig": {
|
|
8
10
|
"access": "public"
|
|
9
11
|
},
|
|
10
12
|
"author": "xiaojunbo",
|
|
13
|
+
"repository": {
|
|
14
|
+
"type": "git",
|
|
15
|
+
"url": "git+https://gitlab.com/neatui/neatui.git",
|
|
16
|
+
"directory": "@neatui/css"
|
|
17
|
+
},
|
|
18
|
+
"homepage": "https://neatui.fekit.cn",
|
|
19
|
+
"bugs": {
|
|
20
|
+
"url": "https://gitlab.com/neatui/neatui/issues"
|
|
21
|
+
},
|
|
22
|
+
"keywords": [
|
|
23
|
+
"neatui",
|
|
24
|
+
"atomic-css",
|
|
25
|
+
"design-system",
|
|
26
|
+
"scss",
|
|
27
|
+
"theme",
|
|
28
|
+
"css",
|
|
29
|
+
"preview"
|
|
30
|
+
],
|
|
31
|
+
"files": [
|
|
32
|
+
"core",
|
|
33
|
+
"components",
|
|
34
|
+
"tokens",
|
|
35
|
+
"dist",
|
|
36
|
+
"neatui.scss",
|
|
37
|
+
"preview.scss",
|
|
38
|
+
"index.scss",
|
|
39
|
+
"index.js",
|
|
40
|
+
"GUIDE.md",
|
|
41
|
+
"MIGRATION-4.0.md",
|
|
42
|
+
"MIGRATION-4.1.md",
|
|
43
|
+
"README.md"
|
|
44
|
+
],
|
|
45
|
+
"exports": {
|
|
46
|
+
".": {
|
|
47
|
+
"sass": "./neatui.scss",
|
|
48
|
+
"style": "./dist/neatui.preview.min.css",
|
|
49
|
+
"default": "./neatui.scss"
|
|
50
|
+
},
|
|
51
|
+
"./neatui.scss": "./neatui.scss",
|
|
52
|
+
"./preview.scss": "./preview.scss",
|
|
53
|
+
"./preview": "./dist/neatui.preview.min.css",
|
|
54
|
+
"./dist/*": "./dist/*",
|
|
55
|
+
"./package.json": "./package.json"
|
|
56
|
+
},
|
|
11
57
|
"scripts": {
|
|
12
|
-
"build": "yarn build:scss && yarn build:optimize && yarn build:minify",
|
|
58
|
+
"build": "yarn build:scss && yarn build:preview && yarn build:optimize && yarn build:minify",
|
|
13
59
|
"build:scss": "sass neatui.scss dist/neatui.css --no-source-map",
|
|
14
|
-
"build:
|
|
15
|
-
"build:
|
|
60
|
+
"build:preview": "sass preview.scss dist/neatui.preview.css --no-source-map",
|
|
61
|
+
"build:optimize": "postcss dist/neatui.css -o dist/neatui.css --no-map && postcss dist/neatui.preview.css -o dist/neatui.preview.css --no-map",
|
|
62
|
+
"build:minify": "cleancss -o dist/neatui.min.css dist/neatui.css && cleancss -o dist/neatui.preview.min.css dist/neatui.preview.css",
|
|
63
|
+
"tokens:generate": "node scripts/generate-vscode-snapshot.mjs && node scripts/generate-docs-index.mjs",
|
|
64
|
+
"tokens:check": "node scripts/generate-vscode-snapshot.mjs --check",
|
|
65
|
+
"tokens:docs": "node scripts/generate-docs-index.mjs",
|
|
66
|
+
"validate": "node scripts/validate-grammar.mjs",
|
|
67
|
+
"prepublishOnly": "yarn build"
|
|
16
68
|
},
|
|
17
69
|
"peerDependencies": {},
|
|
18
70
|
"devDependencies": {
|
package/preview.scss
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// Preview bundle for trial / CDN demos.
|
|
2
|
+
// Includes official default light + dark themes.
|
|
3
|
+
// Production projects should use SCSS:
|
|
4
|
+
// $theme: (...);
|
|
5
|
+
// @use '@neatui/css' as *;
|
|
6
|
+
// @include theme($theme);
|
|
7
|
+
|
|
8
|
+
@use './neatui.scss' as *;
|
|
9
|
+
|
|
10
|
+
@include theme();
|
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Shared NeatUI grammar validator (Node / CommonJS).
|
|
5
|
+
* Pure: pass tokens or a prebuilt grammar spec — no filesystem I/O.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
function resolveValueGroup(tokens, groupRef) {
|
|
9
|
+
const [root, key] = groupRef.split('.');
|
|
10
|
+
const group = tokens[root]?.[key];
|
|
11
|
+
if (!Array.isArray(group)) {
|
|
12
|
+
throw new Error(`Unknown value group: ${groupRef}`);
|
|
13
|
+
}
|
|
14
|
+
return group;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function collectManagedValues(tokens, attrDef) {
|
|
18
|
+
if (Array.isArray(attrDef.values)) {
|
|
19
|
+
return attrDef.values.map((item) => item.value);
|
|
20
|
+
}
|
|
21
|
+
const values = [];
|
|
22
|
+
for (const ref of attrDef.valueGroups || []) {
|
|
23
|
+
for (const item of resolveValueGroup(tokens, ref)) {
|
|
24
|
+
values.push(item.value);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return values;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function escapeRegExp(value) {
|
|
31
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function buildGrammarSpec(tokens) {
|
|
35
|
+
const attributes = {};
|
|
36
|
+
for (const [name, def] of Object.entries(tokens.attributes || {})) {
|
|
37
|
+
if (!def.managed) continue;
|
|
38
|
+
attributes[name] = {
|
|
39
|
+
openEnded: Boolean(def.openEnded),
|
|
40
|
+
expandResponsive: Boolean(def.expandResponsivePrefixes),
|
|
41
|
+
values: collectManagedValues(tokens, def),
|
|
42
|
+
intent: def.intent || '',
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return {
|
|
47
|
+
schemaVersion: tokens.schemaVersion,
|
|
48
|
+
cssVersion: tokens.cssVersion,
|
|
49
|
+
grades: (tokens.grades || []).map((item) => item.value),
|
|
50
|
+
semanticColors: (tokens.semanticColors || []).map((item) => item.value),
|
|
51
|
+
responsivePrefixes: tokens.breakpoints?.attributeResponsivePrefixes || ['mob', 'pad'],
|
|
52
|
+
classPatterns: (tokens.classPatterns || []).map((item) => ({
|
|
53
|
+
id: item.id,
|
|
54
|
+
intent: item.intent,
|
|
55
|
+
example: item.example,
|
|
56
|
+
pattern: item.pattern,
|
|
57
|
+
})),
|
|
58
|
+
attributes,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function compileClassPatterns(spec) {
|
|
63
|
+
const grades = (spec.grades || []).map(escapeRegExp).join('|');
|
|
64
|
+
const colors = (spec.semanticColors || []).map(escapeRegExp).join('|');
|
|
65
|
+
|
|
66
|
+
return (spec.classPatterns || []).map((item) => {
|
|
67
|
+
const source = String(item.pattern)
|
|
68
|
+
.split('({grade})')
|
|
69
|
+
.join(`(${grades})`)
|
|
70
|
+
.split('({semanticColor})')
|
|
71
|
+
.join(`(${colors})`);
|
|
72
|
+
return {
|
|
73
|
+
id: item.id,
|
|
74
|
+
intent: item.intent,
|
|
75
|
+
example: item.example,
|
|
76
|
+
regex: new RegExp(source),
|
|
77
|
+
};
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function createValidatorFromSpec(spec) {
|
|
82
|
+
const classPatterns = compileClassPatterns(spec);
|
|
83
|
+
const responsivePrefixes = new Set(spec.responsivePrefixes || ['mob', 'pad']);
|
|
84
|
+
|
|
85
|
+
const attributes = {};
|
|
86
|
+
for (const [name, def] of Object.entries(spec.attributes || {})) {
|
|
87
|
+
attributes[name] = {
|
|
88
|
+
openEnded: Boolean(def.openEnded),
|
|
89
|
+
expandResponsive: Boolean(def.expandResponsive),
|
|
90
|
+
values: new Set(def.values || []),
|
|
91
|
+
intent: def.intent || '',
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function stripResponsivePrefix(token) {
|
|
96
|
+
const match = /^([a-z]+):(.+)$/i.exec(token);
|
|
97
|
+
if (!match) return { prefix: null, base: token };
|
|
98
|
+
const [, prefix, base] = match;
|
|
99
|
+
if (!responsivePrefixes.has(prefix)) {
|
|
100
|
+
return { prefix: null, base: token, unknownPrefix: prefix };
|
|
101
|
+
}
|
|
102
|
+
return { prefix, base };
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function validateClassToken(raw) {
|
|
106
|
+
const token = String(raw || '').trim();
|
|
107
|
+
if (!token) return null;
|
|
108
|
+
|
|
109
|
+
const { prefix, base, unknownPrefix } = stripResponsivePrefix(token);
|
|
110
|
+
if (unknownPrefix) {
|
|
111
|
+
return {
|
|
112
|
+
kind: 'class',
|
|
113
|
+
token,
|
|
114
|
+
ok: false,
|
|
115
|
+
reason: `未知响应前缀 \`${unknownPrefix}:\`(允许: ${[...responsivePrefixes].join(', ')})`,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
for (const pattern of classPatterns) {
|
|
120
|
+
if (pattern.regex.test(base)) {
|
|
121
|
+
return {
|
|
122
|
+
kind: 'class',
|
|
123
|
+
token,
|
|
124
|
+
ok: true,
|
|
125
|
+
patternId: pattern.id,
|
|
126
|
+
prefix,
|
|
127
|
+
base,
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return {
|
|
133
|
+
kind: 'class',
|
|
134
|
+
token,
|
|
135
|
+
ok: false,
|
|
136
|
+
reason: `无法匹配 token schema 中的 classPatterns(示例: ${classPatterns
|
|
137
|
+
.slice(0, 3)
|
|
138
|
+
.map((item) => item.example)
|
|
139
|
+
.join(', ')}…)`,
|
|
140
|
+
base,
|
|
141
|
+
prefix,
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function validateAttributeToken(attrName, raw) {
|
|
146
|
+
const token = String(raw || '').trim();
|
|
147
|
+
if (!token) return null;
|
|
148
|
+
|
|
149
|
+
const def = attributes[attrName];
|
|
150
|
+
if (!def) {
|
|
151
|
+
return {
|
|
152
|
+
kind: 'attr',
|
|
153
|
+
attr: attrName,
|
|
154
|
+
token,
|
|
155
|
+
ok: false,
|
|
156
|
+
reason: `属性 \`${attrName}\` 不在 token schema 的 managed attributes 中`,
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const { prefix, base, unknownPrefix } = stripResponsivePrefix(token);
|
|
161
|
+
if (unknownPrefix) {
|
|
162
|
+
return {
|
|
163
|
+
kind: 'attr',
|
|
164
|
+
attr: attrName,
|
|
165
|
+
token,
|
|
166
|
+
ok: false,
|
|
167
|
+
reason: `未知响应前缀 \`${unknownPrefix}:\``,
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
if (prefix && !def.expandResponsive) {
|
|
172
|
+
return {
|
|
173
|
+
kind: 'attr',
|
|
174
|
+
attr: attrName,
|
|
175
|
+
token,
|
|
176
|
+
ok: false,
|
|
177
|
+
reason: `属性 \`${attrName}\` 不支持响应前缀`,
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
if (def.values.has(base) || def.openEnded) {
|
|
182
|
+
return {
|
|
183
|
+
kind: 'attr',
|
|
184
|
+
attr: attrName,
|
|
185
|
+
token,
|
|
186
|
+
ok: true,
|
|
187
|
+
prefix,
|
|
188
|
+
base,
|
|
189
|
+
openEnded: def.openEnded && !def.values.has(base),
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
const sample = [...def.values].slice(0, 8).join(', ');
|
|
194
|
+
return {
|
|
195
|
+
kind: 'attr',
|
|
196
|
+
attr: attrName,
|
|
197
|
+
token,
|
|
198
|
+
ok: false,
|
|
199
|
+
reason: `\`${base}\` 不是 \`${attrName}\` 的合法值(例如: ${sample}${def.values.size > 8 ? '…' : ''})`,
|
|
200
|
+
base,
|
|
201
|
+
prefix,
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function validateClassString(classString) {
|
|
206
|
+
return String(classString || '')
|
|
207
|
+
.split(/\s+/)
|
|
208
|
+
.map(validateClassToken)
|
|
209
|
+
.filter(Boolean);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function validateAttributeString(attrName, valueString) {
|
|
213
|
+
return String(valueString || '')
|
|
214
|
+
.split(/\s+/)
|
|
215
|
+
.map((token) => validateAttributeToken(attrName, token))
|
|
216
|
+
.filter(Boolean);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
function extractFromMarkup(source) {
|
|
220
|
+
const classes = [];
|
|
221
|
+
const attrs = [];
|
|
222
|
+
|
|
223
|
+
for (const match of String(source).matchAll(
|
|
224
|
+
/\bclass(?:Name)?\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s>]+))/gi,
|
|
225
|
+
)) {
|
|
226
|
+
classes.push(match[1] ?? match[2] ?? match[3] ?? '');
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
for (const name of Object.keys(attributes)) {
|
|
230
|
+
const re = new RegExp(
|
|
231
|
+
`\\b${name}\\s*=\\s*(?:"([^"]*)"|'([^']*)'|([^\\s>]+))`,
|
|
232
|
+
'gi',
|
|
233
|
+
);
|
|
234
|
+
for (const match of String(source).matchAll(re)) {
|
|
235
|
+
attrs.push({ name, value: match[1] ?? match[2] ?? match[3] ?? '' });
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
return { classes, attrs };
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
function validateMarkup(source) {
|
|
243
|
+
const extracted = extractFromMarkup(source);
|
|
244
|
+
const results = [];
|
|
245
|
+
for (const classString of extracted.classes) {
|
|
246
|
+
results.push(...validateClassString(classString));
|
|
247
|
+
}
|
|
248
|
+
for (const attr of extracted.attrs) {
|
|
249
|
+
results.push(...validateAttributeString(attr.name, attr.value));
|
|
250
|
+
}
|
|
251
|
+
return results;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Flag only tokens that look like NeatUI / common drift — not arbitrary custom classes.
|
|
256
|
+
*/
|
|
257
|
+
function looksLikeNeatuiClass(token) {
|
|
258
|
+
const base = String(token || '').replace(/^(?:mob|pad):/, '');
|
|
259
|
+
if (!base) return false;
|
|
260
|
+
if (/^(?:co|bg|fs|lh|r|o|z)-/.test(base)) return true;
|
|
261
|
+
if (/^-?(?:n|nx|ny|nt|nr|nb|nl|m|mx|my|mt|mr|mb|ml)-/.test(base)) return true;
|
|
262
|
+
if (/^(?:w|min-w|max-w|h|min-h|max-h)-/.test(base)) return true;
|
|
263
|
+
// Common utility drift that should fail loudly
|
|
264
|
+
if (/^(?:p|px|py|pt|pr|pb|pl|gap|space-[xy])-\d/.test(base)) return true;
|
|
265
|
+
if (/^(?:text|bg|border)-(?:red|blue|green|gray|slate|zinc)-\d{2,3}$/.test(base)) return true;
|
|
266
|
+
if (/^(?:justify|items|content)-/.test(base)) return true;
|
|
267
|
+
return false;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
function validateClassStringSelective(classString) {
|
|
271
|
+
return validateClassString(classString).filter((item) => looksLikeNeatuiClass(item.token));
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
function validateMarkupSelective(source) {
|
|
275
|
+
const extracted = extractFromMarkup(source);
|
|
276
|
+
const results = [];
|
|
277
|
+
for (const classString of extracted.classes) {
|
|
278
|
+
results.push(...validateClassStringSelective(classString));
|
|
279
|
+
}
|
|
280
|
+
for (const attr of extracted.attrs) {
|
|
281
|
+
results.push(...validateAttributeString(attr.name, attr.value));
|
|
282
|
+
}
|
|
283
|
+
return results;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
return {
|
|
287
|
+
spec,
|
|
288
|
+
attributes,
|
|
289
|
+
classPatterns,
|
|
290
|
+
validateClassToken,
|
|
291
|
+
validateAttributeToken,
|
|
292
|
+
validateClassString,
|
|
293
|
+
validateAttributeString,
|
|
294
|
+
validateClassStringSelective,
|
|
295
|
+
extractFromMarkup,
|
|
296
|
+
validateMarkup,
|
|
297
|
+
validateMarkupSelective,
|
|
298
|
+
looksLikeNeatuiClass,
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
function createValidator(tokens) {
|
|
303
|
+
return createValidatorFromSpec(buildGrammarSpec(tokens));
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
module.exports = {
|
|
307
|
+
buildGrammarSpec,
|
|
308
|
+
createValidator,
|
|
309
|
+
createValidatorFromSpec,
|
|
310
|
+
};
|