@symbo.ls/scratch 3.1.2 → 3.2.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/dist/cjs/factory.js +23 -475
- package/dist/cjs/index.js +11 -2684
- package/dist/cjs/set.js +66 -1992
- package/dist/esm/factory.js +43 -0
- package/dist/esm/index.js +12 -0
- package/dist/esm/set.js +148 -0
- package/dist/iife/index.js +2580 -0
- package/package.json +26 -16
- package/src/set.js +22 -7
- package/src/system/color.js +48 -11
- package/src/system/font.js +19 -5
- package/src/system/reset.js +12 -3
- package/src/system/shadow.js +20 -13
- package/src/system/spacing.js +63 -56
- package/src/system/theme.js +4 -3
- package/src/system/timing.js +1 -1
- package/src/system/typography.js +12 -20
- package/src/transforms/index.js +212 -95
- package/src/utils/color.js +121 -31
- package/src/utils/font.js +54 -14
- package/src/utils/sequence.js +94 -39
- package/src/utils/sprite.js +10 -6
- package/src/utils/unit.js +69 -2
- package/src/utils/var.js +1 -2
- package/dist/cjs/defaultConfig/animation.js +0 -26
- package/dist/cjs/defaultConfig/cases.js +0 -26
- package/dist/cjs/defaultConfig/class.js +0 -27
- package/dist/cjs/defaultConfig/color.js +0 -28
- package/dist/cjs/defaultConfig/document.js +0 -26
- package/dist/cjs/defaultConfig/font-family.js +0 -34
- package/dist/cjs/defaultConfig/font.js +0 -26
- package/dist/cjs/defaultConfig/grid.js +0 -27
- package/dist/cjs/defaultConfig/icons.js +0 -28
- package/dist/cjs/defaultConfig/index.js +0 -222
- package/dist/cjs/defaultConfig/media.js +0 -31
- package/dist/cjs/defaultConfig/responsive.js +0 -52
- package/dist/cjs/defaultConfig/sequence.js +0 -51
- package/dist/cjs/defaultConfig/shadow.js +0 -26
- package/dist/cjs/defaultConfig/spacing.js +0 -87
- package/dist/cjs/defaultConfig/svg.js +0 -28
- package/dist/cjs/defaultConfig/templates.js +0 -26
- package/dist/cjs/defaultConfig/theme.js +0 -26
- package/dist/cjs/defaultConfig/timing.js +0 -68
- package/dist/cjs/defaultConfig/typography.js +0 -72
- package/dist/cjs/defaultConfig/unit.js +0 -28
- package/dist/cjs/package.json +0 -4
- package/dist/cjs/system/color.js +0 -1096
- package/dist/cjs/system/document.js +0 -906
- package/dist/cjs/system/font.js +0 -928
- package/dist/cjs/system/index.js +0 -2163
- package/dist/cjs/system/reset.js +0 -1018
- package/dist/cjs/system/shadow.js +0 -1305
- package/dist/cjs/system/spacing.js +0 -1257
- package/dist/cjs/system/svg.js +0 -1007
- package/dist/cjs/system/theme.js +0 -1197
- package/dist/cjs/system/timing.js +0 -1132
- package/dist/cjs/system/typography.js +0 -1243
- package/dist/cjs/tests/index.js +0 -30
- package/dist/cjs/transforms/index.js +0 -1534
- package/dist/cjs/utils/color.js +0 -336
- package/dist/cjs/utils/font.js +0 -69
- package/dist/cjs/utils/index.js +0 -1477
- package/dist/cjs/utils/sequence.js +0 -1125
- package/dist/cjs/utils/sprite.js +0 -554
- package/dist/cjs/utils/theme.js +0 -31
- package/dist/cjs/utils/unit.js +0 -28
- package/dist/cjs/utils/var.js +0 -967
package/src/transforms/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { isString, isObject, exec } from '@domql/utils'
|
|
4
4
|
import { getActiveConfig } from '../factory'
|
|
5
5
|
import {
|
|
6
6
|
getSpacingByKey,
|
|
@@ -9,134 +9,251 @@ import {
|
|
|
9
9
|
getMediaColor,
|
|
10
10
|
getTimingByKey,
|
|
11
11
|
getTimingFunction,
|
|
12
|
-
getSpacingBasedOnRatio
|
|
12
|
+
getSpacingBasedOnRatio,
|
|
13
|
+
checkIfBoxSize,
|
|
14
|
+
splitSpacedValue
|
|
13
15
|
} from '../system'
|
|
16
|
+
import {
|
|
17
|
+
getFnPrefixAndValue,
|
|
18
|
+
isResolvedColor,
|
|
19
|
+
isCSSVar,
|
|
20
|
+
CSS_NATIVE_COLOR_REGEX,
|
|
21
|
+
splitTopLevelCommas,
|
|
22
|
+
parseColorToken
|
|
23
|
+
} from '../utils'
|
|
24
|
+
|
|
25
|
+
const BORDER_STYLES = new Set([
|
|
26
|
+
'none', 'hidden', 'dotted', 'dashed', 'solid', 'double',
|
|
27
|
+
'groove', 'ridge', 'inset', 'outset', 'initial'
|
|
28
|
+
])
|
|
29
|
+
|
|
30
|
+
const GRADIENT_KEYWORDS = new Set([
|
|
31
|
+
'to', 'top', 'bottom', 'left', 'right', 'center', 'at',
|
|
32
|
+
'circle', 'ellipse', 'closest-side', 'farthest-side',
|
|
33
|
+
'closest-corner', 'farthest-corner'
|
|
34
|
+
])
|
|
35
|
+
const isBorderStyle = (str) => BORDER_STYLES.has(str)
|
|
36
|
+
|
|
37
|
+
export const transformBorder = (border) => {
|
|
38
|
+
const str = border + ''
|
|
39
|
+
|
|
40
|
+
// CSS passthrough: native CSS color syntax
|
|
41
|
+
if (CSS_NATIVE_COLOR_REGEX.test(str)) return str
|
|
42
|
+
|
|
43
|
+
// Simple CSS keywords
|
|
44
|
+
const trimmed = str.trim()
|
|
45
|
+
if (trimmed === 'none' || trimmed === '0' || trimmed === 'initial' || trimmed === 'inherit' || trimmed === 'unset') return str
|
|
14
46
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
else if (isBorderStyle(v)) return v || 'solid'
|
|
35
|
-
else if (v.slice(-2) === 'px' || v.slice(-2) === 'em') return v // TODO: add map spacing
|
|
36
|
-
else if (getColor(v).length > 2) return getColor(v)
|
|
37
|
-
return getSpacingByKey(v, 'border').border
|
|
38
|
-
}).join(' ')
|
|
47
|
+
// Space-separated tokens (CSS-like syntax)
|
|
48
|
+
const tokens = str.split(/\s+/)
|
|
49
|
+
|
|
50
|
+
return tokens
|
|
51
|
+
.map((v) => {
|
|
52
|
+
v = v.trim()
|
|
53
|
+
if (!v) return ''
|
|
54
|
+
if (isCSSVar(v)) return `var(${v})`
|
|
55
|
+
if (isBorderStyle(v)) return v
|
|
56
|
+
if (/^\d/.test(v) || v === '0') return v
|
|
57
|
+
// Try color resolution
|
|
58
|
+
const color = getColor(v)
|
|
59
|
+
if (isResolvedColor(color)) return color
|
|
60
|
+
// Try spacing key
|
|
61
|
+
const spacing = getSpacingByKey(v, 'border')
|
|
62
|
+
if (spacing && spacing.border) return spacing.border
|
|
63
|
+
return v
|
|
64
|
+
})
|
|
65
|
+
.join(' ')
|
|
39
66
|
}
|
|
40
67
|
|
|
41
|
-
export const transformTextStroke = stroke => {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
68
|
+
export const transformTextStroke = (stroke) => {
|
|
69
|
+
// CSS passthrough
|
|
70
|
+
if (CSS_NATIVE_COLOR_REGEX.test(stroke)) return stroke
|
|
71
|
+
|
|
72
|
+
return stroke
|
|
73
|
+
.split(/\s+/)
|
|
74
|
+
.map((v) => {
|
|
75
|
+
v = v.trim()
|
|
76
|
+
if (!v) return ''
|
|
77
|
+
if (isCSSVar(v)) return `var(${v})`
|
|
78
|
+
if (/^\d/.test(v) || v.includes('px') || v === '0') return v
|
|
79
|
+
const color = getColor(v)
|
|
80
|
+
if (isResolvedColor(color)) return color
|
|
81
|
+
return v
|
|
82
|
+
})
|
|
83
|
+
.join(' ')
|
|
48
84
|
}
|
|
49
85
|
|
|
50
86
|
export const transformShadow = (sh, globalTheme) => getShadow(sh, globalTheme)
|
|
51
87
|
|
|
52
|
-
export const transformBoxShadow = (shadows, globalTheme) =>
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
88
|
+
export const transformBoxShadow = (shadows, globalTheme) => {
|
|
89
|
+
// CSS passthrough: native CSS color syntax
|
|
90
|
+
if (CSS_NATIVE_COLOR_REGEX.test(shadows)) return shadows
|
|
91
|
+
|
|
92
|
+
// Split multiple shadows by commas (CSS standard), respecting parentheses
|
|
93
|
+
return splitTopLevelCommas(shadows)
|
|
94
|
+
.map((shadow) => {
|
|
95
|
+
shadow = shadow.trim()
|
|
96
|
+
if (!shadow) return ''
|
|
97
|
+
|
|
98
|
+
// Each shadow: space-separated tokens
|
|
99
|
+
return shadow.split(/\s+/)
|
|
100
|
+
.map((v) => {
|
|
101
|
+
v = v.trim()
|
|
102
|
+
if (!v) return ''
|
|
103
|
+
if (isCSSVar(v)) return `var(${v})`
|
|
104
|
+
if (v === 'inset' || v === 'none') return v
|
|
105
|
+
|
|
106
|
+
// Try color resolution
|
|
107
|
+
const color = getColor(v)
|
|
108
|
+
if (isResolvedColor(color)) {
|
|
109
|
+
const mediaColor = getMediaColor(v, globalTheme)
|
|
110
|
+
if (isObject(mediaColor))
|
|
111
|
+
return Object.values(mediaColor).filter((c) =>
|
|
112
|
+
c.includes(': ' + globalTheme)
|
|
113
|
+
)[0]
|
|
114
|
+
return mediaColor
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// CSS unit values
|
|
118
|
+
if (/^\d/.test(v) || v === '0' || v.includes('px') || v.slice(-2) === 'em') return v
|
|
119
|
+
|
|
120
|
+
// Spacing key
|
|
121
|
+
const spacing = getSpacingByKey(v, 'shadow')
|
|
122
|
+
if (spacing && spacing.shadow) return spacing.shadow
|
|
123
|
+
|
|
124
|
+
return v
|
|
125
|
+
})
|
|
126
|
+
.join(' ')
|
|
127
|
+
})
|
|
128
|
+
.join(', ')
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Resolve Symbols color tokens inside a CSS gradient string.
|
|
133
|
+
* e.g. 'linear-gradient(to bottom, white.97 65%, white.0 100%)'
|
|
134
|
+
* → 'linear-gradient(to bottom, rgba(255, 255, 255, 0.97) 65%, rgba(255, 255, 255, 0.0) 100%)'
|
|
135
|
+
*/
|
|
136
|
+
export const resolveColorsInGradient = (gradient, globalTheme) => {
|
|
137
|
+
// Find the opening paren after the gradient type
|
|
138
|
+
const parenStart = gradient.indexOf('(')
|
|
139
|
+
if (parenStart === -1) return gradient
|
|
140
|
+
|
|
141
|
+
const prefix = gradient.slice(0, parenStart + 1)
|
|
142
|
+
const inner = gradient.slice(parenStart + 1, gradient.lastIndexOf(')'))
|
|
143
|
+
const suffix = ')'
|
|
144
|
+
|
|
145
|
+
// Split by top-level commas (respects nested rgba() etc.)
|
|
146
|
+
const segments = splitTopLevelCommas(inner)
|
|
147
|
+
|
|
148
|
+
const resolved = segments.map((segment) => {
|
|
149
|
+
segment = segment.trim()
|
|
150
|
+
// Split segment into space-separated tokens
|
|
151
|
+
const tokens = segment.split(/\s+/)
|
|
152
|
+
|
|
153
|
+
return tokens.map((token) => {
|
|
154
|
+
if (!token) return token
|
|
155
|
+
// Skip CSS values: percentages, degrees, direction keywords, native colors
|
|
156
|
+
if (/^\d/.test(token) || token === '0') return token
|
|
157
|
+
if (GRADIENT_KEYWORDS.has(token)) return token
|
|
158
|
+
if (token === 'transparent') return token
|
|
159
|
+
if (CSS_NATIVE_COLOR_REGEX.test(token)) return token
|
|
160
|
+
|
|
161
|
+
// Try to resolve as a Symbols color token
|
|
162
|
+
const color = getColor(token)
|
|
163
|
+
if (isResolvedColor(color)) return color
|
|
164
|
+
|
|
165
|
+
return token
|
|
166
|
+
}).join(' ')
|
|
167
|
+
})
|
|
168
|
+
|
|
169
|
+
return prefix + resolved.join(', ') + suffix
|
|
170
|
+
}
|
|
67
171
|
|
|
68
172
|
export const transformBackgroundImage = (backgroundImage, globalTheme) => {
|
|
69
173
|
const CONFIG = getActiveConfig()
|
|
70
|
-
return backgroundImage
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
return
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
174
|
+
return backgroundImage
|
|
175
|
+
.split(', ')
|
|
176
|
+
.map((v) => {
|
|
177
|
+
if (isCSSVar(v)) return `var(${v})`
|
|
178
|
+
if (v.includes('url')) return v
|
|
179
|
+
if (v.includes('gradient')) return resolveColorsInGradient(v, globalTheme)
|
|
180
|
+
else if (CONFIG.GRADIENT[backgroundImage]) {
|
|
181
|
+
return {
|
|
182
|
+
backgroundImage: getMediaColor(
|
|
183
|
+
backgroundImage,
|
|
184
|
+
globalTheme || CONFIG.globalTheme
|
|
185
|
+
)
|
|
186
|
+
}
|
|
187
|
+
} else if (v.includes('/') || v.startsWith('http') || (v.includes('.') && !parseColorToken(v)))
|
|
188
|
+
return `url(${v})`
|
|
189
|
+
return v
|
|
190
|
+
})
|
|
191
|
+
.join(' ')
|
|
80
192
|
}
|
|
81
193
|
|
|
82
|
-
export const transfromGap =
|
|
83
|
-
|
|
84
|
-
|
|
194
|
+
export const transfromGap = (gap) =>
|
|
195
|
+
isString(gap) &&
|
|
196
|
+
gap
|
|
197
|
+
.split(' ')
|
|
198
|
+
.map((v) => getSpacingByKey(v, 'gap').gap)
|
|
199
|
+
.join(' ')
|
|
85
200
|
|
|
86
|
-
export const transformTransition = transition => {
|
|
201
|
+
export const transformTransition = (transition) => {
|
|
87
202
|
const arr = transition.split(' ')
|
|
88
203
|
|
|
89
204
|
if (!arr.length) return transition
|
|
90
205
|
|
|
91
|
-
return arr
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
206
|
+
return arr
|
|
207
|
+
.map((v) => {
|
|
208
|
+
if (isCSSVar(v)) return `var(${v})`
|
|
209
|
+
if (v.length < 3 || v.includes('ms')) {
|
|
210
|
+
const mapWithSequence = getTimingByKey(v)
|
|
211
|
+
return mapWithSequence.timing || v
|
|
212
|
+
}
|
|
213
|
+
if (getTimingFunction(v)) return getTimingFunction(v)
|
|
214
|
+
return v
|
|
215
|
+
})
|
|
216
|
+
.join(' ')
|
|
100
217
|
}
|
|
101
218
|
|
|
102
219
|
export const transformDuration = (duration, props, propertyName) => {
|
|
103
220
|
if (!isString(duration)) return
|
|
104
|
-
return duration
|
|
221
|
+
return duration
|
|
222
|
+
.split(',')
|
|
223
|
+
.map((v) => getTimingByKey(v).timing || v)
|
|
224
|
+
.join(',')
|
|
105
225
|
}
|
|
106
226
|
|
|
107
|
-
export const splitTransition = transition => {
|
|
227
|
+
export const splitTransition = (transition) => {
|
|
108
228
|
const arr = transition.split(',')
|
|
109
229
|
if (!arr.length) return
|
|
110
230
|
return arr.map(transformTransition).join(',')
|
|
111
231
|
}
|
|
112
232
|
|
|
113
|
-
export
|
|
114
|
-
|
|
115
|
-
return (prop.includes('width') || prop.includes('height')) && !prop.includes('border')
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
export const transformSize = (propertyName, val, props = {}, opts = {}) => {
|
|
119
|
-
let value = val || props[propertyName]
|
|
233
|
+
export function transformSize(propertyName, val, props = {}, opts = {}) {
|
|
234
|
+
let value = exec.call(this, val || props[propertyName])
|
|
120
235
|
|
|
121
|
-
if (
|
|
236
|
+
if (value === undefined || value === null) return
|
|
122
237
|
|
|
123
|
-
|
|
124
|
-
|
|
238
|
+
let fnPrefix
|
|
239
|
+
if (isString(value)) {
|
|
240
|
+
// has function prefix
|
|
241
|
+
if (value.includes('(')) {
|
|
242
|
+
const fnArr = getFnPrefixAndValue(value)
|
|
243
|
+
fnPrefix = fnArr[0]
|
|
244
|
+
value = fnArr[1]
|
|
245
|
+
}
|
|
125
246
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
return v
|
|
132
|
-
}).join(' ')
|
|
247
|
+
const shouldScaleBoxSize = props.scaleBoxSize
|
|
248
|
+
const isBoxSize = checkIfBoxSize(propertyName)
|
|
249
|
+
if (!shouldScaleBoxSize && isBoxSize) {
|
|
250
|
+
value = splitSpacedValue(value)
|
|
251
|
+
}
|
|
133
252
|
}
|
|
134
253
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
return getSpacingByKey(value, propertyName)
|
|
139
|
-
}
|
|
254
|
+
return opts.ratio
|
|
255
|
+
? getSpacingBasedOnRatio(props, propertyName, value, fnPrefix)
|
|
256
|
+
: getSpacingByKey(value, propertyName, undefined, fnPrefix)
|
|
140
257
|
}
|
|
141
258
|
|
|
142
259
|
export const transformSizeRatio = (propertyName, val = null, props) => {
|
package/src/utils/color.js
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
|
|
3
|
+
import {
|
|
4
|
+
document,
|
|
5
|
+
window,
|
|
6
|
+
isString,
|
|
7
|
+
isNumber,
|
|
8
|
+
isNotProduction
|
|
9
|
+
} from '@domql/utils'
|
|
5
10
|
|
|
6
11
|
export const colorStringToRgbaArray = color => {
|
|
7
12
|
if (color === '') return [0, 0, 0, 0]
|
|
@@ -10,11 +15,22 @@ export const colorStringToRgbaArray = color => {
|
|
|
10
15
|
// convert #RGB and #RGBA to #RRGGBB and #RRGGBBAA
|
|
11
16
|
if (color[0] === '#') {
|
|
12
17
|
if (color.length < 7) {
|
|
13
|
-
color =
|
|
14
|
-
|
|
18
|
+
color =
|
|
19
|
+
'#' +
|
|
20
|
+
color[1] +
|
|
21
|
+
color[1] +
|
|
22
|
+
color[2] +
|
|
23
|
+
color[2] +
|
|
24
|
+
color[3] +
|
|
25
|
+
color[3] +
|
|
26
|
+
(color.length > 4 ? color[4] + color[4] : '')
|
|
27
|
+
}
|
|
28
|
+
return [
|
|
29
|
+
parseInt(color.substr(1, 2), 16),
|
|
15
30
|
parseInt(color.substr(3, 2), 16),
|
|
16
31
|
parseInt(color.substr(5, 2), 16),
|
|
17
|
-
color.length > 7 ? parseInt(color.substr(7, 2), 16) / 255 : 1
|
|
32
|
+
color.length > 7 ? parseInt(color.substr(7, 2), 16) / 255 : 1
|
|
33
|
+
]
|
|
18
34
|
}
|
|
19
35
|
|
|
20
36
|
// convert named colors
|
|
@@ -81,11 +97,7 @@ export const hexToRgba = (hex, alpha = 1) => {
|
|
|
81
97
|
export const mixTwoRgb = (colorA, colorB, range = 0.5) => {
|
|
82
98
|
const arr = []
|
|
83
99
|
for (let i = 0; i < 3; i++) {
|
|
84
|
-
arr[i] = ~~(
|
|
85
|
-
colorA[i] + (
|
|
86
|
-
(colorB[i] - colorA[i]) * range
|
|
87
|
-
)
|
|
88
|
-
)
|
|
100
|
+
arr[i] = ~~(colorA[i] + (colorB[i] - colorA[i]) * range)
|
|
89
101
|
}
|
|
90
102
|
return `rgb(${arr})`
|
|
91
103
|
}
|
|
@@ -93,25 +105,27 @@ export const mixTwoRgb = (colorA, colorB, range = 0.5) => {
|
|
|
93
105
|
export const changeLightness = (delta, hsl) => {
|
|
94
106
|
const [hue, saturation, lightness] = hsl
|
|
95
107
|
|
|
96
|
-
const newLightness = Math.max(
|
|
97
|
-
0,
|
|
98
|
-
Math.min(100, lightness + parseFloat(delta))
|
|
99
|
-
)
|
|
108
|
+
const newLightness = Math.max(0, Math.min(100, lightness + parseFloat(delta)))
|
|
100
109
|
|
|
101
110
|
return [hue, saturation, newLightness]
|
|
102
111
|
}
|
|
103
112
|
|
|
104
113
|
export const rgbToHSL = (r, g, b) => {
|
|
105
|
-
const a = Math.max(r, g, b)
|
|
106
|
-
const
|
|
114
|
+
const a = Math.max(r, g, b)
|
|
115
|
+
const n = a - Math.min(r, g, b)
|
|
116
|
+
const f = 1 - Math.abs(a + a - n - 1)
|
|
117
|
+
const h =
|
|
118
|
+
n && (a == r ? (g - b) / n : a == g ? 2 + (b - r) / n : 4 + (r - g) / n) //eslint-disable-line
|
|
107
119
|
return [60 * (h < 0 ? h + 6 : h), f ? n / f : 0, (a + a - n) / 2]
|
|
108
120
|
}
|
|
109
121
|
|
|
110
|
-
export const hslToRgb = (
|
|
122
|
+
export const hslToRgb = (
|
|
123
|
+
h,
|
|
124
|
+
s,
|
|
125
|
+
l,
|
|
111
126
|
a = s * Math.min(l, 1 - l),
|
|
112
|
-
f = (n, k = (n + h / 30) % 12) =>
|
|
113
|
-
Math.min(k - 3, 9 - k, 1), -1
|
|
114
|
-
)
|
|
127
|
+
f = (n, k = (n + h / 30) % 12) =>
|
|
128
|
+
l - a * Math.max(Math.min(k - 3, 9 - k, 1), -1)
|
|
115
129
|
) => [f(0), f(8), f(4)]
|
|
116
130
|
|
|
117
131
|
export const getColorShade = (col, amt) => {
|
|
@@ -122,12 +136,12 @@ export const getColorShade = (col, amt) => {
|
|
|
122
136
|
if (r > 255) r = 255
|
|
123
137
|
else if (r < 0) r = 0
|
|
124
138
|
|
|
125
|
-
let b = ((num >> 8) &
|
|
139
|
+
let b = ((num >> 8) & 0x00ff) + amt
|
|
126
140
|
|
|
127
141
|
if (b > 255) b = 255
|
|
128
142
|
else if (b < 0) b = 0
|
|
129
143
|
|
|
130
|
-
let g = (num &
|
|
144
|
+
let g = (num & 0x0000ff) + amt
|
|
131
145
|
|
|
132
146
|
if (g > 255) g = 255
|
|
133
147
|
else if (g < 0) g = 0
|
|
@@ -138,12 +152,8 @@ export const getColorShade = (col, amt) => {
|
|
|
138
152
|
export const mixTwoRgba = (colorA, colorB, range = 0.5) => {
|
|
139
153
|
const arr = []
|
|
140
154
|
for (let i = 0; i < 4; i++) {
|
|
141
|
-
const round =
|
|
142
|
-
arr[i] = round(
|
|
143
|
-
(colorA[i] + (
|
|
144
|
-
(colorB[i] - colorA[i]) * range
|
|
145
|
-
))
|
|
146
|
-
)
|
|
155
|
+
const round = i === 3 ? x => x : Math.round
|
|
156
|
+
arr[i] = round(colorA[i] + (colorB[i] - colorA[i]) * range)
|
|
147
157
|
}
|
|
148
158
|
return `rgba(${arr})`
|
|
149
159
|
}
|
|
@@ -151,15 +161,95 @@ export const mixTwoRgba = (colorA, colorB, range = 0.5) => {
|
|
|
151
161
|
export const opacify = (color, opacity) => {
|
|
152
162
|
const arr = colorStringToRgbaArray(color)
|
|
153
163
|
if (!arr) {
|
|
154
|
-
if (
|
|
164
|
+
if (isNotProduction()) console.warn(color + ' color is not rgba')
|
|
155
165
|
return
|
|
156
166
|
}
|
|
157
167
|
arr[3] = opacity
|
|
158
168
|
return `rgba(${arr})`
|
|
159
169
|
}
|
|
160
170
|
|
|
171
|
+
// Check if value is a CSS custom property reference (starts with --)
|
|
172
|
+
export const isCSSVar = (v) => v.charCodeAt(0) === 45 && v.charCodeAt(1) === 45
|
|
173
|
+
|
|
174
|
+
// Regex for CSS native color values - signals passthrough
|
|
175
|
+
export const CSS_NATIVE_COLOR_REGEX = /(?:rgba?\(|hsla?\(|#[0-9a-fA-F]{3,8}\b)/
|
|
176
|
+
|
|
177
|
+
// Regex for Symbols color token: colorName[.opacity][+/-/=tone]
|
|
178
|
+
// +N or -N = relative shade, =N = absolute lightness percentage
|
|
179
|
+
const COLOR_TOKEN_REGEX = /^([a-zA-Z]\w*)(?:\.(\d+))?(?:([+-]\d+|=\d+))?$/
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Parse a color token string into its components.
|
|
183
|
+
* Returns { name, alpha, tone } for valid tokens,
|
|
184
|
+
* { passthrough: value } for CSS native values,
|
|
185
|
+
* or null for non-color tokens (e.g. '10px', '0')
|
|
186
|
+
*
|
|
187
|
+
* Tone prefixes:
|
|
188
|
+
* +N relative shade (add N to RGB)
|
|
189
|
+
* -N relative shade (subtract N from RGB)
|
|
190
|
+
* =N absolute lightness (set HSL lightness to N%)
|
|
191
|
+
*/
|
|
192
|
+
export const parseColorToken = (value) => {
|
|
193
|
+
if (!isString(value)) return null
|
|
194
|
+
|
|
195
|
+
// CSS native color passthrough
|
|
196
|
+
if (CSS_NATIVE_COLOR_REGEX.test(value)) return { passthrough: value }
|
|
197
|
+
|
|
198
|
+
// CSS var passthrough
|
|
199
|
+
if (isCSSVar(value)) return { cssVar: value }
|
|
200
|
+
|
|
201
|
+
const match = value.match(COLOR_TOKEN_REGEX)
|
|
202
|
+
if (!match) return null
|
|
203
|
+
|
|
204
|
+
const [, name, alphaDigits, rawTone] = match
|
|
205
|
+
const alpha = alphaDigits !== undefined ? `0.${alphaDigits}` : undefined
|
|
206
|
+
// Strip '=' prefix for absolute tones — getRgbTone handles unsigned values as absolute
|
|
207
|
+
const tone = rawTone && rawTone[0] === '=' ? rawTone.slice(1) : rawTone
|
|
208
|
+
|
|
209
|
+
return { name, alpha, tone }
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Check if a getColor() result is a resolved CSS color
|
|
214
|
+
* (as opposed to the original unresolved value being returned)
|
|
215
|
+
*/
|
|
216
|
+
export const isResolvedColor = (result) => {
|
|
217
|
+
return isString(result) && (
|
|
218
|
+
result.includes('rgb') ||
|
|
219
|
+
result.includes('var(') ||
|
|
220
|
+
result.includes('#')
|
|
221
|
+
)
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Split a string by commas, respecting parenthesized groups.
|
|
226
|
+
* e.g. 'rgba(0,0,0,0.1), white.5 0 A' → ['rgba(0,0,0,0.1)', ' white.5 0 A']
|
|
227
|
+
*/
|
|
228
|
+
export const splitTopLevelCommas = (value) => {
|
|
229
|
+
const result = []
|
|
230
|
+
let current = ''
|
|
231
|
+
let depth = 0
|
|
232
|
+
|
|
233
|
+
for (const char of value) {
|
|
234
|
+
if (char === '(') depth += 1
|
|
235
|
+
else if (char === ')' && depth > 0) depth -= 1
|
|
236
|
+
|
|
237
|
+
if (char === ',' && depth === 0) {
|
|
238
|
+
result.push(current)
|
|
239
|
+
current = ''
|
|
240
|
+
continue
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
current += char
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
if (current.length || !result.length) result.push(current)
|
|
247
|
+
return result
|
|
248
|
+
}
|
|
249
|
+
|
|
161
250
|
export const getRgbTone = (rgb, tone) => {
|
|
162
|
-
if (isString(rgb) && rgb.includes('rgb'))
|
|
251
|
+
if (isString(rgb) && rgb.includes('rgb'))
|
|
252
|
+
rgb = colorStringToRgbaArray(rgb).join(', ')
|
|
163
253
|
if (isString(rgb)) rgb = rgb.split(',').map(v => parseFloat(v.trim()))
|
|
164
254
|
if (isNumber(tone)) tone += ''
|
|
165
255
|
const toHex = rgbArrayToHex(rgb)
|
|
@@ -172,7 +262,7 @@ export const getRgbTone = (rgb, tone) => {
|
|
|
172
262
|
const [r, g, b] = rgb
|
|
173
263
|
const hsl = rgbToHSL(r, g, b)
|
|
174
264
|
const [h, s, l] = hsl // eslint-disable-line
|
|
175
|
-
const newRgb = hslToRgb(h, s, parseFloat(tone) / 100 * 255)
|
|
265
|
+
const newRgb = hslToRgb(h, s, (parseFloat(tone) / 100) * 255)
|
|
176
266
|
return newRgb
|
|
177
267
|
}
|
|
178
268
|
}
|
package/src/utils/font.js
CHANGED
|
@@ -7,41 +7,81 @@ export const getDefaultOrFirstKey = (LIBRARY, key) => {
|
|
|
7
7
|
return hasValue && LIBRARY[hasValue] && LIBRARY[hasValue].value
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
-
export const getFontFormat = url =>
|
|
10
|
+
export const getFontFormat = (url) => {
|
|
11
|
+
const ext = url.split(/[#?]/)[0].split('.').pop().trim()
|
|
12
|
+
if (['woff2', 'woff', 'ttf', 'otf', 'eot'].includes(ext)) return ext
|
|
13
|
+
return null
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const isGoogleFontsUrl = (url) =>
|
|
17
|
+
url &&
|
|
18
|
+
(url.includes('fonts.googleapis.com') || url.includes('fonts.gstatic.com'))
|
|
11
19
|
|
|
12
|
-
export const
|
|
20
|
+
export const setFontImport = (url) => `@import url('${url}');`
|
|
13
21
|
|
|
14
|
-
export const
|
|
22
|
+
export const setInCustomFontMedia = (str) => `@font-face { ${str} }`
|
|
23
|
+
|
|
24
|
+
export const setCustomFont = (name, url, weight, options = {}) => {
|
|
25
|
+
const format = getFontFormat(url)
|
|
26
|
+
const formatStr = format ? ` format('${format}')` : ''
|
|
27
|
+
return `
|
|
15
28
|
font-family: '${name}';
|
|
16
|
-
font-style: normal
|
|
17
|
-
|
|
18
|
-
|
|
29
|
+
font-style: normal;${
|
|
30
|
+
weight
|
|
31
|
+
? `
|
|
32
|
+
font-weight: ${weight};`
|
|
33
|
+
: ''
|
|
34
|
+
}${
|
|
35
|
+
options.fontStretch
|
|
36
|
+
? `
|
|
37
|
+
font-stretch: ${options.fontStretch};`
|
|
38
|
+
: ''
|
|
39
|
+
}${
|
|
40
|
+
options.fontDisplay
|
|
41
|
+
? `
|
|
42
|
+
font-display: ${options.fontDisplay};`
|
|
43
|
+
: ''
|
|
44
|
+
}
|
|
45
|
+
src: url('${url}')${formatStr};`
|
|
46
|
+
}
|
|
19
47
|
|
|
20
|
-
export const setCustomFontMedia = (
|
|
21
|
-
|
|
48
|
+
export const setCustomFontMedia = (
|
|
49
|
+
name,
|
|
50
|
+
url,
|
|
51
|
+
weight,
|
|
52
|
+
options
|
|
53
|
+
) => `@font-face {${setCustomFont(name, url, weight, options)}
|
|
22
54
|
}`
|
|
23
|
-
// src: url('${url}') format('${getFontFormat(url)}');
|
|
24
55
|
|
|
25
56
|
export const getFontFaceEach = (name, weights) => {
|
|
26
57
|
const keys = Object.keys(weights)
|
|
27
|
-
return keys.map(key => {
|
|
58
|
+
return keys.map((key) => {
|
|
28
59
|
const { url, fontWeight } = weights[key]
|
|
29
60
|
return setCustomFont(name, url, fontWeight)
|
|
30
61
|
})
|
|
31
62
|
}
|
|
32
63
|
|
|
33
|
-
export const getFontFace = LIBRARY => {
|
|
64
|
+
export const getFontFace = (LIBRARY) => {
|
|
34
65
|
const keys = Object.keys(LIBRARY)
|
|
35
|
-
return keys.map(key => getFontFaceEach(key, LIBRARY[key].value))
|
|
66
|
+
return keys.map((key) => getFontFaceEach(key, LIBRARY[key].value))
|
|
36
67
|
}
|
|
37
68
|
|
|
38
69
|
export const getFontFaceEachString = (name, weights) => {
|
|
70
|
+
if (weights && weights.isVariable) {
|
|
71
|
+
if (isGoogleFontsUrl(weights.url)) {
|
|
72
|
+
return setFontImport(weights.url)
|
|
73
|
+
}
|
|
74
|
+
return setCustomFontMedia(name, weights.url, weights.fontWeight, {
|
|
75
|
+
fontStretch: weights.fontStretch,
|
|
76
|
+
fontDisplay: weights.fontDisplay || 'swap'
|
|
77
|
+
})
|
|
78
|
+
}
|
|
39
79
|
const isArr = weights[0]
|
|
40
80
|
if (isArr) return getFontFaceEach(name, weights).map(setInCustomFontMedia)
|
|
41
81
|
return setCustomFontMedia(name, weights.url)
|
|
42
82
|
}
|
|
43
83
|
|
|
44
|
-
export const getFontFaceString = LIBRARY => {
|
|
84
|
+
export const getFontFaceString = (LIBRARY) => {
|
|
45
85
|
const keys = Object.keys(LIBRARY)
|
|
46
|
-
return keys.map(key => getFontFaceEachString(key, LIBRARY[key].value))
|
|
86
|
+
return keys.map((key) => getFontFaceEachString(key, LIBRARY[key].value))
|
|
47
87
|
}
|