-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathcore.ts
114 lines (104 loc) · 2.92 KB
/
core.ts
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
export { createElement as c, createText as t }
export function fragment(nodes: Array<Node | { node: Node } | string>) {
const fragment = document.createDocumentFragment()
for (const node of nodes) {
appendChild(fragment, node)
}
return fragment
}
export function createText(value: string = '') {
const node = document.createTextNode(value)
const proxy = createProxy(node)
return proxy
}
let watchFn: (() => void) | null = null
export function watch(fn: () => void) {
watchFn = fn
fn()
watchFn = null
}
export type CreateProxyOptions = {
listen?: 'change' | 'input' | false // default 'input'
}
export type ProxyNode<E> = E & { node: E }
function createProxy<E extends object>(
node: E,
options?: CreateProxyOptions,
): ProxyNode<E> {
const deps = new Map<string, Set<() => void>>()
const listentEventType = options?.listen ?? 'input'
const proxy = new Proxy(node, {
get(target, p, receiver) {
const value = (target as any)[p]
if (listentEventType && watchFn && typeof p === 'string') {
const key =
p === 'value' || p === 'valueAsNumber' || p === 'valueAsDate'
? 'value'
: p
const fn = watchFn
if (key === 'value') {
;(target as HTMLInputElement).addEventListener(
listentEventType,
// wrap the function to avoid the default behavior be cancelled
// if the inline-function returns false
() => fn(),
)
}
let fns = deps.get(key)
if (!fns) {
fns = new Set()
deps.set(key, fns)
}
fns.add(fn)
}
if (typeof value === 'function') {
return value.bind(target)
}
return value
},
set(target, p, value, receiver) {
;(target as any)[p] = value
if (typeof p === 'string') {
const fns = deps.get(p)
if (fns) {
for (const fn of fns) {
fn()
}
}
}
return true
},
})
return Object.assign(proxy, { node })
}
export function genCreateElement<K extends keyof HTMLElementTagNameMap>(
tagName: K,
) {
return (attrs?: Partial<HTMLElementTagNameMap[K]> & CreateProxyOptions) =>
createElement(tagName, attrs)
}
export function createElement<K extends keyof HTMLElementTagNameMap>(
tagName: K,
attrs?: Partial<HTMLElementTagNameMap[K] & CreateProxyOptions>,
) {
const node = document.createElement<K>(tagName)
if (attrs) {
Object.assign(node, attrs)
}
const proxy = createProxy(node, attrs)
return proxy
}
export function appendChild(
parent: ParentNode,
child: Node | { node: Node } | string | number,
) {
if (typeof child == 'string') {
parent.appendChild(document.createTextNode(child))
} else if (typeof child == 'number') {
parent.appendChild(document.createTextNode(String(child)))
} else if ('node' in child) {
parent.appendChild(child.node)
} else {
parent.appendChild(child)
}
}