-
Notifications
You must be signed in to change notification settings - Fork 4
/
Alpine.js
117 lines (99 loc) · 3.36 KB
/
Alpine.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
window.Alpine = {
cache: new Map(),
constants: {
removedElement: "_rmEl",
},
directives: {
"x-text": (el, value) => {
el.textContent = value;
},
"x-show": (el, value) => {
el.style.display = value ? "block" : "none";
},
"x-if": (element, value) => {
if (!value) {
if (element instanceof HTMLElement) {
if (!Alpine.cache.has(Alpine.constants.removedElement)) {
Alpine.cache.set(
Alpine.constants.removedElement,
element.innerHTML.toString()
);
}
element.innerHTML = "";
}
return;
}
if (element.innerHTML === "") {
if (Alpine.cache.has(Alpine.constants.removedElement)) {
element.innerHTML = Alpine.cache.get(
Alpine.constants.removedElement
);
}
}
},
"x-bind": (el, value) => {
const attrName = el.getAttributeNames().find(name => name.startsWith("x-bind:") || name.startsWith(":"));
const actualAttrName = attrName.startsWith(":") ? attrName.substring(1) : attrName.substring(7);
el.setAttribute(actualAttrName, value);
}
},
start() {
this.root = document.querySelector("[x-data]");
this.rawData = this.getInitialData();
this.data = this.observe(this.rawData);
this.refreshDom();
this.registerListeners();
},
getInitialData() {
const dataString = this.root.getAttribute("x-data");
return eval(`(${dataString})`);
},
observe(rawData) {
const self = this;
return new Proxy(rawData, {
set(target, key, value) {
target[key] = value;
self.refreshDom();
self.root.setAttribute("x-data", JSON.stringify(rawData));
return true;
},
});
},
refreshDom() {
this.walkDom(this.root, (el) => {
for (const attribute of el.attributes) {
let { name, value } = attribute;
if (name.startsWith('x-bind') || name.startsWith(':')) {
this.directives['x-bind'](el, eval(`with (this.data) { ${value} }`))
continue
}
if (!Object.keys(this.directives).includes(name)) return;
const directive = this.directives[name];
directive(el, eval(`with (this.data) { ${value} }`));
}
});
},
walkDom(el, callback) {
callback(el);
el = el.firstElementChild;
while (el) {
this.walkDom(el, callback);
el = el.nextElementSibling;
}
},
registerListeners() {
this.walkDom(this.root, (el) => {
this.addListenerToElement(el);
});
},
addListenerToElement(el) {
for (const attribute of el.attributes) {
if (!attribute.name.startsWith("@")) return;
const event = attribute.name.substring(1);
el.addEventListener(event, () => {
eval(`with(this.data) { ${attribute.value} }`);
});
}
},
};
Alpine.start();