@daz4126/helium 1.0.0-beta.1 → 1.0.0-beta.2
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/examples/index-lite.html +9 -0
- package/examples/index.html +16 -1
- package/helium-csp.js +15 -3
- package/helium-lite.js +3 -0
- package/helium.js +16 -2
- package/package.json +1 -1
package/examples/index-lite.html
CHANGED
|
@@ -378,6 +378,15 @@
|
|
|
378
378
|
<p>History: <span @text="history.join(', ') || 'None yet'">None yet</span></p>
|
|
379
379
|
</div>
|
|
380
380
|
|
|
381
|
+
<!-- Clipboard Copy -->
|
|
382
|
+
<div class="card" @data="{ copied: false, textToCopy: 'Hello from Helium!' }">
|
|
383
|
+
<h2>Copy to Clipboard</h2>
|
|
384
|
+
<input @bind="textToCopy" placeholder="Text to copy">
|
|
385
|
+
<button @click="navigator.clipboard.writeText(textToCopy).then(() => (copied = true, setTimeout(() => copied = false, 2000)))">
|
|
386
|
+
<span @text="copied ? 'Copied!' : 'Copy'"></span>
|
|
387
|
+
</button>
|
|
388
|
+
</div>
|
|
389
|
+
|
|
381
390
|
</div>
|
|
382
391
|
</div>
|
|
383
392
|
|
package/examples/index.html
CHANGED
|
@@ -225,6 +225,12 @@
|
|
|
225
225
|
|
|
226
226
|
<div class="examples">
|
|
227
227
|
|
|
228
|
+
<!-- @data Test -->
|
|
229
|
+
<div class="card" data-he="{ word: 'Hello',number: 42 }">
|
|
230
|
+
<h2 data-he-text="word"></h2>
|
|
231
|
+
<p>Number: <b @text="number"></b></p>
|
|
232
|
+
</div>
|
|
233
|
+
|
|
228
234
|
<!-- Counter -->
|
|
229
235
|
<div class="card">
|
|
230
236
|
<h2>Counter</h2>
|
|
@@ -430,12 +436,21 @@
|
|
|
430
436
|
<p>History: <span @text="history.join(', ') || 'None yet'">None yet</span></p>
|
|
431
437
|
</div>
|
|
432
438
|
|
|
439
|
+
<!-- Clipboard Copy -->
|
|
440
|
+
<div class="card" @data="{ copied: false, textToCopy: 'Hello from Helium!' }">
|
|
441
|
+
<h2>Copy to Clipboard</h2>
|
|
442
|
+
<input @bind="textToCopy" placeholder="Text to copy">
|
|
443
|
+
<button @click="navigator.clipboard.writeText(textToCopy).then(() => { copied = true; setTimeout(() => copied = false, 2000) })">
|
|
444
|
+
<span @text="copied ? 'Copied!' : 'Copy'"></span>
|
|
445
|
+
</button>
|
|
446
|
+
</div>
|
|
447
|
+
|
|
433
448
|
</div>
|
|
434
449
|
|
|
435
450
|
<!-- AJAX Examples Section -->
|
|
436
451
|
<div class="page" style="margin-top: 2rem;" @data="{
|
|
437
452
|
templates: {
|
|
438
|
-
post: data => `<section class='post'><h1>${data
|
|
453
|
+
post: data => `<section class='post'><h1>${data && data.title || 'Loading...'}</h1><p>${data && data.body || ''}</p></section>`
|
|
439
454
|
}
|
|
440
455
|
}">
|
|
441
456
|
<h1>AJAX Examples</h1>
|
package/helium-csp.js
CHANGED
|
@@ -178,9 +178,21 @@ const jexprEngine = {
|
|
|
178
178
|
|
|
179
179
|
createScope(ctx) {
|
|
180
180
|
const locals = {
|
|
181
|
-
|
|
182
|
-
Math,
|
|
183
|
-
|
|
181
|
+
// Browser globals
|
|
182
|
+
console, Math, Date, JSON,
|
|
183
|
+
navigator, window, document, location, history,
|
|
184
|
+
localStorage, sessionStorage,
|
|
185
|
+
setTimeout, setInterval, clearTimeout, clearInterval,
|
|
186
|
+
fetch, Promise, URL, URLSearchParams,
|
|
187
|
+
alert, confirm, prompt,
|
|
188
|
+
parseInt, parseFloat, isNaN, isFinite,
|
|
189
|
+
encodeURIComponent, decodeURIComponent, encodeURI, decodeURI,
|
|
190
|
+
atob, btoa,
|
|
191
|
+
// Observers (optional - may not exist in all environments)
|
|
192
|
+
...(typeof IntersectionObserver !== 'undefined' && { IntersectionObserver }),
|
|
193
|
+
...(typeof ResizeObserver !== 'undefined' && { ResizeObserver }),
|
|
194
|
+
...(typeof MutationObserver !== 'undefined' && { MutationObserver }),
|
|
195
|
+
// Helium helpers
|
|
184
196
|
$: ctx.$,
|
|
185
197
|
$el: ctx.el,
|
|
186
198
|
$event: ctx.event,
|
package/helium-lite.js
CHANGED
|
@@ -25,6 +25,7 @@ async function helium(initialState = {}) {
|
|
|
25
25
|
const inits = [];
|
|
26
26
|
const ALL = Symbol("all");
|
|
27
27
|
const root = document.querySelector('[\\@helium]') || document.querySelector('[data-helium]') || document.body;
|
|
28
|
+
const storageKey = root.getAttribute('@local-storage') || root.getAttribute('data-he-local-storage');
|
|
28
29
|
|
|
29
30
|
if (!HELIUM) {
|
|
30
31
|
HELIUM = {
|
|
@@ -78,11 +79,13 @@ async function helium(initialState = {}) {
|
|
|
78
79
|
const parentKey = HELIUM.parentKeys.get(t);
|
|
79
80
|
if (parentKey) HELIUM.bindings.get(parentKey)?.forEach(safeApplyBinding);
|
|
80
81
|
HELIUM.bindings.get(ALL)?.forEach(safeApplyBinding);
|
|
82
|
+
if (storageKey) localStorage.setItem(storageKey, JSON.stringify(state));
|
|
81
83
|
return res;
|
|
82
84
|
}
|
|
83
85
|
};
|
|
84
86
|
|
|
85
87
|
const state = new Proxy({}, handler);
|
|
88
|
+
if (storageKey) try { Object.assign(state, JSON.parse(localStorage.getItem(storageKey))); } catch {}
|
|
86
89
|
Object.assign(state, initialState);
|
|
87
90
|
|
|
88
91
|
const getRefsObject = () => {
|
package/helium.js
CHANGED
|
@@ -52,6 +52,15 @@ export function createHelium(options = {}) {
|
|
|
52
52
|
const prefix = options.prefix ?? 'he'; // 'he' or 'xe'
|
|
53
53
|
const rootAttr = options.rootAttr ?? 'helium'; // 'helium' or 'xenon'
|
|
54
54
|
|
|
55
|
+
// Engine-aware expression parser (CSP-safe when using jexpr engine)
|
|
56
|
+
const evalExpr = v => {
|
|
57
|
+
try {
|
|
58
|
+
const compiled = engine.compile(v, true);
|
|
59
|
+
const scope = engine.createScope ? engine.createScope({ state: {}, el: null, event: {}, refs: {}, $: null, html: null, get: null, post: null, put: null, patch: null, del: null }) : {};
|
|
60
|
+
return compiled.execute(scope);
|
|
61
|
+
} catch { return v; }
|
|
62
|
+
};
|
|
63
|
+
|
|
55
64
|
// Build prefix-aware regex and matcher
|
|
56
65
|
const ATTR_REGEX = new RegExp(`^(@|:|data-${prefix})`);
|
|
57
66
|
|
|
@@ -68,6 +77,7 @@ export function createHelium(options = {}) {
|
|
|
68
77
|
const inits = [];
|
|
69
78
|
const ALL = Symbol("all");
|
|
70
79
|
const root = document.querySelector(`[\\@${rootAttr}]`) || document.querySelector(`[data-${rootAttr}]`) || document.body;
|
|
80
|
+
const storageKey = root.getAttribute('@local-storage') || root.getAttribute(`data-${prefix}-local-storage`);
|
|
71
81
|
|
|
72
82
|
// Initialize or reuse HELIUM object
|
|
73
83
|
if (!HELIUM) {
|
|
@@ -225,6 +235,7 @@ export function createHelium(options = {}) {
|
|
|
225
235
|
const parentKey = HELIUM.parentKeys.get(t);
|
|
226
236
|
if (parentKey) HELIUM.bindings.get(parentKey)?.forEach(safeApplyBinding);
|
|
227
237
|
HELIUM.bindings.get(ALL)?.forEach(safeApplyBinding);
|
|
238
|
+
if (storageKey) localStorage.setItem(storageKey, JSON.stringify(state));
|
|
228
239
|
return res
|
|
229
240
|
}
|
|
230
241
|
};
|
|
@@ -232,6 +243,9 @@ export function createHelium(options = {}) {
|
|
|
232
243
|
// Initialize state if it doesn't exist
|
|
233
244
|
const state = new Proxy({}, handler);
|
|
234
245
|
|
|
246
|
+
// Load from localStorage if key exists
|
|
247
|
+
if (storageKey) try { Object.assign(state, JSON.parse(localStorage.getItem(storageKey))); } catch {}
|
|
248
|
+
|
|
235
249
|
// Merge initial state
|
|
236
250
|
Object.assign(state, initialState);
|
|
237
251
|
|
|
@@ -443,7 +457,7 @@ export function createHelium(options = {}) {
|
|
|
443
457
|
|
|
444
458
|
// Process the attribute
|
|
445
459
|
if (name === "@data" || name === `data-${prefix}`) {
|
|
446
|
-
Object.assign(state,
|
|
460
|
+
Object.assign(state, evalExpr(value));
|
|
447
461
|
}
|
|
448
462
|
else if (name.startsWith(":") || name.startsWith(`data-${prefix}-attr:`)) {
|
|
449
463
|
const propName = name.startsWith(":") ? name.slice(1) : name.slice(prefix.length + 11);
|
|
@@ -508,7 +522,7 @@ export function createHelium(options = {}) {
|
|
|
508
522
|
const target = pairs.map(([target]) => target);
|
|
509
523
|
const action = pairs.map(([, action]) => action);
|
|
510
524
|
const options = {
|
|
511
|
-
...(getAttr("options") &&
|
|
525
|
+
...(getAttr("options") && evalExpr(getAttr("options") || "{}")),
|
|
512
526
|
...(target && { target }),
|
|
513
527
|
...(action && { action }),
|
|
514
528
|
...(getAttr("template") && { template: execExpr(getAttr("template")),}),
|