@daz4126/helium 1.0.0-rc.2 → 1.0.0-rc.3
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/README.md +199 -72
- package/dist/helium-csp-sse.min.js +1 -1
- package/dist/helium-csp.min.js +1 -1
- package/dist/helium-lite.min.js +1 -1
- package/dist/helium-sse.min.js +1 -1
- package/dist/helium.min.js +1 -1
- package/helium-csp.js +9 -3
- package/helium-lite.js +271 -57
- package/helium.js +444 -103
- package/package.json +9 -1
package/README.md
CHANGED
|
@@ -18,7 +18,7 @@ It's really simple to use - just sprinkle the magic @attributes into your HTML a
|
|
|
18
18
|
|
|
19
19
|
Helium is designed for developers who want:
|
|
20
20
|
|
|
21
|
-
- **Lightweight** - About
|
|
21
|
+
- **Lightweight** - About 5.5–13.3KB minified and gzipped, depending on the build
|
|
22
22
|
- **Powerful** - Declarative JavaScript in your HTML
|
|
23
23
|
- **Zero build step** - Works directly in the browser with no compiling
|
|
24
24
|
- **Easy to learn** - If you know HTML and basic JavaScript, you're ready
|
|
@@ -31,7 +31,7 @@ Helium comes in three core versions, with SSE available as an optional add-on, s
|
|
|
31
31
|
|
|
32
32
|
The full-featured version with everything included.
|
|
33
33
|
|
|
34
|
-
- All directives (`@text`, `@html`, `@for`, `@
|
|
34
|
+
- All directives (`@text`, `@html`, `@for`, `@if`, `@bind`, `@data`, `@ref`, `@calculate`, `@effect`, `@init`, etc.)
|
|
35
35
|
- Event handlers with modifiers
|
|
36
36
|
- HTTP requests (`@get`, `@post`, `@put`, `@patch`, `@delete`)
|
|
37
37
|
- Module imports (`@import`)
|
|
@@ -51,7 +51,7 @@ A slimmed-down version with just the core reactivity features.
|
|
|
51
51
|
- Core directives (`@text`, `@html`, `@bind`, `@data`, `@ref`, `@calculate`, `@effect`, `@init`)
|
|
52
52
|
- Event handlers with modifiers
|
|
53
53
|
- One global state namespace
|
|
54
|
-
- No `@
|
|
54
|
+
- No local `@data`, keyed `@for`, HTTP requests, imports, SSE, or Turbo integration
|
|
55
55
|
|
|
56
56
|
**Best for:** Simple interactive pages where you only need reactivity and don't need server communication or module imports.
|
|
57
57
|
|
|
@@ -104,23 +104,25 @@ import helium from "@daz4126/helium/csp"
|
|
|
104
104
|
| Feature | Standard | Lite | CSP |
|
|
105
105
|
|---------|----------|------|-----|
|
|
106
106
|
| Core directives | ✅ | ✅ | ✅ |
|
|
107
|
-
| Element-local `@
|
|
107
|
+
| Element-local `@data` | ✅ | ❌ | ✅ |
|
|
108
108
|
| Keyed `@for` | ✅ | ❌ | ✅ |
|
|
109
|
+
| Conditional `@if` | ✅ | ❌ | ✅ |
|
|
109
110
|
| Event handlers | ✅ | ✅ | ✅ |
|
|
110
111
|
| HTTP requests | ✅ | ❌ | ✅ |
|
|
111
112
|
| `@import` | ✅ | ❌ | ✅ |
|
|
112
113
|
| SSE | Add-on | ❌ | Add-on |
|
|
113
114
|
| Turbo integration | ✅ | ❌ | ✅ |
|
|
114
115
|
| CSP safe | ❌ | ❌ | ✅ |
|
|
115
|
-
| Approx. min+gzip size |
|
|
116
|
+
| Approx. min+gzip size | 8.79KB | 5.52KB | 12.89KB |
|
|
116
117
|
|
|
117
118
|
Sizes are enforced against the production Terser builds with gzip level 9. The
|
|
118
|
-
CSP figure includes its expression parser. Adding SSE produces
|
|
119
|
-
and
|
|
119
|
+
CSP figure includes its expression parser. Adding SSE produces 9.17KB Standard
|
|
120
|
+
and 13.27KB CSP bundles.
|
|
120
121
|
|
|
121
122
|
Lite is generated from the same global-state reactive core as Standard. It has
|
|
122
|
-
identical deep/shared reactivity, lifecycle, binding, and cleanup behavior
|
|
123
|
-
intentionally excludes element-local
|
|
123
|
+
identical deep/shared reactivity, lifecycle, binding, and cleanup behavior for
|
|
124
|
+
root state, but intentionally excludes element-local data and Standard's other
|
|
125
|
+
extensions.
|
|
124
126
|
|
|
125
127
|
## Installation
|
|
126
128
|
|
|
@@ -300,6 +302,32 @@ but keyed `@for` avoids HTML-string interpolation and lets each row use normal
|
|
|
300
302
|
|
|
301
303
|
**Alias:** `data-he-for` (the key remains `:key`)
|
|
302
304
|
|
|
305
|
+
### @if
|
|
306
|
+
|
|
307
|
+
Standard and CSP can conditionally render a `<template>`'s content. Unlike
|
|
308
|
+
`@hidden`/`@visible`, which only toggle visibility, `@if` adds the content to
|
|
309
|
+
the DOM when the expression is truthy and fully removes it — bindings,
|
|
310
|
+
listeners, and `@init` cleanup included — when it becomes falsy:
|
|
311
|
+
|
|
312
|
+
```html
|
|
313
|
+
<template @if="loggedIn">
|
|
314
|
+
<p>Welcome back, <span @text="user.name"></span>!</p>
|
|
315
|
+
<button @click="logout($data)">Log out</button>
|
|
316
|
+
</template>
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
Content that is switched off costs nothing: its expressions never evaluate and
|
|
320
|
+
its listeners don't exist. Use `@if` for anything conditional-but-expensive
|
|
321
|
+
(modals, tab panels, admin-only markup) and `@hidden`/`@visible` for cheap
|
|
322
|
+
show/hide toggles where you want to preserve element state such as form input.
|
|
323
|
+
|
|
324
|
+
`@if` requires a `<template>` element (like `@for`, and like Alpine's `x-if`)
|
|
325
|
+
because template content is inert until inserted — that's what makes the "off"
|
|
326
|
+
state free. It cannot be combined with `@for` on the same template; nest one
|
|
327
|
+
template inside the other instead. Lite excludes this feature.
|
|
328
|
+
|
|
329
|
+
**Alias:** `data-he-if`
|
|
330
|
+
|
|
303
331
|
### @bind
|
|
304
332
|
|
|
305
333
|
Creates a 2-way binding between an input element's value and a variable. Whatever is entered in the following input field will be stored as a variable called name:
|
|
@@ -389,31 +417,23 @@ Makes the element hidden or visible depending on the result of a JavaScript expr
|
|
|
389
417
|
|
|
390
418
|
### @data
|
|
391
419
|
|
|
392
|
-
Initializes variables that can be used in JavaScript expressions.
|
|
420
|
+
Initializes variables that can be used in JavaScript expressions. In Standard
|
|
421
|
+
and CSP, `@data` behaves like Alpine's `x-data`: root-level `@data` initializes
|
|
422
|
+
the mounted root state, while nested `@data` creates local state for that
|
|
423
|
+
element and its descendants. Lite always uses one global state namespace, so
|
|
424
|
+
every `@data` merges into root state.
|
|
393
425
|
|
|
394
426
|
```html
|
|
395
|
-
<
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
You can then use these variables in other Helium attributes:
|
|
399
|
-
|
|
400
|
-
```html
|
|
401
|
-
<div @data="{ count: 0 }">
|
|
402
|
-
<button @click="count++">Increment</button>
|
|
403
|
-
<p @text="count"></p>
|
|
404
|
-
</div>
|
|
427
|
+
<main @helium @data="{ count: 100, name: 'Helium' }">
|
|
428
|
+
<p @text="name + ': ' + count"></p>
|
|
429
|
+
</main>
|
|
405
430
|
```
|
|
406
431
|
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
### @scope (Standard/CSP)
|
|
410
|
-
|
|
411
|
-
Creates reactive state owned by an element and its descendants. Scoped names
|
|
412
|
-
shadow root state without changing the object returned by `helium()`:
|
|
432
|
+
Nested `@data` is local in Standard/CSP:
|
|
413
433
|
|
|
414
434
|
```html
|
|
415
|
-
<main @data="{ count: 100 }">
|
|
416
|
-
<section @
|
|
435
|
+
<main @helium @data="{ count: 100 }">
|
|
436
|
+
<section @data="{ count: 0, open: false }">
|
|
417
437
|
<button @click="count++">Increment local count</button>
|
|
418
438
|
<button @click="open = !open">Toggle</button>
|
|
419
439
|
<p @text="count"></p>
|
|
@@ -424,41 +444,49 @@ shadow root state without changing the object returned by `helium()`:
|
|
|
424
444
|
</main>
|
|
425
445
|
```
|
|
426
446
|
|
|
427
|
-
The
|
|
428
|
-
values. Nested
|
|
429
|
-
|
|
447
|
+
The object is evaluated once in its parent context, so local defaults can use
|
|
448
|
+
outer values. Nested data objects inherit outer names, and assignment updates
|
|
449
|
+
the nearest data object that declares that name:
|
|
430
450
|
|
|
431
451
|
```html
|
|
432
|
-
<div @
|
|
433
|
-
<div @
|
|
452
|
+
<div @data="{ count: startingCount, label: 'outer' }">
|
|
453
|
+
<div @data="{ label: 'inner' }">
|
|
434
454
|
<button @click="count++">Updates the outer local count</button>
|
|
435
455
|
<span @text="label"></span> <!-- inner -->
|
|
436
456
|
</div>
|
|
437
457
|
</div>
|
|
438
458
|
```
|
|
439
459
|
|
|
440
|
-
Declare every name that should remain local. Assigning an undeclared name
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
460
|
+
Declare every name that should remain local. Assigning an undeclared name writes
|
|
461
|
+
to root state. The same applies to `@bind` and `@calculate` targets: declare
|
|
462
|
+
their target names in local `@data` when they should be local. Inside the
|
|
463
|
+
subtree, `$data` is the scoped state view. Local values are not returned from
|
|
464
|
+
`helium()` and are not included in root `@local-storage` persistence.
|
|
445
465
|
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
**Alias:** `data-he-scope`
|
|
449
|
-
|
|
450
|
-
`@scope` is intentionally excluded from Lite, which always uses one global
|
|
451
|
-
state namespace per mounted root.
|
|
466
|
+
**Alias:** `data-he-data` (the legacy bare `data-he` alias is also accepted)
|
|
452
467
|
|
|
453
468
|
### @ref
|
|
454
469
|
|
|
455
|
-
Creates a reference to
|
|
470
|
+
Creates a scoped reference to an element. The first matching element is
|
|
471
|
+
available through `$name`, and every matching element is available as the
|
|
472
|
+
always-array `$refs.name` in current DOM order:
|
|
456
473
|
|
|
457
474
|
```html
|
|
458
|
-
<
|
|
475
|
+
<section @data="{ items: [] }">
|
|
476
|
+
<ul @ref="list"></ul>
|
|
477
|
+
<div @ref="row"></div>
|
|
478
|
+
<div @ref="row"></div>
|
|
479
|
+
<button @click="console.log($list, $refs.row)">Inspect refs</button>
|
|
480
|
+
</section>
|
|
459
481
|
```
|
|
460
482
|
|
|
461
|
-
|
|
483
|
+
Ref lookup starts in the nearest explicit `@data` scope and falls back through
|
|
484
|
+
parent scopes. A ref with the same name in a nested scope shadows the outer
|
|
485
|
+
name. Lite has one root scope, so all of its refs belong to the mounted root.
|
|
486
|
+
When refs are removed or reordered, singular and plural lookup follows the
|
|
487
|
+
current DOM.
|
|
488
|
+
|
|
489
|
+
This element can be accessed in other JavaScript expressions as `$list`:
|
|
462
490
|
|
|
463
491
|
```html
|
|
464
492
|
<button @click="$list.appendChild($html('<li>New item</li>'))">Add Task</button>
|
|
@@ -648,11 +676,22 @@ You can add modifiers by appending them with a dot (`.`) after the event name:
|
|
|
648
676
|
- **once** - Only runs the event handler once, then removes the listener
|
|
649
677
|
- **outside** - Only fires when the event happens outside the element
|
|
650
678
|
- **document** - Attaches the listener to the document instead of the element
|
|
679
|
+
- **window** - Attaches the listener to `window` instead of the element
|
|
680
|
+
- **self** - Only fires when the element itself is the event target
|
|
681
|
+
- **capture** - Handles the event during the capture phase
|
|
682
|
+
- **passive** - Registers a native passive listener
|
|
651
683
|
- **debounce** - Debounces the event handler (default 300ms)
|
|
652
684
|
- **debounce:500** - Debounces with custom delay in milliseconds
|
|
685
|
+
- **throttle** - Runs immediately, then at most once per interval (default 300ms)
|
|
686
|
+
- **throttle:500** - Throttles with a custom interval in milliseconds
|
|
653
687
|
- **shift, ctrl, alt, meta** - Only fires if the modifier key is pressed
|
|
654
688
|
- **Key names** - For keyboard events, specify which key (e.g., `enter`, `esc`, `space`)
|
|
655
689
|
|
|
690
|
+
Throttle uses leading and trailing calls: the first matching event runs
|
|
691
|
+
immediately, and events received during the interval are coalesced into one
|
|
692
|
+
final call using the latest event. `.passive.prevent` is invalid because a
|
|
693
|
+
passive listener cannot cancel an event; Helium warns and does not register it.
|
|
694
|
+
|
|
656
695
|
**Examples:**
|
|
657
696
|
|
|
658
697
|
```html
|
|
@@ -682,6 +721,12 @@ You can add modifiers by appending them with a dot (`.`) after the event name:
|
|
|
682
721
|
|
|
683
722
|
<!-- Listen on document level -->
|
|
684
723
|
<div @keydown.document.esc="closeModal()">Press ESC anywhere</div>
|
|
724
|
+
|
|
725
|
+
<!-- Window listener with throttled updates -->
|
|
726
|
+
<div @resize.window.throttle:100="measureLayout()"></div>
|
|
727
|
+
|
|
728
|
+
<!-- Ignore clicks that originated in descendants -->
|
|
729
|
+
<button @click.self="select()"><span>Select</span></button>
|
|
685
730
|
```
|
|
686
731
|
|
|
687
732
|
**Alias:** Prepend the event name with `data-he-`, for example `data-he-click="count++"`
|
|
@@ -1000,13 +1045,26 @@ You can also use a string:
|
|
|
1000
1045
|
These special variables are available in all JavaScript expressions:
|
|
1001
1046
|
|
|
1002
1047
|
### $
|
|
1003
|
-
|
|
1048
|
+
The `$` query helper preserves `document.querySelector` as its default and adds
|
|
1049
|
+
array-returning plural and scoped forms:
|
|
1004
1050
|
|
|
1005
1051
|
```html
|
|
1006
1052
|
<div @click="$('#header').classList.add('active')">Activate Header!</div>
|
|
1007
1053
|
<button @click="$('.sidebar').style.display = 'none'">Hide Sidebar</button>
|
|
1008
1054
|
```
|
|
1009
1055
|
|
|
1056
|
+
```js
|
|
1057
|
+
$(selector) // first match in document
|
|
1058
|
+
$.all(selector) // all matches in document
|
|
1059
|
+
$.root(selector) // first descendant match in $root
|
|
1060
|
+
$.root.all(selector) // all descendant matches in $root
|
|
1061
|
+
$.scope(selector) // first descendant match in $scope
|
|
1062
|
+
$.scope.all(selector) // all descendant matches in $scope
|
|
1063
|
+
```
|
|
1064
|
+
|
|
1065
|
+
The plural forms return normal arrays. Like `querySelector`, the root and scope
|
|
1066
|
+
forms search descendants and do not include `$root` or `$scope` themselves.
|
|
1067
|
+
|
|
1010
1068
|
### $el
|
|
1011
1069
|
Reference to the current element:
|
|
1012
1070
|
|
|
@@ -1016,6 +1074,17 @@ Reference to the current element:
|
|
|
1016
1074
|
<input @input="console.log($el.value)">
|
|
1017
1075
|
```
|
|
1018
1076
|
|
|
1077
|
+
### $root
|
|
1078
|
+
|
|
1079
|
+
The actual root element owned by the current Helium instance. This is the
|
|
1080
|
+
element selected by `@helium`/`data-helium`, the element passed to
|
|
1081
|
+
`helium.mount()`, or `document.body` for the default fallback root.
|
|
1082
|
+
|
|
1083
|
+
### $scope
|
|
1084
|
+
|
|
1085
|
+
The nearest element that declares local `@data` state, falling back to `$root`.
|
|
1086
|
+
Lite has no local data scopes, so `$scope` and `$root` are always the same.
|
|
1087
|
+
|
|
1019
1088
|
### $event
|
|
1020
1089
|
The event object (available in event handlers):
|
|
1021
1090
|
|
|
@@ -1025,6 +1094,21 @@ The event object (available in event handlers):
|
|
|
1025
1094
|
<form @submit="$event.preventDefault(); handleSubmit()">
|
|
1026
1095
|
```
|
|
1027
1096
|
|
|
1097
|
+
### $dispatch
|
|
1098
|
+
|
|
1099
|
+
Dispatch a bubbling, cancelable `CustomEvent` from the current element. The
|
|
1100
|
+
event is returned, so callers can inspect `defaultPrevented`:
|
|
1101
|
+
|
|
1102
|
+
```html
|
|
1103
|
+
<section @cart:add="total += $event.detail.price">
|
|
1104
|
+
<button @click="$dispatch('cart:add', { price: 12 })">Add</button>
|
|
1105
|
+
</section>
|
|
1106
|
+
```
|
|
1107
|
+
|
|
1108
|
+
The signature is `$dispatch(name, detail = {}, options = {})`. Options can set
|
|
1109
|
+
`target` (default `$el`), `bubbles` (default `true`), and `cancelable` (default
|
|
1110
|
+
`true`).
|
|
1111
|
+
|
|
1028
1112
|
### $data
|
|
1029
1113
|
The reactive data object containing all Helium variables:
|
|
1030
1114
|
|
|
@@ -1060,12 +1144,17 @@ The arguments are `url`,`params` (not for `$get`) and `options`. `options` is an
|
|
|
1060
1144
|
|
|
1061
1145
|
### Named refs
|
|
1062
1146
|
|
|
1063
|
-
|
|
1064
|
-
|
|
1147
|
+
The first element marked with `@ref="name"` in the nearest defining scope is
|
|
1148
|
+
available as `$name`. `$refs.name` is an array of every matching ref in that
|
|
1149
|
+
scope, in current DOM order:
|
|
1065
1150
|
|
|
1066
1151
|
```html
|
|
1067
1152
|
<input @ref="username">
|
|
1068
1153
|
<button @click="console.log($username.value)">Log Username</button>
|
|
1154
|
+
|
|
1155
|
+
<div @ref="item"></div>
|
|
1156
|
+
<div @ref="item"></div>
|
|
1157
|
+
<button @click="console.log($refs.item.length)">Log item count</button>
|
|
1069
1158
|
```
|
|
1070
1159
|
|
|
1071
1160
|
## Functions
|
|
@@ -1164,6 +1253,29 @@ This is the recommended approach when you need to update variables:
|
|
|
1164
1253
|
|
|
1165
1254
|
## Advanced Features
|
|
1166
1255
|
|
|
1256
|
+
### Batched Updates
|
|
1257
|
+
|
|
1258
|
+
State changes are batched: a burst of synchronous mutations triggers a single
|
|
1259
|
+
DOM update on the next microtask, and each affected binding runs once no matter
|
|
1260
|
+
how many mutations fed it. This keeps loops over state cheap — updating 100
|
|
1261
|
+
items in a keyed list re-renders the list once, not 100 times:
|
|
1262
|
+
|
|
1263
|
+
```javascript
|
|
1264
|
+
// One DOM update, not 100
|
|
1265
|
+
for (let i = 0; i < 100; i++) state.items[i].done = true;
|
|
1266
|
+
```
|
|
1267
|
+
|
|
1268
|
+
Two practical consequences:
|
|
1269
|
+
|
|
1270
|
+
- **Reading the DOM right after a mutation shows the old value.** Await a
|
|
1271
|
+
microtask first: `state.count = 5; await Promise.resolve();` (in tests, a
|
|
1272
|
+
`setTimeout(0)` tick also covers content inserted by `@if`/`@for`, which is
|
|
1273
|
+
processed asynchronously by the MutationObserver).
|
|
1274
|
+
- **Assigning a value that is already current is a no-op** — it doesn't
|
|
1275
|
+
re-trigger bindings, so effects that write unchanged state settle instead of
|
|
1276
|
+
looping. A binding cascade that *never* stops writing new values (e.g.
|
|
1277
|
+
`@effect:*="count++"`) is detected and reported after 100 passes.
|
|
1278
|
+
|
|
1167
1279
|
### DOM Morphing with Idiomorph
|
|
1168
1280
|
|
|
1169
1281
|
By default, when you update innerHTML with `@html`, Helium replaces the entire content. This can cause issues like losing focus, resetting scroll positions, or interrupting animations.
|
|
@@ -1306,6 +1418,18 @@ import helium from "@daz4126/helium/csp"
|
|
|
1306
1418
|
<div @effect:page="analytics.track('page_view', { page })"></div>
|
|
1307
1419
|
```
|
|
1308
1420
|
|
|
1421
|
+
**Benchmarking:**
|
|
1422
|
+
|
|
1423
|
+
The repo includes a head-to-head runtime benchmark against Alpine.js, React,
|
|
1424
|
+
and Datastar (keyed list create/update/swap/clear plus rapid counter updates,
|
|
1425
|
+
run in real Chromium; Datastar only runs the counter test since it renders
|
|
1426
|
+
lists server-side). Run it with:
|
|
1427
|
+
|
|
1428
|
+
```bash
|
|
1429
|
+
npm run bench # defaults: 1000 rows, 7 runs
|
|
1430
|
+
node scripts/benchmark.mjs --rows 5000 --runs 5
|
|
1431
|
+
```
|
|
1432
|
+
|
|
1309
1433
|
### Structuring Larger Apps
|
|
1310
1434
|
|
|
1311
1435
|
**Organize state at the root:**
|
|
@@ -1411,7 +1535,11 @@ helium({
|
|
|
1411
1535
|
|
|
1412
1536
|
### JavaScript Expression Errors
|
|
1413
1537
|
|
|
1414
|
-
If a binding expression throws at runtime, Helium logs the error and continues
|
|
1538
|
+
If a binding expression throws at runtime, Helium logs the error and continues
|
|
1539
|
+
without updating that binding. Failed compilation keeps Helium's literal-string
|
|
1540
|
+
ergonomics: plain text such as `@text="Loading..."` stays quiet, while
|
|
1541
|
+
expression-looking failures such as `@text="user.name."` or
|
|
1542
|
+
`@click="count +"` emit a `console.warn` with the attribute and expression.
|
|
1415
1543
|
|
|
1416
1544
|
```html
|
|
1417
1545
|
<!-- If items is undefined, this won't crash the page -->
|
|
@@ -1434,8 +1562,10 @@ promises, so you can handle failures explicitly:
|
|
|
1434
1562
|
|
|
1435
1563
|
### Invalid Attribute Syntax
|
|
1436
1564
|
|
|
1437
|
-
|
|
1438
|
-
|
|
1565
|
+
Helium keeps plain malformed value expressions as literal values. If a failed
|
|
1566
|
+
expression looks like broken JavaScript, Helium emits a warning with directive
|
|
1567
|
+
context. Event handlers, HTTP options, `@data`, `@for`, `@if`, `@calculate`,
|
|
1568
|
+
`@effect`, and `@init` are treated strictly and warn on compile failure.
|
|
1439
1569
|
|
|
1440
1570
|
|
|
1441
1571
|
## Roadmap & Known Limitations
|
|
@@ -1445,42 +1575,36 @@ intentionally absent (or still on the roadmap). Current gaps:
|
|
|
1445
1575
|
|
|
1446
1576
|
### Not yet supported
|
|
1447
1577
|
|
|
1448
|
-
- **No DOM-removing `@if`** — only `@hidden`/`@visible`, which toggle visibility
|
|
1449
|
-
but keep elements in the DOM.
|
|
1450
1578
|
- **No `$watch`/`$nextTick`, and no cross-root shared store** — each mounted
|
|
1451
1579
|
instance has its own isolated state namespace.
|
|
1580
|
+
- **No transition helper** — use CSS transitions/classes directly with
|
|
1581
|
+
`@hidden`, `@visible`, or `@if`.
|
|
1452
1582
|
|
|
1453
1583
|
### Roadmap
|
|
1454
1584
|
|
|
1455
1585
|
Ordered by recommended implementation sequence:
|
|
1456
1586
|
|
|
1457
|
-
1. **
|
|
1458
|
-
containing the expression and build/engine context instead of silently
|
|
1459
|
-
treating every compile failure as a literal value.
|
|
1460
|
-
2. **Add real-browser CI** covering CSP, Turbo, SSE, focus preservation, late
|
|
1461
|
-
imports, generated Lite output, and package exports before adding more DOM
|
|
1462
|
-
lifecycle behavior.
|
|
1463
|
-
3. **Document function and `this` context limitations** alongside the existing
|
|
1587
|
+
1. **Document function and `this` context limitations** alongside the existing
|
|
1464
1588
|
magic-variable guidance, with correct patterns for passing `$data`, `$el`,
|
|
1465
1589
|
and other context explicitly.
|
|
1466
|
-
|
|
1590
|
+
2. **Add opt-in required-CSRF validation for POST/PUT/PATCH** — fail before the
|
|
1467
1591
|
request when an application declares that a token is mandatory, without
|
|
1468
1592
|
breaking tokenless APIs and cross-origin requests.
|
|
1469
|
-
|
|
1593
|
+
3. **Add request cancellation and explicit success/error hooks** using
|
|
1470
1594
|
`AbortController`; define the request lifecycle before expanding loading and
|
|
1471
1595
|
error-state APIs.
|
|
1472
|
-
|
|
1596
|
+
4. **Document a form-validation pattern first**, using native constraint
|
|
1473
1597
|
validation and Helium state; add helpers only if examples reveal recurring
|
|
1474
1598
|
boilerplate.
|
|
1475
|
-
|
|
1599
|
+
5. **Create a complete error-state example** covering validation, HTTP failure,
|
|
1476
1600
|
retry, accessible messaging, and clearing stale errors.
|
|
1477
|
-
|
|
1601
|
+
6. **Complete HTTP loading-state lifecycle behavior** — `@loading` already
|
|
1478
1602
|
exists, so define consistent success, error, cancellation, and restoration
|
|
1479
1603
|
behavior rather than adding a second loading mechanism.
|
|
1480
|
-
|
|
1604
|
+
7. **Add debounced two-way binding updates if needed** after reproducing a real
|
|
1481
1605
|
feedback/caret loop; event-handler debouncing already covers ordinary input
|
|
1482
1606
|
throttling.
|
|
1483
|
-
|
|
1607
|
+
8. **Add a minimal watch/middleware system** together with a clear size budget;
|
|
1484
1608
|
avoid adopting Alpine-scale surface area without demonstrated use cases.
|
|
1485
1609
|
|
|
1486
1610
|
## Contributing
|
|
@@ -1491,9 +1615,12 @@ Helium is open source! Contributions, issues, and feature requests are welcome.
|
|
|
1491
1615
|
- Report issues: Create an issue on GitHub
|
|
1492
1616
|
- Suggest features: Open a discussion on GitHub
|
|
1493
1617
|
|
|
1494
|
-
Before submitting a change, run `npm run check`. It executes the full test
|
|
1495
|
-
suite, creates the production bundles, and fails if any gzip
|
|
1496
|
-
exceeded. `npm run
|
|
1618
|
+
Before submitting a change, run `npm run check`. It executes the full jsdom test
|
|
1619
|
+
suite, creates the production bundles, smoke-tests them, and fails if any gzip
|
|
1620
|
+
size budget is exceeded. `npm run test:browser` runs the Chromium smoke suite
|
|
1621
|
+
used by CI for CSP, Turbo, SSE, keyed focus preservation, late imports, Lite
|
|
1622
|
+
generation, and browser entry imports. `npm run check:ci` runs both paths.
|
|
1623
|
+
`npm run size` also reports raw, gzip, and Brotli sizes.
|
|
1497
1624
|
|
|
1498
1625
|
`helium.js` contains the shared runtime plus sections marked Standard-only.
|
|
1499
1626
|
`npm run generate` derives `helium-lite.js`; do not edit the generated Lite file
|