You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
194 lines
4.5 KiB
194 lines
4.5 KiB
import assign from 'object-assign'
|
|
|
|
export const merge = (a, b) => {
|
|
let result = assign({}, a, b)
|
|
for (const key in a) {
|
|
if (!a[key] || typeof b[key] !== 'object') continue
|
|
assign(result, {
|
|
[key]: assign(a[key], b[key]),
|
|
})
|
|
}
|
|
return result
|
|
}
|
|
|
|
// sort object-value responsive styles
|
|
const sort = obj => {
|
|
const next = {}
|
|
Object.keys(obj)
|
|
.sort((a, b) => a.localeCompare(b, undefined, {
|
|
numeric: true,
|
|
sensitivity: 'base',
|
|
}))
|
|
.forEach(key => {
|
|
next[key] = obj[key]
|
|
})
|
|
return next
|
|
}
|
|
|
|
const defaults = {
|
|
breakpoints: [40, 52, 64].map(n => n + 'em'),
|
|
}
|
|
const createMediaQuery = n => `@media screen and (min-width: ${n})`
|
|
const getValue = (n, scale) => get(scale, n, n)
|
|
|
|
export const get = (obj, key, def, p, undef) => {
|
|
key = key && key.split ? key.split('.') : [key]
|
|
for (p = 0; p < key.length; p++) {
|
|
obj = obj ? obj[key[p]] : undef
|
|
}
|
|
return obj === undef ? def : obj
|
|
}
|
|
|
|
export const createParser = config => {
|
|
const cache = {}
|
|
const parse = props => {
|
|
let styles = {}
|
|
let shouldSort = false
|
|
const isCacheDisabled = props.theme && props.theme.disableStyledSystemCache
|
|
|
|
for (const key in props) {
|
|
if (!config[key]) continue
|
|
const sx = config[key]
|
|
const raw = props[key]
|
|
const scale = get(props.theme, sx.scale, sx.defaults)
|
|
|
|
if (typeof raw === 'object') {
|
|
cache.breakpoints =
|
|
(!isCacheDisabled && cache.breakpoints) ||
|
|
get(props.theme, 'breakpoints', defaults.breakpoints)
|
|
if (Array.isArray(raw)) {
|
|
cache.media = (!isCacheDisabled && cache.media) || [
|
|
null,
|
|
...cache.breakpoints.map(createMediaQuery),
|
|
]
|
|
styles = merge(
|
|
styles,
|
|
parseResponsiveStyle(cache.media, sx, scale, raw, props)
|
|
)
|
|
continue
|
|
}
|
|
if (raw !== null) {
|
|
styles = merge(
|
|
styles,
|
|
parseResponsiveObject(cache.breakpoints, sx, scale, raw, props)
|
|
)
|
|
shouldSort = true
|
|
}
|
|
continue
|
|
}
|
|
|
|
assign(styles, sx(raw, scale, props))
|
|
}
|
|
|
|
// sort object-based responsive styles
|
|
if (shouldSort) {
|
|
styles = sort(styles)
|
|
}
|
|
|
|
return styles
|
|
}
|
|
parse.config = config
|
|
parse.propNames = Object.keys(config)
|
|
parse.cache = cache
|
|
|
|
const keys = Object.keys(config).filter(k => k !== 'config')
|
|
if (keys.length > 1) {
|
|
keys.forEach(key => {
|
|
parse[key] = createParser({ [key]: config[key] })
|
|
})
|
|
}
|
|
|
|
return parse
|
|
}
|
|
|
|
const parseResponsiveStyle = (mediaQueries, sx, scale, raw, _props) => {
|
|
let styles = {}
|
|
raw.slice(0, mediaQueries.length).forEach((value, i) => {
|
|
const media = mediaQueries[i]
|
|
const style = sx(value, scale, _props)
|
|
if (!media) {
|
|
assign(styles, style)
|
|
} else {
|
|
assign(styles, {
|
|
[media]: assign({}, styles[media], style),
|
|
})
|
|
}
|
|
})
|
|
return styles
|
|
}
|
|
|
|
const parseResponsiveObject = (breakpoints, sx, scale, raw, _props) => {
|
|
let styles = {}
|
|
for (let key in raw) {
|
|
const breakpoint = breakpoints[key]
|
|
const value = raw[key]
|
|
const style = sx(value, scale, _props)
|
|
if (!breakpoint) {
|
|
assign(styles, style)
|
|
} else {
|
|
const media = createMediaQuery(breakpoint)
|
|
assign(styles, {
|
|
[media]: assign({}, styles[media], style),
|
|
})
|
|
}
|
|
}
|
|
return styles
|
|
}
|
|
|
|
export const createStyleFunction = ({
|
|
properties,
|
|
property,
|
|
scale,
|
|
transform = getValue,
|
|
defaultScale,
|
|
}) => {
|
|
properties = properties || [property]
|
|
const sx = (value, scale, _props) => {
|
|
const result = {}
|
|
const n = transform(value, scale, _props)
|
|
if (n === null) return
|
|
properties.forEach(prop => {
|
|
result[prop] = n
|
|
})
|
|
return result
|
|
}
|
|
sx.scale = scale
|
|
sx.defaults = defaultScale
|
|
return sx
|
|
}
|
|
|
|
// new v5 API
|
|
export const system = (args = {}) => {
|
|
const config = {}
|
|
Object.keys(args).forEach(key => {
|
|
const conf = args[key]
|
|
if (conf === true) {
|
|
// shortcut definition
|
|
config[key] = createStyleFunction({
|
|
property: key,
|
|
scale: key,
|
|
})
|
|
return
|
|
}
|
|
if (typeof conf === 'function') {
|
|
config[key] = conf
|
|
return
|
|
}
|
|
config[key] = createStyleFunction(conf)
|
|
})
|
|
|
|
const parser = createParser(config)
|
|
return parser
|
|
}
|
|
|
|
export const compose = (...parsers) => {
|
|
let config = {}
|
|
parsers.forEach(parser => {
|
|
if (!parser || !parser.config) return
|
|
assign(config, parser.config)
|
|
})
|
|
const parser = createParser(config)
|
|
|
|
return parser
|
|
}
|