@fusion.dev/helix-headless 0.1.0 → 0.3.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/custom-elements.json +234 -234
- package/dist/accordion/item/accordion-item.js +0 -108
- package/dist/accordion/root/accordion-root.js +0 -33
- package/dist/announcement/root/announcement-root.js +1 -76
- package/dist/app-header/root/app-header-root.js +0 -84
- package/dist/avatar/group/avatar-group.js +0 -77
- package/dist/avatar/root/avatar-root.js +0 -72
- package/dist/badge/root/badge-root.js +0 -67
- package/dist/bar-chart/root/bar-chart-root.js +3 -263
- package/dist/blockquote/root/blockquote-root.js +0 -46
- package/dist/breadcrumbs/breadcrumb/breadcrumb.js +0 -72
- package/dist/breadcrumbs/root/breadcrumbs-root.js +0 -52
- package/dist/button/root/button-root.js +0 -215
- package/dist/button-group/root/button-group-root.js +0 -30
- package/dist/callout/root/callout-root.js +0 -52
- package/dist/card/root/card-root.js +0 -41
- package/dist/carousel/root/carousel-root.d.ts +2 -2
- package/dist/carousel/root/carousel-root.js +2 -362
- package/dist/chat/composer/chat-composer.js +0 -131
- package/dist/chat/message/chat-message.js +0 -137
- package/dist/chat/root/chat-root.js +0 -193
- package/dist/checkbox/root/checkbox-root.js +0 -113
- package/dist/checkbox-group/root/checkbox-group-root.js +1 -157
- package/dist/code/root/code-root.js +0 -26
- package/dist/code-block/root/code-block-root.js +0 -100
- package/dist/color-input/root/color-input-root.js +0 -217
- package/dist/color-picker/color.js +0 -69
- package/dist/color-picker/root/color-picker-root.js +0 -272
- package/dist/combobox/root/combobox-root.js +0 -428
- package/dist/command/root/command-root.js +0 -177
- package/dist/copy-button/root/copy-button-root.js +0 -134
- package/dist/data-table/root/data-table-root.js +0 -336
- package/dist/data-table/root/order.js +0 -100
- package/dist/date-input/root/date-input-root.js +1 -398
- package/dist/date-picker/root/date-picker-root.js +0 -358
- package/dist/dialog/root/dialog-root.js +0 -189
- package/dist/divider/root/divider-root.js +0 -71
- package/dist/donut-chart/root/donut-chart-root.js +0 -214
- package/dist/drawer/root/drawer-root.js +0 -116
- package/dist/drilldown/item/drilldown-item.js +0 -82
- package/dist/drilldown/root/drilldown-root.js +0 -91
- package/dist/element-events.generated.js +0 -5
- package/dist/empty/root/empty-root.js +0 -35
- package/dist/file-input/root/file-input-root.js +0 -232
- package/dist/format-date/root/format-date-root.js +0 -95
- package/dist/heatmap/root/buckets.js +0 -57
- package/dist/heatmap/root/heatmap-root.js +0 -313
- package/dist/icon/root/icon-root.js +0 -48
- package/dist/icon-button/root/icon-button-root.js +0 -30
- package/dist/image/root/image-root.js +0 -174
- package/dist/index.js +0 -9
- package/dist/input/root/input-root.js +0 -228
- package/dist/kbd/root/kbd-root.js +0 -117
- package/dist/legend/root/legend-root.js +0 -73
- package/dist/line-chart/root/line-chart-root.js +0 -172
- package/dist/list/item/list-item.js +0 -92
- package/dist/list/root/list-root.js +0 -26
- package/dist/listbox/root/listbox-root.js +2 -429
- package/dist/loader/root/loader-root.js +0 -40
- package/dist/megamenu/root/megamenu-root.js +0 -284
- package/dist/menu/item/menu-item.js +1 -221
- package/dist/menu/root/menu-root.js +1 -171
- package/dist/meter/root/meter-root.js +1 -152
- package/dist/navigation/root/navigation-root.js +0 -56
- package/dist/notifications/notification/notification.js +0 -159
- package/dist/notifications/root/notifications-root.js +0 -148
- package/dist/number-input/root/number-input-root.js +1 -263
- package/dist/option/group/option-group.js +0 -26
- package/dist/option/root/option-root.js +0 -157
- package/dist/outline/root/outline-root.js +0 -292
- package/dist/pagination/root/pagination-root.js +0 -361
- package/dist/pagination/size/pagination-size.js +0 -104
- package/dist/pagination/summary/pagination-summary.js +0 -90
- package/dist/pin-input/root/pin-input-root.js +0 -276
- package/dist/popover/root/popover-root.js +0 -103
- package/dist/progress/root/progress-root.js +1 -133
- package/dist/qr-code/root/qr-code-root.js +0 -96
- package/dist/quote/root/quote-root.js +0 -25
- package/dist/radio/group/radio-group.js +0 -81
- package/dist/radio/root/radio-root.js +0 -85
- package/dist/rating/root/rating-root.js +0 -220
- package/dist/scroll-area/root/scroll-area-root.js +0 -100
- package/dist/select/root/select-root.js +2 -218
- package/dist/skeleton/root/skeleton-root.js +0 -20
- package/dist/slider/root/slider-root.js +1 -267
- package/dist/sparkline/root/sparkline-root.js +3 -164
- package/dist/speed-dial/item/speed-dial-item.js +0 -52
- package/dist/speed-dial/root/speed-dial-root.js +1 -116
- package/dist/splitter/root/splitter-root.js +0 -489
- package/dist/stat/group/stat-group.js +0 -18
- package/dist/stat/root/stat-root.js +0 -23
- package/dist/steps/root/steps-root.js +0 -62
- package/dist/steps/step/step.js +0 -131
- package/dist/switch/root/switch-root.js +0 -80
- package/dist/table/cell/table-cell.js +0 -23
- package/dist/table/column/table-column.js +0 -158
- package/dist/table/root/table-root.js +0 -82
- package/dist/table/row/table-row.js +0 -21
- package/dist/tabs/panel/tab-panel.js +0 -49
- package/dist/tabs/root/tabs-root.js +0 -98
- package/dist/tabs/tab/tab.js +0 -59
- package/dist/tag/root/tag-root.js +0 -31
- package/dist/textarea/root/textarea-root.js +0 -162
- package/dist/time-input/root/time-input-root.js +0 -283
- package/dist/time-picker/root/time-picker-root.js +0 -326
- package/dist/timeline/item/timeline-item.js +0 -66
- package/dist/timeline/root/timeline-root.js +0 -41
- package/dist/toast/item/toast-item.js +0 -165
- package/dist/toast/root/toast-root.js +0 -51
- package/dist/toggle/group/toggle-group.js +0 -150
- package/dist/toggle/root/toggle-root.js +0 -78
- package/dist/toolbar/root/toolbar-root.js +0 -116
- package/dist/tooltip/root/tooltip-root.js +0 -427
- package/dist/tour/root/tour-root.js +0 -130
- package/dist/tour/stop/tour-stop.js +1 -127
- package/dist/tree/item/tree-item.js +0 -198
- package/dist/tree/root/tree-root.js +0 -97
- package/dist/trend/root/trend-root.js +0 -53
- package/dist/video/root/video-root.d.ts +8 -12
- package/dist/video/root/video-root.js +5 -756
- package/dist/video-playlist/item/video-playlist-item.js +1 -90
- package/dist/video-playlist/menu/video-playlist-menu.js +1 -132
- package/dist/video-playlist/root/video-playlist-root.js +0 -113
- package/dist/virtual-list/root/virtual-list-root.js +0 -105
- package/jsx.d.ts +1408 -2576
- package/package.json +6 -6
- package/vue.d.ts +1 -1
|
@@ -6,64 +6,12 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
6
6
|
};
|
|
7
7
|
import { customElement, HelixElement, hostAria } from '@fusion.dev/helix-core';
|
|
8
8
|
import { html } from 'lit';
|
|
9
|
-
/**
|
|
10
|
-
* @summary Shows the path to the current page.
|
|
11
|
-
*
|
|
12
|
-
* @description
|
|
13
|
-
* The host is the landmark, so `aria-label` on it names the trail directly and
|
|
14
|
-
* there is no property mirroring one. A page may hold more than one navigation,
|
|
15
|
-
* and landmarks are how a screen-reader user chooses between them.
|
|
16
|
-
*
|
|
17
|
-
* `role="list"` is set explicitly on the `<ol>` even though it is already a
|
|
18
|
-
* list: removing the markers with `list-style: none` strips list semantics in
|
|
19
|
-
* Safari, and a breadcrumb trail always removes its markers.
|
|
20
|
-
*
|
|
21
|
-
* Items are slotted rather than wrapped, so they are `<ol>` children in the flat
|
|
22
|
-
* tree and each carries `role="listitem"` itself.
|
|
23
|
-
*
|
|
24
|
-
* **The trail does not measure itself, and the overflow is markup.** Measuring
|
|
25
|
-
* how many crumbs fit can never reach the server: there is no width without a
|
|
26
|
-
* DOM, so the server ships every crumb and the client removes some after
|
|
27
|
-
* hydrating, and a trail that collapses paints wrong on every single load. Nor
|
|
28
|
-
* does anybody specify a breadcrumb in units of fit. Designs say "keep the root
|
|
29
|
-
* and the current page, fold the middle", which is a fact about depth, or "fold
|
|
30
|
-
* below this width", which is a fact CSS knows.
|
|
31
|
-
*
|
|
32
|
-
* So the trail lays out crumbs and separators, and the consumer writes what
|
|
33
|
-
* folds: a `helix-menu` of `helix-menu-item`s in a crumb, where the gap
|
|
34
|
-
* belongs. Server rendered, no observer, no stylesheet, and correct before the
|
|
35
|
-
* first script runs. A trail that should fold only at some widths is a
|
|
36
|
-
* container query in the consumer's own sheet, which is theirs to write and
|
|
37
|
-
* theirs to leave out.
|
|
38
|
-
*
|
|
39
|
-
* **And the crumbs carry no marker for any of it.** CSS can already see it: the
|
|
40
|
-
* crumb with the menu in it is `:has(helix-menu)`, and the middle is
|
|
41
|
-
* `:not(:first-child, [current])`. A marker would be the design rule written
|
|
42
|
-
* twice, once in the HTML and once in the sheet, with nothing keeping the two
|
|
43
|
-
* honest.
|
|
44
|
-
*
|
|
45
|
-
* @since 0.1.0
|
|
46
|
-
* @status stable
|
|
47
|
-
* @category navigation
|
|
48
|
-
* @aliases Breadcrumb, Crumbs
|
|
49
|
-
* @documentation https://helix.fusion.dev/components/breadcrumbs
|
|
50
|
-
*
|
|
51
|
-
* @slot - The trail's items, in order from root to current page, with the
|
|
52
|
-
* crumb holding the fold wherever the gap should appear.
|
|
53
|
-
*
|
|
54
|
-
* @csspart list - The ordered list.
|
|
55
|
-
*/
|
|
56
9
|
let HelixHeadlessBreadcrumbs = class HelixHeadlessBreadcrumbs extends HelixElement {
|
|
57
10
|
willUpdate(changed) {
|
|
58
11
|
super.willUpdate(changed);
|
|
59
|
-
// On the host through internals, so the landmark is the element itself and
|
|
60
|
-
// an author's `aria-label` names it without anything being copied inward.
|
|
61
12
|
hostAria(this.internals, { role: 'navigation' });
|
|
62
13
|
}
|
|
63
14
|
render() {
|
|
64
|
-
/* One slot. The crumb holding the fold is one the consumer placed in
|
|
65
|
-
source order, so there is nothing here to position it: the list renders
|
|
66
|
-
what it was given, in the order it was given. */
|
|
67
15
|
return html `<ol part="list" role="list">
|
|
68
16
|
<slot></slot>
|
|
69
17
|
</ol>`;
|
|
@@ -7,161 +7,23 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
7
7
|
import { AriaForwardController, customElement, HelixFallbackCommandEvent, HelixFormElement, supportsInvokers, } from '@fusion.dev/helix-core';
|
|
8
8
|
import { html, nothing } from 'lit';
|
|
9
9
|
import { property } from 'lit/decorators.js';
|
|
10
|
-
/**
|
|
11
|
-
* @summary Triggers an action, submits a form, or navigates.
|
|
12
|
-
*
|
|
13
|
-
* @description
|
|
14
|
-
* Renders a real `<button>`, so activation, focus, and the browser's own
|
|
15
|
-
* affordances come from the platform rather than from key handlers.
|
|
16
|
-
*
|
|
17
|
-
* **It never renders an anchor.** A router has to render its own `<a>` to
|
|
18
|
-
* intercept a click and cannot render one inside this shadow root, so a button
|
|
19
|
-
* that could also be a link forced every such navigation to be a full page
|
|
20
|
-
* load. A link that looks like a button is styling rather than behavior, so it
|
|
21
|
-
* is a recipe in the base layer: `<Link data-button="solid">` is the same
|
|
22
|
-
* control, and the consumer keeps their anchor.
|
|
23
|
-
*
|
|
24
|
-
* `delegatesFocus` puts focus on the inner control, and it serializes into the
|
|
25
|
-
* server's markup, so the button is keyboard reachable before any JavaScript
|
|
26
|
-
* loads.
|
|
27
|
-
*
|
|
28
|
-
* Form submission is explicit. A `<button type="submit">` inside a shadow root
|
|
29
|
-
* submits nothing, because the form is on the other side of the boundary, so
|
|
30
|
-
* the inner control is always `type="button"` and this element calls
|
|
31
|
-
* `requestSubmit()` on its form instead. That routes through validation, unlike
|
|
32
|
-
* `submit()`.
|
|
33
|
-
*
|
|
34
|
-
* @since 0.1.0
|
|
35
|
-
* @status stable
|
|
36
|
-
* @category actions
|
|
37
|
-
* @documentation https://helix.fusion.dev/components/button
|
|
38
|
-
*
|
|
39
|
-
* @slot - The button's label.
|
|
40
|
-
* @slot end - Content after the label.
|
|
41
|
-
* @slot start - Content before the label, usually an icon.
|
|
42
|
-
*
|
|
43
|
-
* @csspart button - The inner control.
|
|
44
|
-
*/
|
|
45
10
|
let HelixHeadlessButton = class HelixHeadlessButton extends HelixFormElement {
|
|
46
11
|
constructor() {
|
|
47
12
|
super(...arguments);
|
|
48
|
-
/* A consumer's `aria-describedby` names ids in their tree and this control is
|
|
49
|
-
in a shadow root, so the reference has to be carried down as an element
|
|
50
|
-
rather than copied as an id. See the controller: measured, the host held
|
|
51
|
-
the description and the accessibility tree exposed the inner control
|
|
52
|
-
without it. */
|
|
53
13
|
this.aria = new AriaForwardController(this, {
|
|
54
14
|
target: () => this.renderRoot?.querySelector('[part="button"]') ?? null,
|
|
55
15
|
});
|
|
56
|
-
/**
|
|
57
|
-
* What to send when the button is activated, and what to send it to.
|
|
58
|
-
*
|
|
59
|
-
* The platform's invoker attributes, forwarded onto the inner `<button>`
|
|
60
|
-
* rather than left on the host: only a real button is recognized as an
|
|
61
|
-
* invoker, so `commandfor` written on a custom element resolves nothing and
|
|
62
|
-
* the trigger silently does nothing at all. A wrapper has to hand them down,
|
|
63
|
-
* the same way `label` becomes `aria-label` in there.
|
|
64
|
-
*
|
|
65
|
-
* ```html
|
|
66
|
-
* <helix-button commandfor="confirm" command="--show">Delete…</helix-button>
|
|
67
|
-
* ```
|
|
68
|
-
*
|
|
69
|
-
* `commandFor` spells its attribute out, because Lit lowercases a property
|
|
70
|
-
* name to derive one rather than dash-casing it, and what a consumer writes
|
|
71
|
-
* is the platform's `commandfor`.
|
|
72
|
-
*
|
|
73
|
-
* @reference https://html.spec.whatwg.org/multipage/form-elements.html#attr-button-command Invoker Commands
|
|
74
|
-
*/
|
|
75
16
|
this.command = '';
|
|
76
|
-
/**
|
|
77
|
-
* The id of the element the command is sent to.
|
|
78
|
-
*
|
|
79
|
-
* @reference https://html.spec.whatwg.org/multipage/form-elements.html#attr-button-commandfor Invoker Commands
|
|
80
|
-
*/
|
|
81
17
|
this.commandFor = '';
|
|
82
|
-
/**
|
|
83
|
-
* Whether the button holds its group's tab stop.
|
|
84
|
-
*
|
|
85
|
-
* A toolbar keeps one tab stop across its children, and the obvious way to do
|
|
86
|
-
* that is to write `tabindex` onto each of them. That is a change to markup
|
|
87
|
-
* the consumer rendered, so a React consumer's hydration reports it and does
|
|
88
|
-
* not patch it up. A parent sets this property instead, which writes nothing,
|
|
89
|
-
* and the stop lands on the inner control inside the shadow root.
|
|
90
|
-
*
|
|
91
|
-
* No attribute, because it is coordination between elements rather than
|
|
92
|
-
* anything a consumer sets.
|
|
93
|
-
*/
|
|
94
18
|
this.focusable = true;
|
|
95
|
-
/**
|
|
96
|
-
* Where this button's submission goes, overriding the form's own.
|
|
97
|
-
*
|
|
98
|
-
* **The five `form*` overrides work by writing the form and putting it back**,
|
|
99
|
-
* which is the only mechanism available and is closer to the platform than it
|
|
100
|
-
* sounds. `requestSubmit(submitter)` is how a native submitter carries these,
|
|
101
|
-
* and it refuses ours twice over: the inner `<button>` is `type="button"` so
|
|
102
|
-
* it is not a submit button, and it sits in this shadow root so its form owner
|
|
103
|
-
* is not the consumer's form. Both throw. So the submission is started with no
|
|
104
|
-
* submitter and the overrides are applied to the form for the duration of it.
|
|
105
|
-
*
|
|
106
|
-
* The whole submit algorithm runs synchronously inside `requestSubmit()`, so
|
|
107
|
-
* the window is one call and a `finally` closes it whether or not a listener
|
|
108
|
-
* refused. What a `submit` handler observes during that window is the form a
|
|
109
|
-
* native submitter would have given it, which is the point.
|
|
110
|
-
*/
|
|
111
19
|
this.formAction = '';
|
|
112
|
-
/**
|
|
113
|
-
* How the submission is encoded, overriding the form's own.
|
|
114
|
-
*/
|
|
115
20
|
this.formEnctype = '';
|
|
116
|
-
/**
|
|
117
|
-
* Which HTTP method the submission uses, overriding the form's own.
|
|
118
|
-
*/
|
|
119
21
|
this.formMethod = '';
|
|
120
|
-
/**
|
|
121
|
-
* Whether this submission skips the form's validation.
|
|
122
|
-
*/
|
|
123
22
|
this.formNoValidate = false;
|
|
124
|
-
/**
|
|
125
|
-
* Which browsing context the response lands in, overriding the form's own.
|
|
126
|
-
*/
|
|
127
23
|
this.formTarget = '';
|
|
128
|
-
/**
|
|
129
|
-
* Accessible name, for a button whose content is not text.
|
|
130
|
-
*
|
|
131
|
-
* Lives here rather than only on `IconButton` so the two share one render.
|
|
132
|
-
* An icon-only button without it is a control a screen reader announces as
|
|
133
|
-
* "button" and nothing else.
|
|
134
|
-
*/
|
|
135
24
|
this.label = '';
|
|
136
25
|
this.handleClick = () => {
|
|
137
|
-
/* **Re-linked here, and this is the line that makes a trigger reliable.**
|
|
138
|
-
*
|
|
139
|
-
* From `updated` alone the target resolves once, whenever the button last
|
|
140
|
-
* rendered, and never again. A button whose properties do not change does
|
|
141
|
-
* not update again, so one whose target had not been inserted yet stays
|
|
142
|
-
* wired to nothing for good. Measured: `commandForElement` read null with
|
|
143
|
-
* the target absent, null again after the target was inserted, and resolved
|
|
144
|
-
* the instant an unrelated property forced an update. Whether a trigger
|
|
145
|
-
* works then comes down to insertion order, which is why it survives a full
|
|
146
|
-
* reload and not a client navigation, where the same markup arrives in
|
|
147
|
-
* pieces.
|
|
148
|
-
*
|
|
149
|
-
* `sendCommand` resolves through `commandTarget` at activation, so a browser
|
|
150
|
-
* *without* invokers was never affected and one with them was.
|
|
151
|
-
*
|
|
152
|
-
* A click listener is early enough, and that is measured rather than read
|
|
153
|
-
* off the specification: activation behavior runs after the click has
|
|
154
|
-
* finished dispatching, so a handler that assigns `commandForElement` sets
|
|
155
|
-
* it before the browser looks. Probed on a bare `<button command>` whose
|
|
156
|
-
* target was assigned from its own click listener, the dialog opened, and
|
|
157
|
-
* the control that assigned nothing did not.
|
|
158
|
-
*
|
|
159
|
-
* `updated` keeps its call, because the common case is a target that was
|
|
160
|
-
* there all along and `commandForElement` should be readable before anybody
|
|
161
|
-
* clicks anything. This is the guarantee, not the mechanism. */
|
|
162
26
|
this.linkCommand();
|
|
163
|
-
// Before the type check, which returns for `button`, the default and the
|
|
164
|
-
// type every invoker in this library uses.
|
|
165
27
|
this.sendCommand();
|
|
166
28
|
if (this.type === 'button') {
|
|
167
29
|
return;
|
|
@@ -177,47 +39,13 @@ let HelixHeadlessButton = class HelixHeadlessButton extends HelixFormElement {
|
|
|
177
39
|
form.reset();
|
|
178
40
|
}
|
|
179
41
|
};
|
|
180
|
-
/**
|
|
181
|
-
* What activating the button does to its form.
|
|
182
|
-
*/
|
|
183
42
|
this.type = 'button';
|
|
184
|
-
/**
|
|
185
|
-
* What this button submits under its own `name`, the way a native submitter does.
|
|
186
|
-
*
|
|
187
|
-
* **Reported for exactly the submission this button starts and not otherwise.**
|
|
188
|
-
* A native submit button contributes `name=value` only when it is the one
|
|
189
|
-
* that submitted, and every other button on the form contributes nothing. A
|
|
190
|
-
* form-associated element reporting a value continuously would contribute on
|
|
191
|
-
* every submission whoever started it, which is a different feature wearing
|
|
192
|
-
* the same name, so `submit` below sets it, submits, and clears it inside one
|
|
193
|
-
* synchronous call.
|
|
194
|
-
*
|
|
195
|
-
* `CopyButton` has a `value` of its own meaning the text to put on the
|
|
196
|
-
* clipboard, which is not this and is why it is declared there rather than
|
|
197
|
-
* inherited.
|
|
198
|
-
*/
|
|
199
43
|
this.value = '';
|
|
200
44
|
}
|
|
201
|
-
/** Moves focus to the inner control, so the host is never the focus target. */
|
|
202
45
|
static { this.shadowRootOptions = {
|
|
203
46
|
...HelixFormElement.shadowRootOptions,
|
|
204
47
|
delegatesFocus: true,
|
|
205
48
|
}; }
|
|
206
|
-
/**
|
|
207
|
-
* Submits the form as this button, for the length of one call.
|
|
208
|
-
*
|
|
209
|
-
* Everything a native submitter carries is written onto the form, the
|
|
210
|
-
* submission is started, and everything is put back. See `formAction` for why
|
|
211
|
-
* `requestSubmit(submitter)` is not available to a control inside a shadow
|
|
212
|
-
* root.
|
|
213
|
-
*
|
|
214
|
-
* `novalidate` is in the same list rather than reached by `form.submit()`,
|
|
215
|
-
* which looks like the shortcut and is not: `submit()` skips the `submit`
|
|
216
|
-
* event as well as the validation, so a consumer's handler would stop running
|
|
217
|
-
* the moment somebody wrote `formnovalidate`. Turning the flag on and calling
|
|
218
|
-
* `requestSubmit()` skips the validation alone, which is what the attribute
|
|
219
|
-
* means.
|
|
220
|
-
*/
|
|
221
49
|
submit(form) {
|
|
222
50
|
const undo = [];
|
|
223
51
|
const override = (attribute, value) => {
|
|
@@ -256,15 +84,6 @@ let HelixHeadlessButton = class HelixHeadlessButton extends HelixFormElement {
|
|
|
256
84
|
}
|
|
257
85
|
}
|
|
258
86
|
}
|
|
259
|
-
/**
|
|
260
|
-
* The element this button's command is sent to, resolved.
|
|
261
|
-
*
|
|
262
|
-
* The platform's own accessor, because the resolution is otherwise kept
|
|
263
|
-
* inside: `linkCommand` writes the target onto the inner `<button>`, where a
|
|
264
|
-
* consumer cannot reach it, and `commandFor` answers with an id rather than
|
|
265
|
-
* an element. Writing assigns the inner control directly, which is what an
|
|
266
|
-
* invoker without an id in the same tree needs.
|
|
267
|
-
*/
|
|
268
87
|
get commandForElement() {
|
|
269
88
|
const button = this.renderRoot?.querySelector('button');
|
|
270
89
|
return button?.commandForElement ?? this.commandTarget();
|
|
@@ -275,20 +94,6 @@ let HelixHeadlessButton = class HelixHeadlessButton extends HelixFormElement {
|
|
|
275
94
|
button.commandForElement = target;
|
|
276
95
|
}
|
|
277
96
|
}
|
|
278
|
-
/**
|
|
279
|
-
* Points the inner button at the command's target, by reference.
|
|
280
|
-
*
|
|
281
|
-
* **The attribute cannot do this and it fails silently.** `commandfor` is an
|
|
282
|
-
* IDREF, and an IDREF resolves inside the tree it is written in: forwarded
|
|
283
|
-
* onto a button in this shadow root it looks for the id *here*, finds
|
|
284
|
-
* nothing, and the trigger does nothing at all. Measured before this existed:
|
|
285
|
-
* the button clicked, no `command` event was dispatched, and there was
|
|
286
|
-
* nothing anywhere to say why.
|
|
287
|
-
*
|
|
288
|
-
* `commandForElement` takes an element instead, and an element reference
|
|
289
|
-
* resolves outward: a node in a shadow root may point at one in the document.
|
|
290
|
-
* The same asymmetry the listbox relies on for `aria-activedescendant`.
|
|
291
|
-
*/
|
|
292
97
|
linkCommand() {
|
|
293
98
|
const button = this.renderRoot?.querySelector('button');
|
|
294
99
|
if (!button || !supportsInvokers()) {
|
|
@@ -298,30 +103,10 @@ let HelixHeadlessButton = class HelixHeadlessButton extends HelixFormElement {
|
|
|
298
103
|
button.commandForElement =
|
|
299
104
|
this.commandTarget();
|
|
300
105
|
}
|
|
301
|
-
/**
|
|
302
|
-
* The element this button commands, or nothing.
|
|
303
|
-
*
|
|
304
|
-
* Looked up in the button's own root rather than in the document, so a button
|
|
305
|
-
* inside somebody else's shadow root finds a target in that same root and not
|
|
306
|
-
* a coincidence in the page.
|
|
307
|
-
*/
|
|
308
106
|
commandTarget() {
|
|
309
107
|
const root = this.getRootNode();
|
|
310
108
|
return this.commandFor ? (root.getElementById?.(this.commandFor) ?? null) : null;
|
|
311
109
|
}
|
|
312
|
-
/**
|
|
313
|
-
* Sends the `command` event where the browser will not.
|
|
314
|
-
*
|
|
315
|
-
* Invoker commands are recent, and where they are missing `linkCommand` sets a
|
|
316
|
-
* property that does not exist and returns, so the button does nothing at all
|
|
317
|
-
* when clicked. Silently: the markup is right, the component is right, and
|
|
318
|
-
* every `command` in the library is inert at once with no error to find.
|
|
319
|
-
*
|
|
320
|
-
* Only where the platform is absent, so a browser that has invokers sends
|
|
321
|
-
* exactly one event and this sends none. Doing both would open a dialog and
|
|
322
|
-
* immediately toggle it shut again, which is a worse bug than the one this
|
|
323
|
-
* fixes and would only appear on the browsers that work.
|
|
324
|
-
*/
|
|
325
110
|
sendCommand() {
|
|
326
111
|
if (!this.command || supportsInvokers()) {
|
|
327
112
|
return;
|
|
@@ -7,39 +7,9 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
7
7
|
import { customElement, HelixElement, hostAria } from '@fusion.dev/helix-core';
|
|
8
8
|
import { html } from 'lit';
|
|
9
9
|
import { property } from 'lit/decorators.js';
|
|
10
|
-
/**
|
|
11
|
-
* @summary Groups related buttons into one control.
|
|
12
|
-
*
|
|
13
|
-
* @description
|
|
14
|
-
* The host is the group, so `role="group"` sits on it through internals and
|
|
15
|
-
* `label` names it. A group of buttons with no name is announced as a run of
|
|
16
|
-
* separate controls, which is what the grouping exists to say it is not.
|
|
17
|
-
*
|
|
18
|
-
* It manages no focus. A group is not a toolbar: the APG toolbar pattern asks
|
|
19
|
-
* for one tab stop with arrow keys inside it, and that is what Toolbar is for.
|
|
20
|
-
* Here every button keeps its own tab stop, which is what a reader expects from
|
|
21
|
-
* buttons sitting side by side.
|
|
22
|
-
*
|
|
23
|
-
* Buttons are slotted rather than wrapped, so they stay the consumer's own
|
|
24
|
-
* elements and keep whatever appearance and size they were given.
|
|
25
|
-
*
|
|
26
|
-
* @since 0.1.0
|
|
27
|
-
* @status stable
|
|
28
|
-
* @category actions
|
|
29
|
-
* @aliases Segmented buttons, Split button
|
|
30
|
-
* @documentation https://helix.fusion.dev/components/button-group
|
|
31
|
-
*
|
|
32
|
-
* @slot - The buttons in the group.
|
|
33
|
-
*/
|
|
34
10
|
let HelixHeadlessButtonGroup = class HelixHeadlessButtonGroup extends HelixElement {
|
|
35
11
|
constructor() {
|
|
36
12
|
super(...arguments);
|
|
37
|
-
/**
|
|
38
|
-
* Accessible name for the group.
|
|
39
|
-
*
|
|
40
|
-
* On the host through internals, so an author's `aria-label` still overrides
|
|
41
|
-
* it and nothing lands on the host that a consumer's markup did not have.
|
|
42
|
-
*/
|
|
43
13
|
this.label = '';
|
|
44
14
|
}
|
|
45
15
|
willUpdate(changed) {
|
|
@@ -8,59 +8,11 @@ import { customElement, HelixElement, hostAria, TONE_MARKS, toneMark } from '@fu
|
|
|
8
8
|
import { html } from 'lit';
|
|
9
9
|
import { property } from 'lit/decorators.js';
|
|
10
10
|
import { unsafeSVG } from 'lit/directives/unsafe-svg.js';
|
|
11
|
-
/**
|
|
12
|
-
* @summary Sets a passage apart as information, a warning, or an error.
|
|
13
|
-
*
|
|
14
|
-
* @description
|
|
15
|
-
* `tone` lives on the headless tier rather than being a treatment on the styled
|
|
16
|
-
* one, because it is meaning and not decoration. It picks the icon and that
|
|
17
|
-
* icon's accessible name, so a reader who cannot see that the box is red still
|
|
18
|
-
* hears "Error". A callout whose meaning existed only as a color would say
|
|
19
|
-
* nothing to them at all.
|
|
20
|
-
*
|
|
21
|
-
* The icon is the fallback content of a slot, so a consumer replaces it by
|
|
22
|
-
* slotting their own and the default disappears with no emptiness check. That
|
|
23
|
-
* is what slot fallback is for.
|
|
24
|
-
*
|
|
25
|
-
* Not an `Announcement`, which is the other thing this could be mistaken for.
|
|
26
|
-
* A callout interrupts a document to set a passage apart and is read where it
|
|
27
|
-
* sits; an announcement is page-level chrome above the masthead, announces
|
|
28
|
-
* itself as a live region, and can be dismissed. Neither is a variant of the
|
|
29
|
-
* other.
|
|
30
|
-
*
|
|
31
|
-
* @since 0.1.0
|
|
32
|
-
* @status stable
|
|
33
|
-
* @category markers
|
|
34
|
-
* @aliases Admonition, Notice, Note, Alert
|
|
35
|
-
* @documentation https://helix.fusion.dev/components/callout
|
|
36
|
-
*
|
|
37
|
-
* @slot - The callout's content.
|
|
38
|
-
* @slot icon - Replaces the icon chosen by `tone`.
|
|
39
|
-
* @slot title - A heading above the content.
|
|
40
|
-
*
|
|
41
|
-
* @csspart content - The content region.
|
|
42
|
-
* @csspart icon - The icon region.
|
|
43
|
-
* @csspart icon-mark - The tone glyph, where none is slotted.
|
|
44
|
-
*/
|
|
45
11
|
let HelixHeadlessCallout = class HelixHeadlessCallout extends HelixElement {
|
|
46
12
|
willUpdate(changed) {
|
|
47
13
|
super.willUpdate(changed);
|
|
48
|
-
// On the host, since the host is the box.
|
|
49
14
|
hostAria(this.internals, { role: 'note' });
|
|
50
15
|
}
|
|
51
|
-
/**
|
|
52
|
-
* The mark for the callout's tone, behind a seam.
|
|
53
|
-
*
|
|
54
|
-
* Slot fallback, so a consumer replaces it by slotting; and a method, so the
|
|
55
|
-
* tier above replaces it by overriding. Those answer different people: the
|
|
56
|
-
* slot is for the page and the method is for whoever is building the styled
|
|
57
|
-
* layer, and a fallback with only the first is one a tier can restyle and
|
|
58
|
-
* cannot change.
|
|
59
|
-
*
|
|
60
|
-
* The mark and its label are passed in rather than looked up again, since
|
|
61
|
-
* `render` has already resolved the tone and a second lookup is a second
|
|
62
|
-
* place for the fallback to disagree with the tone around it.
|
|
63
|
-
*/
|
|
64
16
|
renderIcon(icon, label) {
|
|
65
17
|
return html `<slot name="icon">
|
|
66
18
|
<svg part="icon-mark" fill="currentColor" viewBox=${icon.viewBox} role="img" aria-label=${label}>
|
|
@@ -69,10 +21,6 @@ let HelixHeadlessCallout = class HelixHeadlessCallout extends HelixElement {
|
|
|
69
21
|
</slot>`;
|
|
70
22
|
}
|
|
71
23
|
render() {
|
|
72
|
-
/* Falls back to `info`, because a callout exists to set a passage apart and
|
|
73
|
-
is therefore always *about* something. `Toast.Item` takes the same lookup
|
|
74
|
-
and draws nothing instead, since a message is just the next thing that
|
|
75
|
-
happened. Same vocabulary, two honest answers to an absent tone. */
|
|
76
24
|
const { icon, label } = toneMark(this.tone) ?? TONE_MARKS.info;
|
|
77
25
|
return html `<span part="icon">${this.renderIcon(icon, label)}</span>
|
|
78
26
|
<div part="content">
|
|
@@ -7,53 +7,12 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
7
7
|
import { customElement, HelixElement, hostAria } from '@fusion.dev/helix-core';
|
|
8
8
|
import { html } from 'lit';
|
|
9
9
|
import { property } from 'lit/decorators.js';
|
|
10
|
-
/**
|
|
11
|
-
* @summary Groups related content and actions into a single surface.
|
|
12
|
-
*
|
|
13
|
-
* @description
|
|
14
|
-
* Regions are slots rather than separate elements, because a header, body, and
|
|
15
|
-
* footer carry no state, no behavior, and no API of their own. Slotting them
|
|
16
|
-
* costs one custom element instead of five and lets the consumer bring their
|
|
17
|
-
* own markup, so a title can be a real `<h2>` rather than an emulated one.
|
|
18
|
-
*
|
|
19
|
-
* The host is the surface. A wrapper `<div part="container">` would be a second
|
|
20
|
-
* box doing nothing the host cannot: the element itself takes the border, the
|
|
21
|
-
* radius, and the grid, and a consumer styles `helix-card` rather than reaching
|
|
22
|
-
* through `::part`. One node instead of two, on every card on the page.
|
|
23
|
-
*
|
|
24
|
-
* Name the card with `aria-label` or `aria-labelledby`. That is the only
|
|
25
|
-
* naming that survives server rendering: reading a title out of the light DOM
|
|
26
|
-
* needs a DOM, and the server has none.
|
|
27
|
-
*
|
|
28
|
-
* @since 0.1.0
|
|
29
|
-
* @status stable
|
|
30
|
-
* @category layout
|
|
31
|
-
* @aliases Panel, Surface
|
|
32
|
-
* @documentation https://helix.fusion.dev/components/card
|
|
33
|
-
*
|
|
34
|
-
* @slot - The card's main content.
|
|
35
|
-
* @slot footer - Actions that conclude the card.
|
|
36
|
-
* @slot header - Title and any actions for the whole card.
|
|
37
|
-
* @slot media - Image, video, or chart that runs edge to edge, outside the inset.
|
|
38
|
-
*/
|
|
39
10
|
let HelixHeadlessCard = class HelixHeadlessCard extends HelixElement {
|
|
40
11
|
willUpdate(changed) {
|
|
41
12
|
super.willUpdate(changed);
|
|
42
|
-
// On the host, since the host is the box. Set here rather than in
|
|
43
|
-
// `connectedCallback`, which the server never calls.
|
|
44
13
|
hostAria(this.internals, { role: 'group' });
|
|
45
14
|
}
|
|
46
15
|
render() {
|
|
47
|
-
// Bare slots, with no wrapper element around any of them. A `<slot>` is
|
|
48
|
-
// `display: contents` in the UA stylesheet, so an unassigned one generates
|
|
49
|
-
// no box at all: no grid row, no gap, nothing. A wrapper generates one
|
|
50
|
-
// whether or not anything is slotted into it, so an omitted region would
|
|
51
|
-
// paint an empty inset band.
|
|
52
|
-
//
|
|
53
|
-
// It has to be layout rather than detection, because the detection does not
|
|
54
|
-
// exist. `:host(:has([slot='header']))` is invalid CSS, so the whole rule is
|
|
55
|
-
// dropped and the region stays hidden even when filled, and the component
|
|
56
|
-
// cannot inspect its children on the server to do it in JavaScript.
|
|
57
16
|
return html `
|
|
58
17
|
<slot name="media"></slot>
|
|
59
18
|
<slot name="header"></slot>
|
|
@@ -341,8 +341,8 @@ export declare class HelixHeadlessCarousel extends HelixElement {
|
|
|
341
341
|
* an assignment changes, and under `slotAssignment: 'manual'` the only thing
|
|
342
342
|
* that changes an assignment is this component calling `assign`, so a slide
|
|
343
343
|
* appended by a consumer would reach no slot, render nowhere and be counted by
|
|
344
|
-
* nothing.
|
|
345
|
-
*
|
|
344
|
+
* nothing. A default slot needs no watcher, taking whatever is there; a manual
|
|
345
|
+
* one does. `Chat` watches for the same reason.
|
|
346
346
|
*/
|
|
347
347
|
private watcher;
|
|
348
348
|
connectedCallback(): void;
|