@alwatr/action 9.14.0 → 9.16.0
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 +137 -76
- package/dist/action-record.d.ts +4 -4
- package/dist/delegate.d.ts +8 -8
- package/dist/delegate.d.ts.map +1 -1
- package/dist/main.d.ts +18 -6
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +3 -3
- package/dist/main.js.map +5 -5
- package/dist/method.d.ts +19 -19
- package/dist/registry.d.ts +4 -4
- package/package.json +5 -5
- package/src/action-record.ts +4 -4
- package/src/delegate.ts +67 -73
- package/src/main.ts +18 -6
- package/src/method.ts +19 -19
- package/src/registry.ts +27 -10
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
**Declarative DOM action-dispatch — the Action layer for Unidirectional Data Flow.**
|
|
4
4
|
|
|
5
|
-
`@alwatr/action` bridges HTML `on
|
|
5
|
+
`@alwatr/action` bridges HTML `on-<eventType>` attributes to typed signal handlers using **global event delegation**. One listener on `document.body` covers every element on the page — including elements added dynamically after bootstrap — with O(1) initialization cost regardless of how many elements exist.
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
@@ -25,7 +25,7 @@ The action bus is powered by a [`ChannelSignal`](../signal/README.md) from `@alw
|
|
|
25
25
|
|
|
26
26
|
### Global Event Delegation
|
|
27
27
|
|
|
28
|
-
A single capture-phase listener on `document.body` handles all `on
|
|
28
|
+
A single capture-phase listener on `document.body` handles all `on-<eventType>` elements. When an event fires, the handler walks up from `event.target` using `closest('[on-click]')` (or the matching attribute), parses the attribute value, runs modifiers, resolves the payload, and dispatches the action.
|
|
29
29
|
|
|
30
30
|
```
|
|
31
31
|
User clicks a button
|
|
@@ -33,13 +33,13 @@ User clicks a button
|
|
|
33
33
|
▼
|
|
34
34
|
document.body capture listener (1 listener per event type)
|
|
35
35
|
│
|
|
36
|
-
└─ closest('[on-
|
|
37
|
-
parse attribute → '
|
|
36
|
+
└─ closest('[on-click]') → finds element
|
|
37
|
+
parse attribute → 'add_to_cart:42'
|
|
38
38
|
run modifiers → none
|
|
39
39
|
resolve payload → '42'
|
|
40
|
-
internalChannel_.dispatch('
|
|
40
|
+
internalChannel_.dispatch('add_to_cart', '42')
|
|
41
41
|
│
|
|
42
|
-
└─ Map.get('
|
|
42
|
+
└─ Map.get('add_to_cart') → O(1) → invoke only matching handlers
|
|
43
43
|
```
|
|
44
44
|
|
|
45
45
|
### Complexity
|
|
@@ -53,7 +53,7 @@ document.body capture listener (1 listener per event type)
|
|
|
53
53
|
|
|
54
54
|
### `once` modifier
|
|
55
55
|
|
|
56
|
-
In delegation mode, `once` is implemented by removing the `on
|
|
56
|
+
In delegation mode, `once` is implemented by removing the `on-<eventType>` attribute from the element after the first fire. This is simpler than a `WeakSet` cache and naturally handles element reuse — if the element is re-rendered with the attribute, it fires again.
|
|
57
57
|
|
|
58
58
|
---
|
|
59
59
|
|
|
@@ -77,10 +77,10 @@ Extend `ActionRecord` via declaration merging. This gives you full type safety a
|
|
|
77
77
|
// src/action-record.ts
|
|
78
78
|
declare module '@alwatr/action' {
|
|
79
79
|
interface ActionRecord {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
80
|
+
open_drawer: string;
|
|
81
|
+
search_query: string;
|
|
82
|
+
add_to_cart: {productId: number; qty: number};
|
|
83
|
+
logout: void;
|
|
84
84
|
}
|
|
85
85
|
}
|
|
86
86
|
```
|
|
@@ -94,8 +94,8 @@ import './action-record.js'; // ensure the declaration is loaded
|
|
|
94
94
|
setupActionDelegation();
|
|
95
95
|
|
|
96
96
|
// Payload types are inferred from ActionRecord — no generics needed.
|
|
97
|
-
onAction('
|
|
98
|
-
onAction('
|
|
97
|
+
onAction('open_drawer', (panel) => openDrawer(panel)); // panel: string
|
|
98
|
+
onAction('add_to_cart', (item) => {
|
|
99
99
|
cartService.add(item.productId, item.qty); // fully typed
|
|
100
100
|
});
|
|
101
101
|
```
|
|
@@ -103,19 +103,22 @@ onAction('add-to-cart', (item) => {
|
|
|
103
103
|
### 3. Add attributes to HTML
|
|
104
104
|
|
|
105
105
|
```html
|
|
106
|
-
<!-- Dispatches '
|
|
107
|
-
<button on-
|
|
106
|
+
<!-- Dispatches 'close_drawer' on click — no payload -->
|
|
107
|
+
<button on-click="close_drawer">Close</button>
|
|
108
108
|
|
|
109
|
-
<!-- Dispatches '
|
|
109
|
+
<!-- Dispatches 'open_drawer' with payload 'main' on click -->
|
|
110
|
+
<button on-click="open_drawer:main">Open Drawer</button>
|
|
111
|
+
|
|
112
|
+
<!-- Dispatches 'search_query' with the input's live value -->
|
|
110
113
|
<input
|
|
111
114
|
type="search"
|
|
112
|
-
on-
|
|
115
|
+
on-input="search_query:$value"
|
|
113
116
|
placeholder="Search…"
|
|
114
117
|
/>
|
|
115
118
|
|
|
116
119
|
<!-- Prevents default, validates, then dispatches all field values -->
|
|
117
120
|
<form
|
|
118
|
-
on-
|
|
121
|
+
on-submit="submit_form:$formdata; prevent,validate"
|
|
119
122
|
novalidate
|
|
120
123
|
>
|
|
121
124
|
<input
|
|
@@ -126,7 +129,7 @@ onAction('add-to-cart', (item) => {
|
|
|
126
129
|
</form>
|
|
127
130
|
|
|
128
131
|
<!-- Fires only once — attribute is removed after first click -->
|
|
129
|
-
<button on-
|
|
132
|
+
<button on-click="welcome_dismissed; once">Got it</button>
|
|
130
133
|
```
|
|
131
134
|
|
|
132
135
|
### 4. Programmatic dispatch
|
|
@@ -135,7 +138,7 @@ onAction('add-to-cart', (item) => {
|
|
|
135
138
|
import {dispatchAction} from '@alwatr/action';
|
|
136
139
|
|
|
137
140
|
await uploadFile(file);
|
|
138
|
-
dispatchAction('
|
|
141
|
+
dispatchAction('upload_complete', fileId);
|
|
139
142
|
|
|
140
143
|
dispatchAction('navigate', '/dashboard');
|
|
141
144
|
```
|
|
@@ -145,23 +148,23 @@ dispatchAction('navigate', '/dashboard');
|
|
|
145
148
|
## Attribute Syntax
|
|
146
149
|
|
|
147
150
|
```
|
|
148
|
-
on
|
|
151
|
+
on-<eventType>="actionId[:payload][; modifier1,modifier2,…]"
|
|
149
152
|
```
|
|
150
153
|
|
|
151
|
-
| Segment
|
|
152
|
-
|
|
|
153
|
-
| `eventType`
|
|
154
|
-
| `
|
|
155
|
-
| `
|
|
156
|
-
|
|
|
154
|
+
| Segment | Description | Example |
|
|
155
|
+
| ------------- | ----------------------------------------------------------- | ----------------------------- |
|
|
156
|
+
| `eventType` | Any standard DOM event name — encoded in the attribute name | `on-click`, `on-submit` |
|
|
157
|
+
| `actionId` | Identifier your handler subscribes to | `open_drawer`, `search_query` |
|
|
158
|
+
| `:payload` | Optional literal string, or a `$`-prefixed resolver token | `:main`, `:$value` |
|
|
159
|
+
| `; modifiers` | Optional comma-separated modifier list after a semicolon | `; prevent,validate` |
|
|
157
160
|
|
|
158
161
|
### Built-in modifiers
|
|
159
162
|
|
|
160
|
-
| Modifier
|
|
161
|
-
|
|
|
162
|
-
|
|
|
163
|
-
|
|
|
164
|
-
|
|
|
163
|
+
| Modifier | Behavior |
|
|
164
|
+
| ---------- | ------------------------------------------------------------------------------------- |
|
|
165
|
+
| `prevent` | Calls `event.preventDefault()` |
|
|
166
|
+
| `once` | Removes the `on-<eventType>` attribute after first fire — action dispatches only once |
|
|
167
|
+
| `validate` | Cancels dispatch if the nearest `<form>` fails `checkValidity()` |
|
|
165
168
|
|
|
166
169
|
### Built-in payload resolvers
|
|
167
170
|
|
|
@@ -181,8 +184,8 @@ The global action type registry. Extend via declaration merging to register type
|
|
|
181
184
|
```ts
|
|
182
185
|
declare module '@alwatr/action' {
|
|
183
186
|
interface ActionRecord {
|
|
184
|
-
|
|
185
|
-
|
|
187
|
+
open_drawer: string;
|
|
188
|
+
logout: void;
|
|
186
189
|
}
|
|
187
190
|
}
|
|
188
191
|
```
|
|
@@ -229,7 +232,7 @@ function onAction<K extends keyof ActionRecord>(
|
|
|
229
232
|
```
|
|
230
233
|
|
|
231
234
|
```ts
|
|
232
|
-
const sub = onAction('
|
|
235
|
+
const sub = onAction('open_drawer', (panel) => openDrawer(panel));
|
|
233
236
|
sub.unsubscribe(); // prevent memory leaks
|
|
234
237
|
```
|
|
235
238
|
|
|
@@ -241,7 +244,7 @@ Dispatches a named action. Payload type is enforced by `ActionRecord`.
|
|
|
241
244
|
|
|
242
245
|
```ts
|
|
243
246
|
// With payload
|
|
244
|
-
dispatchAction('
|
|
247
|
+
dispatchAction('open_drawer', 'settings');
|
|
245
248
|
|
|
246
249
|
// Void payload — no second argument
|
|
247
250
|
dispatchAction('logout');
|
|
@@ -258,15 +261,14 @@ Handler signature: `(event: Event, element: HTMLElement) => boolean`
|
|
|
258
261
|
```ts
|
|
259
262
|
import {registerModifier} from '@alwatr/action';
|
|
260
263
|
|
|
261
|
-
|
|
262
|
-
registerModifier('not-disabled', (_event, element) => {
|
|
264
|
+
registerModifier('not_disabled', (_event, element) => {
|
|
263
265
|
return !(element as HTMLButtonElement).disabled;
|
|
264
266
|
});
|
|
265
267
|
```
|
|
266
268
|
|
|
267
269
|
```html
|
|
268
270
|
<button
|
|
269
|
-
on-
|
|
271
|
+
on-click="select_item:$data_id; not_disabled"
|
|
270
272
|
data-id="42"
|
|
271
273
|
>
|
|
272
274
|
Select
|
|
@@ -288,7 +290,7 @@ registerPayloadResolver('$checked', (_event, element) => {
|
|
|
288
290
|
return (element as HTMLInputElement).checked;
|
|
289
291
|
});
|
|
290
292
|
|
|
291
|
-
registerPayloadResolver('$
|
|
293
|
+
registerPayloadResolver('$data_id', (_event, element) => {
|
|
292
294
|
return (element as HTMLElement).dataset.id ?? null;
|
|
293
295
|
});
|
|
294
296
|
```
|
|
@@ -296,10 +298,10 @@ registerPayloadResolver('$data-id', (_event, element) => {
|
|
|
296
298
|
```html
|
|
297
299
|
<input
|
|
298
300
|
type="checkbox"
|
|
299
|
-
on-
|
|
301
|
+
on-change="toggle_feature:$checked"
|
|
300
302
|
/>
|
|
301
303
|
<li
|
|
302
|
-
on-
|
|
304
|
+
on-click="select_item:$data_id"
|
|
303
305
|
data-id="42"
|
|
304
306
|
>
|
|
305
307
|
Item
|
|
@@ -311,33 +313,33 @@ registerPayloadResolver('$data-id', (_event, element) => {
|
|
|
311
313
|
## Unidirectional Data Flow
|
|
312
314
|
|
|
313
315
|
```
|
|
314
|
-
|
|
315
|
-
│
|
|
316
|
-
│ <button on-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
│
|
|
322
|
-
│ document.body capture listener (1 per event type)
|
|
323
|
-
│ → closest('[on-
|
|
324
|
-
│ → internalChannel_.dispatch('
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
│
|
|
330
|
-
│ onAction('
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
│
|
|
336
|
-
│ cartSignal.set(newCartState)
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
316
|
+
┌──────────────────────────────────────────────────────────┐
|
|
317
|
+
│ UI Layer │
|
|
318
|
+
│ <button on-click="add_to_cart:42">Add</button> │
|
|
319
|
+
└─────────────────────────┬────────────────────────────────┘
|
|
320
|
+
│ DOM event bubbles to body
|
|
321
|
+
▼
|
|
322
|
+
┌──────────────────────────────────────────────────────────┐
|
|
323
|
+
│ Action Layer (@alwatr/action) │
|
|
324
|
+
│ document.body capture listener (1 per event type) │
|
|
325
|
+
│ → closest('[on-click]') → parse → modifiers │
|
|
326
|
+
│ → internalChannel_.dispatch('add_to_cart', '42') [O(1)] │
|
|
327
|
+
└─────────────────────────┬────────────────────────────────┘
|
|
328
|
+
│ O(1) routing via ChannelSignal
|
|
329
|
+
▼
|
|
330
|
+
┌──────────────────────────────────────────────────────────┐
|
|
331
|
+
│ Business Logic Layer │
|
|
332
|
+
│ onAction('add_to_cart', (id) => cartService.add(id)) │
|
|
333
|
+
└─────────────────────────┬────────────────────────────────┘
|
|
334
|
+
│ state update
|
|
335
|
+
▼
|
|
336
|
+
┌──────────────────────────────────────────────────────────┐
|
|
337
|
+
│ State Layer (@alwatr/signal) │
|
|
338
|
+
│ cartSignal.set(newCartState) │
|
|
339
|
+
└─────────────────────────┬────────────────────────────────┘
|
|
340
|
+
│ state flows down to UI
|
|
341
|
+
▼
|
|
342
|
+
UI re-renders
|
|
341
343
|
```
|
|
342
344
|
|
|
343
345
|
---
|
|
@@ -353,6 +355,36 @@ concern, not a user-interaction action.
|
|
|
353
355
|
|
|
354
356
|
## Migration from Previous Versions
|
|
355
357
|
|
|
358
|
+
### Attribute syntax changed
|
|
359
|
+
|
|
360
|
+
The event type is now encoded in the **attribute name** instead of the value, and modifiers are listed after a semicolon instead of dot-chained before the arrow.
|
|
361
|
+
|
|
362
|
+
**Before:**
|
|
363
|
+
|
|
364
|
+
```html
|
|
365
|
+
<button on-action="click->open_drawer:main">Open</button>
|
|
366
|
+
<form
|
|
367
|
+
on-action="submit.prevent.validate->submit_form:$formdata"
|
|
368
|
+
novalidate
|
|
369
|
+
>
|
|
370
|
+
…
|
|
371
|
+
</form>
|
|
372
|
+
<button on-action="click.once->welcome_dismissed">Got it</button>
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
**After:**
|
|
376
|
+
|
|
377
|
+
```html
|
|
378
|
+
<button on-click="open_drawer:main">Open</button>
|
|
379
|
+
<form
|
|
380
|
+
on-submit="submit_form:$formdata; prevent,validate"
|
|
381
|
+
novalidate
|
|
382
|
+
>
|
|
383
|
+
…
|
|
384
|
+
</form>
|
|
385
|
+
<button on-click="welcome_dismissed; once">Got it</button>
|
|
386
|
+
```
|
|
387
|
+
|
|
356
388
|
### `ActionContext` removed
|
|
357
389
|
|
|
358
390
|
The `this` context in modifier and resolver handlers changed to explicit parameters:
|
|
@@ -360,7 +392,7 @@ The `this` context in modifier and resolver handlers changed to explicit paramet
|
|
|
360
392
|
**Before:**
|
|
361
393
|
|
|
362
394
|
```ts
|
|
363
|
-
registerModifier('
|
|
395
|
+
registerModifier('not_disabled', function () {
|
|
364
396
|
return !(this.element as HTMLButtonElement).disabled;
|
|
365
397
|
});
|
|
366
398
|
```
|
|
@@ -368,22 +400,51 @@ registerModifier('not-disabled', function () {
|
|
|
368
400
|
**After:**
|
|
369
401
|
|
|
370
402
|
```ts
|
|
371
|
-
registerModifier('
|
|
403
|
+
registerModifier('not_disabled', (_event, element) => {
|
|
372
404
|
return !(element as HTMLButtonElement).disabled;
|
|
373
405
|
});
|
|
374
406
|
```
|
|
375
407
|
|
|
376
|
-
### `once` behavior changed
|
|
377
|
-
|
|
378
|
-
Previously tracked via `WeakSet`. Now removes the `on-action` attribute after first fire.
|
|
379
|
-
Behavior is equivalent for typical use cases.
|
|
380
|
-
|
|
381
408
|
### `page-ready` moved to `@alwatr/page-ready`
|
|
382
409
|
|
|
383
410
|
`dispatchPageId` / `onPageReady` are no longer part of this package.
|
|
384
411
|
|
|
385
412
|
---
|
|
386
413
|
|
|
414
|
+
---
|
|
415
|
+
|
|
416
|
+
## 🌊 Part of Alwatr Flux
|
|
417
|
+
|
|
418
|
+
`@alwatr/action` is the **Action Layer** of the [Alwatr Flux](https://github.com/Alwatr/alwatr/tree/next/pkg/flux) architecture — a complete Unidirectional Data Flow system for building scalable Progressive Web Applications.
|
|
419
|
+
|
|
420
|
+
```
|
|
421
|
+
View (HTML on-<event> attributes)
|
|
422
|
+
↓
|
|
423
|
+
Action Layer (@alwatr/action) — global delegation, O(1) routing
|
|
424
|
+
↓
|
|
425
|
+
Controller (business logic via onAction)
|
|
426
|
+
↓
|
|
427
|
+
State Layer (@alwatr/signal) — fine-grained reactivity
|
|
428
|
+
↓
|
|
429
|
+
View (re-render only affected nodes)
|
|
430
|
+
```
|
|
431
|
+
|
|
432
|
+
`@alwatr/action` is the bridge between the **View** and **Controller** layers. It captures user intent from HTML attributes and routes it to the right handler — without any coupling between the UI and business logic.
|
|
433
|
+
|
|
434
|
+
**The full Flux bundle** (`@alwatr/flux`) includes actions, signals, directives, page-ready, and storage — everything you need to build a complete reactive application from a single import.
|
|
435
|
+
|
|
436
|
+
```typescript
|
|
437
|
+
// Use @alwatr/flux for the complete architecture
|
|
438
|
+
import {setupActionDelegation, onAction, createStateSignal} from '@alwatr/flux';
|
|
439
|
+
|
|
440
|
+
// Or use @alwatr/action standalone for just the action bus
|
|
441
|
+
import {setupActionDelegation, onAction, dispatchAction} from '@alwatr/action';
|
|
442
|
+
```
|
|
443
|
+
|
|
444
|
+
→ [View the complete Flux documentation](https://github.com/Alwatr/alwatr/tree/next/pkg/flux)
|
|
445
|
+
|
|
446
|
+
---
|
|
447
|
+
|
|
387
448
|
## Contributing
|
|
388
449
|
|
|
389
450
|
Contributions are welcome! Please read our [contribution guidelines](https://github.com/Alwatr/.github/blob/next/CONTRIBUTING.md) before submitting a pull request.
|
package/dist/action-record.d.ts
CHANGED
|
@@ -13,8 +13,8 @@
|
|
|
13
13
|
* // In your package: src/action-record.ts
|
|
14
14
|
* declare module '@alwatr/action' {
|
|
15
15
|
* interface ActionRecord {
|
|
16
|
-
* '
|
|
17
|
-
* '
|
|
16
|
+
* 'open_drawer': string;
|
|
17
|
+
* 'add_to_cart': {productId: number; qty: number};
|
|
18
18
|
* 'logout': void;
|
|
19
19
|
* }
|
|
20
20
|
* }
|
|
@@ -42,8 +42,8 @@
|
|
|
42
42
|
* // pkg/my-feature/src/action-record.ts
|
|
43
43
|
* declare module '@alwatr/action' {
|
|
44
44
|
* interface ActionRecord {
|
|
45
|
-
* '
|
|
46
|
-
* '
|
|
45
|
+
* 'open_drawer': string;
|
|
46
|
+
* 'add_to_cart': {productId: number; qty: number};
|
|
47
47
|
* 'logout': void;
|
|
48
48
|
* }
|
|
49
49
|
* }
|
package/dist/delegate.d.ts
CHANGED
|
@@ -10,12 +10,12 @@
|
|
|
10
10
|
* time — O(N) initialization cost, O(N) memory for listener references, and
|
|
11
11
|
* zero support for elements added after bootstrap.
|
|
12
12
|
*
|
|
13
|
-
* This module implements the
|
|
13
|
+
* This module implements the global delegation pattern:
|
|
14
14
|
* - A single listener per event type is attached to `document.body` with
|
|
15
15
|
* `capture: true` (so it fires even for non-bubbling events).
|
|
16
16
|
* - When an event fires, the handler walks up the DOM from `event.target`
|
|
17
|
-
* using `closest()` to find the nearest element with an `on
|
|
18
|
-
* attribute
|
|
17
|
+
* using `closest()` to find the nearest element with an `on-<eventType>`
|
|
18
|
+
* attribute (e.g. `on-click`, `on-submit`).
|
|
19
19
|
* - Modifiers and payload resolvers run in the same pipeline as before.
|
|
20
20
|
* - `dispatchAction` is called with the resolved payload.
|
|
21
21
|
*
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
* - `stop` stops further bubbling but the delegation handler has already
|
|
36
36
|
* captured the event at `body` level — it does not prevent other delegation
|
|
37
37
|
* handlers from running on the same element.
|
|
38
|
-
* - `once` is emulated by
|
|
38
|
+
* - `once` is emulated by removing the attribute after first fire.
|
|
39
39
|
*/
|
|
40
40
|
/**
|
|
41
41
|
* Default DOM event types that cover the vast majority of interactive elements.
|
|
@@ -50,11 +50,11 @@
|
|
|
50
50
|
*/
|
|
51
51
|
export declare const DEFAULT_DELEGATED_EVENTS: readonly string[];
|
|
52
52
|
/**
|
|
53
|
-
* Registers global event delegation for `on
|
|
53
|
+
* Registers global event delegation for `on-<eventType>` attributes.
|
|
54
54
|
*
|
|
55
55
|
* Attaches a single `capture`-phase listener on `document.body` for each
|
|
56
|
-
* event type in `eventTypes`. All
|
|
57
|
-
*
|
|
56
|
+
* event type in `eventTypes`. All processing — modifier execution, payload
|
|
57
|
+
* resolution, and `dispatchAction` — happens inside that one handler.
|
|
58
58
|
*
|
|
59
59
|
* **Call this once at application bootstrap**, before any user interaction.
|
|
60
60
|
* Subsequent calls with the same event types are no-ops (idempotent).
|
|
@@ -80,7 +80,7 @@ export declare const DEFAULT_DELEGATED_EVENTS: readonly string[];
|
|
|
80
80
|
* // One call activates the entire page.
|
|
81
81
|
* setupActionDelegation();
|
|
82
82
|
*
|
|
83
|
-
* onAction('
|
|
83
|
+
* onAction('open_drawer', (panel) => openDrawer(panel));
|
|
84
84
|
* ```
|
|
85
85
|
*
|
|
86
86
|
* @example — with extra event types
|
package/dist/delegate.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"delegate.d.ts","sourceRoot":"","sources":["../src/delegate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;
|
|
1
|
+
{"version":3,"file":"delegate.d.ts","sourceRoot":"","sources":["../src/delegate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AA8LH;;;;;;;;;;GAUG;AACH,eAAO,MAAM,wBAAwB,EAAE,SAAS,MAAM,EAA2C,CAAC;AAElG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AACH,wBAAgB,qBAAqB,CAAC,UAAU,GAAE,SAAS,MAAM,EAA6B,GAAG,IAAI,CASpG;AAED;;;;;;;GAOG;AACH,wBAAgB,wBAAwB,IAAI,IAAI,CAO/C"}
|
package/dist/main.d.ts
CHANGED
|
@@ -1,17 +1,29 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @alwatr/action — Declarative DOM action-dispatch for Unidirectional Data Flow.
|
|
3
3
|
*
|
|
4
|
-
* ## Activating `on
|
|
4
|
+
* ## Activating `on-<eventType>` attributes
|
|
5
5
|
*
|
|
6
6
|
* Call `setupActionDelegation()` once at bootstrap. A single capture-phase
|
|
7
|
-
* listener on `document.body` handles every `on-
|
|
8
|
-
* elements added dynamically after bootstrap — with O(1) initialization cost.
|
|
7
|
+
* listener on `document.body` handles every `on-click`, `on-submit`, etc. element —
|
|
8
|
+
* including elements added dynamically after bootstrap — with O(1) initialization cost.
|
|
9
9
|
*
|
|
10
10
|
* ```ts
|
|
11
11
|
* import {setupActionDelegation, onAction} from '@alwatr/action';
|
|
12
12
|
*
|
|
13
13
|
* setupActionDelegation();
|
|
14
|
-
* onAction('
|
|
14
|
+
* onAction('open_drawer', (panel) => openDrawer(panel));
|
|
15
|
+
* ```
|
|
16
|
+
*
|
|
17
|
+
* ## Attribute syntax
|
|
18
|
+
*
|
|
19
|
+
* ```
|
|
20
|
+
* on-<eventType>="actionId[:payload][; modifier1,modifier2,…]"
|
|
21
|
+
* ```
|
|
22
|
+
*
|
|
23
|
+
* ```html
|
|
24
|
+
* <button on-click="open_drawer:main">Open</button>
|
|
25
|
+
* <input on-input="search_query:$value" />
|
|
26
|
+
* <form on-submit="submit_form:$formdata; prevent,validate" novalidate>…</form>
|
|
15
27
|
* ```
|
|
16
28
|
*
|
|
17
29
|
* ## Programmatic dispatch
|
|
@@ -30,8 +42,8 @@
|
|
|
30
42
|
* // src/action-record.ts
|
|
31
43
|
* declare module '@alwatr/action' {
|
|
32
44
|
* interface ActionRecord {
|
|
33
|
-
* '
|
|
34
|
-
* '
|
|
45
|
+
* 'open_drawer': string;
|
|
46
|
+
* 'add_to_cart': {productId: number; qty: number};
|
|
35
47
|
* 'logout': void;
|
|
36
48
|
* }
|
|
37
49
|
* }
|
package/dist/main.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8DG;AACH,YAAY,EAAC,YAAY,EAAC,MAAM,oBAAoB,CAAC;AACrD,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC"}
|
package/dist/main.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
/* 📦 @alwatr/action v9.
|
|
2
|
-
import{createLogger as U}from"@alwatr/logger";import{createChannelSignal as
|
|
1
|
+
/* 📦 @alwatr/action v9.16.0 */
|
|
2
|
+
import{createLogger as U}from"@alwatr/logger";import{createChannelSignal as W}from"@alwatr/signal";var F=U("alwatr-action"),Y=W({name:"alwatr-action"});var N=new Map,J=new Map;N.set("prevent",(D)=>{return D.preventDefault(),!0});N.set("validate",(D,q)=>{let z=q instanceof HTMLFormElement?q:q.closest("form");if(!z)return!1;return z.checkValidity()});J.set("$value",(D,q)=>{return"value"in q?q.value:null});J.set("$formdata",(D,q)=>{let z=q instanceof HTMLFormElement?q:q.closest("form");return z?Object.fromEntries(new FormData(z)):null});J.set("$checked",(D,q)=>{return"checked"in q?q.checked:null});function E(D,q){return F.logMethodArgs?.("onAction",{actionId:D}),Y.on(D,q)}function R(...D){let[q,z]=D;F.logMethodArgs?.("dispatchAction",{actionId:q,actionPayload:z}),Y.dispatch(q,z)}function V(D,q){if(F.logMethodArgs?.("registerModifier",{name:D}),N.has(D))F.accident("registerModifier","modifier_already_registered",{name:D});N.set(D,q)}function f(D,q){if(F.logMethodArgs?.("registerPayloadResolver",{name:D}),J.has(D))F.accident("registerPayloadResolver","payload_resolver_already_registered",{name:D});J.set(D,q)}var B=/^([a-z0-9_-]+)(?::([^;]+))?(?:;\s*([a-z0-9_,-]+))?$/,Z=new Map;function L(D){F.logMethodArgs?.("parseDescriptor__",{attributeValue:D});let q=Z.get(D);if(q!==void 0)return q;let z=D.match(B);if(!z)return F.accident("parseDescriptor__","invalid_syntax",{attributeValue:D}),Z.set(D,null),null;let X=z[1],G=z[2],O=z[3],K={modifiers:O?new Set(O.split(",").filter(Boolean)):new Set,actionId:X,payload:G};return Z.set(D,K),K}function M(D){let q=D.type;F.logMethodArgs?.("handleDelegatedEvent__",{eventType:q});let z=D.target;if(!z)return;let X=`on-${q}`,G=z.closest?.(`[${X}]`);if(!G)return;let O=G.getAttribute?.(X)?.trim();if(!O){F.accident("handleDelegatedEvent__","empty_attribute",{eventType:q,actionElement:G});return}if(!(G instanceof HTMLElement)){F.accident("handleDelegatedEvent__","target_not_html_element",{eventType:q,actionElement:G});return}let w=L(O);if(!w)return;if(F.logMethodArgs?.("handleDelegatedEvent__.action",{eventType:q,descriptor:w}),w.modifiers.has("once"))G.removeAttribute(X);for(let Q of w.modifiers){if(Q==="once")continue;let H=N.get(Q);if(!H){F.accident("handleDelegatedEvent__","unknown_modifier",{eventType:q,modifier:Q,attributeValue:O,descriptor:w});return}if(H(D,G)===!1)return}let K=w.payload;if(K){let Q=J.get(K);if(Q)K=Q(D,G)}Y.dispatch(w.actionId,K)}var A=new Set,S=["click","submit","input","change"];function _(D=S){F.logMethodArgs?.("setupActionDelegation",{eventTypes:D});for(let q of D){if(A.has(q))continue;A.add(q),document.body.addEventListener(q,M,{capture:!0})}}function u(){F.logMethod?.("teardownActionDelegation");for(let D of A)document.body.removeEventListener(D,M,{capture:!0});A.clear(),Z.clear()}export{u as teardownActionDelegation,_ as setupActionDelegation,f as registerPayloadResolver,V as registerModifier,E as onAction,R as dispatchAction,S as DEFAULT_DELEGATED_EVENTS};
|
|
3
3
|
|
|
4
|
-
//# debugId=
|
|
4
|
+
//# debugId=C5F4214484C292C364756E2164756E21
|
|
5
5
|
//# sourceMappingURL=main.js.map
|