@daz4126/helium 1.0.0-beta.2 → 1.0.0-beta.4

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/CLAUDE.md CHANGED
@@ -105,9 +105,7 @@ const { helium, heliumTeardown } = createHelium({
105
105
  createScope(ctx) {
106
106
  // Return scope object for expression execution
107
107
  }
108
- },
109
- prefix: 'my', // For data-my-* attributes
110
- rootAttr: 'mylib' // For @mylib / data-mylib root element
108
+ }
111
109
  });
112
110
  ```
113
111
 
@@ -220,17 +220,11 @@
220
220
  </head>
221
221
  <body>
222
222
 
223
- <div class="page" data-helium @data="{ count: 0 }">
223
+ <div class="page" data-helium>
224
224
  <h1>Helium Examples</h1>
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
-
234
228
  <!-- Counter -->
235
229
  <div class="card">
236
230
  <h2>Counter</h2>
@@ -328,7 +322,7 @@
328
322
  </div>
329
323
 
330
324
  <!-- Debounced Input -->
331
- <div class="card" @data="{ search: '', debouncedSearch: '' }">
325
+ <div class="card">
332
326
  <h2>Debounced Input</h2>
333
327
  <p>Type and wait 500ms to see the debounced value update.</p>
334
328
  <input placeholder="Search..." @bind="search" @input.debounce:500="debouncedSearch = search">
@@ -340,13 +334,13 @@
340
334
  <div class="card" @data="{ tab: 'one' }">
341
335
  <h2>Tabs</h2>
342
336
  <div class="tabs">
343
- <button class="tab" :class="{ active: tab === 'one' }" @click="tab = 'one'">Tab 1</button>
344
- <button class="tab" :class="{ active: tab === 'two' }" @click="tab = 'two'">Tab 2</button>
345
- <button class="tab" :class="{ active: tab === 'three' }" @click="tab = 'three'">Tab 3</button>
337
+ <button class="tab" :class="{ active: tab == 'one' }" @click="tab = 'one'">Tab 1</button>
338
+ <button class="tab" :class="{ active: tab == 'two' }" @click="tab = 'two'">Tab 2</button>
339
+ <button class="tab" :class="{ active: tab == 'three' }" @click="tab = 'three'">Tab 3</button>
346
340
  </div>
347
- <div class="tab-content" @visible="tab === 'one'">Content for Tab 1</div>
348
- <div class="tab-content" @visible="tab === 'two'">Content for Tab 2</div>
349
- <div class="tab-content" @visible="tab === 'three'">Content for Tab 3</div>
341
+ <div class="tab-content" @visible="tab == 'one'">Content for Tab 1</div>
342
+ <div class="tab-content" @visible="tab == 'two'">Content for Tab 2</div>
343
+ <div class="tab-content" @visible="tab == 'three'">Content for Tab 3</div>
350
344
  </div>
351
345
 
352
346
  <!-- Element References -->
@@ -395,7 +389,7 @@
395
389
  </div>
396
390
 
397
391
  <!-- Character Counter -->
398
- <div class="card" @data="{ text: '', maxLength: 100 }">
392
+ <div class="card" @data="{ maxLength: 100 }">
399
393
  <h2>Character Counter</h2>
400
394
  <textarea @bind="text" placeholder="Type something..." rows="3"></textarea>
401
395
  <span @calculate:remaining="maxLength - text.length"></span>
@@ -405,7 +399,7 @@
405
399
  </div>
406
400
 
407
401
  <!-- Password Strength (uses @import) -->
408
- <div class="card" @data="{ password: '' }" @import="password-utils">
402
+ <div class="card" @import="https://codepen.io/daz4126/pen/xbOzGNO.js">
409
403
  <h2>Password Strength</h2>
410
404
  <input type="password" @bind="password" placeholder="Enter password">
411
405
  <span @calculate:result="checkStrength(password)"></span>
@@ -418,7 +412,7 @@
418
412
  </div>
419
413
 
420
414
  <!-- Init Example -->
421
- <div class="card" @data="{ loadTime: '' }">
415
+ <div class="card">
422
416
  <h2>Init Example</h2>
423
417
  <p @init="loadTime = new Date().toLocaleTimeString()">
424
418
  Page loaded at: <b @text="loadTime"></b>
@@ -427,7 +421,7 @@
427
421
  </div>
428
422
 
429
423
  <!-- Effect Example -->
430
- <div class="card" @data="{ clicks: 0, history: [] }">
424
+ <div class="card" @data="{ history: [] }">
431
425
  <h2>Effect (Side Effects)</h2>
432
426
  <p>Click the button and watch the history build up.</p>
433
427
  <button @click="clicks++">Click Me</button>
@@ -436,21 +430,12 @@
436
430
  <p>History: <span @text="history.join(', ') || 'None yet'">None yet</span></p>
437
431
  </div>
438
432
 
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
-
448
433
  </div>
449
434
 
450
435
  <!-- AJAX Examples Section -->
451
436
  <div class="page" style="margin-top: 2rem;" @data="{
452
437
  templates: {
453
- post: data => `<section class='post'><h1>${data && data.title || 'Loading...'}</h1><p>${data && data.body || ''}</p></section>`
438
+ post: data => `<section class='post'><h1>${data?.title || 'Loading...'}</h1><p>${data?.body || ''}</p></section>`
454
439
  }
455
440
  }">
456
441
  <h1>AJAX Examples</h1>
@@ -0,0 +1,109 @@
1
+ * { box-sizing: border-box; margin: 0; padding: 0; }
2
+ body {
3
+ font: 14px 'Helvetica Neue', Helvetica, Arial, sans-serif;
4
+ background: #f5f5f5;
5
+ min-height: 100vh;
6
+ }
7
+ .todoapp {
8
+ max-width: 550px;
9
+ margin: 40px auto;
10
+ background: #fff;
11
+ box-shadow: 0 2px 4px rgba(0,0,0,0.2), 0 25px 50px rgba(0,0,0,0.1);
12
+ }
13
+ .todoapp h1 {
14
+ font-size: 80px;
15
+ font-weight: 200;
16
+ text-align: center;
17
+ color: #b83f45;
18
+ padding: 10px 0;
19
+ }
20
+ .new-todo {
21
+ width: 100%;
22
+ padding: 16px 16px 16px 20px;
23
+ font-size: 24px;
24
+ border: none;
25
+ border-bottom: 1px solid #ededed;
26
+ font-family: inherit;
27
+ outline: none;
28
+ }
29
+ .new-todo::placeholder { color: #d9d9d9; font-style: italic; }
30
+ .toggle-all-container {
31
+ display: flex;
32
+ align-items: center;
33
+ padding: 12px 15px;
34
+ border-bottom: 1px solid #ededed;
35
+ color: #777;
36
+ cursor: pointer;
37
+ }
38
+ .toggle-all-container input { margin-right: 10px; }
39
+ .todo-list { list-style: none; }
40
+ .todo-item {
41
+ display: flex;
42
+ align-items: center;
43
+ padding: 15px;
44
+ border-bottom: 1px solid #ededed;
45
+ font-size: 24px;
46
+ }
47
+ .todo-item.completed .todo-text {
48
+ color: #949494;
49
+ text-decoration: line-through;
50
+ }
51
+ .toggle {
52
+ width: 24px;
53
+ height: 24px;
54
+ margin-right: 15px;
55
+ cursor: pointer;
56
+ flex-shrink: 0;
57
+ accent-color: #85cda8;
58
+ }
59
+ .todo-text { flex: 1; word-break: break-all; }
60
+ .destroy {
61
+ display: none;
62
+ background: none;
63
+ border: none;
64
+ color: #cc9a9a;
65
+ font-size: 28px;
66
+ cursor: pointer;
67
+ padding: 0 10px;
68
+ line-height: 1;
69
+ }
70
+ .destroy:hover { color: #af5b5e; }
71
+ .todo-item:hover .destroy { display: block; }
72
+ .footer {
73
+ display: flex;
74
+ align-items: center;
75
+ justify-content: space-between;
76
+ padding: 10px 15px;
77
+ color: #777;
78
+ font-size: 14px;
79
+ border-top: 1px solid #e6e6e6;
80
+ }
81
+ .filters { display: flex; gap: 3px; }
82
+ .filters button {
83
+ background: none;
84
+ border: 1px solid transparent;
85
+ padding: 3px 7px;
86
+ border-radius: 3px;
87
+ cursor: pointer;
88
+ font-size: 14px;
89
+ color: inherit;
90
+ font-family: inherit;
91
+ }
92
+ .filters button:hover { border-color: #db7676; }
93
+ .filters button.selected { border-color: #ce4646; }
94
+ .clear-completed {
95
+ background: none;
96
+ border: none;
97
+ cursor: pointer;
98
+ color: inherit;
99
+ font-size: 14px;
100
+ font-family: inherit;
101
+ }
102
+ .clear-completed:hover { text-decoration: underline; }
103
+ .info {
104
+ text-align: center;
105
+ margin: 30px 0;
106
+ color: #bbb;
107
+ font-size: 12px;
108
+ }
109
+ .info a { color: inherit; }
@@ -0,0 +1,46 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Helium TodoMVC</title>
7
+ <link rel="stylesheet" href="todo.css">
8
+ </head>
9
+ <body>
10
+
11
+ <section class="todoapp" data-helium @local-storage="helium-todos" @import="todo"
12
+ @data="{ todos: [], newTodo: '', filter: 'all', nextId: 1 }">
13
+
14
+ <span hidden @calculate:remaining="todos.filter(t => !t.completed).length"></span>
15
+ <span hidden @calculate:filtered="filter === 'all' ? todos : filter === 'active' ? todos.filter(t => !t.completed) : todos.filter(t => t.completed)"></span>
16
+
17
+ <header>
18
+ <h1>todos</h1>
19
+ <input class="new-todo" @bind="newTodo" @keydown.enter="addTodo($data)" placeholder="What needs to be done?" autofocus>
20
+ </header>
21
+
22
+ <section @visible="todos.length > 0">
23
+ <label class="toggle-all-container">
24
+ <input type="checkbox" @click="toggleAll($data)" :checked="todos.length > 0 && todos.every(t => t.completed)">
25
+ Mark all as complete
26
+ </label>
27
+ <ul class="todo-list" @html="renderTodos(filtered)"></ul>
28
+ </section>
29
+
30
+ <footer class="footer" @visible="todos.length > 0">
31
+ <span @text="remaining + (remaining === 1 ? ' item' : ' items') + ' left'"></span>
32
+ <nav class="filters">
33
+ <button :class="{ selected: filter === 'all' }" @click="filter = 'all'">All</button>
34
+ <button :class="{ selected: filter === 'active' }" @click="filter = 'active'">Active</button>
35
+ <button :class="{ selected: filter === 'completed' }" @click="filter = 'completed'">Completed</button>
36
+ </nav>
37
+ <button class="clear-completed" @click="clearCompleted($data)" @visible="todos.some(t => t.completed)">Clear completed</button>
38
+ </footer>
39
+
40
+ </section>
41
+
42
+ <p class="info">Built with <a href="https://github.com/daz-codes/helium">Helium</a></p>
43
+
44
+ <script type="module" src="../helium.js"></script>
45
+ </body>
46
+ </html>
@@ -0,0 +1,36 @@
1
+ function esc(s) {
2
+ return s.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
3
+ }
4
+
5
+ export function renderTodos(list) {
6
+ return list.map(t => `<li class='todo-item${t.completed ? ' completed' : ''}'>
7
+ <input type='checkbox' class='toggle'${t.completed ? ' checked' : ''} @click='toggleTodo($data, ${t.id})'>
8
+ <span class='todo-text'>${esc(t.text)}</span>
9
+ <button class='destroy' @click='removeTodo($data, ${t.id})'>&#215;</button>
10
+ </li>`).join('');
11
+ }
12
+
13
+ export function addTodo($data) {
14
+ const text = $data.newTodo.trim();
15
+ if (text) {
16
+ $data.todos = [...$data.todos, { id: $data.nextId++, text, completed: false }];
17
+ $data.newTodo = '';
18
+ }
19
+ }
20
+
21
+ export function toggleTodo($data, id) {
22
+ $data.todos = $data.todos.map(t => t.id === id ? { ...t, completed: !t.completed } : t);
23
+ }
24
+
25
+ export function removeTodo($data, id) {
26
+ $data.todos = $data.todos.filter(t => t.id !== id);
27
+ }
28
+
29
+ export function toggleAll($data) {
30
+ const allDone = $data.todos.every(t => t.completed);
31
+ $data.todos = $data.todos.map(t => ({ ...t, completed: !allDone }));
32
+ }
33
+
34
+ export function clearCompleted($data) {
35
+ $data.todos = $data.todos.filter(t => !t.completed);
36
+ }