@excom/listenable-element 0.1.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.
Files changed (30) hide show
  1. package/.rush/temp/chunked-rush-logs/listenable-element.apply-exports.chunks.jsonl +1 -0
  2. package/.rush/temp/chunked-rush-logs/listenable-element.build_docs.chunks.jsonl +1 -0
  3. package/.rush/temp/chunked-rush-logs/listenable-element.build_package-metas.chunks.jsonl +1 -0
  4. package/.rush/temp/operation/apply-exports/all.log +1 -0
  5. package/.rush/temp/operation/apply-exports/log-chunks.jsonl +1 -0
  6. package/.rush/temp/operation/apply-exports/state.json +3 -0
  7. package/.rush/temp/operation/build_docs/all.log +1 -0
  8. package/.rush/temp/operation/build_docs/log-chunks.jsonl +1 -0
  9. package/.rush/temp/operation/build_docs/state.json +3 -0
  10. package/.rush/temp/operation/build_package-metas/all.log +1 -0
  11. package/.rush/temp/operation/build_package-metas/log-chunks.jsonl +1 -0
  12. package/.rush/temp/operation/build_package-metas/state.json +3 -0
  13. package/.rush/temp/shrinkwrap-deps.json +4 -0
  14. package/config/rig.json +6 -0
  15. package/index.ts +282 -0
  16. package/package.json +45 -0
  17. package/rush-logs/listenable-element.apply-exports.cache.log +1 -0
  18. package/rush-logs/listenable-element.apply-exports.log +1 -0
  19. package/rush-logs/listenable-element.build_docs.cache.log +1 -0
  20. package/rush-logs/listenable-element.build_docs.log +1 -0
  21. package/rush-logs/listenable-element.build_package-metas.cache.log +1 -0
  22. package/rush-logs/listenable-element.build_package-metas.log +1 -0
  23. package/src/index.css +12 -0
  24. package/support/custom-elements.json +332 -0
  25. package/support/dist-docs/listenable-element.md +122 -0
  26. package/support/docs/README.md +82 -0
  27. package/support/package-meta.json +148 -0
  28. package/support/tests/listenable-element.matrix.test.ts +300 -0
  29. package/support/tests/listenable-element.test.ts +455 -0
  30. package/tsconfig.json +5 -0
@@ -0,0 +1,332 @@
1
+ {
2
+ "schemaVersion": "1.0.0",
3
+ "modules": [
4
+ {
5
+ "kind": "javascript-module",
6
+ "path": "index.ts",
7
+ "declarations": [
8
+ {
9
+ "kind": "mixin",
10
+ "name": "ListenableElement",
11
+ "summary": "Declarative event / lifecycle listening for Neutron elements.",
12
+ "description": "Composition base that wires event and lifecycle listening. Subclasses implement `actionHandler`. Used by `<spa-a>`, `<event-handler>`, and similar interactive elements.",
13
+ "attributes": [
14
+ {
15
+ "name": "host-ref",
16
+ "type": {
17
+ "text": "string"
18
+ },
19
+ "description": "Listen on another element / `window` / `document` — e.g. Escape to dismiss a dialog from a global `keydown`. Defaults to `:scope`. Used with `listen-for`. Not compatible with `listen-for-lifecycle`. The selector MUST resolve when `host-ref` is set — it will not wait for a match to appear.",
20
+ "fieldName": "hostRef",
21
+ "values": [
22
+ "<CSS Selector>",
23
+ "window",
24
+ "document",
25
+ "html",
26
+ "body",
27
+ "head"
28
+ ]
29
+ },
30
+ {
31
+ "name": "listen-for",
32
+ "type": {
33
+ "text": "tokenlist"
34
+ },
35
+ "description": "Space-separated event names to listen for. Defaults to `click` when unset (and no lifecycle list is set).",
36
+ "fieldName": "listenFor",
37
+ "values": [
38
+ "<EventName>…"
39
+ ]
40
+ },
41
+ {
42
+ "name": "listen-for-lifecycle",
43
+ "type": {
44
+ "text": "tokenlist"
45
+ },
46
+ "description": "Space-separated element lifecycles to handle.",
47
+ "fieldName": "listenForLifecycle",
48
+ "values": [
49
+ "connected",
50
+ "disconnected",
51
+ "adopted"
52
+ ]
53
+ },
54
+ {
55
+ "name": "listen-once",
56
+ "type": {
57
+ "text": "boolean"
58
+ },
59
+ "description": "Handle each distinct event name / lifecycle at most once.",
60
+ "fieldName": "listenOnce"
61
+ },
62
+ {
63
+ "name": "selector-filter",
64
+ "type": {
65
+ "text": "string"
66
+ },
67
+ "description": "Only handle events whose `event.target` matches this CSS selector. Does not support `:scope` in the selector.",
68
+ "fieldName": "selectorFilter",
69
+ "values": [
70
+ "<CSS Selector>"
71
+ ]
72
+ },
73
+ {
74
+ "name": "keycode-filter",
75
+ "type": {
76
+ "text": "tokenlist"
77
+ },
78
+ "description": "Space-separated key filters (OR). Join modifiers with `+` (AND, any order): `shift+k tab` → Shift+K or Tab. Modifiers: `shift`, `alt`, `ctrl`/`control`, `meta`. Case-insensitive.",
79
+ "fieldName": "keycodeFilter",
80
+ "values": [
81
+ "<key",
82
+ "mod+key>…"
83
+ ]
84
+ },
85
+ {
86
+ "name": "pathname-filter",
87
+ "type": {
88
+ "text": "tokenlist"
89
+ },
90
+ "description": "Only handle when `location.pathname` is one of these values — route-aware behaviors without a separate router element.",
91
+ "fieldName": "pathnameFilter",
92
+ "values": [
93
+ "<pathname>…"
94
+ ]
95
+ },
96
+ {
97
+ "name": "prevent-default",
98
+ "type": {
99
+ "text": "boolean"
100
+ },
101
+ "description": "Call `preventDefault()` on matched events (ignored for lifecycles).",
102
+ "fieldName": "preventDefault"
103
+ },
104
+ {
105
+ "name": "stop-propagation",
106
+ "type": {
107
+ "text": "boolean"
108
+ },
109
+ "description": "Call `stopPropagation()` on matched events (ignored for lifecycles).",
110
+ "fieldName": "stopPropagation"
111
+ },
112
+ {
113
+ "name": "stop-immediate-propagation",
114
+ "type": {
115
+ "text": "boolean"
116
+ },
117
+ "description": "Call `stopImmediatePropagation()` on matched events (ignored for lifecycles).",
118
+ "fieldName": "stopImmediatePropagation"
119
+ },
120
+ {
121
+ "name": "vibrate-ms",
122
+ "type": {
123
+ "text": "number"
124
+ },
125
+ "description": "Vibrate on handle (`navigator.vibrate`). Empty / `0` uses a 20ms pulse.",
126
+ "fieldName": "vibrateMs",
127
+ "default": "20 (when attribute is present with no value)"
128
+ },
129
+ {
130
+ "name": "delay-ms",
131
+ "type": {
132
+ "text": "number"
133
+ },
134
+ "description": "Delay handling by this many milliseconds.",
135
+ "fieldName": "delayMs"
136
+ },
137
+ {
138
+ "name": "is-debounced",
139
+ "type": {
140
+ "text": "boolean"
141
+ },
142
+ "description": "With `delay-ms`, coalesce bursts into one trailing call (debounce).",
143
+ "fieldName": "isDebounced"
144
+ }
145
+ ],
146
+ "members": [
147
+ {
148
+ "kind": "field",
149
+ "name": "hostRef",
150
+ "type": {
151
+ "text": "string"
152
+ },
153
+ "privacy": "public",
154
+ "readonly": false,
155
+ "description": "Listen on another element / `window` / `document` — e.g. Escape to dismiss a dialog from a global `keydown`. Defaults to `:scope`. Used with `listen-for`. Not compatible with `listen-for-lifecycle`. The selector MUST resolve when `host-ref` is set — it will not wait for a match to appear.",
156
+ "_neutron": {
157
+ "surface": "option"
158
+ }
159
+ },
160
+ {
161
+ "kind": "field",
162
+ "name": "listenFor",
163
+ "type": {
164
+ "text": "tokenlist"
165
+ },
166
+ "privacy": "public",
167
+ "readonly": false,
168
+ "description": "Space-separated event names to listen for. Defaults to `click` when unset (and no lifecycle list is set).",
169
+ "_neutron": {
170
+ "surface": "option"
171
+ }
172
+ },
173
+ {
174
+ "kind": "field",
175
+ "name": "listenForLifecycle",
176
+ "type": {
177
+ "text": "tokenlist"
178
+ },
179
+ "privacy": "public",
180
+ "readonly": false,
181
+ "description": "Space-separated element lifecycles to handle.",
182
+ "_neutron": {
183
+ "surface": "option"
184
+ }
185
+ },
186
+ {
187
+ "kind": "field",
188
+ "name": "listenOnce",
189
+ "type": {
190
+ "text": "boolean"
191
+ },
192
+ "privacy": "public",
193
+ "readonly": false,
194
+ "description": "Handle each distinct event name / lifecycle at most once.",
195
+ "_neutron": {
196
+ "surface": "option"
197
+ }
198
+ },
199
+ {
200
+ "kind": "field",
201
+ "name": "selectorFilter",
202
+ "type": {
203
+ "text": "string"
204
+ },
205
+ "privacy": "public",
206
+ "readonly": false,
207
+ "description": "Only handle events whose `event.target` matches this CSS selector. Does not support `:scope` in the selector.",
208
+ "_neutron": {
209
+ "surface": "option"
210
+ }
211
+ },
212
+ {
213
+ "kind": "field",
214
+ "name": "keycodeFilter",
215
+ "type": {
216
+ "text": "tokenlist"
217
+ },
218
+ "privacy": "public",
219
+ "readonly": false,
220
+ "description": "Space-separated key filters (OR). Join modifiers with `+` (AND, any order): `shift+k tab` → Shift+K or Tab. Modifiers: `shift`, `alt`, `ctrl`/`control`, `meta`. Case-insensitive.",
221
+ "_neutron": {
222
+ "surface": "option"
223
+ }
224
+ },
225
+ {
226
+ "kind": "field",
227
+ "name": "pathnameFilter",
228
+ "type": {
229
+ "text": "tokenlist"
230
+ },
231
+ "privacy": "public",
232
+ "readonly": false,
233
+ "description": "Only handle when `location.pathname` is one of these values — route-aware behaviors without a separate router element.",
234
+ "_neutron": {
235
+ "surface": "option"
236
+ }
237
+ },
238
+ {
239
+ "kind": "field",
240
+ "name": "preventDefault",
241
+ "type": {
242
+ "text": "boolean"
243
+ },
244
+ "privacy": "public",
245
+ "readonly": false,
246
+ "description": "Call `preventDefault()` on matched events (ignored for lifecycles).",
247
+ "_neutron": {
248
+ "surface": "option"
249
+ }
250
+ },
251
+ {
252
+ "kind": "field",
253
+ "name": "stopPropagation",
254
+ "type": {
255
+ "text": "boolean"
256
+ },
257
+ "privacy": "public",
258
+ "readonly": false,
259
+ "description": "Call `stopPropagation()` on matched events (ignored for lifecycles).",
260
+ "_neutron": {
261
+ "surface": "option"
262
+ }
263
+ },
264
+ {
265
+ "kind": "field",
266
+ "name": "stopImmediatePropagation",
267
+ "type": {
268
+ "text": "boolean"
269
+ },
270
+ "privacy": "public",
271
+ "readonly": false,
272
+ "description": "Call `stopImmediatePropagation()` on matched events (ignored for lifecycles).",
273
+ "_neutron": {
274
+ "surface": "option"
275
+ }
276
+ },
277
+ {
278
+ "kind": "field",
279
+ "name": "vibrateMs",
280
+ "type": {
281
+ "text": "number"
282
+ },
283
+ "privacy": "public",
284
+ "readonly": false,
285
+ "description": "Vibrate on handle (`navigator.vibrate`). Empty / `0` uses a 20ms pulse.",
286
+ "default": "20 (when attribute is present with no value)",
287
+ "_neutron": {
288
+ "surface": "option"
289
+ }
290
+ },
291
+ {
292
+ "kind": "field",
293
+ "name": "delayMs",
294
+ "type": {
295
+ "text": "number"
296
+ },
297
+ "privacy": "public",
298
+ "readonly": false,
299
+ "description": "Delay handling by this many milliseconds.",
300
+ "_neutron": {
301
+ "surface": "option"
302
+ }
303
+ },
304
+ {
305
+ "kind": "field",
306
+ "name": "isDebounced",
307
+ "type": {
308
+ "text": "boolean"
309
+ },
310
+ "privacy": "public",
311
+ "readonly": false,
312
+ "description": "With `delay-ms`, coalesce bursts into one trailing call (debounce).",
313
+ "_neutron": {
314
+ "surface": "option"
315
+ }
316
+ }
317
+ ]
318
+ }
319
+ ],
320
+ "exports": [
321
+ {
322
+ "kind": "js",
323
+ "name": "ListenableElement",
324
+ "declaration": {
325
+ "name": "ListenableElement",
326
+ "module": "index.ts"
327
+ }
328
+ }
329
+ ]
330
+ }
331
+ ]
332
+ }
@@ -0,0 +1,122 @@
1
+ # listenable-element
2
+
3
+ Declarative event and lifecycle listening for Neutron elements —
4
+ filter, debounce, vibrate, and hand off to your `actionHandler`.
5
+
6
+ ## Features
7
+
8
+ - **Event / lifecycle hooks** Listen for DOM events or `connected` /
9
+ `disconnected` / `adopted`
10
+ - **Host retarget** `host-ref="window"` / `document` / any selector —
11
+ Escape to dismiss, shortcuts outside the bubble path
12
+ - **Target filters** Selector, keycode (`shift+k` chords), and pathname gates
13
+ - **Debounce / delay** Coalesce noisy input
14
+ - **Event hygiene** `prevent-default` / `stop-propagation` /
15
+ `stop-immediate-propagation`
16
+ - **Haptic pulse** Optional `vibrate-ms` on handle
17
+
18
+ ## Installation
19
+
20
+
21
+ `@excom/listenable-element` v0.1.0
22
+
23
+ ```bash
24
+ pnpm add @excom/listenable-element
25
+ ```
26
+
27
+ ```bash
28
+ npm install @excom/listenable-element
29
+ ```
30
+
31
+ ```bash
32
+ yarn add @excom/listenable-element
33
+ ```
34
+
35
+ ### Import
36
+
37
+ ```ts
38
+ import { /* … */ } from "@excom/listenable-element";
39
+ ```
40
+
41
+
42
+
43
+ ## Usage
44
+
45
+ Compose `ListenableElement` and implement `actionHandler`. Concrete
46
+ consumers include `<spa-a>` and `<event-handler>`.
47
+
48
+ ```ts
49
+ import { Neutron } from "@excom/neutron";
50
+ import { ListenableElement } from "@excom/listenable-element";
51
+
52
+ export const TapLog = Neutron.compose([
53
+ ListenableElement,
54
+ Neutron({ tag: "tap-log" }),
55
+ ])
56
+ .defineMethods({
57
+ actionHandler: (_el, e) => {
58
+ console.log("handled", e.type);
59
+ },
60
+ });
61
+
62
+ TapLog.define();
63
+ ```
64
+
65
+ ```html
66
+ <tap-log listen-for="click keydown" keycode-filter="enter">
67
+ Tap or Enter
68
+ </tap-log>
69
+ ```
70
+
71
+ `host-ref` moves listening off `:scope` — e.g. `host-ref="window"` for
72
+ global keydown. See `<event-handler>` for Escape-to-dismiss examples.
73
+
74
+ ### API Reference
75
+
76
+
77
+ #### Attributes
78
+
79
+ | Name | Surface | Type | Default | Values | Description |
80
+ | --- | --- | --- | --- | --- | --- |
81
+ | `host-ref` | option | `string` | | `<CSS Selector>` \| `"window"` \| `"document"` \| `"html"` \| `"body"` \| `"head"` | Listen on another element / `window` / `document` — e.g. Escape to dismiss a dialog from a global `keydown`. Defaults to `:scope`. Used with `listen-for`. Not compatible with `listen-for-lifecycle`. The selector MUST resolve when `host-ref` is set — it will not wait for a match to appear. |
82
+ | `listen-for` | option | `tokenlist` | | `<EventName>…` | Space-separated event names to listen for. Defaults to `click` when unset (and no lifecycle list is set). |
83
+ | `listen-for-lifecycle` | option | `tokenlist` | | `"connected"` \| `"disconnected"` \| `"adopted"` | Space-separated element lifecycles to handle. |
84
+ | `listen-once` | option | `boolean` | | | Handle each distinct event name / lifecycle at most once. |
85
+ | `selector-filter` | option | `string` | | `<CSS Selector>` | Only handle events whose `event.target` matches this CSS selector. Does not support `:scope` in the selector. |
86
+ | `keycode-filter` | option | `tokenlist` | | `<key` \| `mod+key>…` | Space-separated key filters (OR). Join modifiers with `+` (AND, any order): `shift+k tab` → Shift+K or Tab. Modifiers: `shift`, `alt`, `ctrl`/`control`, `meta`. Case-insensitive. |
87
+ | `pathname-filter` | option | `tokenlist` | | `<pathname>…` | Only handle when `location.pathname` is one of these values — route-aware behaviors without a separate router element. |
88
+ | `prevent-default` | option | `boolean` | | | Call `preventDefault()` on matched events (ignored for lifecycles). |
89
+ | `stop-propagation` | option | `boolean` | | | Call `stopPropagation()` on matched events (ignored for lifecycles). |
90
+ | `stop-immediate-propagation` | option | `boolean` | | | Call `stopImmediatePropagation()` on matched events (ignored for lifecycles). |
91
+ | `vibrate-ms` | option | `number` | `"20 (when attribute is present with no value)"` | | Vibrate on handle (`navigator.vibrate`). Empty / `0` uses a 20ms pulse. |
92
+ | `delay-ms` | option | `number` | | | Delay handling by this many milliseconds. |
93
+ | `is-debounced` | option | `boolean` | | | With `delay-ms`, coalesce bursts into one trailing call (debounce). |
94
+
95
+
96
+
97
+ ### Examples
98
+
99
+ #### Default click → navigate
100
+
101
+ `<spa-a>` inherits this base and defaults to click when `listen-for`
102
+ is unset:
103
+
104
+ ```html
105
+ <spa-a route-href="/pricing">Pricing</spa-a>
106
+ ```
107
+
108
+ #### Filter & debounce
109
+
110
+ ```html
111
+ <event-handler
112
+ listen-for="input"
113
+ delay-ms="200"
114
+ is-debounced
115
+ fire-event="search-query"
116
+ >
117
+ <input name="q" />
118
+ </event-handler>
119
+ ```
120
+
121
+ See `<event-handler>` and `<spa-a>` package docs for more examples
122
+ built on this mixin.
@@ -0,0 +1,82 @@
1
+ # listenable-element
2
+
3
+ Declarative event and lifecycle listening for Neutron elements —
4
+ filter, debounce, vibrate, and hand off to your `actionHandler`.
5
+
6
+ ## Features
7
+
8
+ - **Event / lifecycle hooks** Listen for DOM events or `connected` /
9
+ `disconnected` / `adopted`
10
+ - **Host retarget** `host-ref="window"` / `document` / any selector —
11
+ Escape to dismiss, shortcuts outside the bubble path
12
+ - **Target filters** Selector, keycode (`shift+k` chords), and pathname gates
13
+ - **Debounce / delay** Coalesce noisy input
14
+ - **Event hygiene** `prevent-default` / `stop-propagation` /
15
+ `stop-immediate-propagation`
16
+ - **Haptic pulse** Optional `vibrate-ms` on handle
17
+
18
+ ## Installation
19
+
20
+ <include-content is-active template-ref="/views/install-section/install-section.html"></include-content>
21
+
22
+ ## Usage
23
+
24
+ Compose `ListenableElement` and implement `actionHandler`. Concrete
25
+ consumers include `<spa-a>` and `<event-handler>`.
26
+
27
+ ```ts
28
+ import { Neutron } from "@excom/neutron";
29
+ import { ListenableElement } from "@excom/listenable-element";
30
+
31
+ export const TapLog = Neutron.compose([
32
+ ListenableElement,
33
+ Neutron({ tag: "tap-log" }),
34
+ ])
35
+ .defineMethods({
36
+ actionHandler: (_el, e) => {
37
+ console.log("handled", e.type);
38
+ },
39
+ });
40
+
41
+ TapLog.define();
42
+ ```
43
+
44
+ ```html
45
+ <tap-log listen-for="click keydown" keycode-filter="enter">
46
+ Tap or Enter
47
+ </tap-log>
48
+ ```
49
+
50
+ `host-ref` moves listening off `:scope` — e.g. `host-ref="window"` for
51
+ global keydown. See `<event-handler>` for Escape-to-dismiss examples.
52
+
53
+ ### API Reference
54
+
55
+ <include-content is-active template-ref="/views/api-reference/api-reference.html"></include-content>
56
+
57
+ ### Examples
58
+
59
+ #### Default click → navigate
60
+
61
+ `<spa-a>` inherits this base and defaults to click when `listen-for`
62
+ is unset:
63
+
64
+ ```html
65
+ <spa-a route-href="/pricing">Pricing</spa-a>
66
+ ```
67
+
68
+ #### Filter & debounce
69
+
70
+ ```html
71
+ <event-handler
72
+ listen-for="input"
73
+ delay-ms="200"
74
+ is-debounced
75
+ fire-event="search-query"
76
+ >
77
+ <input name="q" />
78
+ </event-handler>
79
+ ```
80
+
81
+ See `<event-handler>` and `<spa-a>` package docs for more examples
82
+ built on this mixin.