@daz4126/helium 0.10.0 → 0.10.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/helium.js +46 -83
- package/package.json +1 -1
package/helium.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export default function helium(data = {}) {
|
|
2
|
+
const he = (...args) => args.flatMap(b => [`@${b}`,`data-he-${b}`])
|
|
2
3
|
const root = document.querySelector("[\\@helium]") || document.querySelector("[data-helium]") || document.body;
|
|
3
|
-
const [bindings, refs, listeners
|
|
4
|
+
const [bindings, refs, listeners] = [new Map(), new Map(), new WeakMap()];
|
|
4
5
|
const $ = (s) => document.querySelector(s);
|
|
5
6
|
const html = (s) => {
|
|
6
7
|
const t = document.createElement("template");
|
|
@@ -23,7 +24,7 @@ export default function helium(data = {}) {
|
|
|
23
24
|
credentials: "same-origin"
|
|
24
25
|
})
|
|
25
26
|
.then(r => r.headers.get("content-type")?.includes("application/json") ? r.json() : r.text())
|
|
26
|
-
.then(d => state[target] = d)
|
|
27
|
+
.then(d => {if(target) state[target] = d})
|
|
27
28
|
.catch(e => console.error("AJAX error:", e.message));
|
|
28
29
|
};
|
|
29
30
|
|
|
@@ -31,28 +32,16 @@ export default function helium(data = {}) {
|
|
|
31
32
|
const [post, put, patch, del] = ["POST","PUT","PATCH","DELETE"].map(m => (u, d, t) => ajax(u, m, t, d));
|
|
32
33
|
|
|
33
34
|
const handler = {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
set(target, prop, val, receiver) {
|
|
43
|
-
const result = Reflect.set(target, prop, val, receiver);
|
|
44
|
-
if (target === data && bindings.has(prop)) {
|
|
45
|
-
bindings.get(prop).forEach(applyBinding);
|
|
46
|
-
} else {
|
|
47
|
-
for (const [key, bound] of bindings.entries()) {
|
|
48
|
-
if (key === Object.keys(data).find(k => data[k] === target)) {
|
|
49
|
-
bound.forEach(applyBinding);
|
|
50
|
-
}
|
|
51
|
-
}
|
|
35
|
+
get(target, prop, receiver) {
|
|
36
|
+
const val = Reflect.get(target, prop, receiver);
|
|
37
|
+
return typeof val === "object" && val !== null ? new Proxy(val, handler) : val;
|
|
38
|
+
},
|
|
39
|
+
set(target, prop, val) {
|
|
40
|
+
const result = Reflect.set(target, prop, val);
|
|
41
|
+
bindings.get(prop)?.forEach(applyBinding);
|
|
42
|
+
return result;
|
|
52
43
|
}
|
|
53
|
-
|
|
54
|
-
}
|
|
55
|
-
};
|
|
44
|
+
};
|
|
56
45
|
|
|
57
46
|
const state = new Proxy(data, handler);
|
|
58
47
|
let isUpdatingDOM = false;
|
|
@@ -65,12 +54,7 @@ export default function helium(data = {}) {
|
|
|
65
54
|
el.innerHTML = result;
|
|
66
55
|
isUpdatingDOM = false;
|
|
67
56
|
} else if(prop == "class" && typeof result == "object"){
|
|
68
|
-
|
|
69
|
-
for (const [key, value] of Object.entries(result)) {
|
|
70
|
-
(value ? add : remove).push(key);
|
|
71
|
-
}
|
|
72
|
-
el.classList.add(...add);
|
|
73
|
-
el.classList.remove(...remove);
|
|
57
|
+
Object.entries(result).forEach(([k, v]) => el.classList.toggle(k, v));
|
|
74
58
|
} else if(prop == "style" && typeof result == "object"){
|
|
75
59
|
el.style = Object.entries(result).filter(([k, v]) => v).map(([k, v]) => `${k}:${v}`).join(";");
|
|
76
60
|
} else if (prop in el) {
|
|
@@ -91,63 +75,47 @@ export default function helium(data = {}) {
|
|
|
91
75
|
}
|
|
92
76
|
};
|
|
93
77
|
|
|
94
|
-
|
|
95
|
-
const trackDependencies = (fn, el) => {
|
|
78
|
+
const trackDependencies = (fn, el) => {
|
|
96
79
|
const accessed = new Set();
|
|
97
|
-
const
|
|
98
|
-
get(target, prop
|
|
99
|
-
if (
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
const val = Reflect.get(target, prop, receiver);
|
|
103
|
-
return typeof val === "object" && val !== null ? new Proxy(val, trackingHandler) : val;
|
|
80
|
+
const trackProxy = new Proxy(data, {
|
|
81
|
+
get(target, prop) {
|
|
82
|
+
if (typeof prop === 'string') accessed.add(prop);
|
|
83
|
+
const val = target[prop];
|
|
84
|
+
return typeof val === "object" && val !== null ? new Proxy(val, this) : val;
|
|
104
85
|
}
|
|
105
|
-
};
|
|
106
|
-
|
|
107
|
-
const trackingProxy = new Proxy(data, trackingHandler);
|
|
108
|
-
|
|
109
|
-
try {
|
|
110
|
-
fn($, trackingProxy, {}, el, html, get, post, put, patch, del, ...Object.values(data), ...[...refs.values()]);
|
|
111
|
-
} catch (e) {
|
|
112
|
-
// Expression might fail on first run, that's ok
|
|
113
|
-
}
|
|
86
|
+
});
|
|
114
87
|
|
|
88
|
+
try { fn.call(null, $, trackProxy, refs); } catch {}
|
|
115
89
|
return [...accessed];
|
|
116
90
|
};
|
|
117
91
|
|
|
118
|
-
const cleanup =
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
listeners.delete(e);
|
|
125
|
-
}
|
|
126
|
-
};
|
|
127
|
-
clean(el);
|
|
128
|
-
el.querySelectorAll('*').forEach(clean);
|
|
92
|
+
const cleanup = el => {
|
|
93
|
+
[el, ...el.querySelectorAll('*')].forEach(e => {
|
|
94
|
+
listeners.get(e)?.forEach(({receiver, eventName, handler}) =>
|
|
95
|
+
receiver.removeEventListener(eventName, handler));
|
|
96
|
+
listeners.delete(e);
|
|
97
|
+
});
|
|
129
98
|
};
|
|
130
99
|
|
|
131
100
|
function processElements(element) {
|
|
132
101
|
const newBindings = [];
|
|
133
102
|
if (element.nodeType === 1) {
|
|
134
|
-
if (element.hasAttribute?.("data-
|
|
135
|
-
element.setAttribute("data-
|
|
103
|
+
if (element.hasAttribute?.("data-he-p")) return newBindings;
|
|
104
|
+
element.setAttribute("data-he-p", "");
|
|
136
105
|
}
|
|
137
106
|
|
|
138
107
|
const heElements = [element, ...element.querySelectorAll("*")].filter(el =>
|
|
139
|
-
Array.from(el.attributes).some(attr =>
|
|
140
|
-
attr.name.startsWith("@") || attr.name.startsWith(":") || attr.name.startsWith("data-he-")
|
|
108
|
+
Array.from(el.attributes).some(attr => /^(@|:|data-he-)/.test(attr.name)
|
|
141
109
|
)
|
|
142
110
|
);
|
|
143
111
|
|
|
144
112
|
// Seed default state values
|
|
145
113
|
heElements.forEach(el => {
|
|
146
114
|
for (const { name, value } of el.attributes || []) {
|
|
147
|
-
if (
|
|
115
|
+
if (he("text","html","bind").includes(name)) {
|
|
148
116
|
try {
|
|
149
117
|
new Function(`let ${value}=1`);
|
|
150
|
-
state[value] ||=
|
|
118
|
+
state[value] ||= he("bind").includes(name) ? el.type == "checkbox" ? el.checked : el.value : el.textContent;
|
|
151
119
|
} catch (e) {}
|
|
152
120
|
}
|
|
153
121
|
}
|
|
@@ -163,17 +131,15 @@ export default function helium(data = {}) {
|
|
|
163
131
|
const execFn = v => compile(v, true)($, state, {}, el, html, get, post, put, patch, del, ...Object.values(data), ...[...refs.values()]);
|
|
164
132
|
for (const { name, value } of el.attributes || []) {
|
|
165
133
|
if (["@data","data-he"].includes(name)) Object.assign(state, execFn(value));
|
|
166
|
-
if (
|
|
134
|
+
if (he("ref").includes(name)) refs.set("$" + value, el);
|
|
167
135
|
|
|
168
|
-
if (
|
|
136
|
+
if (he("text","html").includes(name)) {
|
|
169
137
|
const fn = compile(value, true);
|
|
170
|
-
const
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
deps.forEach(dep => addBinding(dep, b));
|
|
138
|
+
const b = { el, prop: he("text").includes(name) ? "textContent" : "innerHTML", fn };
|
|
139
|
+
trackDependencies(fn, el).forEach(dep => addBinding(dep, b));
|
|
174
140
|
}
|
|
175
141
|
|
|
176
|
-
if (
|
|
142
|
+
if (he("bind").includes(name)) {
|
|
177
143
|
const inputType = el.type?.toLowerCase();
|
|
178
144
|
const isCheckbox = inputType === "checkbox";
|
|
179
145
|
const isRadio = inputType === "radio";
|
|
@@ -185,30 +151,27 @@ export default function helium(data = {}) {
|
|
|
185
151
|
if (!listeners.has(el)) listeners.set(el, []);
|
|
186
152
|
listeners.get(el).push({receiver: el, eventName: event, handler: inputHandler});
|
|
187
153
|
|
|
188
|
-
addBinding(value, { el, prop,
|
|
154
|
+
addBinding(value, { el, prop, fn: compile(value, true) });
|
|
189
155
|
|
|
190
156
|
if (isCheckbox) el.checked = !!state[value];
|
|
191
157
|
else if (isRadio) el.checked = el.value == state[value];
|
|
192
158
|
else el.value = state[value] ?? "";
|
|
193
159
|
}
|
|
194
160
|
|
|
195
|
-
if (
|
|
196
|
-
const fn = compile(`${
|
|
197
|
-
const
|
|
198
|
-
const b = { el, prop: "hidden", expr: value, fn };
|
|
161
|
+
if (he("hidden","visible").includes(name)) {
|
|
162
|
+
const fn = compile(`${he("hidden").includes(name) ? "!" : ""}!(${value})`, true);
|
|
163
|
+
const b = { el, prop: "hidden", fn };
|
|
199
164
|
|
|
200
|
-
|
|
165
|
+
trackDependencies(fn, el).forEach(dep => addBinding(dep, b));
|
|
201
166
|
}
|
|
202
167
|
|
|
203
168
|
if (name.startsWith(":")) {
|
|
204
169
|
const fn = compile(value, true);
|
|
205
|
-
const
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
deps.forEach(dep => addBinding(dep, b));
|
|
170
|
+
const b = { el, prop: name.slice(1), fn };
|
|
171
|
+
trackDependencies(fn, el).forEach(dep => addBinding(dep, b));
|
|
209
172
|
}
|
|
210
173
|
|
|
211
|
-
if (
|
|
174
|
+
if (he("init").includes(name)) {
|
|
212
175
|
compile(value, false)($, state, undefined, el, html, get, post, put, patch, del, ...Object.values(data), ...[...refs.values()]);
|
|
213
176
|
} else if (name.startsWith("@") || name.startsWith("data-he-on")) {
|
|
214
177
|
const [eventName, ...mods] = name.slice(name.startsWith("@") ? 1 : 10).split(".");
|
|
@@ -266,7 +229,7 @@ export default function helium(data = {}) {
|
|
|
266
229
|
if (node.nodeType === 1) cleanup(node);
|
|
267
230
|
}
|
|
268
231
|
for (const node of m.addedNodes) {
|
|
269
|
-
if (node.nodeType === 1 && !node.hasAttribute("data-
|
|
232
|
+
if (node.nodeType === 1 && !node.hasAttribute("data-he-p")) {
|
|
270
233
|
processElements(node).forEach(applyBinding);
|
|
271
234
|
}
|
|
272
235
|
}
|