@symbo.ls/scratch 3.8.0 → 3.8.6
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 +50 -1
- package/dist/cjs/defaultConfig/index.js +3 -2
- package/dist/cjs/set.js +35 -20
- package/dist/cjs/system/color.js +6 -1
- package/dist/cjs/system/font.js +6 -2
- package/dist/cjs/system/theme.js +184 -0
- package/dist/cjs/utils/font.js +17 -9
- package/dist/esm/defaultConfig/index.js +3 -2
- package/dist/esm/set.js +36 -21
- package/dist/esm/system/color.js +6 -1
- package/dist/esm/system/font.js +6 -2
- package/dist/esm/system/theme.js +184 -0
- package/dist/esm/utils/font.js +17 -9
- package/dist/iife/index.js +273 -47
- package/package.json +3 -3
- package/src/defaultConfig/index.js +1 -1
- package/src/set.js +50 -30
- package/src/system/color.js +7 -1
- package/src/system/font.js +8 -2
- package/src/system/theme.js +68 -0
- package/src/utils/font.js +22 -10
package/src/set.js
CHANGED
|
@@ -17,14 +17,17 @@ import {
|
|
|
17
17
|
setShadow
|
|
18
18
|
} from './system'
|
|
19
19
|
|
|
20
|
-
import {
|
|
20
|
+
import { deepMerge } from '@domql/utils'
|
|
21
21
|
|
|
22
|
-
const
|
|
23
|
-
|
|
22
|
+
const setVars = (val, key) => {
|
|
23
|
+
const CONFIG = getActiveConfig()
|
|
24
|
+
const { CSS_VARS } = CONFIG
|
|
25
|
+
const varName = key.startsWith('--') ? key : `--${key}`
|
|
26
|
+
CSS_VARS[varName] = val
|
|
24
27
|
return val
|
|
25
28
|
}
|
|
26
29
|
|
|
27
|
-
const
|
|
30
|
+
const asIs = (val) => val
|
|
28
31
|
|
|
29
32
|
export const VALUE_TRANSFORMERS = {
|
|
30
33
|
color: setColor,
|
|
@@ -34,21 +37,21 @@ export const VALUE_TRANSFORMERS = {
|
|
|
34
37
|
fontfamily: setFontFamily,
|
|
35
38
|
theme: setTheme,
|
|
36
39
|
icons: setSvgIcon,
|
|
37
|
-
semantic_icons:
|
|
38
|
-
semanticicons:
|
|
40
|
+
semantic_icons: asIs,
|
|
41
|
+
semanticicons: asIs,
|
|
39
42
|
svg: setSVG,
|
|
40
|
-
svg_data:
|
|
41
|
-
typography:
|
|
42
|
-
cases: setCases,
|
|
43
|
+
svg_data: asIs,
|
|
44
|
+
typography: asIs,
|
|
43
45
|
shadow: setShadow,
|
|
44
|
-
spacing:
|
|
45
|
-
media:
|
|
46
|
-
grid:
|
|
47
|
-
class:
|
|
48
|
-
timing:
|
|
49
|
-
reset:
|
|
50
|
-
unit:
|
|
51
|
-
animation:
|
|
46
|
+
spacing: asIs,
|
|
47
|
+
media: asIs,
|
|
48
|
+
grid: asIs,
|
|
49
|
+
class: asIs,
|
|
50
|
+
timing: asIs,
|
|
51
|
+
reset: asIs,
|
|
52
|
+
unit: asIs,
|
|
53
|
+
animation: asIs,
|
|
54
|
+
vars: setVars
|
|
52
55
|
}
|
|
53
56
|
|
|
54
57
|
/**
|
|
@@ -69,11 +72,13 @@ export const setValue = (factoryName, value, key) => {
|
|
|
69
72
|
FACTORY[key] = result
|
|
70
73
|
return FACTORY
|
|
71
74
|
} catch (error) {
|
|
72
|
-
if (CONFIG.verbose)
|
|
75
|
+
if (CONFIG.verbose)
|
|
76
|
+
console.warn('Error setting', lowerName, 'value', value, key, error)
|
|
73
77
|
}
|
|
74
78
|
}
|
|
75
79
|
|
|
76
|
-
if (CONFIG.verbose)
|
|
80
|
+
if (CONFIG.verbose)
|
|
81
|
+
console.warn('Can not find', lowerName, 'method in scratch')
|
|
77
82
|
}
|
|
78
83
|
|
|
79
84
|
export const setEach = (factoryName, props) => {
|
|
@@ -85,7 +90,15 @@ export const setEach = (factoryName, props) => {
|
|
|
85
90
|
try {
|
|
86
91
|
return setValue(lowerName, props[key], key)
|
|
87
92
|
} catch (error) {
|
|
88
|
-
if (CONFIG.verbose)
|
|
93
|
+
if (CONFIG.verbose)
|
|
94
|
+
console.warn(
|
|
95
|
+
'Error setting',
|
|
96
|
+
lowerName,
|
|
97
|
+
'value',
|
|
98
|
+
props[key],
|
|
99
|
+
key,
|
|
100
|
+
error
|
|
101
|
+
)
|
|
89
102
|
}
|
|
90
103
|
})
|
|
91
104
|
|
|
@@ -131,7 +144,7 @@ export const set = (recivedConfig, options = SET_OPTIONS) => {
|
|
|
131
144
|
useDocumentTheme,
|
|
132
145
|
useDefaultConfig,
|
|
133
146
|
semanticIcons,
|
|
134
|
-
SEMANTIC_ICONS,
|
|
147
|
+
SEMANTIC_ICONS, // deprecated
|
|
135
148
|
semantic_icons,
|
|
136
149
|
files,
|
|
137
150
|
...config
|
|
@@ -150,7 +163,8 @@ export const set = (recivedConfig, options = SET_OPTIONS) => {
|
|
|
150
163
|
if (useIconSprite !== undefined) CONFIG.useIconSprite = useIconSprite
|
|
151
164
|
if (useDocumentTheme !== undefined) CONFIG.useDocumentTheme = useDocumentTheme
|
|
152
165
|
if (globalTheme !== undefined) CONFIG.globalTheme = globalTheme
|
|
153
|
-
if (recivedConfig.useThemeSuffixedVars !== undefined)
|
|
166
|
+
if (recivedConfig.useThemeSuffixedVars !== undefined)
|
|
167
|
+
CONFIG.useThemeSuffixedVars = recivedConfig.useThemeSuffixedVars
|
|
154
168
|
if (useDefaultConfig !== undefined) CONFIG.useDefaultConfig = useDefaultConfig
|
|
155
169
|
const _semanticIcons = semanticIcons || SEMANTIC_ICONS || semantic_icons
|
|
156
170
|
if (_semanticIcons !== undefined) {
|
|
@@ -166,7 +180,7 @@ export const set = (recivedConfig, options = SET_OPTIONS) => {
|
|
|
166
180
|
const keySet = new Set(keys)
|
|
167
181
|
|
|
168
182
|
// Pre-merge: fold UPPERCASE default keys into lowercase project keys
|
|
169
|
-
keys.forEach(key => {
|
|
183
|
+
keys.forEach((key) => {
|
|
170
184
|
const lower = key.toLowerCase()
|
|
171
185
|
if (lower !== key && keySet.has(lower)) {
|
|
172
186
|
deepMerge(config[lower], config[key])
|
|
@@ -174,25 +188,31 @@ export const set = (recivedConfig, options = SET_OPTIONS) => {
|
|
|
174
188
|
})
|
|
175
189
|
|
|
176
190
|
// Process only lowercase keys (skip UPPERCASE when lowercase equivalent exists)
|
|
177
|
-
keys.map(key => {
|
|
191
|
+
keys.map((key) => {
|
|
178
192
|
const lower = key.toLowerCase()
|
|
179
193
|
if (lower !== key && keySet.has(lower)) return
|
|
180
194
|
return setEach(key, config[key])
|
|
181
195
|
})
|
|
182
196
|
|
|
183
197
|
// apply generic configs
|
|
184
|
-
if (config.typography || config.TYPOGRAPHY) {
|
|
185
|
-
try {
|
|
198
|
+
if (config.typography || config.TYPOGRAPHY) { // UPPERCASE deprecated
|
|
199
|
+
try {
|
|
200
|
+
applyTypographySequence()
|
|
201
|
+
} catch (e) {
|
|
186
202
|
if (CONFIG.verbose) console.warn('Error applying typography sequence', e)
|
|
187
203
|
}
|
|
188
204
|
}
|
|
189
|
-
if (config.spacing || config.SPACING) {
|
|
190
|
-
try {
|
|
205
|
+
if (config.spacing || config.SPACING) { // UPPERCASE deprecated
|
|
206
|
+
try {
|
|
207
|
+
applySpacingSequence()
|
|
208
|
+
} catch (e) {
|
|
191
209
|
if (CONFIG.verbose) console.warn('Error applying spacing sequence', e)
|
|
192
210
|
}
|
|
193
211
|
}
|
|
194
|
-
if (config.timing || config.TIMING) {
|
|
195
|
-
try {
|
|
212
|
+
if (config.timing || config.TIMING) { // UPPERCASE deprecated
|
|
213
|
+
try {
|
|
214
|
+
applyTimingSequence()
|
|
215
|
+
} catch (e) {
|
|
196
216
|
if (CONFIG.verbose) console.warn('Error applying timing sequence', e)
|
|
197
217
|
}
|
|
198
218
|
}
|
package/src/system/color.js
CHANGED
|
@@ -26,7 +26,13 @@ export const getColor = (value, key, config) => {
|
|
|
26
26
|
[name, alpha, tone] = value
|
|
27
27
|
} else {
|
|
28
28
|
const parsed = parseColorToken(value)
|
|
29
|
-
if (!parsed)
|
|
29
|
+
if (!parsed) {
|
|
30
|
+
// Direct lookup for hyphenated names (e.g. 'gradient-light', 'gradient-dark-active')
|
|
31
|
+
const { color: COLOR, gradient: GRADIENT } = CONFIG
|
|
32
|
+
const directVal = GRADIENT[value] || COLOR[value]
|
|
33
|
+
if (directVal) return CONFIG.useVariable ? `var(${directVal.var})` : directVal.value
|
|
34
|
+
return value
|
|
35
|
+
}
|
|
30
36
|
if (parsed.passthrough) return parsed.passthrough
|
|
31
37
|
if (parsed.cssVar) return `var(${parsed.cssVar})`
|
|
32
38
|
name = parsed.name
|
package/src/system/font.js
CHANGED
|
@@ -33,8 +33,14 @@ export const setFont = (val, key) => {
|
|
|
33
33
|
} else if (val[0]) {
|
|
34
34
|
fontFace = getFontFaceEach(key, val, CONFIG.files)
|
|
35
35
|
} else {
|
|
36
|
-
const url =
|
|
37
|
-
|
|
36
|
+
const url = Array.isArray(val.url)
|
|
37
|
+
? val.url.map((u) => resolveFileUrl(u, CONFIG.files) || u)
|
|
38
|
+
: resolveFileUrl(val.url, CONFIG.files) || val.url
|
|
39
|
+
fontFace = setCustomFontMedia(key, url, val.fontWeight, {
|
|
40
|
+
fontStyle: val.fontStyle,
|
|
41
|
+
fontDisplay: val.fontDisplay,
|
|
42
|
+
fontStretch: val.fontStretch
|
|
43
|
+
})
|
|
38
44
|
}
|
|
39
45
|
|
|
40
46
|
return { var: CSSvar, value: val, fontFace }
|
package/src/system/theme.js
CHANGED
|
@@ -11,6 +11,38 @@ import {
|
|
|
11
11
|
isArray
|
|
12
12
|
} from '@domql/utils'
|
|
13
13
|
|
|
14
|
+
// Common CSS named colors — used to distinguish valid CSS color names
|
|
15
|
+
// from unresolved design-system tokens (e.g. 'blackRussian', 'grayMid')
|
|
16
|
+
const CSS_NAMED_COLORS = new Set([
|
|
17
|
+
'black', 'white', 'red', 'green', 'blue', 'yellow', 'orange', 'purple',
|
|
18
|
+
'pink', 'brown', 'gray', 'grey', 'cyan', 'magenta', 'lime', 'olive',
|
|
19
|
+
'navy', 'teal', 'aqua', 'maroon', 'silver', 'fuchsia', 'transparent',
|
|
20
|
+
'currentColor', 'currentcolor', 'inherit', 'initial', 'unset', 'none',
|
|
21
|
+
'aliceblue', 'antiquewhite', 'aquamarine', 'azure', 'beige', 'bisque',
|
|
22
|
+
'blanchedalmond', 'blueviolet', 'burlywood', 'cadetblue', 'chartreuse',
|
|
23
|
+
'chocolate', 'coral', 'cornflowerblue', 'cornsilk', 'crimson', 'darkblue',
|
|
24
|
+
'darkcyan', 'darkgoldenrod', 'darkgray', 'darkgreen', 'darkgrey', 'darkkhaki',
|
|
25
|
+
'darkmagenta', 'darkolivegreen', 'darkorange', 'darkorchid', 'darkred',
|
|
26
|
+
'darksalmon', 'darkseagreen', 'darkslateblue', 'darkslategray', 'darkslategrey',
|
|
27
|
+
'darkturquoise', 'darkviolet', 'deeppink', 'deepskyblue', 'dimgray', 'dimgrey',
|
|
28
|
+
'dodgerblue', 'firebrick', 'floralwhite', 'forestgreen', 'gainsboro', 'ghostwhite',
|
|
29
|
+
'gold', 'goldenrod', 'greenyellow', 'honeydew', 'hotpink', 'indianred', 'indigo',
|
|
30
|
+
'ivory', 'khaki', 'lavender', 'lavenderblush', 'lawngreen', 'lemonchiffon',
|
|
31
|
+
'lightblue', 'lightcoral', 'lightcyan', 'lightgoldenrodyellow', 'lightgray',
|
|
32
|
+
'lightgreen', 'lightgrey', 'lightpink', 'lightsalmon', 'lightseagreen',
|
|
33
|
+
'lightskyblue', 'lightslategray', 'lightslategrey', 'lightsteelblue', 'lightyellow',
|
|
34
|
+
'limegreen', 'linen', 'mediumaquamarine', 'mediumblue', 'mediumorchid',
|
|
35
|
+
'mediumpurple', 'mediumseagreen', 'mediumslateblue', 'mediumspringgreen',
|
|
36
|
+
'mediumturquoise', 'mediumvioletred', 'midnightblue', 'mintcream', 'mistyrose',
|
|
37
|
+
'moccasin', 'navajowhite', 'oldlace', 'olivedrab', 'orangered', 'orchid',
|
|
38
|
+
'palegoldenrod', 'palegreen', 'paleturquoise', 'palevioletred', 'papayawhip',
|
|
39
|
+
'peachpuff', 'peru', 'plum', 'powderblue', 'rosybrown', 'royalblue',
|
|
40
|
+
'saddlebrown', 'salmon', 'sandybrown', 'seagreen', 'seashell', 'sienna',
|
|
41
|
+
'skyblue', 'slateblue', 'slategray', 'slategrey', 'snow', 'springgreen',
|
|
42
|
+
'steelblue', 'tan', 'thistle', 'tomato', 'turquoise', 'violet', 'wheat',
|
|
43
|
+
'whitesmoke', 'yellowgreen', 'rebeccapurple'
|
|
44
|
+
])
|
|
45
|
+
|
|
14
46
|
const setThemeValue = theme => {
|
|
15
47
|
const value = {}
|
|
16
48
|
const { state, media, helpers, ...rest } = theme
|
|
@@ -166,6 +198,27 @@ const generateAutoVars = (schemes, varPrefix, CONFIG) => {
|
|
|
166
198
|
if (schemes[scheme]) for (const k of Object.keys(schemes[scheme])) allKeys.add(k)
|
|
167
199
|
}
|
|
168
200
|
|
|
201
|
+
// Pre-scan: detect schemes with unresolvable color values
|
|
202
|
+
// If any color in a scheme fails to resolve, skip the ENTIRE scheme
|
|
203
|
+
// to avoid partial theme application (e.g. white text without dark background)
|
|
204
|
+
const brokenSchemes = new Set()
|
|
205
|
+
if (globalTheme === 'auto') {
|
|
206
|
+
for (const param of allKeys) {
|
|
207
|
+
const symb = param.slice(0, 1)
|
|
208
|
+
if (symb === '@' || symb === '.' || symb === ':') continue
|
|
209
|
+
for (const scheme in schemes) {
|
|
210
|
+
if (brokenSchemes.has(scheme)) continue
|
|
211
|
+
const val = schemes[scheme]?.[param]
|
|
212
|
+
if (val === undefined) continue
|
|
213
|
+
const color = getColor(val, `@${scheme}`)
|
|
214
|
+
if (color === undefined) continue
|
|
215
|
+
if (isString(color) && /^[a-z][a-zA-Z]+$/.test(color) && !CSS_NAMED_COLORS.has(color)) {
|
|
216
|
+
brokenSchemes.add(scheme)
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
169
222
|
for (const param of allKeys) {
|
|
170
223
|
const symb = param.slice(0, 1)
|
|
171
224
|
|
|
@@ -193,12 +246,19 @@ const generateAutoVars = (schemes, varPrefix, CONFIG) => {
|
|
|
193
246
|
const autoVar = `--theme-${varPrefix}-${param}`
|
|
194
247
|
|
|
195
248
|
if (globalTheme === 'auto') {
|
|
249
|
+
let fallbackColor
|
|
196
250
|
for (const scheme in schemes) {
|
|
251
|
+
if (brokenSchemes.has(scheme)) continue
|
|
197
252
|
const val = schemes[scheme]?.[param]
|
|
198
253
|
if (val === undefined) continue
|
|
199
254
|
const color = getColor(val, `@${scheme}`)
|
|
200
255
|
if (color === undefined) continue
|
|
201
256
|
|
|
257
|
+
// Use 'light' scheme (or first resolved) as base fallback
|
|
258
|
+
if (scheme === 'light' || fallbackColor === undefined) {
|
|
259
|
+
fallbackColor = color
|
|
260
|
+
}
|
|
261
|
+
|
|
202
262
|
// [data-theme] selector for ALL schemes (custom + standard)
|
|
203
263
|
const selector = `[data-theme="${scheme}"]`
|
|
204
264
|
if (!MEDIA_VARS[selector]) MEDIA_VARS[selector] = {}
|
|
@@ -211,6 +271,12 @@ const generateAutoVars = (schemes, varPrefix, CONFIG) => {
|
|
|
211
271
|
MEDIA_VARS[mq][autoVar] = color
|
|
212
272
|
}
|
|
213
273
|
}
|
|
274
|
+
|
|
275
|
+
// Set fallback default (light or first scheme) so the variable
|
|
276
|
+
// always resolves even without a matching media query
|
|
277
|
+
if (fallbackColor !== undefined) {
|
|
278
|
+
CSS_VARS[autoVar] = fallbackColor
|
|
279
|
+
}
|
|
214
280
|
} else {
|
|
215
281
|
// Force specific theme — set non-suffixed var directly
|
|
216
282
|
const forced = String(globalTheme).replace(/^'|'$/g, '')
|
|
@@ -310,6 +376,8 @@ const recursiveTheme = val => {
|
|
|
310
376
|
continue
|
|
311
377
|
} else if (symb === ':') {
|
|
312
378
|
obj[`&${param}`] = recursiveTheme(val[param])
|
|
379
|
+
} else if (symb === '.') {
|
|
380
|
+
obj[`&${param}`] = recursiveTheme(val[param])
|
|
313
381
|
}
|
|
314
382
|
} else obj[param] = val[param]
|
|
315
383
|
}
|
package/src/utils/font.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
export const resolveFileUrl = (url, files) => {
|
|
4
4
|
if (!url || !files) return null
|
|
5
|
-
try { new URL(url); return null } catch (e) { }
|
|
5
|
+
try { new URL(url); return null } catch (e) { } // expected: src is not an absolute URL, treating as relative path
|
|
6
6
|
const file = files[url]
|
|
7
7
|
if (file) return file.content && file.content.src
|
|
8
8
|
return null
|
|
@@ -30,11 +30,15 @@ export const setFontImport = (url) => `@import url('${url}');`
|
|
|
30
30
|
export const setInCustomFontMedia = (str) => `@font-face { ${str} }`
|
|
31
31
|
|
|
32
32
|
export const setCustomFont = (name, url, weight, options = {}) => {
|
|
33
|
-
const
|
|
34
|
-
const
|
|
33
|
+
const urls = Array.isArray(url) ? url : [url]
|
|
34
|
+
const srcList = urls.map((u) => {
|
|
35
|
+
const format = getFontFormat(u)
|
|
36
|
+
const formatStr = format ? ` format('${format}')` : ''
|
|
37
|
+
return `url('${u}')${formatStr}`
|
|
38
|
+
}).join(',\n ')
|
|
35
39
|
return `
|
|
36
40
|
font-family: '${name}';
|
|
37
|
-
font-style: normal;${
|
|
41
|
+
font-style: ${options.fontStyle || 'normal'};${
|
|
38
42
|
weight
|
|
39
43
|
? `
|
|
40
44
|
font-weight: ${weight};`
|
|
@@ -50,7 +54,7 @@ export const setCustomFont = (name, url, weight, options = {}) => {
|
|
|
50
54
|
font-display: ${options.fontDisplay};`
|
|
51
55
|
: ''
|
|
52
56
|
}
|
|
53
|
-
src:
|
|
57
|
+
src: ${srcList};`
|
|
54
58
|
}
|
|
55
59
|
|
|
56
60
|
export const setCustomFontMedia = (
|
|
@@ -64,9 +68,11 @@ export const setCustomFontMedia = (
|
|
|
64
68
|
export const getFontFaceEach = (name, weights, files) => {
|
|
65
69
|
const keys = Object.keys(weights)
|
|
66
70
|
return keys.map((key) => {
|
|
67
|
-
const { url, fontWeight } = weights[key]
|
|
68
|
-
const resolvedUrl =
|
|
69
|
-
|
|
71
|
+
const { url, fontWeight, fontStyle, fontDisplay, fontStretch } = weights[key]
|
|
72
|
+
const resolvedUrl = Array.isArray(url)
|
|
73
|
+
? url.map((u) => resolveFileUrl(u, files) || u)
|
|
74
|
+
: resolveFileUrl(url, files) || url
|
|
75
|
+
return setCustomFont(name, resolvedUrl, fontWeight, { fontStyle, fontDisplay, fontStretch })
|
|
70
76
|
})
|
|
71
77
|
}
|
|
72
78
|
|
|
@@ -88,8 +94,14 @@ export const getFontFaceEachString = (name, weights, files) => {
|
|
|
88
94
|
}
|
|
89
95
|
const isArr = weights[0]
|
|
90
96
|
if (isArr) return getFontFaceEach(name, weights, files).map(setInCustomFontMedia)
|
|
91
|
-
const url =
|
|
92
|
-
|
|
97
|
+
const url = Array.isArray(weights.url)
|
|
98
|
+
? weights.url.map((u) => resolveFileUrl(u, files) || u)
|
|
99
|
+
: resolveFileUrl(weights.url, files) || weights.url
|
|
100
|
+
return setCustomFontMedia(name, url, weights.fontWeight, {
|
|
101
|
+
fontStyle: weights.fontStyle,
|
|
102
|
+
fontDisplay: weights.fontDisplay,
|
|
103
|
+
fontStretch: weights.fontStretch
|
|
104
|
+
})
|
|
93
105
|
}
|
|
94
106
|
|
|
95
107
|
export const getFontFaceString = (LIBRARY, files) => {
|