Skip to content

Instantly share code, notes, and snippets.

@fuunnx
Last active January 21, 2020 17:14
Show Gist options
  • Save fuunnx/e81c86cc46a485786f8193563d550680 to your computer and use it in GitHub Desktop.
Save fuunnx/e81c86cc46a485786f8193563d550680 to your computer and use it in GitHub Desktop.
Higher order vnodes utility functions
const typeButton = extended({
attrs: {
type: 'button',
},
})
const blueBackground = extended({
style: {
'background-color': 'blue',
'color': 'white',
},
})
const lotOfPadding = extended({
style: {
'padding': '20px',
},
})
const conditionnalStyle = styled({
'border': ({attrs: {disabled}} : {attrs: {disabled: Boolean}}) => disabled
? '2px solid #fff'
: '10px dotted #000',
})
const hasCookies = modifier({children: (xs: any[]) => (['🍪 ', ...xs, ' 🍪'])})
const defaultButton = typeButton(button)
const primaryButton = blueBackground(lotOfPadding(defaultButton))
const cookieButton = hasCookies(primaryButton)
const conditionnedButton = conditionnalStyle(defaultButton)
export const vNode = (fn: Function) => (...args: any[]) => fn(...identifyArgs(...args))
export function styled (style_: any) {
return modifier({
attributes: context => {
const style = mapObj((prop: Function | String) =>
typeof prop === 'function'
? String(prop(context))
: String(prop)
)(style_)
return mergeDeep({style})(context)
},
})
}
export function extended (...args: any[]) {
const [
selector,
attributes,
children,
] = identifyArgs(...args)
return modifier({
selector: concat(selector),
attributes: mergeDeep(attributes),
children: concat(children),
})
}
export function modifier ({
selector: updateSelector = (x: String) => x,
attributes: updateAttributes = (x: any) => x,
children: updateChildren = (x: any[]) => x
}) {
return (component: Function) => vNode(([sel, attrs, children_]: [String, any, any[]]) => {
const selector = updateSelector(sel)
const attributes = updateAttributes(attrs)
const children = updateChildren(children_)
return selector
? component(selector, attributes, children)
: component(attributes, children)
})
}
function identifyArgs (first?: any, b?: any, c?: String[]): [String, any, any[]] {
let selector = ''
let attributes = {}
let children = []
if (isSelector(first)) {
selector = first
if (b && c) (attributes = b, children = c)
if (b && Array.isArray(b) && !c) (children = b)
if (b && !Array.isArray(b) && !c) (attributes = b)
}
if (!isSelector(first)) {
if (first && b) (attributes = first, children = b)
if (first && Array.isArray(first) && !b) (children = first)
if (first && !Array.isArray(first) && !b) (attributes = first)
}
return [
selector,
attributes,
children,
]
}
function isValidString(param: any) {
return typeof param === 'string' && param.length > 0
}
function isSelector(param: any) {
return isValidString(param) && (param[0] === '.' || param[0] === '#')
}
function mapObj (fn: Function) {
return (obj: any) => Object.entries(obj).reduce((acc: any, [key, value]: [String, any]) => ({...acc, [key]: fn(value)}), {})
}
const concat = (x: Array<String> | String) => (y: Array<String> | String) => (Array.isArray(x) && Array.isArray(y))
? x.concat(y)
: [x, y].join('')
const mergeDeep = (target: any) => (source: any) => {
let output = Object.assign({}, target)
if (isObject(target) && isObject(source)) {
Object.keys(source).forEach(key => {
if (isObject(source[key])) {
if (!(key in target))
Object.assign(output, { [key]: source[key] })
else
output[key] = mergeDeep(target[key])(source[key])
} else {
Object.assign(output, { [key]: source[key] })
}
})
}
return output
}
function isObject(item: any) {
return (item && typeof item === 'object' && !Array.isArray(item))
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment