forked from nan-academy/tron
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathh.js
125 lines (102 loc) · 3.21 KB
/
h.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
import each from './each.js'
import curry from './curry.js'
import { isStr, isChildren, isDef } from './is.js'
import parseTag from './parse-tag.js'
import tags from './html-tags.js'
const appendChild = (elem, child) => {
if (child === undefined) return
if (child instanceof Element) return elem.appendChild(child)
if (Array.isArray(child)) return child.forEach(c => appendChild(elem, c))
return elem.appendChild(document.createTextNode(String(child)))
}
const mergePropsDefault = (a, b) => {
if (!b) return a
const keys = Object.keys(b)
let i = -1
while (++i < keys.length) {
if (!a[keys[i]]) {
a[keys[i]] = b[keys[i]]
} else if (typeof a[keys[i]] === 'object') {
mergePropsDefault(a[keys[i]], b[keys[i]])
} else {
a[keys[i]] = b[keys[i]]
}
}
return a
}
const setAttr = (elem, val, key) => elem.setAttribute(key, val)
const assignAttr = (elem, val, key) => elem[key] = val
const deepAssignAttr = (elem, val, key) => Object.assign(elem[key], val)
const mergeCssClass = (elem, val) =>
elem.classList.add.apply(elem.classList, val.split(' '))
// TODO: create handlers for aria and data
const getHandler = key => {
switch (key) {
case 'class':
case 'className': return mergeCssClass
case 'dataset':
case 'style': return deepAssignAttr
default: {
if (key.indexOf('-') !== -1) return setAttr
return assignAttr
}
}
}
const createElement = (args, props, child) => {
if (isChildren(props)) {
child = props
props = undefined
}
const elem = document.createElement(args.tag)
if (props || args.props) {
const mergeProps = each((value, key) => isDef(value)
&& getHandler(key)(elem, value, key))
args.props && mergeProps(args.props)
props && mergeProps(props)
}
appendChild(elem, child)
return elem
}
const prepareArgs = (tag, props) => {
if (isStr(tag)) {
props || (props = {})
tag = parseTag(tag, props).toLowerCase()
} else {
props = tag
tag = 'div'
}
Object.keys(props).length || (props = undefined)
return { tag, props }
}
const prepareStyleArgs = (tag, style) => isStr(tag)
? prepareArgs(tag, { style: style.style || style })
: prepareArgs('div', { style: tag.style || tag })
const extend = (args, props) =>
preparedH(mergePropsDefault(args, args))
const preparedH = args => {
const create = (props, child) => createElement(args, props, child)
create.style = (style, child) => createElement(args, { style }, child)
create.extend = (tag, props) => extend(args, Array.isArray(tag)
? tag.reduce(mergePropsDefault)
: prepareArgs(tag, props))
create.extend.style = (tag, style) => extend(args, Array.isArray(tag)
? { style: tag.reduce(mergePropsDefault) }
: prepareStyleArgs(tag, style))
return create
}
const h = (tag, props) => preparedH(prepareArgs(tag, props))
h.style = (tag, style) => preparedH(prepareStyleArgs(tag, style))
h.appendChild = curry(appendChild)
const empty = h.empty = el => {
if (!el) return
while (el.lastChild && el.lastChild !== el) {
el.removeChild(el.lastChild)
}
}
h.replaceContent = curry((el, content) => {
empty(el),
appendChild(el, content)
})
h.getHandler = getHandler
tags.forEach(t => h[t] === undefined && (h[t] = h(t)))
export default h