@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.
@@ -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
 
@@ -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?.title || 'Loading...'}</h1><p>${data?.body || ''}</p></section>`
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
- console,
182
- Math,
183
- Date,
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, parseEx(value));
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") && parseEx(getAttr("options") || "{}")),
525
+ ...(getAttr("options") && evalExpr(getAttr("options") || "{}")),
512
526
  ...(target && { target }),
513
527
  ...(action && { action }),
514
528
  ...(getAttr("template") && { template: execExpr(getAttr("template")),}),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@daz4126/helium",
3
- "version": "1.0.0-beta.1",
3
+ "version": "1.0.0-beta.2",
4
4
  "main": "helium.js",
5
5
  "type": "module",
6
6
  "exports": {