css2class 2.0.0 → 2.0.1
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/.github/workflows/deploy-docs.yml +53 -0
- package/.head_config.mjs +68 -0
- package/CONFIG.md +38 -1
- package/README.md +595 -633
- package/bin/class2css.js +32 -4
- package/common.css +1 -1
- package/configs/typography.config.js +1 -0
- package/docs/.vitepress/config.mjs +68 -65
- package/docs/guide/cli.md +97 -97
- package/docs/guide/config-template.md +16 -1
- package/docs/guide/config.md +129 -64
- package/docs/guide/faq.md +202 -202
- package/docs/guide/getting-started.md +86 -83
- package/docs/guide/incremental.md +164 -162
- package/docs/guide/rules-reference.md +73 -1
- package/docs/index.md +71 -68
- package/examples/weapp/README.md +15 -0
- package/examples/weapp/class2css.config.js +70 -0
- package/examples/weapp/src/placeholder.wxml +0 -0
- package/examples/weapp/styles.config.js +201 -0
- package/examples/web/README.md +25 -0
- package/examples/web/class2css.config.js +70 -0
- package/examples/web/demo.html +105 -0
- package/examples/web/src/placeholder.html +5 -0
- package/examples/web/styles.config.js +201 -0
- package/package.json +7 -2
- package/src/README.md +99 -0
- package/src/core/ConfigManager.js +440 -431
- package/src/core/FullScanManager.js +438 -430
- package/src/generators/DynamicClassGenerator.js +270 -72
- package/src/index.js +1091 -1046
- package/src/parsers/ClassParser.js +8 -2
- package/src/utils/CssFormatter.js +400 -47
- package/src/utils/UnitProcessor.js +4 -3
- package/src/watchers/ConfigWatcher.js +413 -413
- package/src/watchers/FileWatcher.js +148 -133
- package/src/writers/FileWriter.js +444 -302
- package/src/writers/UnifiedWriter.js +413 -370
- package/class2css.config.js +0 -124
- package/styles.config.js +0 -250
|
@@ -17,6 +17,40 @@ class DynamicClassGenerator {
|
|
|
17
17
|
// CSS生成缓存
|
|
18
18
|
this.cssCache = new Map();
|
|
19
19
|
this.cacheEnabled = true;
|
|
20
|
+
|
|
21
|
+
// 缓存:已知前缀列表(按长度降序),避免每个 class 解析都重新排序
|
|
22
|
+
this._sortedPrefixes = [];
|
|
23
|
+
this._sortedPrefixesCacheKey = null;
|
|
24
|
+
this.refreshPrefixCache();
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// 刷新前缀缓存(配置变更时调用)
|
|
28
|
+
refreshPrefixCache() {
|
|
29
|
+
try {
|
|
30
|
+
const cssNameMap = this.configManager?.getCssNameMap?.();
|
|
31
|
+
if (!cssNameMap) {
|
|
32
|
+
this._sortedPrefixes = [];
|
|
33
|
+
this._sortedPrefixesCacheKey = null;
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// 以 key 的拼接作为轻量 cache key(避免在热路径里做深比较)
|
|
38
|
+
const keys = Array.from(cssNameMap.keys());
|
|
39
|
+
const nextKey = keys.join('|');
|
|
40
|
+
if (this._sortedPrefixesCacheKey === nextKey && this._sortedPrefixes.length > 0) {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
this._sortedPrefixes = keys.sort((a, b) => b.length - a.length);
|
|
45
|
+
this._sortedPrefixesCacheKey = nextKey;
|
|
46
|
+
this.eventBus.emit('generator:dynamic:prefix_cache:refreshed', {
|
|
47
|
+
prefixCount: this._sortedPrefixes.length,
|
|
48
|
+
});
|
|
49
|
+
} catch (_) {
|
|
50
|
+
// ignore: cache refresh failure should not break generation
|
|
51
|
+
this._sortedPrefixes = [];
|
|
52
|
+
this._sortedPrefixesCacheKey = null;
|
|
53
|
+
}
|
|
20
54
|
}
|
|
21
55
|
|
|
22
56
|
// 生成动态CSS类列表
|
|
@@ -33,6 +67,11 @@ class DynamicClassGenerator {
|
|
|
33
67
|
|
|
34
68
|
this.eventBus.emit('generator:dynamic:started', { classCount: classArr.length });
|
|
35
69
|
|
|
70
|
+
// 确保前缀缓存存在(热更新/首次运行兜底)
|
|
71
|
+
if (!this._sortedPrefixes || this._sortedPrefixes.length === 0) {
|
|
72
|
+
this.refreshPrefixCache();
|
|
73
|
+
}
|
|
74
|
+
|
|
36
75
|
classArr.forEach((className, index) => {
|
|
37
76
|
try {
|
|
38
77
|
const isImportant = this.importantParser.hasImportantFlag(className);
|
|
@@ -47,25 +86,34 @@ class DynamicClassGenerator {
|
|
|
47
86
|
return;
|
|
48
87
|
}
|
|
49
88
|
|
|
50
|
-
// name
|
|
51
|
-
const
|
|
89
|
+
// name 现在是 [prefix, value, responsiveVariant, stateVariants]
|
|
90
|
+
const responsiveVariant = name.length >= 3 ? name[2] : null;
|
|
91
|
+
const stateVariants = name.length >= 4 ? name[3] : [];
|
|
52
92
|
|
|
53
93
|
if (cssNameMap.has(name[0])) {
|
|
54
|
-
const classCss = this.getClassListStr(
|
|
94
|
+
const classCss = this.getClassListStr(
|
|
95
|
+
[name[0], name[1]],
|
|
96
|
+
className,
|
|
97
|
+
isImportant,
|
|
98
|
+
responsiveVariant,
|
|
99
|
+
stateVariants
|
|
100
|
+
);
|
|
55
101
|
cssStr += classCss;
|
|
56
102
|
this.eventBus.emit('generator:dynamic:generated', {
|
|
57
103
|
className,
|
|
58
104
|
cleanName: cleanClassName,
|
|
59
105
|
isImportant,
|
|
60
|
-
|
|
106
|
+
responsiveVariant,
|
|
107
|
+
stateVariants,
|
|
61
108
|
});
|
|
62
109
|
} else {
|
|
63
|
-
userBaseClassArr.push([name[0], name[1], className, isImportant,
|
|
110
|
+
userBaseClassArr.push([name[0], name[1], className, isImportant, responsiveVariant, stateVariants]);
|
|
64
111
|
this.eventBus.emit('generator:dynamic:userBase', {
|
|
65
112
|
className,
|
|
66
113
|
cleanName: cleanClassName,
|
|
67
114
|
isImportant,
|
|
68
|
-
|
|
115
|
+
responsiveVariant,
|
|
116
|
+
stateVariants,
|
|
69
117
|
});
|
|
70
118
|
}
|
|
71
119
|
} catch (error) {
|
|
@@ -86,37 +134,74 @@ class DynamicClassGenerator {
|
|
|
86
134
|
}
|
|
87
135
|
|
|
88
136
|
// 解析响应式变体前缀(如 sm:, md: 等)
|
|
137
|
+
// 保留向后兼容,内部调用 parseVariants
|
|
89
138
|
parseResponsiveVariant(className) {
|
|
139
|
+
const result = this.parseVariants(className);
|
|
140
|
+
return { variant: result.responsiveVariant, baseClass: result.baseClass };
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// 解析所有变体(响应式 + 状态变体如 hover/focus/active)
|
|
144
|
+
// 返回 { responsiveVariant, stateVariants, baseClass }
|
|
145
|
+
parseVariants(className) {
|
|
90
146
|
if (!className || typeof className !== 'string') {
|
|
91
|
-
return {
|
|
147
|
+
return { responsiveVariant: null, stateVariants: [], baseClass: className };
|
|
92
148
|
}
|
|
93
149
|
|
|
94
150
|
const variants = this.configManager.getVariants();
|
|
95
151
|
const responsiveVariants = variants.responsive || [];
|
|
152
|
+
const stateVariants = variants.states || [];
|
|
96
153
|
|
|
97
|
-
// 按 :
|
|
154
|
+
// 按 : 拆分前缀链
|
|
98
155
|
const parts = className.split(':');
|
|
99
|
-
if (parts.length
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
156
|
+
if (parts.length < 2) {
|
|
157
|
+
return { responsiveVariant: null, stateVariants: [], baseClass: className };
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
let responsiveVariant = null;
|
|
161
|
+
const foundStateVariants = [];
|
|
162
|
+
let baseClass = className;
|
|
163
|
+
|
|
164
|
+
// 从前往后解析:最多一个响应式变体(通常在链的最前面),可以有多个状态变体
|
|
165
|
+
// 例如:lg:hover:focus:w-20 -> responsive: lg, states: [hover, focus], base: w-20
|
|
166
|
+
let i = 0;
|
|
167
|
+
while (i < parts.length - 1) {
|
|
168
|
+
const potentialVariant = parts[i];
|
|
169
|
+
|
|
170
|
+
// 检查是否是响应式变体(只取第一个)
|
|
171
|
+
if (!responsiveVariant && responsiveVariants.includes(potentialVariant)) {
|
|
172
|
+
responsiveVariant = potentialVariant;
|
|
173
|
+
i++;
|
|
174
|
+
continue;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// 检查是否是状态变体
|
|
178
|
+
if (stateVariants.includes(potentialVariant)) {
|
|
179
|
+
foundStateVariants.push(potentialVariant);
|
|
180
|
+
i++;
|
|
181
|
+
continue;
|
|
104
182
|
}
|
|
183
|
+
|
|
184
|
+
// 遇到未知变体,停止解析,剩余部分作为 baseClass
|
|
185
|
+
break;
|
|
105
186
|
}
|
|
106
187
|
|
|
107
|
-
|
|
188
|
+
// baseClass 是最后一部分(或剩余部分)
|
|
189
|
+
baseClass = parts.slice(i).join(':');
|
|
190
|
+
|
|
191
|
+
return {
|
|
192
|
+
responsiveVariant,
|
|
193
|
+
stateVariants: foundStateVariants,
|
|
194
|
+
baseClass,
|
|
195
|
+
};
|
|
108
196
|
}
|
|
109
197
|
|
|
110
|
-
// 智能前缀解析方法 - 支持复合前缀如 max-w, min-h
|
|
198
|
+
// 智能前缀解析方法 - 支持复合前缀如 max-w, min-h 等,同时支持响应式和状态变体
|
|
111
199
|
parseClassNameIntelligent(className) {
|
|
112
|
-
//
|
|
113
|
-
const {
|
|
200
|
+
// 解析所有变体(响应式 + 状态)
|
|
201
|
+
const { responsiveVariant, stateVariants, baseClass } = this.parseVariants(className);
|
|
114
202
|
|
|
115
|
-
//
|
|
116
|
-
const
|
|
117
|
-
|
|
118
|
-
// 获取所有已知前缀,按长度降序排列
|
|
119
|
-
const prefixes = Array.from(cssNameMap.keys()).sort((a, b) => b.length - a.length);
|
|
203
|
+
// 使用缓存的前缀列表进行解析(避免每次都重新排序)
|
|
204
|
+
const prefixes = this._sortedPrefixes || [];
|
|
120
205
|
|
|
121
206
|
// 尝试匹配每个前缀
|
|
122
207
|
for (const prefix of prefixes) {
|
|
@@ -128,25 +213,31 @@ class DynamicClassGenerator {
|
|
|
128
213
|
className,
|
|
129
214
|
prefix,
|
|
130
215
|
value,
|
|
131
|
-
|
|
216
|
+
responsiveVariant,
|
|
217
|
+
stateVariants,
|
|
132
218
|
method: 'intelligent_parsing',
|
|
133
219
|
});
|
|
134
|
-
|
|
220
|
+
// 返回包含所有变体信息的数组:[prefix, value, responsiveVariant, stateVariants]
|
|
221
|
+
return [prefix, value, responsiveVariant, stateVariants];
|
|
135
222
|
}
|
|
136
223
|
}
|
|
137
224
|
}
|
|
138
225
|
|
|
139
|
-
//
|
|
140
|
-
|
|
141
|
-
|
|
226
|
+
// 降级到原有逻辑:支持首个 - 分割,允许 value 包含 -
|
|
227
|
+
// 例如:bg-hex-fff -> ['bg', 'hex-fff']
|
|
228
|
+
const firstDashIndex = baseClass.indexOf('-');
|
|
229
|
+
if (firstDashIndex > 0 && firstDashIndex < baseClass.length - 1) {
|
|
230
|
+
const prefix = baseClass.substring(0, firstDashIndex);
|
|
231
|
+
const value = baseClass.substring(firstDashIndex + 1);
|
|
142
232
|
this.eventBus.emit('generator:dynamic:prefix_matched', {
|
|
143
233
|
className,
|
|
144
|
-
prefix
|
|
145
|
-
value
|
|
146
|
-
|
|
147
|
-
|
|
234
|
+
prefix,
|
|
235
|
+
value,
|
|
236
|
+
responsiveVariant,
|
|
237
|
+
stateVariants,
|
|
238
|
+
method: 'fallback_first_dash_split',
|
|
148
239
|
});
|
|
149
|
-
return [
|
|
240
|
+
return [prefix, value, responsiveVariant, stateVariants];
|
|
150
241
|
}
|
|
151
242
|
|
|
152
243
|
this.eventBus.emit('generator:dynamic:parse_failed', {
|
|
@@ -157,15 +248,17 @@ class DynamicClassGenerator {
|
|
|
157
248
|
}
|
|
158
249
|
|
|
159
250
|
// 生成单个类的CSS字符串(优化版)
|
|
160
|
-
getClassListStr(name, originalClassName, isImportant = false,
|
|
161
|
-
//
|
|
162
|
-
const
|
|
251
|
+
getClassListStr(name, originalClassName, isImportant = false, responsiveVariant = null, stateVariants = []) {
|
|
252
|
+
// 检查缓存(包含所有变体信息)
|
|
253
|
+
const stateKey = stateVariants.length > 0 ? stateVariants.join(',') : '';
|
|
254
|
+
const cacheKey = `${name[0]}-${name[1]}-${isImportant}-${responsiveVariant || ''}-${stateKey}`;
|
|
163
255
|
if (this.cacheEnabled && this.cssCache.has(cacheKey)) {
|
|
164
256
|
const cached = this.cssCache.get(cacheKey);
|
|
165
|
-
//
|
|
257
|
+
// 替换选择器时需要考虑转义和状态伪类
|
|
166
258
|
const escapedSelector = this.cssFormatter.escapeSelector(originalClassName);
|
|
259
|
+
const pseudoSelectors = this.cssFormatter.buildStatePseudoSelectors(stateVariants);
|
|
167
260
|
// 修复:将 - 放在字符类末尾,避免被解释为范围操作符
|
|
168
|
-
return cached.replace(/\.[\w\\:-]+\s*{/, `.${escapedSelector} {`);
|
|
261
|
+
return cached.replace(/\.[\w\\:-]+\s*{/, `.${escapedSelector}${pseudoSelectors} {`);
|
|
169
262
|
}
|
|
170
263
|
|
|
171
264
|
const classNameDefinition = this.configManager.getCssNameMap().get(name[0]);
|
|
@@ -186,7 +279,8 @@ class DynamicClassGenerator {
|
|
|
186
279
|
originalClassName,
|
|
187
280
|
classNameDefinition,
|
|
188
281
|
isImportant,
|
|
189
|
-
|
|
282
|
+
responsiveVariant,
|
|
283
|
+
stateVariants
|
|
190
284
|
);
|
|
191
285
|
} else {
|
|
192
286
|
// 处理字符串类型的CSS定义
|
|
@@ -195,7 +289,8 @@ class DynamicClassGenerator {
|
|
|
195
289
|
originalClassName,
|
|
196
290
|
classNameDefinition,
|
|
197
291
|
isImportant,
|
|
198
|
-
|
|
292
|
+
responsiveVariant,
|
|
293
|
+
stateVariants
|
|
199
294
|
);
|
|
200
295
|
}
|
|
201
296
|
|
|
@@ -208,7 +303,7 @@ class DynamicClassGenerator {
|
|
|
208
303
|
}
|
|
209
304
|
|
|
210
305
|
// 生成基于对象定义的CSS
|
|
211
|
-
generateObjectBasedCSS(name, originalClassName, classNameDefinition, isImportant,
|
|
306
|
+
generateObjectBasedCSS(name, originalClassName, classNameDefinition, isImportant, responsiveVariant = null, stateVariants = []) {
|
|
212
307
|
if (!classNameDefinition.classArr) {
|
|
213
308
|
this.eventBus.emit('generator:warning', {
|
|
214
309
|
warning: `classArr not found in definition for: ${name[0]}`,
|
|
@@ -228,7 +323,8 @@ class DynamicClassGenerator {
|
|
|
228
323
|
processedValue = this.unitProcessor.parseValue(
|
|
229
324
|
rawValue,
|
|
230
325
|
cssProperty,
|
|
231
|
-
classNameDefinition.unit
|
|
326
|
+
classNameDefinition.unit,
|
|
327
|
+
{ skipSpecialProcessing: !!classNameDefinition.skipSpecialProcessing }
|
|
232
328
|
);
|
|
233
329
|
} else {
|
|
234
330
|
// 使用传统逻辑(支持skipConversion)
|
|
@@ -247,19 +343,19 @@ class DynamicClassGenerator {
|
|
|
247
343
|
value: isImportant ? `${value} !important` : value,
|
|
248
344
|
}));
|
|
249
345
|
|
|
250
|
-
// 使用格式化器格式化CSS
|
|
251
|
-
const cssRule = this.cssFormatter.formatRule(originalClassName, processedValuesArray);
|
|
346
|
+
// 使用格式化器格式化CSS(传入状态变体用于生成伪类选择器)
|
|
347
|
+
const cssRule = this.cssFormatter.formatRule(originalClassName, processedValuesArray, null, stateVariants);
|
|
252
348
|
|
|
253
349
|
// 如果有响应式变体,用 @media 包裹
|
|
254
|
-
if (
|
|
255
|
-
return this.wrapWithMediaQuery(cssRule,
|
|
350
|
+
if (responsiveVariant) {
|
|
351
|
+
return this.wrapWithMediaQuery(cssRule, responsiveVariant);
|
|
256
352
|
}
|
|
257
353
|
|
|
258
354
|
return cssRule;
|
|
259
355
|
}
|
|
260
356
|
|
|
261
357
|
// 生成基于字符串定义的CSS
|
|
262
|
-
generateStringBasedCSS(name, originalClassName, classNameDefinition, isImportant,
|
|
358
|
+
generateStringBasedCSS(name, originalClassName, classNameDefinition, isImportant, responsiveVariant = null, stateVariants = []) {
|
|
263
359
|
const rawValue = name[1];
|
|
264
360
|
let processedValue;
|
|
265
361
|
|
|
@@ -273,25 +369,25 @@ class DynamicClassGenerator {
|
|
|
273
369
|
|
|
274
370
|
const finalValue = isImportant ? `${processedValue} !important` : processedValue;
|
|
275
371
|
|
|
276
|
-
// 使用格式化器格式化CSS
|
|
277
|
-
const cssRule = this.cssFormatter.formatRule(originalClassName, `${classNameDefinition}: ${finalValue}
|
|
372
|
+
// 使用格式化器格式化CSS(传入状态变体用于生成伪类选择器)
|
|
373
|
+
const cssRule = this.cssFormatter.formatRule(originalClassName, `${classNameDefinition}: ${finalValue}`, null, stateVariants);
|
|
278
374
|
|
|
279
375
|
// 如果有响应式变体,用 @media 包裹
|
|
280
|
-
if (
|
|
281
|
-
return this.wrapWithMediaQuery(cssRule,
|
|
376
|
+
if (responsiveVariant) {
|
|
377
|
+
return this.wrapWithMediaQuery(cssRule, responsiveVariant);
|
|
282
378
|
}
|
|
283
379
|
|
|
284
380
|
return cssRule;
|
|
285
381
|
}
|
|
286
382
|
|
|
287
383
|
// 用 @media 查询包裹 CSS 规则
|
|
288
|
-
wrapWithMediaQuery(cssRule,
|
|
384
|
+
wrapWithMediaQuery(cssRule, responsiveVariant) {
|
|
289
385
|
const breakpoints = this.configManager.getBreakpoints();
|
|
290
|
-
const breakpoint = breakpoints[
|
|
386
|
+
const breakpoint = breakpoints[responsiveVariant];
|
|
291
387
|
|
|
292
388
|
if (!breakpoint) {
|
|
293
389
|
this.eventBus.emit('generator:warning', {
|
|
294
|
-
warning: `Breakpoint not found for variant: ${
|
|
390
|
+
warning: `Breakpoint not found for variant: ${responsiveVariant}`,
|
|
295
391
|
});
|
|
296
392
|
return cssRule; // 如果没有找到断点,返回原始规则
|
|
297
393
|
}
|
|
@@ -355,15 +451,15 @@ class DynamicClassGenerator {
|
|
|
355
451
|
}
|
|
356
452
|
|
|
357
453
|
let str = '';
|
|
358
|
-
const
|
|
454
|
+
const baseClassNameMap = this.configManager.getBaseClassNameMap();
|
|
359
455
|
const cssWrite = new Set(); // 临时防重复集合
|
|
360
456
|
|
|
361
457
|
this.eventBus.emit('generator:userBase:started', { classCount: arr.length });
|
|
362
458
|
|
|
363
459
|
arr.forEach((item, index) => {
|
|
364
460
|
try {
|
|
365
|
-
// item
|
|
366
|
-
const [className, value, originalClassName, isImportant,
|
|
461
|
+
// item 现在是 [className, value, originalClassName, isImportant, responsiveVariant, stateVariants]
|
|
462
|
+
const [className, value, originalClassName, isImportant, responsiveVariant, stateVariants = []] = item;
|
|
367
463
|
const classKey = originalClassName;
|
|
368
464
|
|
|
369
465
|
if (cssWrite.has(classKey)) {
|
|
@@ -374,9 +470,9 @@ class DynamicClassGenerator {
|
|
|
374
470
|
return;
|
|
375
471
|
}
|
|
376
472
|
|
|
377
|
-
const
|
|
378
|
-
|
|
379
|
-
if (
|
|
473
|
+
const baseDef = baseClassNameMap ? baseClassNameMap.get(className) : undefined;
|
|
474
|
+
// userBaseClass 应该是 object 类型(如颜色族:{ ABBR: 'color', red: '#f00', ... })
|
|
475
|
+
if (!baseDef || typeof baseDef !== 'object' || Array.isArray(baseDef)) {
|
|
380
476
|
this.eventBus.emit('generator:userBase:skipped', {
|
|
381
477
|
className: originalClassName,
|
|
382
478
|
reason: 'not_found_in_config',
|
|
@@ -388,22 +484,36 @@ class DynamicClassGenerator {
|
|
|
388
484
|
const cssClassName = className.replaceAll('_', '-');
|
|
389
485
|
|
|
390
486
|
let cssRule = '';
|
|
391
|
-
if (this.isArray(
|
|
487
|
+
if (this.isArray(baseDef)) {
|
|
392
488
|
const cssValue = isImportant ? `${value} !important` : value;
|
|
393
|
-
// 使用格式化器格式化CSS
|
|
394
|
-
cssRule = this.cssFormatter.formatRule(originalClassName, `${cssClassName}: ${cssValue}
|
|
395
|
-
} else if (this.isObject(
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
489
|
+
// 使用格式化器格式化CSS(传入状态变体)
|
|
490
|
+
cssRule = this.cssFormatter.formatRule(originalClassName, `${cssClassName}: ${cssValue}`, null, stateVariants);
|
|
491
|
+
} else if (this.isObject(baseDef)) {
|
|
492
|
+
// 优先使用映射值(如果存在)
|
|
493
|
+
if (baseDef[value] !== undefined) {
|
|
494
|
+
const cssValue = isImportant
|
|
495
|
+
? `${baseDef[value]} !important`
|
|
496
|
+
: baseDef[value];
|
|
497
|
+
const propertyName = baseDef['ABBR'] || cssClassName;
|
|
498
|
+
// 使用格式化器格式化CSS(传入状态变体)
|
|
499
|
+
cssRule = this.cssFormatter.formatRule(originalClassName, `${propertyName}: ${cssValue}`, null, stateVariants);
|
|
500
|
+
} else if (baseDef['ABBR']) {
|
|
501
|
+
// 映射不存在,但有 ABBR,尝试解析为直接颜色值
|
|
502
|
+
const parsedColor = this.parseColorValue(value);
|
|
503
|
+
if (parsedColor) {
|
|
504
|
+
const cssValue = isImportant
|
|
505
|
+
? `${parsedColor} !important`
|
|
506
|
+
: parsedColor;
|
|
507
|
+
const propertyName = baseDef['ABBR'];
|
|
508
|
+
// 使用格式化器格式化CSS(传入状态变体)
|
|
509
|
+
cssRule = this.cssFormatter.formatRule(originalClassName, `${propertyName}: ${cssValue}`, null, stateVariants);
|
|
510
|
+
}
|
|
511
|
+
}
|
|
402
512
|
}
|
|
403
513
|
|
|
404
514
|
// 如果有响应式变体,用 @media 包裹
|
|
405
|
-
if (
|
|
406
|
-
cssRule = this.wrapWithMediaQuery(cssRule,
|
|
515
|
+
if (responsiveVariant && cssRule) {
|
|
516
|
+
cssRule = this.wrapWithMediaQuery(cssRule, responsiveVariant);
|
|
407
517
|
}
|
|
408
518
|
|
|
409
519
|
str += cssRule;
|
|
@@ -411,7 +521,8 @@ class DynamicClassGenerator {
|
|
|
411
521
|
this.eventBus.emit('generator:userBase:generated', {
|
|
412
522
|
className: originalClassName,
|
|
413
523
|
isImportant,
|
|
414
|
-
|
|
524
|
+
responsiveVariant,
|
|
525
|
+
stateVariants,
|
|
415
526
|
});
|
|
416
527
|
} catch (error) {
|
|
417
528
|
this.eventBus.emit('generator:userBase:error', {
|
|
@@ -438,6 +549,90 @@ class DynamicClassGenerator {
|
|
|
438
549
|
return Object.prototype.toString.call(obj) === '[object Array]';
|
|
439
550
|
}
|
|
440
551
|
|
|
552
|
+
// 解析颜色值(支持 hex、rgb、rgba)
|
|
553
|
+
// 返回解析后的 CSS 颜色值字符串,失败返回 null
|
|
554
|
+
parseColorValue(value) {
|
|
555
|
+
if (!value || typeof value !== 'string') {
|
|
556
|
+
return null;
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
// Hex 格式:hex-fff, hex-112233, hex-ffffffff
|
|
560
|
+
if (value.startsWith('hex-')) {
|
|
561
|
+
const hexValue = value.substring(4);
|
|
562
|
+
// 验证 hex 值:3、4、6、8 位十六进制数字
|
|
563
|
+
if (/^[0-9a-fA-F]{3}$/.test(hexValue)) {
|
|
564
|
+
return `#${hexValue}`;
|
|
565
|
+
}
|
|
566
|
+
if (/^[0-9a-fA-F]{4}$/.test(hexValue)) {
|
|
567
|
+
return `#${hexValue}`;
|
|
568
|
+
}
|
|
569
|
+
if (/^[0-9a-fA-F]{6}$/.test(hexValue)) {
|
|
570
|
+
return `#${hexValue}`;
|
|
571
|
+
}
|
|
572
|
+
if (/^[0-9a-fA-F]{8}$/.test(hexValue)) {
|
|
573
|
+
return `#${hexValue}`;
|
|
574
|
+
}
|
|
575
|
+
return null;
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
// RGB 格式:rgb-255-0-0
|
|
579
|
+
if (value.startsWith('rgb-')) {
|
|
580
|
+
const parts = value.substring(4).split('-');
|
|
581
|
+
if (parts.length === 3) {
|
|
582
|
+
const r = parseInt(parts[0], 10);
|
|
583
|
+
const g = parseInt(parts[1], 10);
|
|
584
|
+
const b = parseInt(parts[2], 10);
|
|
585
|
+
if (
|
|
586
|
+
!isNaN(r) && r >= 0 && r <= 255 &&
|
|
587
|
+
!isNaN(g) && g >= 0 && g <= 255 &&
|
|
588
|
+
!isNaN(b) && b >= 0 && b <= 255
|
|
589
|
+
) {
|
|
590
|
+
return `rgb(${r}, ${g}, ${b})`;
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
return null;
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
// RGBA 格式:rgba-255-0-0-05 或 rgba-255-0-0-0_5
|
|
597
|
+
if (value.startsWith('rgba-')) {
|
|
598
|
+
const parts = value.substring(5).split('-');
|
|
599
|
+
if (parts.length === 4) {
|
|
600
|
+
const r = parseInt(parts[0], 10);
|
|
601
|
+
const g = parseInt(parts[1], 10);
|
|
602
|
+
const b = parseInt(parts[2], 10);
|
|
603
|
+
// 解析 alpha:支持 05 (0.5) 或 0_5 (0.5) 格式
|
|
604
|
+
let alpha = parts[3];
|
|
605
|
+
let alphaNum;
|
|
606
|
+
|
|
607
|
+
// 如果包含下划线,替换为点
|
|
608
|
+
if (alpha.includes('_')) {
|
|
609
|
+
alpha = alpha.replace('_', '.');
|
|
610
|
+
alphaNum = parseFloat(alpha);
|
|
611
|
+
} else if (/^\d{2}$/.test(alpha) && alpha.length === 2) {
|
|
612
|
+
// 两位数字格式:
|
|
613
|
+
// - 如果 < 10(如 05, 08),除以 10 得到 0.5, 0.8
|
|
614
|
+
// - 如果 >= 10(如 50, 99),除以 100 得到 0.5, 0.99(作为百分比)
|
|
615
|
+
const num = parseInt(alpha, 10);
|
|
616
|
+
alphaNum = num < 10 ? num / 10 : num / 100;
|
|
617
|
+
} else {
|
|
618
|
+
// 其他格式(如 0.5, 1 等)直接解析
|
|
619
|
+
alphaNum = parseFloat(alpha);
|
|
620
|
+
}
|
|
621
|
+
if (
|
|
622
|
+
!isNaN(r) && r >= 0 && r <= 255 &&
|
|
623
|
+
!isNaN(g) && g >= 0 && g <= 255 &&
|
|
624
|
+
!isNaN(b) && b >= 0 && b <= 255 &&
|
|
625
|
+
!isNaN(alphaNum) && alphaNum >= 0 && alphaNum <= 1
|
|
626
|
+
) {
|
|
627
|
+
return `rgba(${r}, ${g}, ${b}, ${alphaNum})`;
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
return null;
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
return null;
|
|
634
|
+
}
|
|
635
|
+
|
|
441
636
|
// 验证CSS生成结果
|
|
442
637
|
validateGeneratedCSS(cssStr) {
|
|
443
638
|
const errors = [];
|
|
@@ -542,6 +737,9 @@ class DynamicClassGenerator {
|
|
|
542
737
|
// 清空缓存,因为配置可能已更改
|
|
543
738
|
this.clearCache();
|
|
544
739
|
|
|
740
|
+
// 配置更新后刷新前缀缓存
|
|
741
|
+
this.refreshPrefixCache();
|
|
742
|
+
|
|
545
743
|
this.eventBus.emit('generator:config:updated', {
|
|
546
744
|
timestamp: Date.now(),
|
|
547
745
|
hasUnitProcessor: !!this.unitProcessor,
|