-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathelement.js
90 lines (76 loc) · 2.93 KB
/
element.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
export default Class => {
const proto = Class.prototype
const attributes = Class.attributes || {}
const props = []
Object.entries(attributes).forEach(([name, attribute]) => {
const htmlName = name.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase()
props.push(htmlName)
const prop = {}
prop.get = typeof attribute.get == "function"
? function() { return attribute.get.call(this, this.getAttribute(htmlName)) }
: function() { return this.getAttribute(htmlName) }
prop.set = typeof attribute.set == "function"
? function(val) { return this.setAttribute(htmlName, attribute.set.call(this, val)) }
: attribute.set === false
? function() { throw(Error(`Attribute ${name} cannot be set`)) }
: function(val) { this.setAttribute(htmlName, val) }
Object.defineProperty(proto, name, prop)
})
Object.freeze(props)
Object.defineProperty(Class.prototype, "props", { get() { return Object.fromEntries(props.map(prop => [prop, this[prop]])) } })
const observedAttributes = Object.freeze([...Object.keys(attributes)])
Object.defineProperty(Class, "observedAttributes", {
get() { return observedAttributes }
})
Class.prototype.attributeChangedCallback = function(name, oldValue, newValue) {
name = name.replaceAll(/-(.)/g, (_, b) => b.toUpperCase())
const get_transform = attributes[name]?.get
if (`${name}Changed` in this) {
if (typeof get_transform == "function")
return this[`${name}Changed`](get_transform(oldValue), get_transform(newValue))
else
return this[`${name}Changed`](oldValue, newValue)
}
if (`changed` in this) {
if (typeof get_transform == "function")
return this.changed(name, get_transform(oldValue), get_transform(newValue))
else
return this.changed(name, oldValue, newValue)
}
}
/* Enable batch-processing for dollar-methods */
/* This would be much nicer if decorators were a thing */
for (const name of Object.getOwnPropertyNames(Class.prototype)) {
if (name.startsWith("$")) {
const prop = Object.getOwnPropertyDescriptor(Class.prototype, name)
if (typeof prop.value == "function") {
Class.queues = new WeakMap()
Class.prototype[name.slice(1)] = function(...args) {
const queue = Class.queues.has(this) ? Class.queues.get(this) : []
if (!queue.length) queueMicrotask(() => {
this[name](...queue)
queue.length = 0
})
queue.push(args)
Class.queues.set(this, queue)
}
}
}
}
proto.insert = function(...args) {
return (this.shadowRoot || this).append(...args)
}
proto.replace = function(...args) {
return (this.shadowRoot || this).replaceChildren(...args)
}
Object.prototype[Symbol.toPrimitive] = function(hint) {
const name = `to${hint.replace(/./, e => e.toUpperCase())}`
return name in this
? this[name]()
: "toDefault" in this
? this.toDefault()
: `[object ${Class.name}]`
}
name = Class.name.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase()
customElements.define(name, Class, {extends: Class.is})
}