@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.
Files changed (2) hide show
  1. package/helium.js +46 -83
  2. 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, proxyCache] = [new Map(), new Map(), new WeakMap(), new WeakMap()];
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
- get(target, prop, receiver) {
35
- const val = Reflect.get(target, prop, receiver);
36
- if (typeof val === "object" && val !== null) {
37
- if (!proxyCache.has(val)) proxyCache.set(val, new Proxy(val, handler));
38
- return proxyCache.get(val);
39
- }
40
- return val;
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
- return result;
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
- const [add, remove] = [[], []];
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
- // Track which state properties an expression depends on
95
- const trackDependencies = (fn, el) => {
78
+ const trackDependencies = (fn, el) => {
96
79
  const accessed = new Set();
97
- const trackingHandler = {
98
- get(target, prop, receiver) {
99
- if (prop !== Symbol.toStringTag && typeof prop === 'string') {
100
- accessed.add(prop);
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 = (el) => {
119
- const clean = (e) => {
120
- if (listeners.has(e)) {
121
- listeners.get(e).forEach(({receiver, eventName, handler}) => {
122
- receiver.removeEventListener(eventName, handler);
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-helium-processed")) return newBindings;
135
- element.setAttribute("data-helium-processed", "");
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 (["@html","data-he-html","@text","data-he-text","@bind","data-he-bind"].includes(name)) {
115
+ if (he("text","html","bind").includes(name)) {
148
116
  try {
149
117
  new Function(`let ${value}=1`);
150
- state[value] ||= ["@bind","data-he-bind"].includes(name) ? el.value : el.textContent;
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 (["@ref","data-he-ref"].includes(name)) refs.set("$" + value, el);
134
+ if (he("ref").includes(name)) refs.set("$" + value, el);
167
135
 
168
- if (["@text","data-he-text","@html","data-he-html"].includes(name)) {
136
+ if (he("text","html").includes(name)) {
169
137
  const fn = compile(value, true);
170
- const deps = trackDependencies(fn, el);
171
- const b = { el, prop: ["@text","data-he-text"].includes(name) ? "textContent" : "innerHTML", expr: value, fn };
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 (["@bind", "data-he-bind"].includes(name)) {
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, expr: value, fn: compile(value, true) });
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 (["@hidden","@visible","data-he-hidden","data-he-visible"].includes(name)) {
196
- const fn = compile(`${["@hidden","data-he-hidden"].includes(name) ? "!" : ""}!(${value})`, true);
197
- const deps = trackDependencies(fn, el);
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
- deps.forEach(dep => addBinding(dep, b));
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 deps = trackDependencies(fn, el);
206
- const b = { el, prop: name.slice(1), expr: value, fn };
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 (["@init","data-he-init"].includes(name)) {
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-helium-processed")) {
232
+ if (node.nodeType === 1 && !node.hasAttribute("data-he-p")) {
270
233
  processElements(node).forEach(applyBinding);
271
234
  }
272
235
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@daz4126/helium",
3
- "version": "0.10.0",
3
+ "version": "0.10.1",
4
4
  "main": "helium.js",
5
5
  "type": "module",
6
6
  "scripts": {