@fusion.dev/helix-docs 0.104.1 → 0.105.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/content/accessibility.mdx +92 -71
- package/content/angular.mdx +26 -24
- package/content/browser-support.mdx +61 -58
- package/content/building-on-the-headless-tier.mdx +100 -74
- package/content/charting.mdx +98 -79
- package/content/coding-agents.mdx +87 -69
- package/content/color-modes.mdx +40 -37
- package/content/color.mdx +67 -54
- package/content/composition.mdx +85 -83
- package/content/elevation-and-material.mdx +32 -29
- package/content/events.mdx +77 -65
- package/content/forms.mdx +69 -58
- package/content/iconography.mdx +75 -71
- package/content/installation.mdx +77 -68
- package/content/intent-reference.mdx +59 -54
- package/content/introduction.mdx +25 -23
- package/content/localization.mdx +122 -82
- package/content/maps.mdx +222 -0
- package/content/metrics.mdx +294 -297
- package/content/motion.mdx +44 -36
- package/content/other-frameworks.mdx +33 -31
- package/content/overlays.mdx +72 -54
- package/content/overriding-a-component.mdx +85 -74
- package/content/react.mdx +72 -64
- package/content/recipes.mdx +64 -47
- package/content/routing.mdx +100 -93
- package/content/sandbox.mdx +39 -39
- package/content/server-rendering.mdx +95 -84
- package/content/shape-and-borders.mdx +44 -37
- package/content/size-and-density.mdx +51 -46
- package/content/space-and-size.mdx +41 -34
- package/content/state-and-control.mdx +63 -51
- package/content/surfaces-and-separation.mdx +55 -53
- package/content/svelte.mdx +20 -19
- package/content/theme-builder.mdx +42 -31
- package/content/theming.mdx +58 -51
- package/content/tiers.mdx +69 -61
- package/content/tokens.mdx +72 -53
- package/content/typography.mdx +47 -47
- package/content/using-helix-with-an-agent.mdx +147 -115
- package/content/using-the-elements.mdx +55 -50
- package/content/versioning.mdx +42 -35
- package/content/visual-axes.mdx +168 -146
- package/content/vue.mdx +24 -24
- package/content/without-a-bundler.mdx +40 -23
- package/dist/pages.generated.js +49 -43
- package/dist/sources.generated.js +3337 -2680
- package/package.json +3 -3
package/content/composition.mdx
CHANGED
|
@@ -1,64 +1,68 @@
|
|
|
1
1
|
---
|
|
2
|
-
description:
|
|
2
|
+
description: How Helix is organized into families, parts, and groups, and which one to use.
|
|
3
3
|
section: Essentials
|
|
4
4
|
title: Composition
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# Composition
|
|
8
8
|
|
|
9
|
-
Helix
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
Helix components come in families, parts, and groups. Each kind has a different
|
|
10
|
+
purpose. When you know which kind you are looking at, you know what to import
|
|
11
|
+
and what to write.
|
|
12
12
|
|
|
13
|
-
##
|
|
13
|
+
## A namespace means one component goes inside another
|
|
14
14
|
|
|
15
|
-
`Tabs.Root` contains `Tabs.Tab`.
|
|
15
|
+
`Tabs.Root` contains `Tabs.Tab`. A namespace means the part goes inside the
|
|
16
|
+
family's root.
|
|
16
17
|
|
|
17
|
-
|
|
18
|
-
|
|
18
|
+
A namespace never marks a version of a component. An icon-only button is its
|
|
19
|
+
own component, so `IconButton` is top-level. One rule decides this:
|
|
19
20
|
|
|
20
21
|
> Variants are props. Alternatives are separate components.
|
|
21
22
|
|
|
22
|
-
A button
|
|
23
|
-
|
|
24
|
-
accessible
|
|
23
|
+
A filled button and an outlined button are the same component with a different
|
|
24
|
+
`emphasis`. A button that holds only an icon has different requirements for its
|
|
25
|
+
accessible name and a different shape, so it is its own component.
|
|
25
26
|
|
|
26
|
-
## When a region
|
|
27
|
+
## When a region is an element and when it is a slot
|
|
27
28
|
|
|
28
|
-
|
|
29
|
+
A region is an element when it has behavior, state, or an API. Otherwise it is a
|
|
30
|
+
slot.
|
|
29
31
|
|
|
30
|
-
`Card`'s header, body and footer
|
|
31
|
-
|
|
32
|
-
`Select.Option
|
|
32
|
+
`Card`'s header, body, and footer had none of the three. Five elements became
|
|
33
|
+
one, and a title became a real `<h2>` that you write. `Tabs.Tab`,
|
|
34
|
+
`Select.Option`, and `Accordion.Item` have state, so they stay elements.
|
|
33
35
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
36
|
+
An empty wrapper element still takes up layout space. A `<slot>` is
|
|
37
|
+
`display: contents`, so an unassigned slot generates no box. It adds no grid
|
|
38
|
+
row, no flex item, and no gap. A wrapper generates a box either way. We measured
|
|
39
|
+
`Card` with identical content. With empty wrappers present, it rendered at 85px,
|
|
40
|
+
and the ideal is 53px. With bare slots, it rendered at 53px with no hiding logic
|
|
41
|
+
at all.
|
|
39
42
|
|
|
40
|
-
## When a group is a part
|
|
43
|
+
## When a group is a part of a family
|
|
41
44
|
|
|
42
|
-
|
|
45
|
+
A group is a part of a family when it reports a form value.
|
|
43
46
|
|
|
44
|
-
|
|
45
|
-
the validity
|
|
46
|
-
|
|
47
|
-
|
|
47
|
+
`Radio.Group` and `Toggle.Group` report a form value. They own the `name`, the
|
|
48
|
+
`value`, and the validity. A single radio is an answer, and the group is the
|
|
49
|
+
control it answers. The family is about the group, so the group is a part of the
|
|
50
|
+
family, and the member belongs nowhere else.
|
|
48
51
|
|
|
49
|
-
|
|
50
|
-
Each checkbox is form-associated and submits itself
|
|
51
|
-
`role="group"`, a shared name, and one place to read the set. A button
|
|
52
|
-
group at all. Both are their own families, because their members
|
|
53
|
-
|
|
52
|
+
`Checkbox Group` and `Button Group` do not report a form value. Neither calls
|
|
53
|
+
`setFormValue`. Each checkbox is form-associated and submits itself. The group
|
|
54
|
+
adds `role="group"`, a shared name, and one place to read the set. A button
|
|
55
|
+
needs no group at all. Both groups are their own families, because their members
|
|
56
|
+
are complete controls without the group.
|
|
54
57
|
|
|
55
|
-
A container
|
|
56
|
-
holds buttons, dropdowns and inputs,
|
|
58
|
+
A container that holds mixed content is its own family for the same reason.
|
|
59
|
+
`Toolbar` holds buttons, dropdowns, and inputs, so it is not a part of any of
|
|
60
|
+
their families.
|
|
57
61
|
|
|
58
|
-
## A content property is its slot
|
|
62
|
+
## A content property is the fallback for its slot
|
|
59
63
|
|
|
60
|
-
|
|
61
|
-
|
|
64
|
+
A property that holds content, such as a tooltip's text, is the fallback content
|
|
65
|
+
for a slot of the same name. Helix never renders both.
|
|
62
66
|
|
|
63
67
|
```html
|
|
64
68
|
<!-- The property -->
|
|
@@ -70,21 +74,21 @@ content of a slot of the same name, never a second thing rendered beside it.
|
|
|
70
74
|
</helix-tooltip>
|
|
71
75
|
```
|
|
72
76
|
|
|
73
|
-
Text stays an attribute, because
|
|
74
|
-
|
|
75
|
-
|
|
77
|
+
Text stays an attribute, because most tooltips and most avatars hold only text.
|
|
78
|
+
To use richer content, put it in the slot, and it replaces the attribute. The
|
|
79
|
+
two never show at once, so no component has to check which one you used.
|
|
76
80
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
81
|
+
A text node that holds only whitespace is assigned to the default slot and hides
|
|
82
|
+
its fallback. A newline between the tags is enough. For this reason, some
|
|
83
|
+
examples put the closing tag on the same line as the opening tag.
|
|
80
84
|
|
|
81
|
-
##
|
|
85
|
+
## Set a button's width with the layout
|
|
82
86
|
|
|
83
|
-
|
|
84
|
-
|
|
87
|
+
A button has no `width` or `fill` property, for the same reason it has no `size`
|
|
88
|
+
property. How wide a control is depends on the region it is in.
|
|
85
89
|
|
|
86
|
-
A button already stretches in a column flex or a grid, because
|
|
87
|
-
|
|
90
|
+
A button already stretches in a column flex or a grid, because a stretch-aligned
|
|
91
|
+
parent stretches its children:
|
|
88
92
|
|
|
89
93
|
```html
|
|
90
94
|
<helix-flex direction="column">
|
|
@@ -95,7 +99,7 @@ stretch-aligned parent does to its children:
|
|
|
95
99
|
</helix-flex>
|
|
96
100
|
```
|
|
97
101
|
|
|
98
|
-
To make
|
|
102
|
+
To make a button only as wide as its label, set that on the layout:
|
|
99
103
|
|
|
100
104
|
```css
|
|
101
105
|
.hug {
|
|
@@ -107,13 +111,13 @@ To make one hug its label instead, tell the layout:
|
|
|
107
111
|
<helix-button class="hug">Hugs its label</helix-button>
|
|
108
112
|
```
|
|
109
113
|
|
|
110
|
-
|
|
111
|
-
|
|
114
|
+
This default can surprise you. A button in a column is full width, and you might
|
|
115
|
+
look for a property to turn that off. The layout controls it.
|
|
112
116
|
|
|
113
|
-
**A fixed width
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
+
**A fixed width belongs to your site's styles.** A marketing page whose hero
|
|
118
|
+
buttons all share one width is a common and reusable practice. The number is
|
|
119
|
+
part of that page's design, so it does not belong to the button component. Name
|
|
120
|
+
it once, where your other page constants are defined:
|
|
117
121
|
|
|
118
122
|
```css
|
|
119
123
|
:root {
|
|
@@ -125,12 +129,12 @@ Name it once where your other page constants live:
|
|
|
125
129
|
}
|
|
126
130
|
```
|
|
127
131
|
|
|
128
|
-
|
|
129
|
-
marketing hero is not a component, so its measurements
|
|
132
|
+
For the same reason, a hero's type size is outside the component scale. A
|
|
133
|
+
marketing hero is not a component, so its measurements belong to your site.
|
|
130
134
|
|
|
131
|
-
##
|
|
135
|
+
## Name a field control with `label` or `aria-label`
|
|
132
136
|
|
|
133
|
-
Every field control
|
|
137
|
+
Every field control is named the same way, and there are two cases:
|
|
134
138
|
|
|
135
139
|
```html
|
|
136
140
|
<helix-input label="Files"></helix-input> <!-- a visible label -->
|
|
@@ -138,34 +142,32 @@ Every field control names itself the same way, and there are exactly two cases.
|
|
|
138
142
|
<helix-input></helix-input> <!-- unnamed. avoid -->
|
|
139
143
|
```
|
|
140
144
|
|
|
141
|
-
`label`
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
the
|
|
145
|
+
`label` gives the control a visible label. `aria-label` gives it a name and
|
|
146
|
+
draws nothing. With neither, a screen reader announces the control as "edit
|
|
147
|
+
text", which is almost never what you want. If the page already makes the
|
|
148
|
+
purpose plain, use `aria-label`. Do not leave the control unnamed.
|
|
145
149
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
input came back with no label and no name at all.
|
|
150
|
+
`aria-label` works on a Helix field because the component forwards it. Most
|
|
151
|
+
custom elements do not. A custom element cannot delegate a label to the control
|
|
152
|
+
inside its shadow root. An `aria-label` you write on the host stays on a wrapper
|
|
153
|
+
and never reaches the `<input>` that takes focus. We measured this in Helix
|
|
154
|
+
before we fixed it: the inner input had no label and no name at all.
|
|
152
155
|
|
|
153
|
-
|
|
154
|
-
same `<label for>` a visible
|
|
156
|
+
The field renders the label and clips it. The name then reaches the input
|
|
157
|
+
through the same `<label for>` that a visible label uses.
|
|
155
158
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
the first, spelled twice.
|
|
159
|
+
`hide-label` was a boolean beside `label`, and we removed it. Setting a label
|
|
160
|
+
and then a second attribute to hide it is the same as having no visible label.
|
|
159
161
|
|
|
160
|
-
## Shared behavior
|
|
162
|
+
## Shared behavior is its own family
|
|
161
163
|
|
|
162
|
-
`Option`, `Listbox
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
164
|
+
`Option`, `Listbox`, and `Select` are three families so that the keyboard
|
|
165
|
+
handling, the filtering, the multi-select rules, and the active-item tracking
|
|
166
|
+
are written once. `Select` renders a `Listbox` internally. A future combobox
|
|
167
|
+
will too, and so would a command palette.
|
|
166
168
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
and `Drilldown` share it
|
|
169
|
+
For the same reason, we wrote `DrilldownController`. Since it was first written,
|
|
170
|
+
`Menu` had held a page stack and derived its rows, and none of that code
|
|
171
|
+
was specific to menus. `Menu` and `Drilldown` now share it.
|
|
170
172
|
|
|
171
|
-
If you are about to write
|
|
173
|
+
If you are about to write a second copy of some behavior, make it shared.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
description:
|
|
2
|
+
description: The layer, shadow, and material tokens for stacking, lifting, and translucent surfaces.
|
|
3
3
|
section: Foundations
|
|
4
4
|
title: Elevation and material
|
|
5
5
|
---
|
|
@@ -21,9 +21,9 @@ title: Elevation and material
|
|
|
21
21
|
|
|
22
22
|
[//]: # (/tokens)
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
context bugs
|
|
24
|
+
The five main layers are named after what sits at each one. A numbered scale
|
|
25
|
+
leads to values such as `z-index: 1001` that nobody can explain, and those
|
|
26
|
+
values cause stacking context bugs.
|
|
27
27
|
|
|
28
28
|
| Layer | What is at it |
|
|
29
29
|
|---|---|
|
|
@@ -33,10 +33,13 @@ context bugs start.
|
|
|
33
33
|
| `overlay` | Menus, dialogs, popovers, tooltips |
|
|
34
34
|
| `toast` | Above everything, including a modal |
|
|
35
35
|
|
|
36
|
-
**Most overlays do not need these
|
|
37
|
-
in the browser's top layer, which is above every z-index
|
|
38
|
-
|
|
39
|
-
|
|
36
|
+
**Most overlays do not need these tokens.** They use the popover API and render
|
|
37
|
+
in the browser's top layer, which is always above every z-index. Use the tokens
|
|
38
|
+
for the cases the top layer does not cover:
|
|
39
|
+
|
|
40
|
+
- your own sticky header
|
|
41
|
+
- your own dragged element
|
|
42
|
+
- anything that has to sit between two of our layers
|
|
40
43
|
|
|
41
44
|
## Shadows
|
|
42
45
|
|
|
@@ -53,41 +56,41 @@ your own dragged element, and anything that has to sit between two of ours.
|
|
|
53
56
|
|
|
54
57
|
[//]: # (/tokens)
|
|
55
58
|
|
|
56
|
-
The first four are the values. The last two are
|
|
57
|
-
|
|
58
|
-
|
|
59
|
+
The first four are the values. The last two are policy tokens. A theme sets
|
|
60
|
+
them to make your interface shadow-first or border-first. See [Surfaces and
|
|
61
|
+
separation](./surfaces-and-separation).
|
|
59
62
|
|
|
60
63
|
## Material
|
|
61
64
|
|
|
62
|
-
Eight tokens
|
|
65
|
+
Eight tokens describe translucency:
|
|
63
66
|
|
|
64
67
|
[//]: # (tokens:material)
|
|
65
68
|
|
|
66
69
|
| Token | Value | For |
|
|
67
70
|
| --- | --- | --- |
|
|
68
|
-
| `--helix-material-backdrop` | `blur(var(--helix-material-blur-md)) saturate(1.5)` | The
|
|
71
|
+
| `--helix-material-backdrop` | `blur(var(--helix-material-blur-md)) saturate(1.5)` | The tint behind a modal. |
|
|
69
72
|
| `--helix-material-opacity` | `1` | How opaque a translucent surface is. |
|
|
70
73
|
| `--helix-material-blur-sm` | `8px` | The lightest backdrop blur. |
|
|
71
|
-
| `--helix-material-blur-md` | `16px` | The backdrop blur a modal
|
|
74
|
+
| `--helix-material-blur-md` | `16px` | The backdrop blur behind a modal. |
|
|
72
75
|
| `--helix-material-blur-lg` | `32px` | The heaviest backdrop blur. |
|
|
73
|
-
| `--helix-material-translucent` | `color-mix(in oklab, light-dark(oklch(1 0 0), oklch(0.182 var(--helix-seed-neutral-chroma, 0) var(--helix-seed-neutral-hue, 258))) 30%, transparent)` | The translucent
|
|
74
|
-
| `--helix-material-translucent-strong` | `color-mix(in oklab, light-dark(oklch(1 0 0), oklch(0.182 var(--helix-seed-neutral-chroma, 0) var(--helix-seed-neutral-hue, 258))) 88%, transparent)` | The
|
|
75
|
-
| `--helix-material-border` | `light-dark(color-mix(in oklab, oklch(0.15 var(--helix-seed-neutral-chroma, 0) var(--helix-seed-neutral-hue, 258)) 14%, transparent), color-mix(in oklab, oklch(1 0 0) 12%, transparent))` | The
|
|
76
|
+
| `--helix-material-translucent` | `color-mix(in oklab, light-dark(oklch(1 0 0), oklch(0.182 var(--helix-seed-neutral-chroma, 0) var(--helix-seed-neutral-hue, 258))) 30%, transparent)` | The translucent background, for a surface that shows what is behind it. |
|
|
77
|
+
| `--helix-material-translucent-strong` | `color-mix(in oklab, light-dark(oklch(1 0 0), oklch(0.182 var(--helix-seed-neutral-chroma, 0) var(--helix-seed-neutral-hue, 258))) 88%, transparent)` | The stronger translucent background, for a surface that shows less of what is behind it. |
|
|
78
|
+
| `--helix-material-border` | `light-dark(color-mix(in oklab, oklch(0.15 var(--helix-seed-neutral-chroma, 0) var(--helix-seed-neutral-hue, 258)) 14%, transparent), color-mix(in oklab, oklch(1 0 0) 12%, transparent))` | The border a translucent surface needs to keep a visible edge. |
|
|
76
79
|
|
|
77
80
|
[//]: # (/tokens)
|
|
78
81
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
82
|
+
The `material` axis draws its three values, `frosted`, `glass`, and
|
|
83
|
+
`transparent`, with these tokens. Material is an axis of its own, separate from
|
|
84
|
+
`emphasis`, because a panel can be glass and outlined at the same time. Two
|
|
85
|
+
properties that can apply at once belong on two axes. See [Visual
|
|
83
86
|
axes](./visual-axes).
|
|
84
87
|
|
|
85
|
-
**
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
88
|
+
**Use blur with care, because it is expensive.** A `backdrop-filter` makes the
|
|
89
|
+
compositor read what is behind the element again on every frame the element
|
|
90
|
+
moves. A frosted header that is always on screen pays that cost all the time.
|
|
91
|
+
When you want depth without the frosted texture, use `translucent` without the
|
|
92
|
+
blur.
|
|
90
93
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
+
A translucent surface takes its color from what is behind it, so on a busy
|
|
95
|
+
background it has no visible edge. `material-border` gives it one, so it keeps
|
|
96
|
+
its shape.
|
package/content/events.mdx
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
---
|
|
2
|
-
description: The
|
|
2
|
+
description: The two events each state change sends, and why one name has a prefix and the other does not.
|
|
3
3
|
section: Essentials
|
|
4
4
|
title: Events
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# Events
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
Each state change sends two events. The first is a request that you can cancel.
|
|
10
|
+
The second is a notification that the change happened.
|
|
10
11
|
|
|
11
12
|
```ts
|
|
12
13
|
// The request. Cancelable, carries what is being proposed, and fires before
|
|
@@ -24,59 +25,67 @@ element.addEventListener('change', (event) => {
|
|
|
24
25
|
})
|
|
25
26
|
```
|
|
26
27
|
|
|
27
|
-
Most
|
|
28
|
-
and read the target,
|
|
29
|
-
prefixed
|
|
28
|
+
Most of the time you need the notification. Bind `onChange` in React or
|
|
29
|
+
`onchange=` in HTML and read the target, as you would for an `<input>`. Use the
|
|
30
|
+
prefixed event when you want to cancel the change.
|
|
30
31
|
|
|
31
|
-
The class
|
|
32
|
-
|
|
33
|
-
framework typings emit a typed callback instead of `(detail: unknown)`; the
|
|
34
|
-
package augments `HTMLElementEventMap`, which types `addEventListener` for
|
|
35
|
-
people writing plain HTML; and the name lives on the class, so a dispatch and a
|
|
36
|
-
listener cannot disagree about the string.
|
|
32
|
+
Each event has its own class. The class gets the event's type to you in three
|
|
33
|
+
ways:
|
|
37
34
|
|
|
38
|
-
|
|
35
|
+
- The class name is in the manifest, so the framework typings can emit a typed
|
|
36
|
+
callback. A bare `CustomEvent` would give `(detail: unknown)`.
|
|
37
|
+
- The package augments `HTMLElementEventMap`, which types `addEventListener`
|
|
38
|
+
when you write plain HTML.
|
|
39
|
+
- The name is defined on the class, so a dispatch and a listener always use the
|
|
40
|
+
same string.
|
|
39
41
|
|
|
40
|
-
|
|
41
|
-
like it came from the host, so a bare `input` from a `<helix-input>` is
|
|
42
|
-
indistinguishable from the inner control's own. Prefixed, it cannot be mistaken
|
|
43
|
-
for either.
|
|
42
|
+
## Why the request has a prefix and the notification does not
|
|
44
43
|
|
|
45
|
-
|
|
46
|
-
`submit`, `copy`, `focus` and the rest, and a prop matching one of those is
|
|
47
|
-
wired to the native event instead. `onhelix-before-change` matches none of them
|
|
48
|
-
and reaches us, which is exactly what a request needs.
|
|
44
|
+
### Retargeting
|
|
49
45
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
46
|
+
An event dispatched inside a shadow root is retargeted so that it appears to
|
|
47
|
+
come from the host. A bare `input` from a `<helix-input>` would look the same
|
|
48
|
+
as the `input` from the control inside it. A prefixed event cannot be mistaken
|
|
49
|
+
for either one.
|
|
54
50
|
|
|
55
|
-
|
|
56
|
-
in your application. Our classes extend `Event`, so an entry named `select`
|
|
57
|
-
would not fail to compile: it would silently retype
|
|
58
|
-
`textarea.addEventListener('select', …)` as carrying our detail, which at
|
|
59
|
-
runtime is `undefined`. Measured. An unprefixed name is not a collision we would
|
|
60
|
-
notice, it is one you would.
|
|
51
|
+
### React's event names
|
|
61
52
|
|
|
62
|
-
|
|
53
|
+
React's synthetic event system handles `change`, `input`, `submit`, `copy`,
|
|
54
|
+
`focus`, and its other event names. A prop that matches one of those names is
|
|
55
|
+
connected to the native event of that name. `onhelix-before-change` matches none
|
|
56
|
+
of them, so it reaches our event. A request needs exactly that.
|
|
63
57
|
|
|
64
|
-
|
|
65
|
-
`helix-before-close`. Overlays nest, and an inner one's lifecycle must never reach the
|
|
66
|
-
outer one that contains it: a menu closing inside a dialog is not the dialog
|
|
67
|
-
closing.
|
|
58
|
+
### The notification uses the native name
|
|
68
59
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
60
|
+
The notification has no prefix for the same reason. `onChange` on a Helix
|
|
61
|
+
element goes through React's synthetic system to the native `change` event the
|
|
62
|
+
element sends. You use it the way you already use it on any input.
|
|
72
63
|
|
|
73
|
-
|
|
74
|
-
lifecycle one and the listener belongs on the element itself.
|
|
64
|
+
### The type map is global
|
|
75
65
|
|
|
76
|
-
|
|
66
|
+
`HTMLElementEventMap` is shared by every element in your application. Our event
|
|
67
|
+
classes extend `Event`, so an entry named `select` would still compile. It would
|
|
68
|
+
silently retype `textarea.addEventListener('select', …)` as carrying our detail,
|
|
69
|
+
which is `undefined` at runtime. We measured this. We would not see a collision
|
|
70
|
+
from an unprefixed name, and you would.
|
|
77
71
|
|
|
78
|
-
|
|
79
|
-
|
|
72
|
+
## Which events bubble
|
|
73
|
+
|
|
74
|
+
- **Lifecycle events** do not bubble and are not composed. These are
|
|
75
|
+
`helix-before-open` and `helix-before-close`. Overlays nest, and an inner
|
|
76
|
+
overlay's lifecycle must never reach the outer overlay that contains it. A
|
|
77
|
+
menu that closes inside a dialog does not close the dialog.
|
|
78
|
+
- **Coordination events** bubble and are composed. These are `helix-select`,
|
|
79
|
+
`helix-dismiss`, and `helix-before-change`. A parent or a router can intercept
|
|
80
|
+
them across a shadow boundary, so you can use a `Tree` as navigation.
|
|
81
|
+
|
|
82
|
+
If you listen on a container and receive nothing, the event is probably a
|
|
83
|
+
lifecycle event. Add the listener to the element itself.
|
|
84
|
+
|
|
85
|
+
## Cancel a change
|
|
86
|
+
|
|
87
|
+
You can cancel the events sent before a state change. Canceling is the only way
|
|
88
|
+
to stop the change:
|
|
80
89
|
|
|
81
90
|
```ts
|
|
82
91
|
dialog.addEventListener('helix-before-close', (event) => {
|
|
@@ -86,18 +95,18 @@ dialog.addEventListener('helix-before-close', (event) => {
|
|
|
86
95
|
})
|
|
87
96
|
```
|
|
88
97
|
|
|
89
|
-
Every dispatched event is
|
|
90
|
-
dispatch, so
|
|
98
|
+
Every dispatched event is checked against a declaration, and every declaration
|
|
99
|
+
against a dispatch, so every event you can listen for is documented.
|
|
91
100
|
|
|
92
|
-
##
|
|
101
|
+
## Helix events
|
|
93
102
|
|
|
94
|
-
| Event |
|
|
103
|
+
| Event | Meaning |
|
|
95
104
|
|---|---|
|
|
96
|
-
| `helix-before-change` | A value about to
|
|
97
|
-
| `helix-before-toggle` | A checked state about to flip,
|
|
98
|
-
| `helix-before-open`, `helix-before-close` | An open state about to
|
|
99
|
-
| `helix-select` | An item chosen from a set |
|
|
100
|
-
| `helix-dismiss` |
|
|
105
|
+
| `helix-before-change` | A value is about to change, cancelable |
|
|
106
|
+
| `helix-before-toggle` | A checked state is about to flip, cancelable |
|
|
107
|
+
| `helix-before-open`, `helix-before-close` | An open state is about to change, cancelable |
|
|
108
|
+
| `helix-select` | An item was chosen from a set |
|
|
109
|
+
| `helix-dismiss` | A user asked for something to close |
|
|
101
110
|
| `helix-next`, `helix-previous` | Movement through a sequence |
|
|
102
111
|
| `helix-filter` | A query changed in a filtering control |
|
|
103
112
|
| `helix-navigate` | A component is about to navigate |
|
|
@@ -105,21 +114,24 @@ dispatch, so an event you can listen for is an event that is documented.
|
|
|
105
114
|
| `helix-caret` | The cursor moved in a text control |
|
|
106
115
|
| `helix-complete` | A control that fills up has filled |
|
|
107
116
|
| `helix-create` | A new item was made from a query |
|
|
108
|
-
| `helix-console` | The sandbox
|
|
117
|
+
| `helix-console` | The sandbox passes on a log message |
|
|
109
118
|
| `helix-tour-ask`, `helix-sample-start`, `helix-sample-end` | Tour and color sampling |
|
|
110
119
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
120
|
+
Every stateful component also sends native events once the change is applied:
|
|
121
|
+
|
|
122
|
+
- `input` and `change` on a value control
|
|
123
|
+
- `toggle` on anything that opens, carrying `newState` and `oldState` as
|
|
124
|
+
`<details>` does
|
|
125
|
+
- `close` on a dialog
|
|
126
|
+
- `load` and `error` on an image
|
|
115
127
|
|
|
116
|
-
Each component's page lists the events it
|
|
117
|
-
|
|
128
|
+
Each component's page lists the events it dispatches, with the class and the
|
|
129
|
+
detail.
|
|
118
130
|
|
|
119
|
-
##
|
|
131
|
+
## Events in a framework
|
|
120
132
|
|
|
121
|
-
|
|
122
|
-
every framework already
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
[Svelte](./svelte).
|
|
133
|
+
Native events use their usual props, such as `onChange`, `onInput`, and
|
|
134
|
+
`onToggle`, which every framework already understands. A prefixed event uses
|
|
135
|
+
`on` followed by the exact name, such as `onhelix-before-change`. Nothing
|
|
136
|
+
removes the prefix or converts it to camel case. That is React's own behavior.
|
|
137
|
+
See [React](./react), [Vue](./vue), and [Svelte](./svelte).
|