@daz4126/helium 0.12.0 → 0.13.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.
Files changed (2) hide show
  1. package/helium.js +39 -13
  2. package/package.json +1 -1
package/helium.js CHANGED
@@ -3,9 +3,10 @@ const getEvent = el => ({form:"submit",input:"input",textarea:"input",select:"ch
3
3
  const debounce=(f,d)=>{let t;return(...a)=>(clearTimeout(t),t=setTimeout(f,d,...a))}
4
4
 
5
5
  export default function helium(data = {}) {
6
+ let initFn;
6
7
  const he = (n,...a) => a.map(b => `|@${b}|data-he-${b}|`).join``.includes(`|${n.split('.')[0]}|`);
7
8
  const root = document.querySelector("[\\@helium]") || document.querySelector("[data-helium]") || document.body;
8
- const [bindings, refs, listeners] = [new Map(), new Map(), new WeakMap()];
9
+ const [bindings, refs, listeners, processed] = [new Map(), new Map(), new WeakMap(), new WeakSet()];
9
10
  const $ = s => document.querySelector(s);
10
11
  const html = s => Object.assign(document.createElement("template"),{innerHTML:s.trim()}).content.firstChild
11
12
 
@@ -27,9 +28,16 @@ const ajax = (u,m,o={},p={}) => {
27
28
  ...(!fd&&m!=="GET"&&{"Content-Type":"application/json"}),
28
29
  ...(t&&{"X-CSRF-Token":t})
29
30
  },body:m==="GET"?null:(fd?p:JSON.stringify(p)),credentials:"same-origin"})
30
- .then(r=>r.headers.get("content-type")?.includes("json")?r.json():r.text())
31
- .then(d=>o.loading?update(d,o.target,"replace",o.template):update(d,o.target,o.action,o.template))
32
- .catch(e=>console.error("AJAX:",e.message));
31
+ .then(r => {
32
+ const type = r.headers.get("content-type") || "";
33
+ return (type.includes("turbo-stream") ? r.text().then(d => ({ t: true, d })) :
34
+ type.includes("json") ? r.json() :
35
+ r.text());
36
+ }).then(d =>
37
+ d.t && "Turbo"
38
+ ? Turbo.renderStreamMessage(d.d)
39
+ : update(d, o.target, o.loading ? "replace" : o.action, o.template)
40
+ ).catch(e => console.error("AJAX:", e.message));
33
41
  }
34
42
 
35
43
  const get = (u,t) => ajax(u,"GET",t);
@@ -46,7 +54,12 @@ const ajax = (u,m,o={},p={}) => {
46
54
  function applyBinding(b,e={},elCtx=b.el){
47
55
  const {el,prop,fn}=b;
48
56
  const r=fn($,state,e,elCtx,html,...Object.values(data),...[...refs.values()]);
49
- if(prop==="innerHTML"){isUpdatingDOM=1;el.innerHTML=Array.isArray(r)?r.join``:r;isUpdatingDOM=0}
57
+ if(prop==="innerHTML"){
58
+ isUpdatingDOM=1;
59
+ const content = el.innerHTML=Array.isArray(r)?r.join``:r
60
+ typeof Idiomorph === "object" ? Idiomorph.morph(el, content,{morphStyle:'innerHTML'}) : el.innerHTML = content;
61
+ isUpdatingDOM=0
62
+ }
50
63
  else if(prop==="class"&&r&&typeof r==="object")for(const[k,v]of Object.entries(r))k.split(/\s+/).forEach(c=>el.classList.toggle(c,v));
51
64
  else if(prop==="style"&&r&&typeof r==="object")el.style=Object.entries(r).filter(([,v])=>v).map(([k,v])=>`${k}:${v}`).join(";")
52
65
  else if(prop in el){if(el.type==="radio")el.checked=el.value===r;else el[prop]=prop==="textContent"?r:parseEx(r)}
@@ -60,10 +73,19 @@ function applyBinding(b,e={},elCtx=b.el){
60
73
  } catch {return () => expr}
61
74
  };
62
75
 
63
- const trackDependencies = fn => {
64
- const s=new Set(),p=new Proxy(state,{get:(t,k)=>{if(typeof k==="string")s.add(k);const v=t[k];return v&&typeof v==="object"?new Proxy(v,this):v}});
65
- try{fn($,p,refs)}catch{};return[...s]
66
- }
76
+ const trackDependencies = (fn, el) => {
77
+ const accessed = new Set();
78
+ const trackProxy = new Proxy(data, {
79
+ get(target, prop) {
80
+ if (typeof prop == 'string') accessed.add(prop);
81
+ const val = target[prop];
82
+ return typeof val == "object" && val != null ? new Proxy(val, this) : val;
83
+ }
84
+ });
85
+
86
+ try { fn.call(null, $, trackProxy, refs); } catch {}
87
+ return [...accessed];
88
+ };
67
89
 
68
90
  const cleanup = el => {
69
91
  [el,...el.querySelectorAll('*')].forEach(e => {
@@ -75,8 +97,8 @@ const trackDependencies = fn => {
75
97
  function processElements(element) {
76
98
  const newBindings = [];
77
99
  if (element.nodeType == 1) {
78
- if (element.hasAttribute?.("data-he-p")) return newBindings;
79
- element.setAttribute("data-he-p", "");
100
+ if (processed.has(element)) return newBindings;
101
+ processed.add(element);
80
102
  }
81
103
 
82
104
  const heElements=[element,...element.querySelectorAll("*")].filter(e=>[...e.attributes].some(a=>/^(@|:|data-he)/.test(a.name)))
@@ -98,6 +120,9 @@ const trackDependencies = fn => {
98
120
  };
99
121
 
100
122
  heElements.forEach(el => {
123
+ if (processed.has(el)) return;
124
+ processed.add(el);
125
+
101
126
  const execFn = v => compile(v, true)($, state, {}, el, html,get,post, put,patch,del, ...Object.values(data), ...[...refs.values()]);
102
127
  const inputType = el.type?.toLowerCase();
103
128
  const isCheckbox = inputType == "checkbox", isRadio = inputType == "radio", isSelect = el.tagName == "SELECT";
@@ -131,7 +156,7 @@ const trackDependencies = fn => {
131
156
  trackDependencies(fn, el).forEach(dep => addBinding(dep, {el, prop: name.slice(1), fn}));
132
157
  }
133
158
  if (he(name,"init")) {
134
- execFn(value);
159
+ initFn = compile(value,true);
135
160
  } else if (name.startsWith("@") || name.startsWith("data-he")) {
136
161
  const fullName = name.startsWith("@") ? name.slice(1) : name.slice(8);
137
162
  const [eventName, ...mods] = fullName.split(".");
@@ -189,9 +214,10 @@ const trackDependencies = fn => {
189
214
  if(isUpdatingDOM)return;
190
215
  for(const m of ms){
191
216
  m.removedNodes.forEach(n=>n.nodeType===1&&cleanup(n));
192
- m.addedNodes.forEach(n=>n.nodeType===1&&!n.hasAttribute("data-he-p")&&processElements(n).forEach(applyBinding));
217
+ m.addedNodes.forEach(n=>n.nodeType===1&&!processed.has(n)&&processElements(n).forEach(applyBinding));
193
218
  }
194
219
  }).observe(root,{childList:1,subtree:1});
195
220
  processElements(root);
196
221
  for (const [key, items] of bindings.entries()) items.forEach(applyBinding);
222
+ if(initFn) initFn($, state, {}, {}, html,get,post, put,patch,del, ...Object.values(data), ...[...refs.values()])
197
223
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@daz4126/helium",
3
- "version": "0.12.0",
3
+ "version": "0.13.0",
4
4
  "main": "helium.js",
5
5
  "type": "module",
6
6
  "scripts": {