@daz4126/helium 0.24.0 → 0.26.0
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 +61 -45
- package/package.json +1 -1
package/helium.js
CHANGED
|
@@ -1,18 +1,23 @@
|
|
|
1
1
|
const parseEx=v=>{try{return Function(`return(${v})`)()}catch{return v}}
|
|
2
|
-
const
|
|
2
|
+
const INPUT_EVENTS = { form: "submit", input: "input", textarea:"input", select:"change" };
|
|
3
|
+
const getEvent = el => INPUT_EVENTS[el.tagName.toLowerCase()] || "click";
|
|
3
4
|
const debounce=(f,d)=>{let t;return(...a)=>(clearTimeout(t),t=setTimeout(f,d,...a))}
|
|
4
5
|
|
|
5
6
|
// Single global object to hold all settings
|
|
6
7
|
let HELIUM = null;
|
|
7
8
|
|
|
8
|
-
window.helium = function() {
|
|
9
|
+
window.helium = async function() {
|
|
9
10
|
let initFn;
|
|
10
11
|
const ALL = Symbol("all");
|
|
11
|
-
const
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
12
|
+
const HE_ATTR_REGEX = /^(@|:|data-he)/;
|
|
13
|
+
const he = (n, ...a) => {
|
|
14
|
+
const prefix = n.split(/[.:]/)[0];
|
|
15
|
+
if (prefix === ":" || prefix === "") return false;
|
|
16
|
+
for (const attr of a) {
|
|
17
|
+
if (prefix === `@${attr}` || prefix === `data-he-${attr}`) return true;
|
|
18
|
+
}
|
|
19
|
+
return false;
|
|
20
|
+
};
|
|
16
21
|
const root = document.querySelector("[\\@helium]") || document.querySelector("[data-helium]") || document.body;
|
|
17
22
|
|
|
18
23
|
// Initialize or reuse HELIUM object
|
|
@@ -66,7 +71,7 @@ fetch(url, {
|
|
|
66
71
|
type.includes("json") ? res.json() :
|
|
67
72
|
res.text());
|
|
68
73
|
}).then(data =>
|
|
69
|
-
data.turbo &&
|
|
74
|
+
(data.turbo && window.Turbo)
|
|
70
75
|
? Turbo.renderStreamMessage(data.data)
|
|
71
76
|
: update(data, options.target, options.loading ? options.action.map(a => a && "replace") : options.action, options.template)
|
|
72
77
|
).catch(e => console.error("AJAX:", e.message));
|
|
@@ -103,7 +108,7 @@ const handler = {
|
|
|
103
108
|
|
|
104
109
|
function applyBinding(b,e={},elCtx=b.el){
|
|
105
110
|
const {el,prop,fn,calc}=b;
|
|
106
|
-
const r=fn($,state,e,elCtx,html
|
|
111
|
+
const r=fn($,state,e,elCtx,html,get,post,put,patch,del);
|
|
107
112
|
if (calc) state[calc] = r
|
|
108
113
|
|
|
109
114
|
if (prop==="innerHTML") {
|
|
@@ -117,7 +122,10 @@ function applyBinding(b,e={},elCtx=b.el){
|
|
|
117
122
|
k.split(/\s+/).forEach(c => el.classList.toggle(c,v)));
|
|
118
123
|
|
|
119
124
|
if (prop==="style" && r && typeof r==="object")
|
|
120
|
-
return el.style.cssText = Object.entries(r)
|
|
125
|
+
return el.style.cssText = Object.entries(r)
|
|
126
|
+
.filter(([_, v]) => v != null && v !== false)
|
|
127
|
+
.map(([k, v]) => `${k}:${v}`)
|
|
128
|
+
.join("; ");
|
|
121
129
|
|
|
122
130
|
if (prop in el) {
|
|
123
131
|
if (el.type === "radio" && prop != "checked") el.checked = el.value===r;
|
|
@@ -134,7 +142,6 @@ const compile = (expr, withReturn = false) => {
|
|
|
134
142
|
try {
|
|
135
143
|
const fn = new Function(
|
|
136
144
|
"$","$data","$event","$el","$html","$get","$post","$put","$patch","$delete",
|
|
137
|
-
...Object.keys(state), ...[...HELIUM.refs.keys()],
|
|
138
145
|
withReturn
|
|
139
146
|
? `with($data){return(${expr.trim()})}`
|
|
140
147
|
: `with($data){${expr.trim()}}`
|
|
@@ -142,17 +149,7 @@ const compile = (expr, withReturn = false) => {
|
|
|
142
149
|
HELIUM.fnCache.set(key, fn);
|
|
143
150
|
return fn;
|
|
144
151
|
} catch {
|
|
145
|
-
|
|
146
|
-
const fn = new Function(
|
|
147
|
-
"$","$data","$event","$el","$html","$get","$post","$put","$patch","$delete",
|
|
148
|
-
...Object.keys(state), ...[...HELIUM.refs.keys()],
|
|
149
|
-
`with($data){${expr.trim()}}`
|
|
150
|
-
);
|
|
151
|
-
HELIUM.fnCache.set(key, fn);
|
|
152
|
-
return fn;
|
|
153
|
-
} catch {
|
|
154
|
-
return () => expr;
|
|
155
|
-
}
|
|
152
|
+
return () => expr;
|
|
156
153
|
}
|
|
157
154
|
};
|
|
158
155
|
|
|
@@ -173,7 +170,7 @@ const trackDependencies = (fn, el, excludeChanged = false) => {
|
|
|
173
170
|
return excludeChanged ? [...accessed.keys()].filter(prop => state[prop] === accessed.get(prop)) : [...accessed];
|
|
174
171
|
};
|
|
175
172
|
|
|
176
|
-
|
|
173
|
+
const cleanup = el => {
|
|
177
174
|
[el,...el.querySelectorAll('*')].forEach(e => {
|
|
178
175
|
HELIUM.listeners.get(e)?.forEach(({receiver,event,handler}) => receiver.removeEventListener(event,handler));
|
|
179
176
|
HELIUM.listeners.delete(e);
|
|
@@ -184,33 +181,46 @@ async function processElements(element) {
|
|
|
184
181
|
const newBindings = [];
|
|
185
182
|
const deferredBindings = [];
|
|
186
183
|
|
|
187
|
-
|
|
188
|
-
|
|
184
|
+
const heElements = [element, ...element.querySelectorAll("*")].filter(e => {
|
|
185
|
+
if (HELIUM.processed.has(e)) return false;
|
|
186
|
+
for (let i = 0; i < e.attributes.length; i++) {
|
|
187
|
+
if (HE_ATTR_REGEX.test(e.attributes[i].name)) return true;
|
|
188
|
+
}
|
|
189
|
+
return false;
|
|
190
|
+
});
|
|
189
191
|
|
|
190
192
|
const addBinding = (val, b) => {
|
|
191
193
|
HELIUM.bindings.set(val, b.calc ? [b,...(HELIUM.bindings.get(val) || [])] : [...(HELIUM.bindings.get(val) || []), b]);
|
|
192
194
|
b.calc ? newBindings.unshift(b) : newBindings.push(b);
|
|
193
195
|
};
|
|
194
196
|
|
|
195
|
-
|
|
197
|
+
const importPromises = [];
|
|
198
|
+
|
|
199
|
+
heElements.forEach(el => {
|
|
196
200
|
const importAttr = el.getAttribute("@import") || el.getAttribute("data-he-import");
|
|
197
|
-
if (importAttr) {
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
const
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
201
|
+
if (importAttr) {
|
|
202
|
+
importAttr.split(",").map(m => m.trim()).forEach(moduleName => {
|
|
203
|
+
importPromises.push(
|
|
204
|
+
const prefix = 'helium_' + 'modules/';
|
|
205
|
+
import(prefix + moduleName)
|
|
206
|
+
.then(module => {
|
|
207
|
+
Object.keys(module).forEach(key => state[key] = module[key]);
|
|
208
|
+
})
|
|
209
|
+
.catch(error => {
|
|
210
|
+
console.error(`Failed to import module: ${moduleName}`, error.message);
|
|
211
|
+
})
|
|
212
|
+
);
|
|
213
|
+
});
|
|
206
214
|
}
|
|
207
|
-
})
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
if (importPromises.length > 0) await Promise.all(importPromises);
|
|
208
218
|
|
|
209
219
|
heElements.forEach(el => {
|
|
210
220
|
HELIUM.processed.add(el);
|
|
211
221
|
|
|
212
222
|
const attrs = el.attributes;
|
|
213
|
-
const execFn = v => compile(v, true)($, state, {}, el, html, get, post, put, patch, del
|
|
223
|
+
const execFn = v => compile(v, true)($, state, {}, el, html, get, post, put, patch, del);
|
|
214
224
|
const inputType = el.type?.toLowerCase();
|
|
215
225
|
const isCheckbox = inputType == "checkbox", isRadio = inputType == "radio", isSelect = el.tagName == "SELECT";
|
|
216
226
|
|
|
@@ -230,7 +240,7 @@ async function processElements(element) {
|
|
|
230
240
|
}
|
|
231
241
|
|
|
232
242
|
// Process the attribute
|
|
233
|
-
if (
|
|
243
|
+
if (name === "@data" || name === "data-he") {
|
|
234
244
|
Object.assign(state, parseEx(value));
|
|
235
245
|
}
|
|
236
246
|
else if (name.startsWith(":") || name.startsWith("data-he-attr:")) {
|
|
@@ -275,7 +285,7 @@ async function processElements(element) {
|
|
|
275
285
|
const debounceMod = mods.find(m => m.startsWith("debounce"));
|
|
276
286
|
const debounceDelay = debounceMod ? (t => t && !isNaN(t) ? Number(t) : 300)(debounceMod.split(":")[1]) : 0;
|
|
277
287
|
const _handler = e => {
|
|
278
|
-
const exFn = v => compile(v,true)($, state, e, el, html, get, post, put, patch, del
|
|
288
|
+
const exFn = v => compile(v,true)($, state, e, el, html, get, post, put, patch, del)
|
|
279
289
|
if (mods.includes("prevent")) e.preventDefault();
|
|
280
290
|
const keyMods = {shift: "shiftKey", ctrl: "ctrlKey", alt: "altKey", meta: "metaKey"};
|
|
281
291
|
for (const [mod, prop] of Object.entries(keyMods)) if (mods.includes(mod) && !e[prop]) return;
|
|
@@ -329,17 +339,23 @@ const action = pairs.map(([, action]) => action);
|
|
|
329
339
|
}
|
|
330
340
|
|
|
331
341
|
// Create new observer
|
|
332
|
-
HELIUM.observer = new MutationObserver(ms => {
|
|
342
|
+
HELIUM.observer = new MutationObserver(async (ms) => {
|
|
333
343
|
for (const m of ms) {
|
|
334
|
-
m.removedNodes.forEach(n => n.nodeType === 1 && cleanup(n));
|
|
335
|
-
|
|
344
|
+
m.removedNodes.forEach((n) => n.nodeType === 1 && cleanup(n));
|
|
345
|
+
|
|
346
|
+
for (const n of m.addedNodes) {
|
|
347
|
+
if (n.nodeType === 1 && !HELIUM.processed.has(n)) {
|
|
348
|
+
const bindings = await processElements(n);
|
|
349
|
+
bindings?.forEach(applyBinding);
|
|
350
|
+
}
|
|
351
|
+
}
|
|
336
352
|
}
|
|
337
353
|
});
|
|
338
354
|
HELIUM.observer.observe(root, { childList: true, subtree: true });
|
|
339
355
|
|
|
340
|
-
processElements(root);
|
|
341
|
-
|
|
342
|
-
if(initFn) initFn($, state, {}, {}, html, get, post, put, patch, del
|
|
356
|
+
const initialBindings = await processElements(root);
|
|
357
|
+
initialBindings.forEach(applyBinding);
|
|
358
|
+
if(initFn) initFn($, state, {}, {}, html, get, post, put, patch, del)
|
|
343
359
|
}
|
|
344
360
|
|
|
345
361
|
window.heliumTeardown = function() {
|