@fusion.dev/helix-core 0.0.0-dev

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 (115) hide show
  1. package/LICENSE +21 -0
  2. package/custom-elements.json +8005 -0
  3. package/dist/controllers/aria-forward-controller/aria-forward-controller.d.ts +67 -0
  4. package/dist/controllers/aria-forward-controller/aria-forward-controller.js +146 -0
  5. package/dist/controllers/chart-pointer-controller/chart-pointer-controller.d.ts +79 -0
  6. package/dist/controllers/chart-pointer-controller/chart-pointer-controller.js +140 -0
  7. package/dist/controllers/command-controller/command-controller.d.ts +83 -0
  8. package/dist/controllers/command-controller/command-controller.js +93 -0
  9. package/dist/controllers/copy-controller/copy-controller.d.ts +58 -0
  10. package/dist/controllers/copy-controller/copy-controller.js +128 -0
  11. package/dist/controllers/disabled-cascade-controller/disabled-cascade-controller.d.ts +76 -0
  12. package/dist/controllers/disabled-cascade-controller/disabled-cascade-controller.js +67 -0
  13. package/dist/controllers/dismiss-controller/dismiss-controller.d.ts +58 -0
  14. package/dist/controllers/dismiss-controller/dismiss-controller.js +165 -0
  15. package/dist/controllers/drilldown-controller/drilldown-controller.d.ts +127 -0
  16. package/dist/controllers/drilldown-controller/drilldown-controller.js +171 -0
  17. package/dist/controllers/floating-controller/floating-controller.d.ts +123 -0
  18. package/dist/controllers/floating-controller/floating-controller.js +227 -0
  19. package/dist/controllers/focus-trap-controller/focus-trap-controller.d.ts +26 -0
  20. package/dist/controllers/focus-trap-controller/focus-trap-controller.js +93 -0
  21. package/dist/controllers/indicator-controller/indicator-controller.d.ts +97 -0
  22. package/dist/controllers/indicator-controller/indicator-controller.js +164 -0
  23. package/dist/controllers/keyboard-navigation-controller/keyboard-navigation-controller.d.ts +113 -0
  24. package/dist/controllers/keyboard-navigation-controller/keyboard-navigation-controller.js +117 -0
  25. package/dist/controllers/popover-controller/popover-controller.d.ts +132 -0
  26. package/dist/controllers/popover-controller/popover-controller.js +150 -0
  27. package/dist/controllers/source-controller/source-controller.d.ts +55 -0
  28. package/dist/controllers/source-controller/source-controller.js +94 -0
  29. package/dist/controllers/token-color-controller/token-color-controller.d.ts +63 -0
  30. package/dist/controllers/token-color-controller/token-color-controller.js +105 -0
  31. package/dist/controllers/virtualize-controller/virtualize-controller.d.ts +127 -0
  32. package/dist/controllers/virtualize-controller/virtualize-controller.js +216 -0
  33. package/dist/events/element-events.d.ts +4 -0
  34. package/dist/events/element-events.js +34 -0
  35. package/dist/events/events.d.ts +743 -0
  36. package/dist/events/events.js +799 -0
  37. package/dist/helix-element/helix-element.d.ts +201 -0
  38. package/dist/helix-element/helix-element.js +695 -0
  39. package/dist/helix-form-element/helix-form-element.d.ts +152 -0
  40. package/dist/helix-form-element/helix-form-element.js +194 -0
  41. package/dist/index.d.ts +54 -0
  42. package/dist/index.js +54 -0
  43. package/dist/mixins/mixin-chart-source/mixin-chart-source.d.ts +137 -0
  44. package/dist/mixins/mixin-chart-source/mixin-chart-source.js +140 -0
  45. package/dist/mixins/mixin-collection/index.d.ts +1 -0
  46. package/dist/mixins/mixin-collection/index.js +1 -0
  47. package/dist/mixins/mixin-collection/mixin-collection.d.ts +58 -0
  48. package/dist/mixins/mixin-collection/mixin-collection.js +154 -0
  49. package/dist/mixins/mixin-controller-active/mixin-controller-active.d.ts +53 -0
  50. package/dist/mixins/mixin-controller-active/mixin-controller-active.js +155 -0
  51. package/dist/mixins/mixin-controller-checked/mixin-controller-checked.d.ts +54 -0
  52. package/dist/mixins/mixin-controller-checked/mixin-controller-checked.js +157 -0
  53. package/dist/mixins/mixin-controller-open/mixin-controller-open.d.ts +49 -0
  54. package/dist/mixins/mixin-controller-open/mixin-controller-open.js +176 -0
  55. package/dist/mixins/mixin-controller-value/mixin-controller-value.d.ts +73 -0
  56. package/dist/mixins/mixin-controller-value/mixin-controller-value.js +173 -0
  57. package/dist/mixins/mixin-dismissible/mixin-dismissible.d.ts +41 -0
  58. package/dist/mixins/mixin-dismissible/mixin-dismissible.js +79 -0
  59. package/dist/mixins/mixin-field/mixin-field.d.ts +145 -0
  60. package/dist/mixins/mixin-field/mixin-field.js +423 -0
  61. package/dist/utils/anchor-element/anchor-element.d.ts +23 -0
  62. package/dist/utils/anchor-element/anchor-element.js +39 -0
  63. package/dist/utils/capture-pointer/capture-pointer.d.ts +22 -0
  64. package/dist/utils/capture-pointer/capture-pointer.js +30 -0
  65. package/dist/utils/closest-instance/closest-instance.d.ts +25 -0
  66. package/dist/utils/closest-instance/closest-instance.js +34 -0
  67. package/dist/utils/create-sheet/create-sheet.d.ts +16 -0
  68. package/dist/utils/create-sheet/create-sheet.js +16 -0
  69. package/dist/utils/custom-element/custom-element.d.ts +26 -0
  70. package/dist/utils/custom-element/custom-element.js +114 -0
  71. package/dist/utils/draw-icon/draw-icon.d.ts +38 -0
  72. package/dist/utils/draw-icon/draw-icon.js +39 -0
  73. package/dist/utils/draw-tooltip/draw-tooltip.d.ts +56 -0
  74. package/dist/utils/draw-tooltip/draw-tooltip.js +89 -0
  75. package/dist/utils/elapsed-time/elapsed-time.d.ts +10 -0
  76. package/dist/utils/elapsed-time/elapsed-time.js +117 -0
  77. package/dist/utils/focusable-elements/focusable-elements.d.ts +40 -0
  78. package/dist/utils/focusable-elements/focusable-elements.js +88 -0
  79. package/dist/utils/format-numbers/format-numbers.d.ts +1 -0
  80. package/dist/utils/format-numbers/format-numbers.js +29 -0
  81. package/dist/utils/host-aria/host-aria.d.ts +38 -0
  82. package/dist/utils/host-aria/host-aria.js +41 -0
  83. package/dist/utils/inherit-disabled/inherit-disabled.d.ts +33 -0
  84. package/dist/utils/inherit-disabled/inherit-disabled.js +55 -0
  85. package/dist/utils/line-path/line-path.d.ts +23 -0
  86. package/dist/utils/line-path/line-path.js +107 -0
  87. package/dist/utils/on-unless-false/on-unless-false.d.ts +28 -0
  88. package/dist/utils/on-unless-false/on-unless-false.js +32 -0
  89. package/dist/utils/read-series/read-series.d.ts +78 -0
  90. package/dist/utils/read-series/read-series.js +187 -0
  91. package/dist/utils/relative-time/relative-time.d.ts +24 -0
  92. package/dist/utils/relative-time/relative-time.js +84 -0
  93. package/dist/utils/request-change/request-change.d.ts +48 -0
  94. package/dist/utils/request-change/request-change.js +7 -0
  95. package/dist/utils/resolve-active-item/resolve-active-item.d.ts +28 -0
  96. package/dist/utils/resolve-active-item/resolve-active-item.js +21 -0
  97. package/dist/utils/restore-value/restore-value.d.ts +32 -0
  98. package/dist/utils/restore-value/restore-value.js +84 -0
  99. package/dist/utils/scale/scale.d.ts +23 -0
  100. package/dist/utils/scale/scale.js +32 -0
  101. package/dist/utils/scale/ticks.d.ts +27 -0
  102. package/dist/utils/scale/ticks.js +52 -0
  103. package/dist/utils/scroll-item-into-view/scroll-item-into-view.d.ts +10 -0
  104. package/dist/utils/scroll-item-into-view/scroll-item-into-view.js +98 -0
  105. package/dist/utils/slot-filled/slot-filled.d.ts +26 -0
  106. package/dist/utils/slot-filled/slot-filled.js +26 -0
  107. package/dist/utils/tell-disclosure/tell-disclosure.d.ts +51 -0
  108. package/dist/utils/tell-disclosure/tell-disclosure.js +76 -0
  109. package/dist/utils/ticker/ticker.d.ts +8 -0
  110. package/dist/utils/ticker/ticker.js +55 -0
  111. package/dist/utils/tone-marks/tone-marks.d.ts +52 -0
  112. package/dist/utils/tone-marks/tone-marks.js +36 -0
  113. package/dist/utils/value-as/value-as.d.ts +64 -0
  114. package/dist/utils/value-as/value-as.js +105 -0
  115. package/package.json +58 -0
@@ -0,0 +1,799 @@
1
+ /**
2
+ * Event classes, one per event a Helix component dispatches.
3
+ *
4
+ * A named class rather than `dispatchEvent(new CustomEvent(name, …))` for three
5
+ * reasons, all of them about the type reaching a consumer:
6
+ *
7
+ * 1. `@event {HelixBeforeOpenEvent} helix-before-open` puts the class name in the manifest,
8
+ * which is what lets the React generator emit a typed callback instead of
9
+ * `(detail: unknown)`.
10
+ * 2. The augmentation of `HTMLElementEventMap` below types `addEventListener`
11
+ * for consumers writing plain HTML, who never see the React wrappers.
12
+ * 3. The event name lives on the class as `eventName`, so a listener and a
13
+ * dispatch cannot disagree about the string.
14
+ */
15
+ /**
16
+ * Fired when an element is about to open.
17
+ *
18
+ * Cancelable: `preventDefault()` refuses the change, and the element stays
19
+ * closed. Paired with `helix-before-close` rather than being one event carrying a
20
+ * boolean, so a listener that only cares about one direction says so by which
21
+ * event it listens for.
22
+ *
23
+ * Neither bubbles nor composed. A dialog inside a drawer inside a popover is
24
+ * three overlays, and an inner one opening must not read as the outer one
25
+ * opening.
26
+ */
27
+ export class HelixBeforeOpenEvent extends CustomEvent {
28
+ static { this.eventName = 'helix-before-open'; }
29
+ constructor() {
30
+ super(HelixBeforeOpenEvent.eventName, {
31
+ bubbles: false,
32
+ cancelable: true,
33
+ composed: false,
34
+ detail: { open: true },
35
+ });
36
+ }
37
+ }
38
+ /** Fired when an element is about to close. See `HelixBeforeOpenEvent`. */
39
+ export class HelixBeforeCloseEvent extends CustomEvent {
40
+ static { this.eventName = 'helix-before-close'; }
41
+ constructor() {
42
+ super(HelixBeforeCloseEvent.eventName, {
43
+ bubbles: false,
44
+ cancelable: true,
45
+ composed: false,
46
+ detail: { open: false },
47
+ });
48
+ }
49
+ }
50
+ /**
51
+ * Fired when the reader asks for the next thing in a set.
52
+ *
53
+ * Paired with `helix-previous` rather than being one event carrying a
54
+ * direction, which is the reason `helix-before-open` gives for its own pair: a
55
+ * listener that only cares about one direction says so by which event it
56
+ * listens for. The detail carries the direction anyway, so a consumer who does
57
+ * want one handler can have one.
58
+ *
59
+ * Bubbles and is composed, because the thing that answers this is usually not
60
+ * the thing that asked. A video has no notion of a set: it plays one resource,
61
+ * and what comes next is a playlist's business or the consumer's, so the event
62
+ * has to reach a wrapper.
63
+ *
64
+ * Not cancelable. There is no default action to refuse: the component that
65
+ * fires this does nothing on its own, and an event whose `preventDefault` means
66
+ * nothing is an event that lies about what it offers.
67
+ */
68
+ export class HelixNextEvent extends CustomEvent {
69
+ static { this.eventName = 'helix-next'; }
70
+ constructor() {
71
+ super(HelixNextEvent.eventName, {
72
+ bubbles: true,
73
+ cancelable: false,
74
+ composed: true,
75
+ detail: { to: 'next' },
76
+ });
77
+ }
78
+ }
79
+ /** Fired when the reader asks for the previous one. See `HelixNextEvent`. */
80
+ export class HelixPreviousEvent extends CustomEvent {
81
+ static { this.eventName = 'helix-previous'; }
82
+ constructor() {
83
+ super(HelixPreviousEvent.eventName, {
84
+ bubbles: true,
85
+ cancelable: false,
86
+ composed: true,
87
+ detail: { to: 'previous' },
88
+ });
89
+ }
90
+ }
91
+ /**
92
+ * A stop asking the tour to move.
93
+ *
94
+ * Composed and bubbling, because a stop is slotted into the root and the root
95
+ * has to hear it across that boundary.
96
+ *
97
+ * **A class rather than the raw `CustomEvent` this was.** It dispatched, it
98
+ * bubbled, and it was invisible: `check:events` matches `new HelixSomethingEvent(`,
99
+ * so a hand-rolled `CustomEvent` was not held to being declared, and nothing
100
+ * carried it into the manifest. Consumers could hear it and could not find it,
101
+ * which is the trap #75 is about, in its purest form.
102
+ */
103
+ export class HelixTourAskEvent extends CustomEvent {
104
+ static { this.eventName = 'helix-tour-ask'; }
105
+ constructor(detail) {
106
+ super(HelixTourAskEvent.eventName, {
107
+ bubbles: true,
108
+ /* Not cancelable, and this is the one of the four where that was a real
109
+ promise rather than an idle flag. The name says *ask*, so a consumer had
110
+ every reason to think refusing it held the tour, and the root's
111
+ `handleAsk` never read `defaultPrevented`: the tour moved anyway.
112
+ Honoring it there would not have worked either, and the reason is
113
+ ordering. The root registers its listener in `connectedCallback` and a
114
+ consumer registers later, so on a bubbling event both are on the same
115
+ target and the root's runs first: it would read `defaultPrevented`
116
+ before the consumer had a chance to set it. `helix-shortcut` gets this
117
+ right by reading the flag on the element that dispatched, where every
118
+ listener has already run.
119
+
120
+ **The veto exists and is elsewhere**, which is why this is a
121
+ downgrade rather than a gap. Moving between stops goes through
122
+ `requestChange` and announces `helix-before-activate`; leaving early
123
+ dispatches `helix-before-close` and reads the flag before writing
124
+ `open`. Both are honored. This event is the stop telling the root a
125
+ button was pressed, and the decision is taken one step later. */
126
+ cancelable: false,
127
+ composed: true,
128
+ detail,
129
+ });
130
+ }
131
+ }
132
+ /**
133
+ * The eyedropper opening, and closing again.
134
+ *
135
+ * Two events rather than one carrying a flag, because what a consumer does with
136
+ * them is not symmetrical: the surface holding the picker has to get out of the
137
+ * way for the sample and come back afterwards, and those are two moments with
138
+ * different work in each.
139
+ *
140
+ * **A class rather than the bare `Event` these were.** They dispatched, they
141
+ * bubbled, `ColorInput` bound both, and `check:events` matched
142
+ * `new CustomEvent(` and so held neither to being declared: two pieces of
143
+ * public API that a consumer could hear and could not find. The check reads
144
+ * `new Event(` now as well.
145
+ *
146
+ * No detail, because there is nothing to say beyond that it happened.
147
+ */
148
+ export class HelixSampleStartEvent extends Event {
149
+ static { this.eventName = 'helix-sample-start'; }
150
+ constructor() {
151
+ super(HelixSampleStartEvent.eventName, { bubbles: true, cancelable: false, composed: true });
152
+ }
153
+ }
154
+ /** The eyedropper closing, whether or not a color was taken. */
155
+ export class HelixSampleEndEvent extends Event {
156
+ static { this.eventName = 'helix-sample-end'; }
157
+ constructor() {
158
+ super(HelixSampleEndEvent.eventName, { bubbles: true, cancelable: false, composed: true });
159
+ }
160
+ }
161
+ /**
162
+ * Fired when an element asks to be taken away.
163
+ *
164
+ * A request rather than a notification: the element does not remove itself,
165
+ * because the consumer owns the markup it is in and removing the node under
166
+ * them would put their state and the document out of step. Cancelable, so
167
+ * `preventDefault()` is a refusal.
168
+ *
169
+ * One verb for both shapes. A tag leaving a list and an announcement closing
170
+ * itself are the same act from the element's side.
171
+ */
172
+ export class HelixDismissEvent extends CustomEvent {
173
+ static { this.eventName = 'helix-dismiss'; }
174
+ constructor(detail) {
175
+ super(HelixDismissEvent.eventName, {
176
+ bubbles: true,
177
+ cancelable: true,
178
+ composed: true,
179
+ detail,
180
+ });
181
+ }
182
+ }
183
+ /**
184
+ * A set is about to make a different child current, and can be refused.
185
+ *
186
+ * **The generic `helix-change` covered this and should not have.** Eleven
187
+ * families that are not form controls announced their state under the word the
188
+ * platform uses for a committed value, which is 294's whole complaint read from
189
+ * the other end: `Tabs` moving to another tab, `Tree` marking another row and
190
+ * `Steps` advancing are not a value changing, they are a set choosing. One word
191
+ * for four questions is what `appearance` was before `check:axes`.
192
+ *
193
+ * `active` names which child is current, so `activate` is the verb, and unlike
194
+ * `change` the platform has no claim on it.
195
+ *
196
+ * **It has a notification half now, and this said it did not.** *Reversed 28 Aug
197
+ * 2026.* The paragraph here read "there is no notification half: a native
198
+ * `change` would be a lie about what moved, and no other native event means
199
+ * this. The before event is the whole channel." The first half of that is still
200
+ * exactly right, which is why the pair is `helix-activate` and not `change`. The
201
+ * conclusion did not follow: that no *native* word fits is an argument against
202
+ * borrowing one, not an argument against having a notification at all.
203
+ *
204
+ * What the missing half cost is the thing 294 fixed for values and left here.
205
+ * A consumer who only wants to know which child is current now had one channel,
206
+ * and it fires *before* `apply`: `currentTarget.active` still holds the previous
207
+ * value inside the handler, and `detail.value` is a proposal another listener may
208
+ * refuse. Measured across the four families taking this mixin, nothing at all was
209
+ * announced afterwards. `Tabs` and `Tree` looked covered because `helix-select`
210
+ * bubbles up from a child, which fires when an item is *chosen* and not when
211
+ * `active` is assigned; `Steps` and `Video Playlist` had nothing.
212
+ *
213
+ * So the shape is the one `MixinControllerOpen` already has, a cancelable
214
+ * request and a plain notification after the state has moved. See #325.
215
+ */
216
+ export class HelixBeforeActivateEvent extends CustomEvent {
217
+ static { this.eventName = 'helix-before-activate'; }
218
+ constructor(detail) {
219
+ super(HelixBeforeActivateEvent.eventName, {
220
+ bubbles: true,
221
+ cancelable: true,
222
+ composed: true,
223
+ detail,
224
+ });
225
+ }
226
+ }
227
+ /**
228
+ * A set has made a different child current.
229
+ *
230
+ * The notification half of `helix-before-activate`, dispatched after the state
231
+ * has moved, so `currentTarget.active` is the new value and the detail is a fact
232
+ * rather than a proposal.
233
+ *
234
+ * **Not cancelable**, for the reason every notification here is not: the
235
+ * decision was taken by the event before it, and a second refusable event for
236
+ * one transition is two answers to one question.
237
+ *
238
+ * **Prefixed, and it has to be.** `activate` unprefixed is not in any
239
+ * `*EventMap` in the DOM library, checked rather than assumed, but the prefix is
240
+ * what stops an event dispatched inside a shadow root and retargeted to the host
241
+ * being mistaken for something the host's own contents sent. That is the same
242
+ * argument `helix-select` and `helix-resize` are excused under.
243
+ */
244
+ export class HelixActivateEvent extends CustomEvent {
245
+ static { this.eventName = 'helix-activate'; }
246
+ constructor(detail) {
247
+ super(HelixActivateEvent.eventName, {
248
+ bubbles: true,
249
+ cancelable: false,
250
+ composed: true,
251
+ detail,
252
+ });
253
+ }
254
+ }
255
+ /** A message is about to be pinned or unpinned, and can be refused. */
256
+ export class HelixBeforePinEvent extends CustomEvent {
257
+ static { this.eventName = 'helix-before-pin'; }
258
+ constructor(detail) {
259
+ super(HelixBeforePinEvent.eventName, {
260
+ bubbles: true,
261
+ cancelable: true,
262
+ composed: true,
263
+ detail,
264
+ });
265
+ }
266
+ }
267
+ /** A divider is about to move, and can be refused. */
268
+ export class HelixBeforeResizeEvent extends CustomEvent {
269
+ static { this.eventName = 'helix-before-resize'; }
270
+ constructor(detail) {
271
+ super(HelixBeforeResizeEvent.eventName, {
272
+ bubbles: true,
273
+ cancelable: true,
274
+ composed: true,
275
+ detail,
276
+ });
277
+ }
278
+ }
279
+ /**
280
+ * The divider between a sandbox's panes is about to move, and can be refused.
281
+ *
282
+ * Its own verb rather than a `helix-before-resize` carrying which divider moved:
283
+ * a sandbox has two, and they were told apart by a field on a detail every other
284
+ * request in this file also carries. See 306.
285
+ */
286
+ export class HelixBeforeSplitEvent extends CustomEvent {
287
+ static { this.eventName = 'helix-before-split'; }
288
+ constructor(detail) {
289
+ super(HelixBeforeSplitEvent.eventName, {
290
+ bubbles: true,
291
+ cancelable: true,
292
+ composed: true,
293
+ detail,
294
+ });
295
+ }
296
+ }
297
+ /**
298
+ * A sandbox's panel is about to change height, and can be refused.
299
+ *
300
+ * The sibling of `helix-before-split`, and named for the divider rather than for
301
+ * the axis because a reader binding one of the two is choosing which divider they
302
+ * care about. `value` is pixels here and a percentage there, which is what each
303
+ * property already publishes.
304
+ */
305
+ export class HelixBeforePanelResizeEvent extends CustomEvent {
306
+ static { this.eventName = 'helix-before-panel-resize'; }
307
+ constructor(detail) {
308
+ super(HelixBeforePanelResizeEvent.eventName, {
309
+ bubbles: true,
310
+ cancelable: true,
311
+ composed: true,
312
+ detail,
313
+ });
314
+ }
315
+ }
316
+ /** A pane is about to collapse or reopen, and can be refused. */
317
+ export class HelixBeforeCollapseEvent extends CustomEvent {
318
+ static { this.eventName = 'helix-before-collapse'; }
319
+ constructor(detail) {
320
+ super(HelixBeforeCollapseEvent.eventName, {
321
+ bubbles: true,
322
+ cancelable: true,
323
+ composed: true,
324
+ detail,
325
+ });
326
+ }
327
+ }
328
+ /** Source is about to be edited, and can be refused. */
329
+ export class HelixBeforeEditEvent extends CustomEvent {
330
+ static { this.eventName = 'helix-before-edit'; }
331
+ constructor(detail) {
332
+ super(HelixBeforeEditEvent.eventName, {
333
+ bubbles: true,
334
+ cancelable: true,
335
+ composed: true,
336
+ detail,
337
+ });
338
+ }
339
+ }
340
+ /**
341
+ * A value is about to change, and the change can be refused.
342
+ *
343
+ * **The request half of every value control.** It announces the value being
344
+ * *proposed*, before anything moves, so a listener calling `preventDefault()`
345
+ * refuses it and the control stays where it was. That is how a consumer takes
346
+ * one decision without declaring a mode, which is what 294 settled: the element
347
+ * always owns and applies its state, and refusing is the way to take it over.
348
+ *
349
+ * **The notification is native and separate.** Once this has gone out and the
350
+ * value has moved, the control announces it again under the platform's own
351
+ * name, `input` while a reader is editing and `change` when they are done, so
352
+ * `onChange` in React and `onchange=` in HTML both reach it and
353
+ * `currentTarget.value` is correct by the time a handler reads it. This event
354
+ * carries the detail because at the moment it fires the value is not on the
355
+ * element yet.
356
+ *
357
+ * **It replaced `helix-change` and `helix-input` on the value controls**, which
358
+ * were one mechanism under two names chosen by `announcesInput`. The name said
359
+ * *phase* and the flag meant *granularity*, so a consumer reading "change" as
360
+ * "the reader is done" wired a request to `onhelix-change` and got one per
361
+ * character. Granularity is now what the platform already says it is, `input`
362
+ * against `change`, and `announcesInput` picks which native one fires rather
363
+ * than which name this carries. See 294.
364
+ */
365
+ export class HelixBeforeChangeEvent extends CustomEvent {
366
+ static { this.eventName = 'helix-before-change'; }
367
+ constructor(detail) {
368
+ super(HelixBeforeChangeEvent.eventName, {
369
+ bubbles: true,
370
+ cancelable: true,
371
+ composed: true,
372
+ detail,
373
+ });
374
+ }
375
+ }
376
+ /**
377
+ * A checked state is about to change, and the change can be refused.
378
+ *
379
+ * **Its own event rather than a `helix-before-change` carrying a boolean**, and
380
+ * the reason is recorded rather than aesthetic. Reported through the value
381
+ * event the state arrived as `String(checked)`, so a consumer received the
382
+ * string `"false"`, which is truthy, and this library's own checkbox group
383
+ * decoded it with `detail.value === 'true'`. A test asserting the value would
384
+ * have passed on that bug. A control whose state is a boolean wants an event
385
+ * with a typed boolean in it.
386
+ *
387
+ * The notification is native, as it is for a value: the control relays `change`
388
+ * from the inner input and announces `input` beside it, so `onChange` reads
389
+ * `currentTarget.checked` and gets a boolean from the platform.
390
+ *
391
+ * It also ends a collision. Checkbox Group listened for its boxes' change and
392
+ * dispatched its own under the same name from the same host, so it had to call
393
+ * `stopImmediatePropagation` to keep a consumer from receiving one box's
394
+ * boolean alongside the whole set. Two names, two facts.
395
+ */
396
+ export class HelixBeforeToggleEvent extends CustomEvent {
397
+ static { this.eventName = 'helix-before-toggle'; }
398
+ constructor(detail) {
399
+ super(HelixBeforeToggleEvent.eventName, {
400
+ bubbles: true,
401
+ cancelable: true,
402
+ composed: true,
403
+ detail,
404
+ });
405
+ }
406
+ }
407
+ /**
408
+ * The reader asked for something the list does not hold.
409
+ *
410
+ * Cancelable, and announced before anything is applied, because the options are
411
+ * the consumer's own light DOM and no component here writes into it. What the
412
+ * control can do is say what was asked for and take the value; putting an
413
+ * option in the list, or a row in a database, is the consumer's, and a consumer
414
+ * who validates first refuses this and adds nothing.
415
+ *
416
+ * The detail carries the text as typed, not a slug of it. Trimming or lowering
417
+ * it here would be this element deciding what a name means, and a consumer who
418
+ * wants an identifier can make one from what they were given.
419
+ */
420
+ export class HelixCreateEvent extends CustomEvent {
421
+ static { this.eventName = 'helix-create'; }
422
+ constructor(detail) {
423
+ super(HelixCreateEvent.eventName, {
424
+ bubbles: true,
425
+ cancelable: true,
426
+ composed: true,
427
+ detail,
428
+ });
429
+ }
430
+ }
431
+ /**
432
+ * Something was copied to the clipboard.
433
+ *
434
+ * Not cancelable, and deliberately: it is announced after the write, because a
435
+ * clipboard write is a permission the platform may refuse and there is nothing
436
+ * useful to report until it has either happened or not.
437
+ */
438
+ export class HelixCopyEvent extends CustomEvent {
439
+ static { this.eventName = 'helix-copy'; }
440
+ constructor(detail) {
441
+ super(HelixCopyEvent.eventName, {
442
+ bubbles: true,
443
+ composed: true,
444
+ detail,
445
+ });
446
+ }
447
+ }
448
+ /**
449
+ * Fired when something being previewed writes to the console.
450
+ *
451
+ * Carries text rather than the logged values. The preview is a separate
452
+ * document, so what arrives has crossed `postMessage` and been through
453
+ * structured clone: a DOM node or a function does not survive that, and half a
454
+ * value is worse than a string of it.
455
+ */
456
+ export class HelixConsoleEvent extends CustomEvent {
457
+ static { this.eventName = 'helix-console'; }
458
+ constructor(detail) {
459
+ super(HelixConsoleEvent.eventName, {
460
+ bubbles: true,
461
+ /* Not cancelable, for the reason `helix-resize` is not: a log line the
462
+ frame already wrote is a fact being relayed, and this element's own
463
+ comment at the dispatch says "relayed rather than acted on". Nothing
464
+ read the result. */
465
+ cancelable: false,
466
+ composed: true,
467
+ detail,
468
+ });
469
+ }
470
+ }
471
+ /**
472
+ * Fired when something reports how much room its content actually needs.
473
+ *
474
+ * **A measurement crossing a boundary the measurer is on the far side of.** A
475
+ * sandboxed preview is its own document: nothing outside it can read the height
476
+ * of what it drew, because the frame is opaque and a box sized from the outside
477
+ * only ever reports the box. So the document measures itself and says so, and
478
+ * this is what it says it with.
479
+ *
480
+ * A number rather than a box, because the width is the container's to decide and
481
+ * only the block size follows the content. A consumer that wants to act on it
482
+ * clamps it: what arrives is what was drawn, which for an empty example is
483
+ * nearly nothing and for a long one is more than a page.
484
+ */
485
+ export class HelixResizeEvent extends CustomEvent {
486
+ static { this.eventName = 'helix-resize'; }
487
+ constructor(detail) {
488
+ super(HelixResizeEvent.eventName, {
489
+ bubbles: true,
490
+ /* Not cancelable, and it was until 27 Aug 2026. A measurement is not a
491
+ proposal: the content is that tall whether or not anybody wants it to
492
+ be, and neither dispatcher, `Heatmap` nor `Sandbox.Preview`, ever read
493
+ the result. So `preventDefault()` on it was accepted and ignored, which
494
+ is worse than refusing the call: a consumer had every reason to believe
495
+ they had stopped something. */
496
+ cancelable: false,
497
+ composed: true,
498
+ detail,
499
+ });
500
+ }
501
+ }
502
+ /**
503
+ * Fired when a header crosses the threshold between resting at the top of the
504
+ * page and floating over it.
505
+ *
506
+ * **State a consumer used to read off an attribute, said out loud instead.**
507
+ * The header reflected this as `data-scrolled` on its own host, which is an
508
+ * attribute nobody rendered: a server sends the element without it and the
509
+ * element adds it on upgrade, so a framework hydrating the page finds markup
510
+ * that no longer matches what it sent. The threshold now lives in a custom
511
+ * state, which is deliberately not in the DOM and therefore not something a
512
+ * hydration pass can disagree with.
513
+ *
514
+ * That closes the observability the attribute accidentally provided. A
515
+ * `MutationObserver` can watch an attribute and there is no equivalent for
516
+ * `ElementInternals.states`, so a component holding a boolean nothing can read
517
+ * is a component with a private opinion its consumers demonstrably need: the
518
+ * bar's own backdrop is drawn by the page around it, and the page has to know
519
+ * when to light it.
520
+ *
521
+ * Not cancelable. It reports where the page is rather than asking permission to
522
+ * be there, and refusing it would leave the state and the announcement
523
+ * disagreeing.
524
+ *
525
+ * The hysteresis stays inside the header, which is the point of announcing at
526
+ * all: a consumer listening to `scroll` itself would be a second opinion on
527
+ * when the page counts as scrolled, and the two would part company at the eight
528
+ * pixels of travel this waits for.
529
+ */
530
+ export class HelixScrolledEvent extends CustomEvent {
531
+ static { this.eventName = 'helix-scrolled'; }
532
+ constructor(detail) {
533
+ super(HelixScrolledEvent.eventName, {
534
+ bubbles: true,
535
+ cancelable: false,
536
+ composed: true,
537
+ detail,
538
+ });
539
+ }
540
+ }
541
+ /**
542
+ * Fired when the cursor moves in a text editor.
543
+ *
544
+ * Not cancelable, unlike everything else here. The others announce something
545
+ * about to happen and a listener can refuse it; this one reports where the
546
+ * caret already is, and there is nothing left to refuse.
547
+ *
548
+ * It fires on every keystroke and every arrow press, which is why the status
549
+ * bar reads it from the DOM rather than through a framework's state: routing a
550
+ * caret position through React would re-render the workbench on every cursor
551
+ * move.
552
+ */
553
+ export class HelixCaretEvent extends CustomEvent {
554
+ static { this.eventName = 'helix-caret'; }
555
+ constructor(detail) {
556
+ super(HelixCaretEvent.eventName, {
557
+ bubbles: true,
558
+ cancelable: false,
559
+ composed: true,
560
+ detail,
561
+ });
562
+ }
563
+ }
564
+ /**
565
+ * Fired when a component moves the view it is showing, rather than its value.
566
+ *
567
+ * A calendar paging to another month is not choosing a date, and the two have
568
+ * to be separable: a consumer driving the view needs to hear that it moved
569
+ * without being told the answer changed. `helix-change` belongs to the value,
570
+ * and giving it both jobs would make either one unusable on its own.
571
+ */
572
+ export class HelixNavigateEvent extends CustomEvent {
573
+ static { this.eventName = 'helix-navigate'; }
574
+ constructor(detail) {
575
+ super(HelixNavigateEvent.eventName, {
576
+ bubbles: true,
577
+ cancelable: true,
578
+ composed: true,
579
+ detail,
580
+ });
581
+ }
582
+ }
583
+ /**
584
+ * Fired when a control that fills up has filled.
585
+ *
586
+ * The moment a code entry exists for: a six-character field is worth submitting
587
+ * the instant the sixth character lands, and a consumer watching `helix-change`
588
+ * has to know the length and compare it on every keystroke to find that moment
589
+ * themselves.
590
+ *
591
+ * Separate from `helix-change` rather than a flag on it, because they are
592
+ * answered by different code: one keeps a value in step and the other decides
593
+ * whether to send a form. A listener that only cares about the second says so by
594
+ * which event it listens for.
595
+ *
596
+ * `complete` is free as a verb. React's synthetic system claims `change`,
597
+ * `input`, `submit`, `reset`, `copy`, `paste`, `scroll`, `drag`, `focus` and
598
+ * `blur`, measured, and this is none of them.
599
+ *
600
+ * Not cancelable. It reports that something already happened, which is the same
601
+ * reason `helix-caret` is not: there is nothing left to refuse once the last
602
+ * character is in the box.
603
+ */
604
+ export class HelixCompleteEvent extends CustomEvent {
605
+ static { this.eventName = 'helix-complete'; }
606
+ constructor(detail) {
607
+ super(HelixCompleteEvent.eventName, {
608
+ bubbles: true,
609
+ cancelable: false,
610
+ composed: true,
611
+ detail,
612
+ });
613
+ }
614
+ }
615
+ /**
616
+ * Fired when the reader offers something they wrote.
617
+ *
618
+ * **Cancelable, and the composer clears only when nobody refused.** Sending is a
619
+ * round trip: a composer that empties itself and then hears the request failed
620
+ * has thrown away the only copy of what the reader typed, and a reader who has
621
+ * just written three paragraphs is exactly who cannot afford that. Refusing
622
+ * leaves the text where it is.
623
+ *
624
+ * Not `helix-create`, which is the combobox's event for a value a list does not
625
+ * hold, and not `helix-change`, because nothing about a value changed: the
626
+ * reader finished something and handed it over.
627
+ */
628
+ export class HelixSendEvent extends CustomEvent {
629
+ static { this.eventName = 'helix-send'; }
630
+ constructor(detail) {
631
+ super(HelixSendEvent.eventName, {
632
+ bubbles: true,
633
+ cancelable: true,
634
+ composed: true,
635
+ detail,
636
+ });
637
+ }
638
+ }
639
+ /**
640
+ * Fired when something a user comes back to is marked read, or unread.
641
+ *
642
+ * **Read state is reported rather than owned.** Marking a notification read is
643
+ * usually a round trip, and a component that flips the state itself and then
644
+ * hears the request failed has already told the reader something untrue. So this
645
+ * is cancelable, and refusing it leaves the item alone; a component left
646
+ * uncontrolled applies it once nobody has refused, which is the same contract
647
+ * every other axis in the library uses.
648
+ *
649
+ * Bubbles and is composed, so a feed hears its own items: one handler on the
650
+ * list serves every notification in it, which is what a consumer syncing to a
651
+ * server actually wants.
652
+ */
653
+ export class HelixReadEvent extends CustomEvent {
654
+ static { this.eventName = 'helix-read'; }
655
+ constructor(detail) {
656
+ super(HelixReadEvent.eventName, {
657
+ bubbles: true,
658
+ cancelable: true,
659
+ composed: true,
660
+ detail,
661
+ });
662
+ }
663
+ }
664
+ /**
665
+ * Fired when the reader asks for a different order.
666
+ *
667
+ * Cancelable, and refusing it leaves the order alone, which is what makes a
668
+ * server-side sort expressible: the consumer refuses the local reorder, fetches,
669
+ * and hands back a new set. In controlled mode nothing is written whatever the
670
+ * answer, and this is the element reporting that a heading was clicked.
671
+ *
672
+ * Not `helix-change`: nothing about the data changed, only the order it is read
673
+ * in, and a consumer listening for changes to a value should not hear a sort.
674
+ * That is the same distinction `helix-filter` is drawn on.
675
+ */
676
+ export class HelixSortEvent extends CustomEvent {
677
+ static { this.eventName = 'helix-sort'; }
678
+ constructor(detail) {
679
+ super(HelixSortEvent.eventName, {
680
+ bubbles: true,
681
+ cancelable: true,
682
+ composed: true,
683
+ detail,
684
+ });
685
+ }
686
+ }
687
+ /**
688
+ * Fired when a component narrows what it is showing.
689
+ *
690
+ * Filtering hides rather than removes, so nothing about the value changes and
691
+ * `helix-change` would be a lie. What a consumer wants to know is that the list
692
+ * in front of the reader is no longer the whole list.
693
+ */
694
+ export class HelixFilterEvent extends CustomEvent {
695
+ static { this.eventName = 'helix-filter'; }
696
+ constructor(detail) {
697
+ super(HelixFilterEvent.eventName, {
698
+ bubbles: true,
699
+ cancelable: true,
700
+ composed: true,
701
+ detail,
702
+ });
703
+ }
704
+ }
705
+ /**
706
+ * Fired when a shortcut's chord is pressed.
707
+ *
708
+ * Cancelable, and the key event is only consumed where this is not refused: a
709
+ * consumer that decides the moment is wrong, a dialog is over the control or
710
+ * the reader is mid-composition, calls `preventDefault()` and the keystroke
711
+ * goes on to whatever would have had it.
712
+ */
713
+ export class HelixShortcutEvent extends CustomEvent {
714
+ static { this.eventName = 'helix-shortcut'; }
715
+ constructor(detail) {
716
+ super(HelixShortcutEvent.eventName, {
717
+ bubbles: true,
718
+ cancelable: true,
719
+ composed: true,
720
+ detail,
721
+ });
722
+ }
723
+ }
724
+ /**
725
+ * Fired when the reader selects an item in a set.
726
+ *
727
+ * Bubbles and is composed, because this is how a set coordinates: a tree, a
728
+ * tab list and a toggle group all learn which of their children was chosen by
729
+ * hearing it arrive.
730
+ */
731
+ export class HelixSelectEvent extends CustomEvent {
732
+ static { this.eventName = 'helix-select'; }
733
+ constructor(detail) {
734
+ super(HelixSelectEvent.eventName, {
735
+ bubbles: true,
736
+ cancelable: true,
737
+ composed: true,
738
+ detail,
739
+ });
740
+ }
741
+ }
742
+ /**
743
+ * Every event this file publishes, and the one place a new one is added.
744
+ *
745
+ * The map below is derived from this rather than written beside it. Kept by
746
+ * hand, the two drifted: `helix-filter` and `helix-navigate` were dispatched by
747
+ * the calendar, the listbox and the pagination and were missing from the map
748
+ * for weeks, so the one thing the augmentation exists to do was not done for
749
+ * them and a consumer writing `addEventListener('helix-navigate', …)` got
750
+ * `Event` and had to reach into `detail` through a cast. Nothing failed, which
751
+ * is why two of the nine went missing.
752
+ *
753
+ * Derived, a class that is in here is typed and a class that is not is absent
754
+ * from both halves at once, so the two cannot disagree. `check:events` fails
755
+ * the build on an exported event class left out of this object, which is the
756
+ * only step left that can be forgotten.
757
+ */
758
+ const registry = {
759
+ HelixActivateEvent,
760
+ HelixBeforeActivateEvent,
761
+ HelixBeforeChangeEvent,
762
+ HelixBeforeCloseEvent,
763
+ HelixBeforeCollapseEvent,
764
+ HelixBeforeEditEvent,
765
+ HelixBeforeOpenEvent,
766
+ HelixBeforePanelResizeEvent,
767
+ HelixBeforePinEvent,
768
+ HelixBeforeResizeEvent,
769
+ HelixBeforeSplitEvent,
770
+ HelixBeforeToggleEvent,
771
+ HelixCaretEvent,
772
+ HelixCompleteEvent,
773
+ HelixConsoleEvent,
774
+ HelixCopyEvent,
775
+ HelixCreateEvent,
776
+ HelixDismissEvent,
777
+ HelixFilterEvent,
778
+ HelixNavigateEvent,
779
+ HelixNextEvent,
780
+ HelixPreviousEvent,
781
+ HelixReadEvent,
782
+ HelixResizeEvent,
783
+ HelixSampleEndEvent,
784
+ HelixSampleStartEvent,
785
+ HelixScrolledEvent,
786
+ HelixSelectEvent,
787
+ HelixSendEvent,
788
+ HelixShortcutEvent,
789
+ HelixSortEvent,
790
+ HelixTourAskEvent,
791
+ };
792
+ /**
793
+ * Every name this library dispatches.
794
+ *
795
+ * Exported so a runtime check can ask whether a name is one of ours without
796
+ * keeping a second list of them. `HelixElement` uses it to tell a consumer that
797
+ * the handler prop they wrote is attached to nothing.
798
+ */
799
+ export const helixEventNames = new Set(Object.values(registry).map((event) => event.eventName));