dolphincss 1.3.6 → 1.3.7
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 +38 -0
- package/core-templates/dolphin-file-tree.html +152 -0
- package/dist/assets/css-DgFYAglH.css +1 -0
- package/dist/assets/main-DcIhIwoX.css +1 -0
- package/dist/index.html +1 -1
- package/dolphin-css.css +2 -2
- package/marker.json +4 -0
- package/package.json +5 -3
- package/src/ub-vanilla.js +1077 -0
- package/dist/assets/main-CQGewVI6.css +0 -1
|
@@ -0,0 +1,1077 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
const isWeb = typeof document !== 'undefined';
|
|
4
|
+
const SCALE_MAX = 255;
|
|
5
|
+
const PX_MULTIPLIER = 4;
|
|
6
|
+
const BORDER_MULTIPLIER = 1;
|
|
7
|
+
const GAP_MULTIPLIER = 4;
|
|
8
|
+
const SIZE_MULTIPLIER = 4;
|
|
9
|
+
|
|
10
|
+
const BASE_COLORS = {
|
|
11
|
+
red: [0.62, 0.28, 25],
|
|
12
|
+
blue: [0.68, 0.24, 260],
|
|
13
|
+
green: [0.67, 0.22, 145],
|
|
14
|
+
purple: [0.65, 0.22, 310],
|
|
15
|
+
orange: [0.78, 0.22, 60],
|
|
16
|
+
pink: [0.78, 0.24, 350],
|
|
17
|
+
teal: [0.70, 0.18, 180],
|
|
18
|
+
amber: [0.84, 0.18, 80],
|
|
19
|
+
gray: [0.88, 0.04, 240],
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const BREAKPOINTS = {
|
|
23
|
+
sm: 640,
|
|
24
|
+
md: 768,
|
|
25
|
+
lg: 1024,
|
|
26
|
+
xl: 1280,
|
|
27
|
+
'2xl': 1536,
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const SPACING_MAP = {
|
|
31
|
+
p: 'padding', pt: 'padding-top', pb: 'padding-bottom',
|
|
32
|
+
pl: 'padding-left', pr: 'padding-right',
|
|
33
|
+
m: 'margin', mt: 'margin-top', mb: 'margin-bottom',
|
|
34
|
+
ml: 'margin-left', mr: 'margin-right',
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const BORDER_SIDE_MAP = {
|
|
38
|
+
t: 'top', r: 'right', b: 'bottom', l: 'left'
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const FLEX_MAP = {
|
|
42
|
+
'flex-left': ['display: flex;', 'justify-content: flex-start;', 'align-items: center;'],
|
|
43
|
+
'flex-right': ['display: flex;', 'justify-content: flex-end;', 'align-items: center;'],
|
|
44
|
+
'flex-center': ['display: flex;', 'justify-content: center;', 'align-items: center;'],
|
|
45
|
+
'flex-between': ['display: flex;', 'justify-content: space-between;', 'align-items: center;'],
|
|
46
|
+
'flex-around': ['display: flex;', 'justify-content: space-around;', 'align-items: center;'],
|
|
47
|
+
'flex-evenly': ['display: flex;', 'justify-content: space-evenly;', 'align-items: center;'],
|
|
48
|
+
'flex-start': ['display: flex;', 'justify-content: flex-start;', 'align-items: flex-start;'],
|
|
49
|
+
'flex-end': ['display: flex;', 'justify-content: flex-end;', 'align-items: flex-end;'],
|
|
50
|
+
'flex-stretch': ['display: flex;', 'justify-content: center;', 'align-items: stretch;'],
|
|
51
|
+
'flexcol-left': ['display: flex;', 'flex-direction: column;', 'justify-content: flex-start;', 'align-items: flex-start;'],
|
|
52
|
+
'flexcol-right': ['display: flex;', 'flex-direction: column;', 'justify-content: flex-start;', 'align-items: flex-end;'],
|
|
53
|
+
'flexcol-center': ['display: flex;', 'flex-direction: column;', 'justify-content: center;', 'align-items: center;'],
|
|
54
|
+
'flexcol-between': ['display: flex;', 'flex-direction: column;', 'justify-content: space-between;', 'align-items: center;'],
|
|
55
|
+
'flexcol-start': ['display: flex;', 'flex-direction: column;', 'justify-content: flex-start;', 'align-items: flex-start;'],
|
|
56
|
+
'flexcol-end': ['display: flex;', 'flex-direction: column;', 'justify-content: flex-end;', 'align-items: flex-end;'],
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const BUTTON_STYLES = {
|
|
60
|
+
'btn': [
|
|
61
|
+
'display: inline-flex;',
|
|
62
|
+
'align-items: center;',
|
|
63
|
+
'justify-content: center;',
|
|
64
|
+
'padding: 10px 20px;',
|
|
65
|
+
'font-size: 14px;',
|
|
66
|
+
'font-weight: 500;',
|
|
67
|
+
'border-radius: 8px;',
|
|
68
|
+
'cursor: pointer;',
|
|
69
|
+
'transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);',
|
|
70
|
+
'border: none;',
|
|
71
|
+
'outline: none;',
|
|
72
|
+
'gap: 8px;',
|
|
73
|
+
'position: relative;',
|
|
74
|
+
'overflow: hidden;',
|
|
75
|
+
'transform: translateY(0);',
|
|
76
|
+
'box-shadow: 0 2px 4px rgba(0,0,0,0.1);',
|
|
77
|
+
'&:active { transform: translateY(1px); }',
|
|
78
|
+
'&:disabled { opacity: 0.5; cursor: not-allowed; transform: none; }'
|
|
79
|
+
],
|
|
80
|
+
'btn-sm': [
|
|
81
|
+
'padding: 6px 12px;',
|
|
82
|
+
'font-size: 12px;',
|
|
83
|
+
'border-radius: 6px;'
|
|
84
|
+
],
|
|
85
|
+
'btn-md': [
|
|
86
|
+
'padding: 10px 20px;',
|
|
87
|
+
'font-size: 14px;',
|
|
88
|
+
'border-radius: 8px;'
|
|
89
|
+
],
|
|
90
|
+
'btn-lg': [
|
|
91
|
+
'padding: 14px 28px;',
|
|
92
|
+
'font-size: 16px;',
|
|
93
|
+
'border-radius: 10px;'
|
|
94
|
+
],
|
|
95
|
+
'btn-primary': [
|
|
96
|
+
'background: linear-gradient(135deg, #3b82f6, #2563eb);',
|
|
97
|
+
'color: white;',
|
|
98
|
+
'&:hover { background: linear-gradient(135deg, #2563eb, #1d4ed8); transform: translateY(-2px); box-shadow: 0 4px 12px rgba(59,130,246,0.3); }',
|
|
99
|
+
'&:active { transform: translateY(0); }'
|
|
100
|
+
],
|
|
101
|
+
'btn-secondary': [
|
|
102
|
+
'background: linear-gradient(135deg, #6b7280, #4b5563);',
|
|
103
|
+
'color: white;',
|
|
104
|
+
'&:hover { background: linear-gradient(135deg, #4b5563, #374151); transform: translateY(-2px); box-shadow: 0 4px 12px rgba(107,114,128,0.3); }',
|
|
105
|
+
'&:active { transform: translateY(0); }'
|
|
106
|
+
],
|
|
107
|
+
'btn-success': [
|
|
108
|
+
'background: linear-gradient(135deg, #10b981, #059669);',
|
|
109
|
+
'color: white;',
|
|
110
|
+
'&:hover { background: linear-gradient(135deg, #059669, #047857); transform: translateY(-2px); box-shadow: 0 4px 12px rgba(16,185,129,0.3); }',
|
|
111
|
+
'&:active { transform: translateY(0); }'
|
|
112
|
+
],
|
|
113
|
+
'btn-danger': [
|
|
114
|
+
'background: linear-gradient(135deg, #ef4444, #dc2626);',
|
|
115
|
+
'color: white;',
|
|
116
|
+
'&:hover { background: linear-gradient(135deg, #dc2626, #b91c1c); transform: translateY(-2px); box-shadow: 0 4px 12px rgba(239,68,68,0.3); }',
|
|
117
|
+
'&:active { transform: translateY(0); }'
|
|
118
|
+
],
|
|
119
|
+
'btn-warning': [
|
|
120
|
+
'background: linear-gradient(135deg, #f59e0b, #d97706);',
|
|
121
|
+
'color: white;',
|
|
122
|
+
'&:hover { background: linear-gradient(135deg, #d97706, #b45309); transform: translateY(-2px); box-shadow: 0 4px 12px rgba(245,158,11,0.3); }',
|
|
123
|
+
'&:active { transform: translateY(0); }'
|
|
124
|
+
],
|
|
125
|
+
'btn-outline': [
|
|
126
|
+
'background: transparent;',
|
|
127
|
+
'border: 2px solid;',
|
|
128
|
+
'transition: all 0.3s ease;',
|
|
129
|
+
'&:hover { transform: translateY(-2px); }',
|
|
130
|
+
'&:active { transform: translateY(0); }'
|
|
131
|
+
],
|
|
132
|
+
'btn-ghost': [
|
|
133
|
+
'background: transparent;',
|
|
134
|
+
'box-shadow: none;',
|
|
135
|
+
'&:hover { background: rgba(0,0,0,0.05); transform: translateY(-1px); }',
|
|
136
|
+
'&:active { transform: translateY(0); }'
|
|
137
|
+
],
|
|
138
|
+
'btn-glow': [
|
|
139
|
+
'animation: btn-glow-pulse 2s infinite;',
|
|
140
|
+
'&:hover { animation: none; transform: translateY(-2px); }'
|
|
141
|
+
]
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
const INPUT_STYLES = {
|
|
145
|
+
'input': [
|
|
146
|
+
'padding: 10px 14px;',
|
|
147
|
+
'font-size: 14px;',
|
|
148
|
+
'border: 2px solid #e2e8f0;',
|
|
149
|
+
'border-radius: 8px;',
|
|
150
|
+
'outline: none;',
|
|
151
|
+
'transition: all 0.3s ease;',
|
|
152
|
+
'width: 100%;',
|
|
153
|
+
'box-sizing: border-box;',
|
|
154
|
+
'background: white;',
|
|
155
|
+
'&:focus { border-color: #3b82f6; box-shadow: 0 0 0 3px rgba(59,130,246,0.1); transform: translateY(-1px); }',
|
|
156
|
+
'&:hover { border-color: #cbd5e1; }'
|
|
157
|
+
],
|
|
158
|
+
'input-sm': [
|
|
159
|
+
'padding: 6px 10px;',
|
|
160
|
+
'font-size: 12px;',
|
|
161
|
+
'border-radius: 6px;'
|
|
162
|
+
],
|
|
163
|
+
'input-md': [
|
|
164
|
+
'padding: 10px 14px;',
|
|
165
|
+
'font-size: 14px;',
|
|
166
|
+
'border-radius: 8px;'
|
|
167
|
+
],
|
|
168
|
+
'input-lg': [
|
|
169
|
+
'padding: 14px 18px;',
|
|
170
|
+
'font-size: 16px;',
|
|
171
|
+
'border-radius: 10px;'
|
|
172
|
+
],
|
|
173
|
+
'input-error': [
|
|
174
|
+
'border-color: #ef4444;',
|
|
175
|
+
'&:focus { border-color: #ef4444; box-shadow: 0 0 0 3px rgba(239,68,68,0.1); }'
|
|
176
|
+
],
|
|
177
|
+
'input-success': [
|
|
178
|
+
'border-color: #10b981;',
|
|
179
|
+
'&:focus { border-color: #10b981; box-shadow: 0 0 0 3px rgba(16,185,129,0.1); }'
|
|
180
|
+
]
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
const CARD_STYLES = {
|
|
184
|
+
'card': [
|
|
185
|
+
'background: white;',
|
|
186
|
+
'border-radius: 16px;',
|
|
187
|
+
'padding: 24px;',
|
|
188
|
+
'box-shadow: 0 4px 6px -1px rgba(0,0,0,0.1), 0 2px 4px -1px rgba(0,0,0,0.06);',
|
|
189
|
+
'transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);',
|
|
190
|
+
'border: 1px solid rgba(0,0,0,0.05);'
|
|
191
|
+
],
|
|
192
|
+
'card-hover': [
|
|
193
|
+
'&:hover { transform: translateY(-4px); box-shadow: 0 20px 25px -5px rgba(0,0,0,0.1), 0 10px 10px -5px rgba(0,0,0,0.04); }'
|
|
194
|
+
],
|
|
195
|
+
'card-click': [
|
|
196
|
+
'cursor: pointer;',
|
|
197
|
+
'&:active { transform: translateY(0); }'
|
|
198
|
+
],
|
|
199
|
+
'card-glass': [
|
|
200
|
+
'background: rgba(255,255,255,0.9);',
|
|
201
|
+
'backdrop-filter: blur(10px);',
|
|
202
|
+
'border: 1px solid rgba(255,255,255,0.2);'
|
|
203
|
+
]
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
const GLOW_KEYFRAME = `
|
|
207
|
+
@keyframes btn-glow-pulse {
|
|
208
|
+
0%, 100% { box-shadow: 0 0 5px rgba(59,130,246,0.5); }
|
|
209
|
+
50% { box-shadow: 0 0 20px rgba(59,130,246,0.8); }
|
|
210
|
+
}
|
|
211
|
+
`;
|
|
212
|
+
|
|
213
|
+
const safeToString = (value) => {
|
|
214
|
+
if (value === null || value === undefined) return '';
|
|
215
|
+
if (typeof value === 'string') return value;
|
|
216
|
+
if (typeof value === 'number') return value.toString();
|
|
217
|
+
if (typeof value === 'boolean') return value ? 'true' : 'false';
|
|
218
|
+
try { return String(value); } catch { return ''; }
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
const safeParseFloat = (value) => {
|
|
222
|
+
if (typeof value === 'number' && !isNaN(value)) return Math.max(0, value);
|
|
223
|
+
const str = safeToString(value);
|
|
224
|
+
const num = parseFloat(str);
|
|
225
|
+
return isNaN(num) ? 0 : Math.max(0, num);
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
const safeParseInt = (value, defaultValue = 0) => {
|
|
229
|
+
if (typeof value === 'number' && !isNaN(value)) return Math.max(0, Math.floor(value));
|
|
230
|
+
const str = safeToString(value);
|
|
231
|
+
const num = parseInt(str, 10);
|
|
232
|
+
return isNaN(num) ? defaultValue : Math.max(0, num);
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
const safeClamp = (value, min, max) =>
|
|
236
|
+
Math.min(max, Math.max(min, value));
|
|
237
|
+
|
|
238
|
+
const px = (n) => `${n * PX_MULTIPLIER}px`;
|
|
239
|
+
const borderPx = (n) => `${n * BORDER_MULTIPLIER}px`;
|
|
240
|
+
const gapPx = (n) => `${n * GAP_MULTIPLIER}px`;
|
|
241
|
+
const sizePx = (n) => `${n * SIZE_MULTIPLIER}px`;
|
|
242
|
+
|
|
243
|
+
const parseNumber = (str) => safeParseFloat(str);
|
|
244
|
+
const parseFloatShade = (str) => safeClamp(safeParseFloat(str), 0, 255) || 128;
|
|
245
|
+
|
|
246
|
+
const simpleHash = (str) => {
|
|
247
|
+
let h1 = 0xdeadbeef, h2 = 0x41c6ce57, h3 = 0x9e3779b9, h4 = 0x85ebca6b;
|
|
248
|
+
for (let i = 0; i < str.length; i++) {
|
|
249
|
+
const ch = str.charCodeAt(i);
|
|
250
|
+
h1 = Math.imul(h1 ^ ch, 0x85ebca6b);
|
|
251
|
+
h2 = Math.imul(h2 ^ ch, 0xc2b2ae35);
|
|
252
|
+
h3 = Math.imul(h3 ^ ch, 0x9e3779b9);
|
|
253
|
+
h4 = Math.imul(h4 ^ ch, 0x1b873593);
|
|
254
|
+
h1 ^= h2 ^ h3 ^ h4;
|
|
255
|
+
h2 ^= h1; h3 ^= h1; h4 ^= h1;
|
|
256
|
+
}
|
|
257
|
+
h1 = Math.imul(h1 ^ (h1 >>> 16), 0x85ebca6b);
|
|
258
|
+
h2 = Math.imul(h2 ^ (h2 >>> 16), 0xc2b2ae35);
|
|
259
|
+
h3 = Math.imul(h3 ^ (h3 >>> 16), 0x9e3779b9);
|
|
260
|
+
h4 = Math.imul(h4 ^ (h4 >>> 16), 0x1b873593);
|
|
261
|
+
h1 ^= h2 ^ h3 ^ h4;
|
|
262
|
+
return Math.abs(h1).toString(36).substring(0, 12);
|
|
263
|
+
};
|
|
264
|
+
|
|
265
|
+
const applyOpacity = (color, opacity) => {
|
|
266
|
+
if (opacity === undefined) return color;
|
|
267
|
+
const opacityValue = safeClamp(opacity, 0, 1);
|
|
268
|
+
if (color.startsWith('oklch(')) {
|
|
269
|
+
return color.replace('oklch(', 'oklch(').replace(')', ` / ${opacityValue})`);
|
|
270
|
+
}
|
|
271
|
+
return color;
|
|
272
|
+
};
|
|
273
|
+
|
|
274
|
+
const parseOKLCH = (oklchColor) => {
|
|
275
|
+
const match = oklchColor.match(/oklch\(([\d.]+)\s+([\d.]+)\s+([\d.]+)(?:\s*\/\s*[\d.]+)?\)/);
|
|
276
|
+
if (!match) return null;
|
|
277
|
+
return { L: parseFloat(match[1]), C: parseFloat(match[2]), H: parseFloat(match[3]) };
|
|
278
|
+
};
|
|
279
|
+
|
|
280
|
+
class LRUCache {
|
|
281
|
+
cache = new Map();
|
|
282
|
+
maxSize;
|
|
283
|
+
|
|
284
|
+
constructor(maxSize = 1000) {
|
|
285
|
+
this.maxSize = maxSize;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
get(key) {
|
|
289
|
+
const value = this.cache.get(key);
|
|
290
|
+
if (value !== undefined) {
|
|
291
|
+
this.cache.delete(key);
|
|
292
|
+
this.cache.set(key, value);
|
|
293
|
+
}
|
|
294
|
+
return value;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
set(key, value) {
|
|
298
|
+
if (this.cache.size >= this.maxSize) {
|
|
299
|
+
const firstKey = this.cache.keys().next().value;
|
|
300
|
+
if (firstKey) this.cache.delete(firstKey);
|
|
301
|
+
}
|
|
302
|
+
this.cache.set(key, value);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
clear() { this.cache.clear(); }
|
|
306
|
+
get size() { return this.cache.size; }
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
const COLOR_CACHE = new LRUCache(500);
|
|
310
|
+
|
|
311
|
+
const getOKLCH = (name, shade, darkMode) => {
|
|
312
|
+
const safeShade = safeClamp(shade, 0, 255);
|
|
313
|
+
const key = `${name}-${safeShade.toFixed(2)}-${darkMode}`;
|
|
314
|
+
const cached = COLOR_CACHE.get(key);
|
|
315
|
+
if (cached) return cached;
|
|
316
|
+
|
|
317
|
+
const baseColor = BASE_COLORS[name] || BASE_COLORS.gray;
|
|
318
|
+
const [baseL, baseC, H] = baseColor;
|
|
319
|
+
const t = safeShade / SCALE_MAX;
|
|
320
|
+
|
|
321
|
+
let L, C;
|
|
322
|
+
|
|
323
|
+
if (name === 'gray') {
|
|
324
|
+
L = 0.98 - (t * 0.90);
|
|
325
|
+
C = 0.04 + (t * 0.08);
|
|
326
|
+
} else {
|
|
327
|
+
L = 0.92 - (t * 0.77);
|
|
328
|
+
const chromaMap = {
|
|
329
|
+
'blue': 0.20 + (t * 0.14), 'purple': 0.20 + (t * 0.14),
|
|
330
|
+
'red': 0.22 + (t * 0.12), 'orange': 0.22 + (t * 0.12),
|
|
331
|
+
'green': 0.18 + (t * 0.14), 'teal': 0.18 + (t * 0.14),
|
|
332
|
+
'pink': 0.20 + (t * 0.12), 'amber': 0.20 + (t * 0.12)
|
|
333
|
+
};
|
|
334
|
+
C = chromaMap[name] || 0.16 + (t * 0.16);
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
if (darkMode) {
|
|
338
|
+
L = L * 0.9 + 0.05;
|
|
339
|
+
C = C * 0.95;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
L = safeClamp(L, 0.05, 0.98);
|
|
343
|
+
C = safeClamp(C, 0.03, 0.35);
|
|
344
|
+
|
|
345
|
+
const result = `oklch(${L.toFixed(3)} ${C.toFixed(3)} ${H})`;
|
|
346
|
+
COLOR_CACHE.set(key, result);
|
|
347
|
+
return result;
|
|
348
|
+
};
|
|
349
|
+
|
|
350
|
+
const getTextColorForBg = (oklchColor) => {
|
|
351
|
+
const match = oklchColor.match(/oklch\(([\d.]+)\s+([\d.]+)\s+([\d.]+)(?:\s*\/\s*[\d.]+)?\)/);
|
|
352
|
+
if (!match) return 'oklch(0 0 0)';
|
|
353
|
+
const L = parseFloat(match[1]), C = parseFloat(match[2]), H = parseFloat(match[3]);
|
|
354
|
+
if (H >= 220 && H <= 260 && C < 0.1) return L > 0.62 ? `oklch(0.10 0.01 ${H})` : `oklch(0.99 0.005 ${H})`;
|
|
355
|
+
let threshold = 0.5;
|
|
356
|
+
if (H >= 70 && H <= 180) threshold = 0.42;
|
|
357
|
+
else if (H >= 220 && H <= 320) threshold = 0.58;
|
|
358
|
+
else if ((H >= 0 && H <= 40) || (H >= 340 && H <= 360)) threshold = 0.52;
|
|
359
|
+
else if (H >= 50 && H <= 90) threshold = 0.4;
|
|
360
|
+
return L > threshold ? `oklch(0.10 0.01 ${H})` : `oklch(0.99 0.005 ${H})`;
|
|
361
|
+
};
|
|
362
|
+
|
|
363
|
+
const getTextColorForGradient = (colors) => {
|
|
364
|
+
let totalLightness = 0, hueSum = 0;
|
|
365
|
+
for (const color of colors) {
|
|
366
|
+
const parsed = parseOKLCH(color);
|
|
367
|
+
if (parsed) { totalLightness += parsed.L; hueSum += parsed.H; }
|
|
368
|
+
}
|
|
369
|
+
const avgLightness = totalLightness / colors.length;
|
|
370
|
+
const avgHue = hueSum / colors.length;
|
|
371
|
+
let threshold = 0.55;
|
|
372
|
+
if (avgHue >= 70 && avgHue <= 180) threshold = 0.48;
|
|
373
|
+
else if (avgHue >= 220 && avgHue <= 320) threshold = 0.62;
|
|
374
|
+
else if ((avgHue >= 0 && avgHue <= 40) || (avgHue >= 340 && avgHue <= 360)) threshold = 0.58;
|
|
375
|
+
else if (avgHue >= 50 && avgHue <= 90) threshold = 0.52;
|
|
376
|
+
return avgLightness > threshold ? `oklch(0.10 0.01 ${avgHue})` : `oklch(0.99 0.005 ${avgHue})`;
|
|
377
|
+
};
|
|
378
|
+
|
|
379
|
+
const roundedToRem = (value) => {
|
|
380
|
+
const roundedMap = {
|
|
381
|
+
0: '0', 1: '0.25rem', 2: '0.5rem', 3: '0.75rem',
|
|
382
|
+
4: '1rem', 5: '1.25rem', 6: '1.5rem', 8: '2rem',
|
|
383
|
+
10: '2.5rem', 12: '3rem', 16: '4rem'
|
|
384
|
+
};
|
|
385
|
+
return roundedMap[value] || `${value * 0.25}rem`;
|
|
386
|
+
};
|
|
387
|
+
|
|
388
|
+
const SHADOW_SCALES = {
|
|
389
|
+
'1': '0 1px 2px 0 rgb(0 0 0 / 0.05)',
|
|
390
|
+
'2': '0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1)',
|
|
391
|
+
'3': '0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)',
|
|
392
|
+
'4': '0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1)',
|
|
393
|
+
'5': '0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1)',
|
|
394
|
+
'6': '0 25px 50px -12px rgb(0 0 0 / 0.25)',
|
|
395
|
+
'7': '0 35px 60px -15px rgb(0 0 0 / 0.3)',
|
|
396
|
+
'8': '0 45px 65px -15px rgb(0 0 0 / 0.35)',
|
|
397
|
+
'9': '0 50px 70px -15px rgb(0 0 0 / 0.4)',
|
|
398
|
+
'10': '0 60px 80px -20px rgb(0 0 0 / 0.45)',
|
|
399
|
+
};
|
|
400
|
+
|
|
401
|
+
class VirtualCSSMap {
|
|
402
|
+
styleSheet = null;
|
|
403
|
+
insertedRules = new Set();
|
|
404
|
+
pendingRules = [];
|
|
405
|
+
pendingFlush = false;
|
|
406
|
+
keyframeCache = new Set();
|
|
407
|
+
|
|
408
|
+
constructor() {
|
|
409
|
+
if (!isWeb) return;
|
|
410
|
+
try {
|
|
411
|
+
this.styleSheet = new CSSStyleSheet();
|
|
412
|
+
document.adoptedStyleSheets = [...document.adoptedStyleSheets, this.styleSheet];
|
|
413
|
+
} catch (e) {
|
|
414
|
+
const styleEl = document.createElement('style');
|
|
415
|
+
styleEl.setAttribute('data-ub', 'v19.0.3');
|
|
416
|
+
document.head.appendChild(styleEl);
|
|
417
|
+
this.styleSheet = styleEl.sheet;
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
add(className, rules, pseudo, media, child) {
|
|
422
|
+
const selector = child ? `.${className} > ${child}` : pseudo ? `.${className}:${pseudo}` : `.${className}`;
|
|
423
|
+
const ruleText = `${selector} { ${rules.join(' ')} }`;
|
|
424
|
+
const finalRule = media ? `${media} { ${ruleText} }` : ruleText;
|
|
425
|
+
if (this.insertedRules.has(finalRule)) return;
|
|
426
|
+
this.insertedRules.add(finalRule);
|
|
427
|
+
this.pendingRules.push(finalRule);
|
|
428
|
+
this.scheduleFlush();
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
addKeyframe(keyframeName, keyframeRule) {
|
|
432
|
+
if (this.keyframeCache.has(keyframeName)) return;
|
|
433
|
+
this.keyframeCache.add(keyframeName);
|
|
434
|
+
this.pendingRules.push(keyframeRule);
|
|
435
|
+
this.scheduleFlush();
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
scheduleFlush() {
|
|
439
|
+
if (this.pendingFlush || !isWeb || !this.styleSheet) return;
|
|
440
|
+
this.pendingFlush = true;
|
|
441
|
+
queueMicrotask(() => { this.flush(); this.pendingFlush = false; });
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
flush() {
|
|
445
|
+
if (!this.styleSheet || this.pendingRules.length === 0) return;
|
|
446
|
+
for (const rule of this.pendingRules) {
|
|
447
|
+
try { this.styleSheet.insertRule(rule, this.styleSheet.cssRules.length); } catch (e) {}
|
|
448
|
+
}
|
|
449
|
+
this.pendingRules = [];
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
clear() {
|
|
453
|
+
this.insertedRules.clear();
|
|
454
|
+
this.keyframeCache.clear();
|
|
455
|
+
this.pendingRules = [];
|
|
456
|
+
if (this.styleSheet) { try { this.styleSheet.replaceSync(''); } catch (e) {} }
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
getStyleCount() { return this.insertedRules.size + this.keyframeCache.size; }
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
class WebStyleEngine {
|
|
463
|
+
static instance;
|
|
464
|
+
classCache = new LRUCache(2000);
|
|
465
|
+
virtualMap = new VirtualCSSMap();
|
|
466
|
+
darkMode = isWeb ? window.matchMedia('(prefers-color-scheme: dark)').matches : false;
|
|
467
|
+
darkModeListeners = new Set();
|
|
468
|
+
totalRequests = 0;
|
|
469
|
+
cacheHits = 0;
|
|
470
|
+
totalSegmentRequests = 0;
|
|
471
|
+
glowAdded = false;
|
|
472
|
+
|
|
473
|
+
static getInstance() {
|
|
474
|
+
if (!WebStyleEngine.instance) {
|
|
475
|
+
WebStyleEngine.instance = new WebStyleEngine();
|
|
476
|
+
if (isWeb) {
|
|
477
|
+
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
|
|
478
|
+
const handler = (e) => {
|
|
479
|
+
WebStyleEngine.instance.darkMode = 'matches' in e ? e.matches : e.matches;
|
|
480
|
+
WebStyleEngine.instance.classCache.clear();
|
|
481
|
+
WebStyleEngine.instance.virtualMap.clear();
|
|
482
|
+
COLOR_CACHE.clear();
|
|
483
|
+
WebStyleEngine.instance.darkModeListeners.forEach(fn => fn());
|
|
484
|
+
};
|
|
485
|
+
try { mediaQuery.addEventListener('change', handler); } catch { mediaQuery.addListener(handler); }
|
|
486
|
+
|
|
487
|
+
if (!WebStyleEngine.instance.glowAdded) {
|
|
488
|
+
WebStyleEngine.instance.virtualMap.addKeyframe('btn-glow-pulse', GLOW_KEYFRAME);
|
|
489
|
+
WebStyleEngine.instance.glowAdded = true;
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
return WebStyleEngine.instance;
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
inject(classes) {
|
|
497
|
+
if (!isWeb || !classes) return classes;
|
|
498
|
+
this.totalRequests++;
|
|
499
|
+
|
|
500
|
+
const results = [];
|
|
501
|
+
const parts = classes.split(/\s+/).filter(Boolean);
|
|
502
|
+
|
|
503
|
+
for (const cls of parts) {
|
|
504
|
+
if (cls.startsWith('ub-')) { results.push(cls); continue; }
|
|
505
|
+
|
|
506
|
+
this.totalSegmentRequests++;
|
|
507
|
+
|
|
508
|
+
const cached = this.classCache.get(cls);
|
|
509
|
+
if (cached) {
|
|
510
|
+
this.cacheHits++;
|
|
511
|
+
results.push(cached);
|
|
512
|
+
continue;
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
const segments = cls.split(':');
|
|
516
|
+
const name = segments.pop();
|
|
517
|
+
const variants = segments;
|
|
518
|
+
|
|
519
|
+
let pseudo;
|
|
520
|
+
let media;
|
|
521
|
+
|
|
522
|
+
for (const v of variants) {
|
|
523
|
+
if (v === 'hover' || v === 'active' || v === 'focus' || v === 'group-hover' || v === 'infinite') pseudo = v;
|
|
524
|
+
else if (v in BREAKPOINTS) media = `@media (min-width: ${BREAKPOINTS[v]}px)`;
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
const className = `ub-${simpleHash(cls)}`;
|
|
528
|
+
let rules = null;
|
|
529
|
+
|
|
530
|
+
if (BUTTON_STYLES[name]) {
|
|
531
|
+
rules = [...BUTTON_STYLES[name]];
|
|
532
|
+
if (this.darkMode && name === 'btn') {
|
|
533
|
+
rules = rules.map(r => r.replace('background: white;', 'background: #1f2937;').replace('color: #000;', 'color: #fff;'));
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
else if (INPUT_STYLES[name]) {
|
|
537
|
+
rules = [...INPUT_STYLES[name]];
|
|
538
|
+
if (this.darkMode && name === 'input') {
|
|
539
|
+
rules = rules.map(r => r.replace('background: white;', 'background: #1f2937;').replace('color: #000;', 'color: #fff;'));
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
else if (CARD_STYLES[name]) {
|
|
543
|
+
rules = [...CARD_STYLES[name]];
|
|
544
|
+
if (this.darkMode && name === 'card') {
|
|
545
|
+
rules = rules.map(r => r.replace('background: white;', 'background: #1f2937;'));
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
else {
|
|
549
|
+
const opacityMatch = name.match(/^opacity-(\d+)$/);
|
|
550
|
+
const borderFloatMatch = name.match(/^border(?:-(\d+(?:\.\d+)?))?$/);
|
|
551
|
+
const borderSideMatch = name.match(/^border-(t|r|b|l)-(\d+(?:\.\d+)?)$/);
|
|
552
|
+
const borderXMatch = name.match(/^border-x-(\d+(?:\.\d+)?)$/);
|
|
553
|
+
const borderYMatch = name.match(/^border-y-(\d+(?:\.\d+)?)$/);
|
|
554
|
+
const gridMatch = name.match(/^grid-(\d+)x(\d+)-(\d+(?:\.\d+)?)$/);
|
|
555
|
+
const autoGridMatch = name.match(/^auto-grid-(\d+(?:\.\d+)?)-(\d+(?:\.\d+)?)$/);
|
|
556
|
+
const autoLayoutMatch = name.match(/^auto-layout-(row|col|wrap)(?:-(left|right|center|between|around|evenly|stretch))?-(\d+(?:\.\d+)?)$/);
|
|
557
|
+
const spanMatch = name.match(/^span-(\d+)$/);
|
|
558
|
+
const rowMatch = name.match(/^row-(\d+)$/);
|
|
559
|
+
const roundedMatch = name.match(/^rounded(?:-(\d+(?:\.\d+)?|full))?$/);
|
|
560
|
+
const shadowMatch = name.match(/^shadow(?:-(\d+))?$/);
|
|
561
|
+
const sizeMatch = name.match(/^(w|h)-(\d+(?:\.\d+)?)$/);
|
|
562
|
+
const spacingMatch = name.match(/^(p|m|pl|pr|ml|mr|pt|pb|mt|mb)-(\d+(?:\.\d+)?)$/);
|
|
563
|
+
const scaleMatch = name.match(/^scale-(\d+(?:\.\d+)?)$/);
|
|
564
|
+
const bgFillMatch = name.match(/^bg-fill-(left|right|top|bottom)-([a-z]+)-(\d+(?:\.\d+)?)-(\d+(?:\.\d+)?)(ms|s)$/);
|
|
565
|
+
const gradientMatch = name.match(/^gradient-([a-z]+)-(\d+(?:\.\d+)?)-([a-z]+)-(\d+(?:\.\d+)?)$/);
|
|
566
|
+
const gradientWithAngleMatch = name.match(/^gradient-(\d+(?:\.\d+)?)deg-([a-z]+)-(\d+(?:\.\d+)?)-([a-z]+)-(\d+(?:\.\d+)?)$/);
|
|
567
|
+
const gradientVerticalMatch = name.match(/^gradient-vert-([a-z]+)-(\d+(?:\.\d+)?)-([a-z]+)-(\d+(?:\.\d+)?)$/);
|
|
568
|
+
const gradientHorizontalMatch = name.match(/^gradient-horiz-([a-z]+)-(\d+(?:\.\d+)?)-([a-z]+)-(\d+(?:\.\d+)?)$/);
|
|
569
|
+
const gradientRadialMatch = name.match(/^gradient-radial-([a-z]+)-(\d+(?:\.\d+)?)-([a-z]+)-(\d+(?:\.\d+)?)$/);
|
|
570
|
+
const gradientTripleMatch = name.match(/^gradient-([a-z]+)-(\d+(?:\.\d+)?)-([a-z]+)-(\d+(?:\.\d+)?)-([a-z]+)-(\d+(?:\.\d+)?)$/);
|
|
571
|
+
const colorAnimMatch = name.match(/^(bg|text)-([a-z]+)-(\d+(?:\.\d+)?)-(\d+(?:\.\d+)?)-(\d+(?:\.\d+)?)(ms|s)(?:-(infinite))?$/);
|
|
572
|
+
const propAnimMatch = name.match(/^([a-z]+(?:-[a-z]+)?)-(\d+(?:\.\d+)?)-(\d+(?:\.\d+)?)-(\d+(?:\.\d+)?)(ms|s)(?:-(infinite))?$/);
|
|
573
|
+
const isColor = name.startsWith('bg-') || name.startsWith('text-') || name.startsWith('border-');
|
|
574
|
+
|
|
575
|
+
if (FLEX_MAP[name]) {
|
|
576
|
+
rules = [...FLEX_MAP[name]];
|
|
577
|
+
}
|
|
578
|
+
else if (bgFillMatch) {
|
|
579
|
+
const [, direction, colorName, shade, duration, unit] = bgFillMatch;
|
|
580
|
+
const durationMs = unit === 's' ? parseFloat(duration) * 1000 : parseFloat(duration);
|
|
581
|
+
const color = getOKLCH(colorName, parseFloatShade(shade), this.darkMode);
|
|
582
|
+
|
|
583
|
+
const keyframeName = `ub-fill-${direction}-${colorName}-${shade}`;
|
|
584
|
+
let gradientDir = 'to right';
|
|
585
|
+
let fromPosition = '100%';
|
|
586
|
+
let toPosition = '0%';
|
|
587
|
+
|
|
588
|
+
if (direction === 'right') {
|
|
589
|
+
gradientDir = 'to left';
|
|
590
|
+
fromPosition = '0%';
|
|
591
|
+
toPosition = '100%';
|
|
592
|
+
} else if (direction === 'top') {
|
|
593
|
+
gradientDir = 'to bottom';
|
|
594
|
+
fromPosition = '100%';
|
|
595
|
+
toPosition = '0%';
|
|
596
|
+
} else if (direction === 'bottom') {
|
|
597
|
+
gradientDir = 'to top';
|
|
598
|
+
fromPosition = '0%';
|
|
599
|
+
toPosition = '100%';
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
const keyframeRule = `
|
|
603
|
+
@keyframes ${keyframeName} {
|
|
604
|
+
from { background-position: ${fromPosition}; }
|
|
605
|
+
to { background-position: ${toPosition}; }
|
|
606
|
+
}
|
|
607
|
+
`;
|
|
608
|
+
|
|
609
|
+
this.virtualMap.addKeyframe(keyframeName, keyframeRule);
|
|
610
|
+
const bgSize = direction === 'left' || direction === 'right' ? '200% 100%' : '100% 200%';
|
|
611
|
+
|
|
612
|
+
rules = [
|
|
613
|
+
`background-image: linear-gradient(${gradientDir}, ${color} 50%, transparent 50%) !important;`,
|
|
614
|
+
`background-size: ${bgSize} !important;`,
|
|
615
|
+
`background-repeat: no-repeat !important;`,
|
|
616
|
+
`animation: ${keyframeName} ${durationMs}ms ease-out forwards;`
|
|
617
|
+
];
|
|
618
|
+
}
|
|
619
|
+
else if (opacityMatch) {
|
|
620
|
+
const opacity = safeClamp(safeParseInt(opacityMatch[1], 100) / 100, 0, 1);
|
|
621
|
+
rules = [`opacity: ${opacity};`];
|
|
622
|
+
}
|
|
623
|
+
else if (scaleMatch) {
|
|
624
|
+
const scaleVal = safeClamp(safeParseInt(scaleMatch[1], 100) / 100, 0, 2);
|
|
625
|
+
rules = [`scale: ${scaleVal} !important;`];
|
|
626
|
+
if (pseudo !== 'hover') rules.push(`transition: scale 0.2s ease !important;`);
|
|
627
|
+
}
|
|
628
|
+
else if (borderFloatMatch) {
|
|
629
|
+
const width = borderFloatMatch[1] || '1';
|
|
630
|
+
rules = [`border-width: ${borderPx(parseNumber(width))};`, `border-style: solid;`];
|
|
631
|
+
}
|
|
632
|
+
else if (borderSideMatch) {
|
|
633
|
+
const [, side, width] = borderSideMatch;
|
|
634
|
+
rules = [`border-${BORDER_SIDE_MAP[side]}-width: ${borderPx(parseNumber(width))};`, `border-${BORDER_SIDE_MAP[side]}-style: solid;`];
|
|
635
|
+
}
|
|
636
|
+
else if (borderXMatch) {
|
|
637
|
+
const width = borderXMatch[1];
|
|
638
|
+
rules = [`border-left-width: ${borderPx(parseNumber(width))};`, `border-right-width: ${borderPx(parseNumber(width))};`, `border-left-style: solid;`, `border-right-style: solid;`];
|
|
639
|
+
}
|
|
640
|
+
else if (borderYMatch) {
|
|
641
|
+
const width = borderYMatch[1];
|
|
642
|
+
rules = [`border-top-width: ${borderPx(parseNumber(width))};`, `border-bottom-width: ${borderPx(parseNumber(width))};`, `border-top-style: solid;`, `border-bottom-style: solid;`];
|
|
643
|
+
}
|
|
644
|
+
else if (gridMatch) {
|
|
645
|
+
const [, cols, rows, gapScale] = gridMatch;
|
|
646
|
+
rules = [`display: grid;`, `grid-template-columns: repeat(${cols}, minmax(0, 1fr));`, `grid-template-rows: repeat(${rows}, auto);`, `gap: ${gapPx(parseNumber(gapScale))};`, `width: 100%;`];
|
|
647
|
+
}
|
|
648
|
+
else if (autoGridMatch) {
|
|
649
|
+
const [, minScale, gapScale] = autoGridMatch;
|
|
650
|
+
rules = [`display: grid;`, `grid-template-columns: repeat(auto-fit, minmax(${px(parseNumber(minScale))}, 1fr));`, `gap: ${gapPx(parseNumber(gapScale))};`, `width: 100%;`];
|
|
651
|
+
}
|
|
652
|
+
else if (autoLayoutMatch) {
|
|
653
|
+
const [, direction, alignment, gapScale] = autoLayoutMatch;
|
|
654
|
+
const flexDir = direction === 'col' ? 'column' : 'row';
|
|
655
|
+
const flexWrap = direction === 'wrap' ? 'wrap' : 'nowrap';
|
|
656
|
+
|
|
657
|
+
let justify = 'flex-start';
|
|
658
|
+
let align = direction === 'col' ? 'stretch' : 'center';
|
|
659
|
+
|
|
660
|
+
if (alignment === 'left') {
|
|
661
|
+
justify = 'flex-start';
|
|
662
|
+
align = direction === 'col' ? 'flex-start' : 'center';
|
|
663
|
+
} else if (alignment === 'right') {
|
|
664
|
+
justify = 'flex-end';
|
|
665
|
+
align = direction === 'col' ? 'flex-end' : 'center';
|
|
666
|
+
} else if (alignment === 'center') {
|
|
667
|
+
justify = 'center';
|
|
668
|
+
align = 'center';
|
|
669
|
+
} else if (alignment === 'between') {
|
|
670
|
+
justify = 'space-between';
|
|
671
|
+
align = 'center';
|
|
672
|
+
} else if (alignment === 'around') {
|
|
673
|
+
justify = 'space-around';
|
|
674
|
+
align = 'center';
|
|
675
|
+
} else if (alignment === 'evenly') {
|
|
676
|
+
justify = 'space-evenly';
|
|
677
|
+
align = 'center';
|
|
678
|
+
} else if (alignment === 'stretch') {
|
|
679
|
+
justify = 'stretch';
|
|
680
|
+
align = 'stretch';
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
rules = [
|
|
684
|
+
`display: flex;`,
|
|
685
|
+
`flex-direction: ${flexDir};`,
|
|
686
|
+
`flex-wrap: ${flexWrap};`,
|
|
687
|
+
`justify-content: ${justify};`,
|
|
688
|
+
`align-items: ${align};`,
|
|
689
|
+
`gap: ${gapPx(parseNumber(gapScale))};`,
|
|
690
|
+
`width: 100%;`
|
|
691
|
+
];
|
|
692
|
+
}
|
|
693
|
+
else if (spanMatch) { rules = [`grid-column: span ${safeParseInt(spanMatch[1], 1)};`]; }
|
|
694
|
+
else if (rowMatch) { rules = [`grid-row: span ${safeParseInt(rowMatch[1], 1)};`]; }
|
|
695
|
+
else if (name === 'full') { rules = [`grid-column: 1 / -1;`]; }
|
|
696
|
+
else if (roundedMatch) {
|
|
697
|
+
const scale = roundedMatch[1] || '2';
|
|
698
|
+
rules = [scale === 'full' ? `border-radius: 9999px;` : `border-radius: ${roundedToRem(parseNumber(scale))};`];
|
|
699
|
+
}
|
|
700
|
+
else if (shadowMatch) {
|
|
701
|
+
const scale = shadowMatch[1] || '3';
|
|
702
|
+
if (SHADOW_SCALES[scale]) rules = [`box-shadow: ${SHADOW_SCALES[scale]};`];
|
|
703
|
+
}
|
|
704
|
+
else if (sizeMatch) {
|
|
705
|
+
const [, prop, scaleStr] = sizeMatch;
|
|
706
|
+
rules = [`${prop === 'w' ? 'width' : 'height'}: ${sizePx(parseNumber(scaleStr))};`];
|
|
707
|
+
}
|
|
708
|
+
else if (colorAnimMatch) {
|
|
709
|
+
const [, type, colorName, fromShade, toShade, duration, unit, infiniteFlag] = colorAnimMatch;
|
|
710
|
+
const durationMs = unit === 's' ? parseFloat(duration) * 1000 : parseFloat(duration);
|
|
711
|
+
const fromShadeNum = safeClamp(parseFloatShade(fromShade), 0, 255);
|
|
712
|
+
const toShadeNum = safeClamp(parseFloatShade(toShade), 0, 255);
|
|
713
|
+
|
|
714
|
+
const cssProp = type === 'bg' ? 'background-color' : 'color';
|
|
715
|
+
const fromValue = getOKLCH(colorName, fromShadeNum, this.darkMode);
|
|
716
|
+
const toValue = getOKLCH(colorName, toShadeNum, this.darkMode);
|
|
717
|
+
|
|
718
|
+
const keyframeName = `ub-anim-${type}-${colorName}-${fromShade}-${toShade}`;
|
|
719
|
+
const isInfinite = infiniteFlag === 'infinite' || pseudo === 'infinite';
|
|
720
|
+
|
|
721
|
+
if (pseudo === 'hover') {
|
|
722
|
+
rules = [
|
|
723
|
+
`${cssProp}: ${fromValue};`,
|
|
724
|
+
`transition: ${cssProp} ${durationMs}ms ease-out;`,
|
|
725
|
+
`&:hover { ${cssProp}: ${toValue} !important; }`
|
|
726
|
+
];
|
|
727
|
+
}
|
|
728
|
+
else if (pseudo === 'active' || pseudo === 'click') {
|
|
729
|
+
const keyframeRule = `@keyframes ${keyframeName} { 0% { ${cssProp}: ${fromValue}; } 50% { ${cssProp}: ${toValue}; } 100% { ${cssProp}: ${fromValue}; } }`;
|
|
730
|
+
this.virtualMap.addKeyframe(keyframeName, keyframeRule);
|
|
731
|
+
rules = [`animation: ${keyframeName} ${durationMs}ms ease-out;`];
|
|
732
|
+
}
|
|
733
|
+
else if (isInfinite) {
|
|
734
|
+
const keyframeRule = `@keyframes ${keyframeName} { 0% { ${cssProp}: ${fromValue}; } 50% { ${cssProp}: ${toValue}; } 100% { ${cssProp}: ${fromValue}; } }`;
|
|
735
|
+
this.virtualMap.addKeyframe(keyframeName, keyframeRule);
|
|
736
|
+
rules = [`animation: ${keyframeName} ${durationMs}ms infinite ease-in-out;`];
|
|
737
|
+
}
|
|
738
|
+
else {
|
|
739
|
+
const keyframeRule = `@keyframes ${keyframeName} { 0% { ${cssProp}: ${fromValue}; } 100% { ${cssProp}: ${toValue}; } }`;
|
|
740
|
+
this.virtualMap.addKeyframe(keyframeName, keyframeRule);
|
|
741
|
+
rules = [`animation: ${keyframeName} ${durationMs}ms ease forwards;`];
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
else if (propAnimMatch && !colorAnimMatch) {
|
|
745
|
+
const [, prop, fromVal, toVal, duration, unit, infiniteFlag] = propAnimMatch;
|
|
746
|
+
const durationMs = unit === 's' ? parseFloat(duration) * 1000 : parseFloat(duration);
|
|
747
|
+
|
|
748
|
+
let cssProp = prop;
|
|
749
|
+
let fromValue = fromVal;
|
|
750
|
+
let toValue = toVal;
|
|
751
|
+
|
|
752
|
+
if (prop === 'w') { cssProp = 'width'; fromValue = sizePx(parseFloat(fromVal)); toValue = sizePx(parseFloat(toVal)); }
|
|
753
|
+
else if (prop === 'h') { cssProp = 'height'; fromValue = sizePx(parseFloat(fromVal)); toValue = sizePx(parseFloat(toVal)); }
|
|
754
|
+
else if (prop === 'scale') { cssProp = 'transform'; fromValue = `scale(${parseFloat(fromVal) / 100})`; toValue = `scale(${parseFloat(toVal) / 100})`; }
|
|
755
|
+
else if (prop === 'p' || prop === 'm' || prop === 'pt' || prop === 'pb' || prop === 'pl' || prop === 'pr' ||
|
|
756
|
+
prop === 'mt' || prop === 'mb' || prop === 'ml' || prop === 'mr') {
|
|
757
|
+
cssProp = SPACING_MAP[prop];
|
|
758
|
+
fromValue = px(parseFloat(fromVal));
|
|
759
|
+
toValue = px(parseFloat(toVal));
|
|
760
|
+
}
|
|
761
|
+
else if (prop === 'rounded') { cssProp = 'border-radius'; fromValue = roundedToRem(parseFloat(fromVal)); toValue = roundedToRem(parseFloat(toVal)); }
|
|
762
|
+
else if (prop === 'opacity') { cssProp = 'opacity'; fromValue = (parseFloat(fromVal) / 100).toString(); toValue = (parseFloat(toVal) / 100).toString(); }
|
|
763
|
+
|
|
764
|
+
const keyframeName = `ub-animate-${prop}-${fromVal}-${toVal}`;
|
|
765
|
+
const keyframeRule = `@keyframes ${keyframeName} { from { ${cssProp}: ${fromValue}; } to { ${cssProp}: ${toValue}; } }`;
|
|
766
|
+
this.virtualMap.addKeyframe(keyframeName, keyframeRule);
|
|
767
|
+
|
|
768
|
+
const isInfinite = infiniteFlag === 'infinite' || pseudo === 'infinite';
|
|
769
|
+
|
|
770
|
+
if (pseudo === 'hover') {
|
|
771
|
+
rules = [`${cssProp}: ${fromValue};`, `transition: ${cssProp} ${durationMs}ms ease-out;`, `&:hover { ${cssProp}: ${toValue}; }`];
|
|
772
|
+
} else if (isInfinite) {
|
|
773
|
+
rules = [`animation: ${keyframeName} ${durationMs}ms infinite ease-in-out alternate;`];
|
|
774
|
+
} else {
|
|
775
|
+
rules = [`animation: ${keyframeName} ${durationMs}ms ease forwards;`];
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
else if (gradientMatch) {
|
|
779
|
+
const [, fromColor, fromShade, toColor, toShade] = gradientMatch;
|
|
780
|
+
const fromShadeNum = safeClamp(parseFloatShade(fromShade), 0, 255);
|
|
781
|
+
const toShadeNum = safeClamp(parseFloatShade(toShade), 0, 255);
|
|
782
|
+
const from = getOKLCH(fromColor, fromShadeNum, this.darkMode);
|
|
783
|
+
const to = getOKLCH(toColor, toShadeNum, this.darkMode);
|
|
784
|
+
const textColor = getTextColorForGradient([from, to]);
|
|
785
|
+
|
|
786
|
+
if (pseudo === 'hover') {
|
|
787
|
+
const hoverFromShade = Math.min(255, fromShadeNum + 20);
|
|
788
|
+
const hoverToShade = Math.min(255, toShadeNum + 20);
|
|
789
|
+
const hoverFrom = getOKLCH(fromColor, hoverFromShade, this.darkMode);
|
|
790
|
+
const hoverTo = getOKLCH(toColor, hoverToShade, this.darkMode);
|
|
791
|
+
const hoverTextColor = getTextColorForGradient([hoverFrom, hoverTo]);
|
|
792
|
+
rules = [
|
|
793
|
+
`background: linear-gradient(135deg, ${from}, ${to}) !important;`,
|
|
794
|
+
`color: ${textColor} !important;`,
|
|
795
|
+
`transition: all 0.3s ease;`,
|
|
796
|
+
`&:hover { background: linear-gradient(135deg, ${hoverFrom}, ${hoverTo}) !important; color: ${hoverTextColor} !important; }`
|
|
797
|
+
];
|
|
798
|
+
} else {
|
|
799
|
+
rules = [`background: linear-gradient(135deg, ${from}, ${to}) !important;`, `color: ${textColor} !important;`, `caret-color: ${textColor} !important;`];
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
else if (gradientWithAngleMatch) {
|
|
803
|
+
const [, angle, fromColor, fromShade, toColor, toShade] = gradientWithAngleMatch;
|
|
804
|
+
const fromShadeNum = safeClamp(parseFloatShade(fromShade), 0, 255);
|
|
805
|
+
const toShadeNum = safeClamp(parseFloatShade(toShade), 0, 255);
|
|
806
|
+
const from = getOKLCH(fromColor, fromShadeNum, this.darkMode);
|
|
807
|
+
const to = getOKLCH(toColor, toShadeNum, this.darkMode);
|
|
808
|
+
const textColor = getTextColorForGradient([from, to]);
|
|
809
|
+
rules = [`background: linear-gradient(${angle}deg, ${from}, ${to}) !important;`, `color: ${textColor} !important;`, `caret-color: ${textColor} !important;`];
|
|
810
|
+
}
|
|
811
|
+
else if (gradientVerticalMatch) {
|
|
812
|
+
const [, fromColor, fromShade, toColor, toShade] = gradientVerticalMatch;
|
|
813
|
+
const fromShadeNum = safeClamp(parseFloatShade(fromShade), 0, 255);
|
|
814
|
+
const toShadeNum = safeClamp(parseFloatShade(toShade), 0, 255);
|
|
815
|
+
const from = getOKLCH(fromColor, fromShadeNum, this.darkMode);
|
|
816
|
+
const to = getOKLCH(toColor, toShadeNum, this.darkMode);
|
|
817
|
+
const textColor = getTextColorForGradient([from, to]);
|
|
818
|
+
rules = [`background: linear-gradient(to bottom, ${from}, ${to}) !important;`, `color: ${textColor} !important;`, `caret-color: ${textColor} !important;`];
|
|
819
|
+
}
|
|
820
|
+
else if (gradientHorizontalMatch) {
|
|
821
|
+
const [, fromColor, fromShade, toColor, toShade] = gradientHorizontalMatch;
|
|
822
|
+
const fromShadeNum = safeClamp(parseFloatShade(fromShade), 0, 255);
|
|
823
|
+
const toShadeNum = safeClamp(parseFloatShade(toShade), 0, 255);
|
|
824
|
+
const from = getOKLCH(fromColor, fromShadeNum, this.darkMode);
|
|
825
|
+
const to = getOKLCH(toColor, toShadeNum, this.darkMode);
|
|
826
|
+
const textColor = getTextColorForGradient([from, to]);
|
|
827
|
+
rules = [`background: linear-gradient(to right, ${from}, ${to}) !important;`, `color: ${textColor} !important;`, `caret-color: ${textColor} !important;`];
|
|
828
|
+
}
|
|
829
|
+
else if (gradientRadialMatch) {
|
|
830
|
+
const [, fromColor, fromShade, toColor, toShade] = gradientRadialMatch;
|
|
831
|
+
const fromShadeNum = safeClamp(parseFloatShade(fromShade), 0, 255);
|
|
832
|
+
const toShadeNum = safeClamp(parseFloatShade(toShade), 0, 255);
|
|
833
|
+
const from = getOKLCH(fromColor, fromShadeNum, this.darkMode);
|
|
834
|
+
const to = getOKLCH(toColor, toShadeNum, this.darkMode);
|
|
835
|
+
const textColor = getTextColorForGradient([from, to]);
|
|
836
|
+
rules = [`background: radial-gradient(circle, ${from}, ${to}) !important;`, `color: ${textColor} !important;`, `caret-color: ${textColor} !important;`];
|
|
837
|
+
}
|
|
838
|
+
else if (gradientTripleMatch) {
|
|
839
|
+
const [, c1, s1, c2, s2, c3, s3] = gradientTripleMatch;
|
|
840
|
+
const shade1 = safeClamp(parseFloatShade(s1), 0, 255);
|
|
841
|
+
const shade2 = safeClamp(parseFloatShade(s2), 0, 255);
|
|
842
|
+
const shade3 = safeClamp(parseFloatShade(s3), 0, 255);
|
|
843
|
+
const color1 = getOKLCH(c1, shade1, this.darkMode);
|
|
844
|
+
const color2 = getOKLCH(c2, shade2, this.darkMode);
|
|
845
|
+
const color3 = getOKLCH(c3, shade3, this.darkMode);
|
|
846
|
+
const textColor = getTextColorForGradient([color1, color2, color3]);
|
|
847
|
+
rules = [`background: linear-gradient(135deg, ${color1}, ${color2}, ${color3}) !important;`, `color: ${textColor} !important;`, `caret-color: ${textColor} !important;`];
|
|
848
|
+
}
|
|
849
|
+
else if (isColor) {
|
|
850
|
+
const type = name.split('-')[0];
|
|
851
|
+
const colorPart = name.substring(type.length + 1);
|
|
852
|
+
const shadeMatch = colorPart.match(/^([a-z]+)-(\d+(?:\.\d+)?)(?:\/(\d+))?$/);
|
|
853
|
+
if (shadeMatch) {
|
|
854
|
+
const [, colorName, shadeStr, opacityStr] = shadeMatch;
|
|
855
|
+
const shade = parseFloatShade(shadeStr);
|
|
856
|
+
const opacity = opacityStr ? safeParseInt(opacityStr, 100) / 100 : undefined;
|
|
857
|
+
|
|
858
|
+
if (type === 'bg') {
|
|
859
|
+
const color = getOKLCH(colorName, shade, this.darkMode);
|
|
860
|
+
const textColor = getTextColorForBg(color);
|
|
861
|
+
const bgColor = applyOpacity(color, opacity);
|
|
862
|
+
|
|
863
|
+
if (pseudo === 'hover') {
|
|
864
|
+
const hoverColor = getOKLCH(colorName, Math.min(255, shade + 15), this.darkMode);
|
|
865
|
+
const hoverTextColor = getTextColorForBg(hoverColor);
|
|
866
|
+
const hoverBgColor = applyOpacity(hoverColor, opacity);
|
|
867
|
+
rules = [
|
|
868
|
+
`background: ${bgColor} !important;`,
|
|
869
|
+
`color: ${textColor} !important;`,
|
|
870
|
+
`transition: all 0.2s ease;`,
|
|
871
|
+
`&:hover { background: ${hoverBgColor} !important; color: ${hoverTextColor} !important; }`
|
|
872
|
+
];
|
|
873
|
+
} else {
|
|
874
|
+
rules = [`background: ${bgColor} !important;`, `color: ${textColor} !important;`, `caret-color: ${textColor} !important;`];
|
|
875
|
+
}
|
|
876
|
+
} else {
|
|
877
|
+
const prop = type === 'text' ? 'color' : 'border-color';
|
|
878
|
+
const color = getOKLCH(colorName, shade, this.darkMode);
|
|
879
|
+
rules = [`${prop}: ${applyOpacity(color, opacity)};`];
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
else if (spacingMatch) {
|
|
884
|
+
const [, prop, scaleStr] = spacingMatch;
|
|
885
|
+
rules = [`${SPACING_MAP[prop]}: ${px(parseNumber(scaleStr))};`];
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
|
|
889
|
+
if (rules) {
|
|
890
|
+
this.virtualMap.add(className, rules, pseudo, media);
|
|
891
|
+
results.push(className);
|
|
892
|
+
this.classCache.set(cls, className);
|
|
893
|
+
} else {
|
|
894
|
+
results.push(cls);
|
|
895
|
+
this.classCache.set(cls, cls);
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
return results.join(' ');
|
|
900
|
+
}
|
|
901
|
+
|
|
902
|
+
debug() {
|
|
903
|
+
return {
|
|
904
|
+
classCache: this.classCache.size,
|
|
905
|
+
styleCount: this.virtualMap.getStyleCount(),
|
|
906
|
+
totalRequests: this.totalRequests,
|
|
907
|
+
cacheHits: this.cacheHits,
|
|
908
|
+
totalSegmentRequests: this.totalSegmentRequests,
|
|
909
|
+
version: 'v19.0.3'
|
|
910
|
+
};
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
|
|
914
|
+
|
|
915
|
+
|
|
916
|
+
export const ub = (str) => {
|
|
917
|
+
try {
|
|
918
|
+
const safeStr = safeToString(str);
|
|
919
|
+
if (!safeStr) return '';
|
|
920
|
+
return WebStyleEngine.getInstance().inject(safeStr);
|
|
921
|
+
} catch (e) {
|
|
922
|
+
console.warn('UB Error:', e);
|
|
923
|
+
return safeToString(str);
|
|
924
|
+
}
|
|
925
|
+
};
|
|
926
|
+
|
|
927
|
+
export const debugUB = () => {
|
|
928
|
+
try { return WebStyleEngine.getInstance().debug(); }
|
|
929
|
+
catch { return { classCache: 0, styleCount: 0, totalRequests: 0, version: 'error' }; }
|
|
930
|
+
};
|
|
931
|
+
|
|
932
|
+
export const clearUBCache = () => {
|
|
933
|
+
try {
|
|
934
|
+
const engine = WebStyleEngine.getInstance();
|
|
935
|
+
engine.classCache.clear();
|
|
936
|
+
engine.virtualMap.clear();
|
|
937
|
+
COLOR_CACHE.clear();
|
|
938
|
+
engine.totalRequests = 0;
|
|
939
|
+
engine.cacheHits = 0;
|
|
940
|
+
engine.totalSegmentRequests = 0;
|
|
941
|
+
} catch (e) {}
|
|
942
|
+
};
|
|
943
|
+
|
|
944
|
+
export const oklch = getOKLCH;
|
|
945
|
+
|
|
946
|
+
const createHelper = (prefix) => (v) => `${prefix}-${v}`;
|
|
947
|
+
const createHelperWithDefault = (prefix, defaultValue = 1) => (v = defaultValue) => `${prefix}-${v}`;
|
|
948
|
+
|
|
949
|
+
export const p = createHelper('p');
|
|
950
|
+
export const m = createHelper('m');
|
|
951
|
+
export const pl = createHelper('pl');
|
|
952
|
+
export const pr = createHelper('pr');
|
|
953
|
+
export const ml = createHelper('ml');
|
|
954
|
+
export const mr = createHelper('mr');
|
|
955
|
+
export const pt = createHelper('pt');
|
|
956
|
+
export const pb = createHelper('pb');
|
|
957
|
+
export const mt = createHelper('mt');
|
|
958
|
+
export const mb = createHelper('mb');
|
|
959
|
+
export const w = createHelper('w');
|
|
960
|
+
export const h = createHelper('h');
|
|
961
|
+
export const scale = createHelper('scale');
|
|
962
|
+
|
|
963
|
+
export const border = createHelperWithDefault('border');
|
|
964
|
+
export const borderT = createHelperWithDefault('border-t');
|
|
965
|
+
export const borderR = createHelperWithDefault('border-r');
|
|
966
|
+
export const borderB = createHelperWithDefault('border-b');
|
|
967
|
+
export const borderL = createHelperWithDefault('border-l');
|
|
968
|
+
export const borderX = createHelperWithDefault('border-x');
|
|
969
|
+
export const borderY = createHelperWithDefault('border-y');
|
|
970
|
+
|
|
971
|
+
export const rounded = (v) => v === 'full' ? 'rounded-full' : `rounded-${v}`;
|
|
972
|
+
export const shadow = (v) => `shadow-${safeClamp(safeParseInt(v, 3), 1, 10)}`;
|
|
973
|
+
export const opacity = (v) => `opacity-${safeClamp(safeParseInt(v, 100), 0, 100)}`;
|
|
974
|
+
|
|
975
|
+
export const bg = (c, s, o) => o !== undefined ? `bg-${c}-${s}/${o}` : `bg-${c}-${s}`;
|
|
976
|
+
export const text = (c, s, o) => o !== undefined ? `text-${c}-${s}/${o}` : `text-${c}-${s}`;
|
|
977
|
+
export const grid = (cols, rows, gap) => `grid-${cols}x${rows}-${gap}`;
|
|
978
|
+
export const autoGrid = (minWidth, gap) => `auto-grid-${minWidth}-${gap}`;
|
|
979
|
+
export const autoLayout = (direction, alignment, gap) => {
|
|
980
|
+
if (gap === undefined) {
|
|
981
|
+
return `auto-layout-${direction}-${alignment}`;
|
|
982
|
+
}
|
|
983
|
+
return `auto-layout-${direction}-${alignment}-${gap}`;
|
|
984
|
+
};
|
|
985
|
+
export const span = (n) => `span-${safeClamp(safeParseInt(n, 1), 1, 12)}`;
|
|
986
|
+
export const row = (n) => `row-${safeClamp(safeParseInt(n, 1), 1, 6)}`;
|
|
987
|
+
export const bgFill = (direction, color, shade, duration) =>
|
|
988
|
+
`bg-fill-${direction}-${color}-${shade}-${duration}${typeof duration === 'number' && duration < 1000 ? 'ms' : 'ms'}`;
|
|
989
|
+
|
|
990
|
+
export const btn = {
|
|
991
|
+
base: 'btn',
|
|
992
|
+
sm: 'btn btn-sm',
|
|
993
|
+
md: 'btn btn-md',
|
|
994
|
+
lg: 'btn btn-lg',
|
|
995
|
+
primary: 'btn btn-primary',
|
|
996
|
+
secondary: 'btn btn-secondary',
|
|
997
|
+
success: 'btn btn-success',
|
|
998
|
+
danger: 'btn btn-danger',
|
|
999
|
+
warning: 'btn btn-warning',
|
|
1000
|
+
outline: 'btn btn-outline',
|
|
1001
|
+
ghost: 'btn btn-ghost',
|
|
1002
|
+
glow: 'btn btn-glow',
|
|
1003
|
+
};
|
|
1004
|
+
|
|
1005
|
+
export const input = {
|
|
1006
|
+
base: 'input',
|
|
1007
|
+
sm: 'input input-sm',
|
|
1008
|
+
md: 'input input-md',
|
|
1009
|
+
lg: 'input input-lg',
|
|
1010
|
+
error: 'input input-error',
|
|
1011
|
+
success: 'input input-success',
|
|
1012
|
+
};
|
|
1013
|
+
|
|
1014
|
+
export const card = {
|
|
1015
|
+
base: 'card',
|
|
1016
|
+
hover: 'card card-hover',
|
|
1017
|
+
click: 'card card-click card-hover',
|
|
1018
|
+
glass: 'card card-glass',
|
|
1019
|
+
};
|
|
1020
|
+
|
|
1021
|
+
export const gradient = (fromColor, fromShade, toColor, toShade) => `gradient-${fromColor}-${fromShade}-${toColor}-${toShade}`;
|
|
1022
|
+
export const gradientAngle = (angle, fromColor, fromShade, toColor, toShade) => `gradient-${angle}deg-${fromColor}-${fromShade}-${toColor}-${toShade}`;
|
|
1023
|
+
export const gradientVertical = (fromColor, fromShade, toColor, toShade) => `gradient-vert-${fromColor}-${fromShade}-${toColor}-${toShade}`;
|
|
1024
|
+
export const gradientHorizontal = (fromColor, fromShade, toColor, toShade) => `gradient-horiz-${fromColor}-${fromShade}-${toColor}-${toShade}`;
|
|
1025
|
+
export const gradientRadial = (fromColor, fromShade, toColor, toShade) => `gradient-radial-${fromColor}-${fromShade}-${toColor}-${toShade}`;
|
|
1026
|
+
export const gradientTriple = (c1, s1, c2, s2, c3, s3) => `gradient-${c1}-${s1}-${c2}-${s2}-${c3}-${s3}`;
|
|
1027
|
+
|
|
1028
|
+
export const animate = (prop, from, to, duration, infinite) =>
|
|
1029
|
+
`${prop}-${from}-${to}-${duration}${typeof duration === 'number' && duration < 1000 ? 'ms' : 'ms'}${infinite ? '-infinite' : ''}`;
|
|
1030
|
+
export const widthAnim = (from, to, duration, infinite) => animate('w', from, to, duration, infinite);
|
|
1031
|
+
export const heightAnim = (from, to, duration, infinite) => animate('h', from, to, duration, infinite);
|
|
1032
|
+
export const paddingAnim = (from, to, duration, infinite) => animate('p', from, to, duration, infinite);
|
|
1033
|
+
export const marginAnim = (from, to, duration, infinite) => animate('m', from, to, duration, infinite);
|
|
1034
|
+
export const bgAnim = (fromColor, fromShade, toColor, toShade, duration, infinite) =>
|
|
1035
|
+
`bg-${fromColor}-${fromShade}-${toColor}-${toShade}-${duration}${typeof duration === 'number' && duration < 1000 ? 'ms' : 'ms'}${infinite ? '-infinite' : ''}`;
|
|
1036
|
+
export const opacityAnim = (from, to, duration, infinite) => animate('opacity', from, to, duration, infinite);
|
|
1037
|
+
export const roundedAnim = (from, to, duration, infinite) => animate('rounded', from, to, duration, infinite);
|
|
1038
|
+
export const scaleAnim = (from, to, duration, infinite) => animate('scale', from, to, duration, infinite);
|
|
1039
|
+
export const infiniteAnim = (prop, from, to, duration) => animate(prop, from, to, duration, true);
|
|
1040
|
+
export const clickAnim = (prop, from, to, duration) => `click:${prop}-${from}-${to}-${duration}${typeof duration === 'number' && duration < 1000 ? 'ms' : 'ms'}`;
|
|
1041
|
+
|
|
1042
|
+
const _clamp = (v, min, max) => Math.max(min, Math.min(max, v));
|
|
1043
|
+
const _t = (v, min, max) => (_clamp(v, min, max) - min) / (max - min);
|
|
1044
|
+
const _shade = (s, e, t) => Math.floor(s + (e - s) * t);
|
|
1045
|
+
|
|
1046
|
+
export const map = {
|
|
1047
|
+
linear: (v, min, max, sc, ss, ec, es) => {
|
|
1048
|
+
const t = _t(v, min, max);
|
|
1049
|
+
return t < 0.5 ? `${sc}-${_shade(ss, es, t * 2)}` : `${ec}-${_shade(ss, es, (t - 0.5) * 2)}`;
|
|
1050
|
+
},
|
|
1051
|
+
shade: (v, min, max, color, sMin = 0, sMax = 255) =>
|
|
1052
|
+
`${color}-${_shade(sMin, sMax, _t(v, min, max))}`,
|
|
1053
|
+
fuel: (v, min = 0, max = 100) => {
|
|
1054
|
+
const t = _t(v, min, max);
|
|
1055
|
+
if (t < 0.33) return `red-${_shade(128, 255, t / 0.33)}`;
|
|
1056
|
+
if (t < 0.66) return `orange-${_shade(128, 255, (t - 0.33) / 0.33)}`;
|
|
1057
|
+
return `green-${_shade(128, 255, (t - 0.66) / 0.34)}`;
|
|
1058
|
+
},
|
|
1059
|
+
heat: (v, min = 0, max = 100) => {
|
|
1060
|
+
const t = _t(v, min, max);
|
|
1061
|
+
if (t < 0.5) return `green-${_shade(255, 128, t * 2)}`;
|
|
1062
|
+
return `red-${_shade(128, 255, (t - 0.5) * 2)}`;
|
|
1063
|
+
},
|
|
1064
|
+
coolWarm: (v, min = 0, max = 100) => {
|
|
1065
|
+
const t = _t(v, min, max);
|
|
1066
|
+
if (t < 0.5) return `blue-${_shade(128, 255, t * 2)}`;
|
|
1067
|
+
return `red-${_shade(128, 255, (t - 0.5) * 2)}`;
|
|
1068
|
+
},
|
|
1069
|
+
rainbow: (v, min = 0, max = 100) => {
|
|
1070
|
+
const t = _t(v, min, max);
|
|
1071
|
+
if (t < 0.2) return `purple-${_shade(128, 255, t / 0.2)}`;
|
|
1072
|
+
if (t < 0.4) return `blue-${_shade(128, 255, (t - 0.2) / 0.2)}`;
|
|
1073
|
+
if (t < 0.6) return `green-${_shade(128, 255, (t - 0.4) / 0.2)}`;
|
|
1074
|
+
if (t < 0.8) return `orange-${_shade(128, 255, (t - 0.6) / 0.2)}`;
|
|
1075
|
+
return `red-${_shade(128, 255, (t - 0.8) / 0.2)}`;
|
|
1076
|
+
}
|
|
1077
|
+
};
|