-
Notifications
You must be signed in to change notification settings - Fork 257
/
Copy pathindex.js
457 lines (357 loc) · 12.2 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
import { assign, create, createComponent, defineProperties, getOwnPropertyDescriptors } from './Object.js'
import { from } from './Array.js'
import { ownKeys } from './Reflect.js'
import StringSet from './StringSet.js'
import createGetComputedCss from './createGetComputedCss.js'
import defaultThemeMap from './defaultThemeMap.js'
import getCustomProperties from './getCustomProperties.js'
import getHashString from './getHashString.js'
import ThemeToken from './ThemeToken.js'
import { $$composers } from './Symbol.js'
import StringArray from './StringArray.js'
/** Returns a new styled sheet and accompanying API. */
const createCss = (init) => {
init = Object(init)
/** Named conditions (media and support queries). */
const conditions = assign({ initial: '@media all' }, init.conditions)
/** Theme tokens enabled by default on the styled sheet. */
const themeInit = Object(init.theme)
const themeMap = Object(init.themeMap || defaultThemeMap)
/** Properties corresponding to functions that take in CSS values and return aliased CSS declarations. */
const utils = Object(init.utils)
/** Names of variants passed through to props. */
const passThru = new Set([].concat(init.passthru || ['as', 'className']))
/** Prefix added before all generated class names. */
const prefix = init.prefix || 'sx'
const emptyClassName = '03kze'
const config = {
theme: themeInit,
conditions,
prefix,
themeMap,
utils,
}
/** Returns a string of unnested CSS from an object of nestable CSS. */
const getComputedCss = createGetComputedCss(config)
/** Collection of `@import` CSS rules. */
const importCss = new StringSet()
/** Collection of theming CSS rules. */
const themedCss = new StringSet()
/** Collection of global CSS rules. */
const globalCss = new StringSet()
/** Collection of component CSS rules. */
const styledCss = new StringSet()
const unitedCss = new StringSet([importCss, themedCss, globalCss, styledCss])
let currentCssText = ''
let currentCssHead = null
let currentCssNode = null
const update = () => {
const nextUpdate = from(unitedCss).join('')
if (currentCssText !== nextUpdate) {
currentCssText = nextUpdate
if (typeof document === 'object') {
if (!currentCssHead) currentCssHead = document.head || document.documentElement
if (!currentCssNode) currentCssNode = document.getElementById('stitches') || assign(document.createElement('style'), { id: 'stitches' })
if (!currentCssNode.parentNode) currentCssHead.prepend(currentCssNode)
currentCssNode.textContent = nextUpdate
}
}
}
/** Prepares global CSS and returns a function that enables the styles on the styled sheet. */
const theme = (
/** Class name */
className,
/** Object of theme scales with inner token values. */
theme,
) => {
// theme is the first argument if it is an object, otherwise the second argument as an object
theme = className === Object(className) ? className : Object(theme)
// class name is the first argument if it is a string, otherwise an empty string
className = typeof className === 'string' ? className : ''
/** Custom property styles representing themed token values. */
const customPropertyStyles = getCustomProperties(theme)
// class name is either itself or the unique hash representing its styles
className = className || getHashString(prefix, customPropertyStyles)
/** CSS Selector */
const selector = className.replace(/^\w/, '.$&')
/** Computed CSS */
const cssText = className === prefix + emptyClassName ? '' : getComputedCss({ [selector]: customPropertyStyles })
const expression = createComponent(create(null), 'className', {
className,
selector,
})
for (const scale in theme) {
expression[scale] = create(null)
for (const token in theme[scale]) {
expression[scale][token] = new ThemeToken(theme[scale][token], token, scale)
}
}
return createComponent(expression, 'className', {
get className() {
const { hasChanged } = themedCss
themedCss.add(cssText)
if (hasChanged()) {
update()
}
return className
},
selector,
})
}
/** Returns a function that enables the styles on the styled sheet. */
const global = (
/** Styles representing global CSS. */
style,
) => {
/** List of global import styles. */
const localImportCss = new StringSet()
/** List of global styles. */
const localGlobalCss = new StringSet()
for (const name in style) {
if (style[name] !== Object(style[name]) || ownKeys(style[name]).length) {
const cssText = getComputedCss({ [name]: style[name] })
;(name === '@import' ? localImportCss : localGlobalCss).add(cssText)
}
}
const expression = createComponent(create(null), 'displayName', {
displayName: '',
})
return createComponent(
() => {
let hasImportChanged = importCss.hasChanged
let hasGlobalChanged = globalCss.hasChanged
localImportCss.forEach((localImportCss) => {
importCss.add(localImportCss)
})
localGlobalCss.forEach((localGlobalCss) => {
globalCss.add(localGlobalCss)
})
if (hasImportChanged() || hasGlobalChanged()) {
update()
}
return expression
},
'displayName',
expression,
)
}
/** Returns a function that enables the keyframe styles on the styled sheet. */
const keyframes = (
/** Styles representing global CSS. */
style,
) => {
/** Unique name representing the current keyframes rule. */
const displayName = getHashString(prefix, style)
return assign(global({ ['@keyframes ' + displayName]: style }), { displayName })
}
const createComposer = (initStyle) => {
const primalCss = new StringSet()
const variedCss = new StringArray()
const inlineCss = new StringSet()
const unitedCss = new StringSet([primalCss, variedCss, inlineCss])
let { variants: singularVariants, compoundVariants, defaultVariants, ...style } = initStyle
defaultVariants = Object(defaultVariants)
const className = getHashString(prefix, initStyle)
const selector = '.' + className
const cssText = className === prefix + emptyClassName ? '' : getComputedCss({ [selector]: style })
styledCss.add(unitedCss)
const variantProps = create(null)
const variants = []
const compounds = []
for (const key in singularVariants) {
for (const value in singularVariants[key]) {
const css = singularVariants[key][value]
compounds.push({
[key]: value,
css,
})
}
}
compounds.push(...(compoundVariants || []))
for (const index in compounds) {
const { css, ...variantConfig } = compounds[index]
const variantConfigKeys = ownKeys(variantConfig)
const variantConfigIndex = variantConfigKeys.length
for (const variantKey of variantConfigKeys) {
variantProps[variantKey] = variantProps[variantKey] || create(null)
variantProps[variantKey][variantConfig[variantKey]] = true
}
const applyVariant = (variantInput, defaultVariants) => {
variantInput = { ...variantInput }
for (const defaultVariantName in defaultVariants) {
if (variantInput[defaultVariantName] === undefined && !variantProps[defaultVariantName][variantInput[defaultVariantName]]) {
variantInput[defaultVariantName] = defaultVariants[defaultVariantName]
}
}
const variantConditions = new Set()
if (
variantConfigKeys.length &&
variantConfigKeys.every((key) => {
const value = variantInput[key]
const compareValue = String(variantConfig[key])
if (compareValue === String(value)) return true
if (value === Object(value)) {
for (const condition in value) {
if (compareValue == String(value[condition])) {
variantConditions.add(condition)
return true
}
}
}
})
) {
let conditionedCss = Object(css)
for (const variantCondition of variantConditions) {
conditionedCss = { [variantCondition in conditions ? conditions[variantCondition] : variantCondition]: conditionedCss }
}
const variantClassName = className + getHashString('', conditionedCss) + '--' + (variantConfigIndex === 1 ? variantConfigKeys[0] + '-' + variantConfig[variantConfigKeys[0]] : 'c' + variantConfigIndex)
const variantSelector = '.' + variantClassName
const variantCssText = getComputedCss({ [variantSelector]: conditionedCss })
const variantCssByIndex = variedCss[variantConfigIndex - 1] || (variedCss[variantConfigIndex - 1] = new StringSet())
variantCssByIndex.add(variantCssText)
return variantClassName
}
}
variants.push(applyVariant)
}
return {
apply(props, classNames, defaultVariants) {
const hasPrimalChanged = primalCss.hasChanged
const hasVariedChanged = variedCss.hasChanged
primalCss.add(cssText)
if (props) {
classNames.add(className)
for (const variant of variants) {
const variantClassName = variant(props, defaultVariants)
if (variantClassName) {
classNames.add(variantClassName)
}
}
}
if (hasPrimalChanged() || hasVariedChanged()) {
styledCss.add(unitedCss)
return true
}
},
inline(css, classNames) {
const inlineSuffix = getHashString('-', css)
const inlineSelector = selector + inlineSuffix
const inlineCssText = className === '-' + inlineSuffix ? '' : getComputedCss({ [inlineSelector]: css })
classNames.add(className + inlineSuffix)
const { hasChanged } = inlineCss
if (inlineCssText) {
inlineCss.add(inlineCssText)
}
return hasChanged()
},
className,
defaultVariants,
selector,
variantProps,
}
}
const css = (...inits) => {
let type
let composers = []
let composer
let defaultVariants = create(null)
for (const init of inits) {
if ($$composers in Object(init)) {
type = init.type || type
for (const composer of init[$$composers]) {
composers.push(composer)
assign(defaultVariants, composer.defaultVariants)
}
} else if (init && typeof init === 'object' && !('type' in init)) {
composers.push((composer = createComposer(init)))
assign(defaultVariants, composer.defaultVariants)
} else {
type = ('type' in Object(init) ? init.type : init) || type
}
}
composer = composer || createComposer({})
return createComponent(
(initProps) => {
const { css, ...props } = Object(initProps)
const classNames = new Set()
let hasComposerChanged = false
for (const composer of composers) {
hasComposerChanged = composer.apply(props, classNames, defaultVariants) || hasComposerChanged
}
let hasInlineChanged
if (css === Object(css)) {
hasInlineChanged = composer.inline(css, classNames)
}
if (hasComposerChanged || hasInlineChanged) {
update()
}
for (const variantName in composer.variantProps) {
if (!passThru.has(variantName)) {
delete props[variantName]
}
}
if ('className' in props) {
String(props.className).split(/\s+/).forEach(classNames.add, classNames)
}
const classNameSetArray = from(classNames)
props.className = classNameSetArray.join(' ')
return createComponent(create(null), 'className', {
get [$$composers]() {
return composers
},
className: props.className,
props,
selector: composer.selector,
})
},
'className',
{
get [$$composers]() {
return composers
},
/** Applies the primary composer and returns the class name. */
get className() {
if (composer.apply()) {
update()
}
return composer.className
},
selector: composer.selector,
type,
},
)
}
const defaultTheme = theme(':root', themeInit)
const sheet = createComponent(
{
css,
config,
global,
keyframes,
prefix,
reset() {
importCss.clear()
themedCss.clear()
globalCss.clear()
styledCss.clear()
defaultTheme.className
return sheet
},
theme: assign(theme, defaultTheme),
get cssText() {
return currentCssText
},
getCssString() {
return currentCssText
},
},
'cssText',
{},
)
return sheet
}
const getReusableSheet = () => getReusableSheet.config || (getReusableSheet.config = createCss())
const css = (...args) => getReusableSheet().css(...args)
const global = (...args) => getReusableSheet().global(...args)
const keyframes = (...args) => getReusableSheet().keyframes(...args)
export { createCss as default, createCss, defaultThemeMap, css, global, keyframes }