@fusion.dev/helix-headless 0.147.0 → 0.148.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 +4512 -790
- package/dist/accordion/item/accordion-item.d.ts +5 -2
- package/dist/accordion/item/accordion-item.js +11 -7
- package/dist/accordion/root/accordion-root.js +4 -6
- package/dist/announcement/root/announcement-root.d.ts +56 -5
- package/dist/announcement/root/announcement-root.js +124 -21
- package/dist/area-chart/root/area-chart-root.js +3 -4
- package/dist/badge/count.d.ts +14 -0
- package/dist/badge/count.js +20 -0
- package/dist/badge/root/badge-root.js +8 -22
- package/dist/blockquote/root/blockquote-root.d.ts +66 -8
- package/dist/blockquote/root/blockquote-root.js +85 -8
- package/dist/breadcrumbs/breadcrumb/breadcrumb.js +2 -2
- package/dist/button-group/arrows.d.ts +14 -0
- package/dist/button-group/arrows.js +10 -0
- package/dist/button-group/root/button-group-root.d.ts +141 -3
- package/dist/button-group/root/button-group-root.js +142 -2
- package/dist/canvas/edge-pan.d.ts +61 -0
- package/dist/canvas/edge-pan.js +63 -0
- package/dist/canvas/node/canvas-node.d.ts +12 -2
- package/dist/canvas/node/canvas-node.js +6 -2
- package/dist/canvas/root/canvas-document.d.ts +10 -0
- package/dist/canvas/root/canvas-document.js +1 -0
- package/dist/canvas/root/canvas-root.d.ts +107 -5
- package/dist/canvas/root/canvas-root.js +140 -36
- package/dist/canvas/snap.d.ts +42 -0
- package/dist/canvas/snap.js +32 -0
- package/dist/carousel/root/carousel-root.d.ts +4 -3
- package/dist/carousel/root/carousel-root.js +2 -6
- package/dist/chat/root/chat-root.d.ts +13 -3
- package/dist/chat/root/chat-root.js +2 -6
- package/dist/checkbox-group/root/checkbox-group-root.d.ts +13 -0
- package/dist/checkbox-group/root/checkbox-group-root.js +18 -10
- package/dist/checkbox-group/select-all.d.ts +11 -0
- package/dist/checkbox-group/select-all.js +5 -0
- package/dist/code-block/root/code-block-root.d.ts +8 -6
- package/dist/code-block/root/code-block-root.js +4 -3
- package/dist/color-picker/root/color-picker-root.js +6 -12
- package/dist/color-picker/wheel.d.ts +11 -0
- package/dist/color-picker/wheel.js +9 -0
- package/dist/combobox/root/combobox-root.d.ts +70 -11
- package/dist/combobox/root/combobox-root.js +68 -18
- package/dist/command/root/command-root.d.ts +4 -3
- package/dist/data-table/root/data-table-root.js +17 -44
- package/dist/description-list/item/description-list-item.d.ts +4 -0
- package/dist/description-list/item/description-list-item.js +7 -1
- package/dist/divider/root/divider-root.d.ts +45 -16
- package/dist/divider/root/divider-root.js +28 -4
- package/dist/drilldown/item/drilldown-item.js +3 -4
- package/dist/element-events.generated.js +1 -0
- package/dist/empty/root/empty-root.d.ts +17 -3
- package/dist/empty/root/empty-root.js +29 -5
- package/dist/file-input/root/file-input-root.js +3 -4
- package/dist/file-input/status.js +2 -1
- package/dist/filter-input/chip/filter-chip.js +2 -3
- package/dist/format-date/fields.d.ts +20 -0
- package/dist/format-date/fields.js +14 -0
- package/dist/format-date/options.d.ts +2 -1
- package/dist/format-date/options.js +9 -38
- package/dist/format-date/root/format-date-root.d.ts +35 -3
- package/dist/format-date/root/format-date-root.js +24 -32
- package/dist/graph/root/graph-root.d.ts +23 -5
- package/dist/graph/root/graph-root.js +17 -20
- package/dist/heatmap/root/heatmap-root.d.ts +12 -0
- package/dist/heatmap/root/heatmap-root.js +26 -11
- package/dist/image/root/image-root.d.ts +11 -0
- package/dist/kbd/root/kbd-root.js +1 -2
- package/dist/legend/root/legend-root.js +4 -17
- package/dist/list/root/list-root.js +8 -22
- package/dist/listbox/root/listbox-root.d.ts +119 -24
- package/dist/listbox/root/listbox-root.js +130 -132
- package/dist/map/marker/map-marker.d.ts +2 -1
- package/dist/map/root/map-root.d.ts +40 -2
- package/dist/map/root/map-root.js +64 -23
- package/dist/megamenu/root/megamenu-root.js +4 -11
- package/dist/menu/root/menu-root.d.ts +2 -2
- package/dist/navigation/root/navigation-root.js +5 -8
- package/dist/notifications/group/notification-group.d.ts +8 -1
- package/dist/notifications/group/notification-group.js +12 -6
- package/dist/notifications/notification/notification.d.ts +12 -1
- package/dist/notifications/notification/notification.js +3 -1
- package/dist/notifications/root/notifications-root.d.ts +41 -5
- package/dist/notifications/root/notifications-root.js +27 -7
- package/dist/object-inspector/root/object-inspector-root.d.ts +5 -1
- package/dist/object-inspector/root/object-inspector-root.js +21 -11
- package/dist/pagination/size/pagination-size.d.ts +1 -0
- package/dist/pagination/size/pagination-size.js +1 -1
- package/dist/pin-input/code.d.ts +23 -0
- package/dist/pin-input/code.js +25 -0
- package/dist/pin-input/root/pin-input-root.js +9 -23
- package/dist/qr-code/root/qr-code-root.d.ts +14 -3
- package/dist/qr-code/root/qr-code-root.js +19 -5
- package/dist/scatter-chart/measures.d.ts +22 -0
- package/dist/scatter-chart/measures.js +11 -0
- package/dist/scatter-chart/root/scatter-chart-root.d.ts +2 -6
- package/dist/scatter-chart/root/scatter-chart-root.js +6 -19
- package/dist/select/root/select-root.d.ts +56 -8
- package/dist/select/root/select-root.js +33 -9
- package/dist/speed-dial/group/speed-dial-group.d.ts +48 -0
- package/dist/speed-dial/group/speed-dial-group.js +43 -0
- package/dist/speed-dial/index.d.ts +1 -0
- package/dist/speed-dial/index.js +1 -0
- package/dist/speed-dial/item/speed-dial-item.d.ts +26 -1
- package/dist/speed-dial/item/speed-dial-item.js +41 -3
- package/dist/speed-dial/long-press.d.ts +50 -0
- package/dist/speed-dial/long-press.js +48 -0
- package/dist/speed-dial/root/speed-dial-root.d.ts +80 -1
- package/dist/speed-dial/root/speed-dial-root.js +114 -5
- package/dist/splitter/root/splitter-root.d.ts +7 -3
- package/dist/splitter/root/splitter-root.js +16 -3
- package/dist/table/cell/table-cell.d.ts +8 -1
- package/dist/table/cell/table-cell.js +6 -2
- package/dist/table/grid-controller.d.ts +117 -0
- package/dist/table/grid-controller.js +293 -0
- package/dist/table/root/table-root.d.ts +47 -3
- package/dist/table/root/table-root.js +81 -24
- package/dist/table/row/table-row.d.ts +13 -1
- package/dist/table/row/table-row.js +20 -6
- package/dist/tag/group/tag-group.js +6 -15
- package/dist/time-picker/root/time-picker-root.d.ts +1 -0
- package/dist/time-picker/root/time-picker-root.js +3 -0
- package/dist/toast/item/toast-item.d.ts +6 -3
- package/dist/toast/item/toast-item.js +1 -0
- package/dist/toast/root/toast-root.d.ts +13 -1
- package/dist/toast/root/toast-root.js +20 -14
- package/dist/toggle/group/toggle-group.js +3 -5
- package/dist/toolbar/root/toolbar-root.d.ts +31 -0
- package/dist/toolbar/root/toolbar-root.js +39 -8
- package/dist/tooltip/root/tooltip-root.js +1 -2
- package/dist/video/root/video-root.d.ts +11 -0
- package/dist/video/root/video-root.js +15 -1
- package/dist/virtual-list/root/virtual-list-root.js +6 -18
- package/lint.json +1 -1
- package/package.json +5 -5
- package/types/jsx.d.ts +2005 -266
- package/types/svelte.d.ts +1 -0
- package/types/vue.d.ts +338 -62
- package/dist/kbd/keys.d.ts +0 -29
- package/dist/kbd/keys.js +0 -52
- package/dist/tooltip/focus-target.d.ts +0 -6
- package/dist/tooltip/focus-target.js +0 -9
|
@@ -148,8 +148,11 @@ export declare class HelixHeadlessAccordionItem extends HelixHeadlessAccordionIt
|
|
|
148
148
|
/**
|
|
149
149
|
* Opens the section find in page has matched in, before the match is shown.
|
|
150
150
|
*
|
|
151
|
-
*
|
|
152
|
-
*
|
|
151
|
+
* A refusal puts `hidden` back, so the trigger and the region still agree.
|
|
152
|
+
* The browser removes the attribute after this event's listeners have
|
|
153
|
+
* returned, and after the microtasks they queued, so writing it here or in a
|
|
154
|
+
* microtask is undone at once. A mutation observer hears the removal itself
|
|
155
|
+
* and runs before the next paint or key press.
|
|
153
156
|
*/
|
|
154
157
|
private readonly handleBeforeMatch;
|
|
155
158
|
/** Whether the item was open at the last update, and `undefined` before one. */
|
|
@@ -4,7 +4,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
4
4
|
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
5
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
6
|
};
|
|
7
|
-
import { closestInstance, customElement, HelixElement, headingLevel, MixinControllerOpen, onUnlessFalse, } from '@fusion.dev/helix-core';
|
|
7
|
+
import { animationsFinished, closestInstance, customElement, HelixElement, headingLevel, MixinControllerOpen, onUnlessFalse, } from '@fusion.dev/helix-core';
|
|
8
8
|
import { caretDown } from '@fusion.dev/helix-icons';
|
|
9
9
|
import { html, nothing } from 'lit';
|
|
10
10
|
import { property, state } from 'lit/decorators.js';
|
|
@@ -23,8 +23,14 @@ let HelixHeadlessAccordionItem = class HelixHeadlessAccordionItem extends MixinC
|
|
|
23
23
|
if (this.open || this.requestOpen(true)) {
|
|
24
24
|
return;
|
|
25
25
|
}
|
|
26
|
-
;
|
|
27
|
-
|
|
26
|
+
const content = event.currentTarget;
|
|
27
|
+
const observer = new MutationObserver(() => {
|
|
28
|
+
observer.disconnect();
|
|
29
|
+
if (!this.open && !this.closing && !content.hasAttribute('hidden')) {
|
|
30
|
+
content.setAttribute('hidden', 'until-found');
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
observer.observe(content, { attributeFilter: ['hidden'] });
|
|
28
34
|
};
|
|
29
35
|
this.wasOpen = undefined;
|
|
30
36
|
this.handleToggle = () => {
|
|
@@ -72,13 +78,11 @@ let HelixHeadlessAccordionItem = class HelixHeadlessAccordionItem extends MixinC
|
|
|
72
78
|
}
|
|
73
79
|
}
|
|
74
80
|
finishClosing() {
|
|
75
|
-
|
|
76
|
-
const done = () => {
|
|
81
|
+
void animationsFinished(this.partNamed('content')).then(() => {
|
|
77
82
|
if (!this.open) {
|
|
78
83
|
this.closing = false;
|
|
79
84
|
}
|
|
80
|
-
};
|
|
81
|
-
Promise.all(running.map((animation) => animation.finished)).then(done, done);
|
|
85
|
+
});
|
|
82
86
|
}
|
|
83
87
|
get accordion() {
|
|
84
88
|
return closestInstance(this, HelixHeadlessAccordion);
|
|
@@ -4,16 +4,14 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
4
4
|
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
5
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
6
|
};
|
|
7
|
-
import { customElement, HelixElement, KeyboardNavigationController, onUnlessFalse, whenHydrated, } from '@fusion.dev/helix-core';
|
|
7
|
+
import { customElement, focusItemOf, HelixElement, KeyboardNavigationController, onUnlessFalse, whenHydrated, } from '@fusion.dev/helix-core';
|
|
8
8
|
import { html } from 'lit';
|
|
9
9
|
import { property } from 'lit/decorators.js';
|
|
10
10
|
import { HelixHeadlessAccordionItem } from '../item/accordion-item.js';
|
|
11
|
-
const navigable = (item) => ({
|
|
12
|
-
contains: (node) => node === item,
|
|
11
|
+
const navigable = (item) => focusItemOf(item, {
|
|
13
12
|
disabled: item.disabled,
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
selected: false,
|
|
13
|
+
focus: () => item.focusTrigger(),
|
|
14
|
+
owns: (node) => node === item,
|
|
17
15
|
});
|
|
18
16
|
let HelixHeadlessAccordion = class HelixHeadlessAccordion extends HelixElement {
|
|
19
17
|
constructor() {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ArrivalController, HelixElement } from '@fusion.dev/helix-core';
|
|
2
|
-
import { type PropertyValues, type TemplateResult } from 'lit';
|
|
2
|
+
import { nothing, type PropertyValues, type TemplateResult } from 'lit';
|
|
3
3
|
declare const HelixHeadlessAnnouncement_base: (new (...args: any[]) => import("@fusion.dev/helix-core").DismissibleElement) & typeof HelixElement;
|
|
4
4
|
/**
|
|
5
5
|
* @summary A message for the whole page, shown above its header, that can be dismissed.
|
|
@@ -29,6 +29,23 @@ declare const HelixHeadlessAnnouncement_base: (new (...args: any[]) => import("@
|
|
|
29
29
|
* announcement beside it, move focus yourself, for example to the page's main
|
|
30
30
|
* heading.
|
|
31
31
|
*
|
|
32
|
+
* **The announcement is a landmark region**, named by `label`, "Announcement"
|
|
33
|
+
* unless you set it, so a screen reader lists it among the page's regions.
|
|
34
|
+
* `hotkey`, F6 unless you set it, moves focus to the announcement's first
|
|
35
|
+
* control, an action or the dismiss button, and pressed there moves focus
|
|
36
|
+
* back to where it was, as React Aria's toast region does. An announcement
|
|
37
|
+
* with no control leaves the key to the browser. When an announcement that
|
|
38
|
+
* the key moved focus into is removed with focus still inside, focus goes
|
|
39
|
+
* back to where it was before the key.
|
|
40
|
+
*
|
|
41
|
+
* **Set `queued` to show several announcements one at a time.** A queued
|
|
42
|
+
* announcement waits while a queued announcement before it in the same parent
|
|
43
|
+
* is still there, and shows when that one is removed: its message is then
|
|
44
|
+
* read out, and its `duration` starts. While it waits it renders nothing,
|
|
45
|
+
* is no region, and carries `:state(waiting)`. A server cannot see the
|
|
46
|
+
* announcements beside it, so a served page shows every one until the script
|
|
47
|
+
* runs.
|
|
48
|
+
*
|
|
32
49
|
* Set `duration` to dismiss it after a time. The clock stops while the pointer
|
|
33
50
|
* or focus is on the announcement, and continues from what was left, so a
|
|
34
51
|
* reader partway through is not cut off. It stops while the page is hidden too,
|
|
@@ -57,6 +74,7 @@ declare const HelixHeadlessAnnouncement_base: (new (...args: any[]) => import("@
|
|
|
57
74
|
* @csspart live - A copy of the message in a live region, read by screen readers when the announcement appears after the page has loaded, and emptied seven seconds later. Visually hidden in the styled tier.
|
|
58
75
|
*
|
|
59
76
|
* @cssstate paused - The clock `duration` started is stopped, while the pointer or focus is on the announcement or the page is hidden.
|
|
77
|
+
* @cssstate waiting - The announcement is `queued` behind another, and renders nothing until that one is removed.
|
|
60
78
|
*/
|
|
61
79
|
export declare class HelixHeadlessAnnouncement extends HelixHeadlessAnnouncement_base {
|
|
62
80
|
/**
|
|
@@ -67,6 +85,28 @@ export declare class HelixHeadlessAnnouncement extends HelixHeadlessAnnouncement
|
|
|
67
85
|
* own. The clock stops while the pointer or focus is on the announcement.
|
|
68
86
|
*/
|
|
69
87
|
duration?: number;
|
|
88
|
+
/**
|
|
89
|
+
* The key that moves focus to the announcement's first control, and back
|
|
90
|
+
* to where it was, written as keys joined by `+`, such as `F6` or `Alt+A`.
|
|
91
|
+
*
|
|
92
|
+
* F6 by default, the key React Aria's regions answer to. Empty turns it
|
|
93
|
+
* off. With several announcements, the first one in the page takes focus.
|
|
94
|
+
*/
|
|
95
|
+
hotkey: string;
|
|
96
|
+
/**
|
|
97
|
+
* The name of the announcement's region, which screen readers list among
|
|
98
|
+
* the page's landmarks. Translate it with the rest of your interface.
|
|
99
|
+
*
|
|
100
|
+
* Empty leaves the announcement no region.
|
|
101
|
+
*/
|
|
102
|
+
label: string;
|
|
103
|
+
/**
|
|
104
|
+
* Whether the announcement waits its turn behind the queued announcements
|
|
105
|
+
* before it in the same parent, showing when the last of them is removed.
|
|
106
|
+
*
|
|
107
|
+
* Off by default, so every announcement on a page shows at once.
|
|
108
|
+
*/
|
|
109
|
+
queued: boolean;
|
|
70
110
|
/**
|
|
71
111
|
* The band's color.
|
|
72
112
|
*
|
|
@@ -90,18 +130,29 @@ export declare class HelixHeadlessAnnouncement extends HelixHeadlessAnnouncement
|
|
|
90
130
|
* axes, which is the test the registry answers to.
|
|
91
131
|
*/
|
|
92
132
|
tone?: 'accent' | 'danger' | 'info' | 'neutral' | 'primary' | 'success' | 'warning';
|
|
133
|
+
/** Whether a queued announcement before this one is still there. */
|
|
134
|
+
private waiting;
|
|
135
|
+
/** Watches the parent for the announcements before this one leaving. */
|
|
136
|
+
private queue;
|
|
137
|
+
/** Whether a queued announcement comes before this one in its parent. */
|
|
138
|
+
private behind;
|
|
93
139
|
protected readonly arrival: ArrivalController;
|
|
94
140
|
/** The time `duration` gives it, held while a reader is on it. */
|
|
95
141
|
private readonly countdown;
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
private
|
|
142
|
+
private readonly scope;
|
|
143
|
+
/** Where focus was when `hotkey` moved it in. */
|
|
144
|
+
private origin;
|
|
145
|
+
/** Whether focus is inside the announcement, kept as it moves. */
|
|
146
|
+
private focused;
|
|
147
|
+
private readonly handleHotkey;
|
|
148
|
+
protected willUpdate(changed: PropertyValues): void;
|
|
99
149
|
private readonly handleHold;
|
|
100
150
|
private readonly handleRelease;
|
|
101
151
|
connectedCallback(): void;
|
|
102
152
|
disconnectedCallback(): void;
|
|
103
153
|
protected firstUpdated(changed: PropertyValues): void;
|
|
104
154
|
protected updated(changed: PropertyValues): void;
|
|
155
|
+
private watchQueue;
|
|
105
156
|
/**
|
|
106
157
|
* Says on the `actions` region whether anything is in it.
|
|
107
158
|
*
|
|
@@ -126,7 +177,7 @@ export declare class HelixHeadlessAnnouncement extends HelixHeadlessAnnouncement
|
|
|
126
177
|
* draws a tone mark. An announcement has no tone mark at all.
|
|
127
178
|
*/
|
|
128
179
|
protected renderDismissIcon(): TemplateResult;
|
|
129
|
-
protected render(): TemplateResult;
|
|
180
|
+
protected render(): TemplateResult | typeof nothing;
|
|
130
181
|
}
|
|
131
182
|
declare global {
|
|
132
183
|
interface HTMLElementTagNameMap {
|
|
@@ -4,54 +4,109 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
4
4
|
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
5
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
6
|
};
|
|
7
|
-
|
|
7
|
+
var HelixHeadlessAnnouncement_1;
|
|
8
|
+
import { ArrivalController, ariaChord, CountdownController, customElement, deepActiveElement, FocusScopeController, HelixElement, hostAria, MixinDismissible, markRegion, matchesChord, onApple, reportRuntime, slottedWords, } from '@fusion.dev/helix-core';
|
|
8
9
|
import { x } from '@fusion.dev/helix-icons';
|
|
9
10
|
import { html, nothing } from 'lit';
|
|
10
|
-
import { property } from 'lit/decorators.js';
|
|
11
|
+
import { property, state } from 'lit/decorators.js';
|
|
11
12
|
import { unsafeSVG } from 'lit/directives/unsafe-svg.js';
|
|
12
|
-
let HelixHeadlessAnnouncement = class HelixHeadlessAnnouncement extends MixinDismissible(HelixElement) {
|
|
13
|
+
let HelixHeadlessAnnouncement = HelixHeadlessAnnouncement_1 = class HelixHeadlessAnnouncement extends MixinDismissible(HelixElement) {
|
|
13
14
|
constructor() {
|
|
14
15
|
super(...arguments);
|
|
16
|
+
this.hotkey = 'F6';
|
|
17
|
+
this.label = 'Announcement';
|
|
18
|
+
this.queued = false;
|
|
19
|
+
this.waiting = false;
|
|
15
20
|
this.arrival = new ArrivalController(this, {
|
|
16
21
|
arriving: () => !this.serverRendered && document.readyState === 'complete',
|
|
17
|
-
text: () => slottedWords(this, ['']),
|
|
22
|
+
text: () => (this.waiting ? '' : slottedWords(this, [''])),
|
|
18
23
|
});
|
|
19
24
|
this.countdown = new CountdownController(this, {
|
|
20
25
|
done: () => {
|
|
21
26
|
this.dismiss();
|
|
22
27
|
},
|
|
28
|
+
holdWhileHidden: true,
|
|
23
29
|
paused: (paused) => {
|
|
24
30
|
this.toggleState('paused', paused);
|
|
25
31
|
},
|
|
26
32
|
});
|
|
27
|
-
this.
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
33
|
+
this.scope = new FocusScopeController(this, {
|
|
34
|
+
contents: () => [
|
|
35
|
+
...(this.renderRoot
|
|
36
|
+
?.querySelector('slot[name="actions"]')
|
|
37
|
+
?.assignedElements({ flatten: true }) ?? []),
|
|
38
|
+
...[...(this.renderRoot?.querySelectorAll('[part="dismiss"]') ?? [])],
|
|
39
|
+
],
|
|
40
|
+
surface: () => null,
|
|
41
|
+
});
|
|
42
|
+
this.focused = false;
|
|
43
|
+
this.handleHotkey = (event) => {
|
|
44
|
+
if (event.defaultPrevented || !matchesChord(event, this.hotkey, onApple())) {
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
if (this.scope.holdsFocus) {
|
|
48
|
+
if (this.scope.leave()) {
|
|
49
|
+
event.preventDefault();
|
|
50
|
+
}
|
|
51
|
+
this.origin = undefined;
|
|
52
|
+
return;
|
|
32
53
|
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
54
|
+
const other = event
|
|
55
|
+
.composedPath()
|
|
56
|
+
.some((node) => node !== this && node instanceof HelixHeadlessAnnouncement_1);
|
|
57
|
+
if (other || this.scope.controls.length === 0) {
|
|
58
|
+
return;
|
|
36
59
|
}
|
|
60
|
+
event.preventDefault();
|
|
61
|
+
const active = deepActiveElement();
|
|
62
|
+
this.origin = active instanceof HTMLElement ? active : undefined;
|
|
63
|
+
this.scope.enter();
|
|
37
64
|
};
|
|
38
|
-
this.handleHold = () => {
|
|
65
|
+
this.handleHold = (event) => {
|
|
66
|
+
this.focused ||= event.type === 'focusin';
|
|
39
67
|
this.countdown.hold();
|
|
40
68
|
};
|
|
41
|
-
this.handleRelease = () => {
|
|
69
|
+
this.handleRelease = (event) => {
|
|
70
|
+
if (event.type === 'focusout') {
|
|
71
|
+
queueMicrotask(() => {
|
|
72
|
+
this.focused = this.matches(':focus-within');
|
|
73
|
+
});
|
|
74
|
+
}
|
|
42
75
|
this.countdown.release();
|
|
43
76
|
};
|
|
44
77
|
}
|
|
78
|
+
behind() {
|
|
79
|
+
if (!this.queued || (this.shadowRoot !== null && !this.hasUpdated)) {
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
|
+
for (let at = this.previousElementSibling; at; at = at.previousElementSibling) {
|
|
83
|
+
if (at instanceof HelixHeadlessAnnouncement_1 && at.queued) {
|
|
84
|
+
return true;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
willUpdate(changed) {
|
|
90
|
+
super.willUpdate(changed);
|
|
91
|
+
hostAria(this.internals, {
|
|
92
|
+
label: this.label,
|
|
93
|
+
role: this.label.trim() && !this.waiting ? 'region' : '',
|
|
94
|
+
});
|
|
95
|
+
if ('ariaKeyShortcuts' in this.internals) {
|
|
96
|
+
this.internals.ariaKeyShortcuts = ariaChord(this.hotkey, onApple()) || null;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
45
99
|
connectedCallback() {
|
|
100
|
+
this.waiting = this.behind();
|
|
46
101
|
super.connectedCallback();
|
|
102
|
+
this.watchQueue();
|
|
47
103
|
this.addEventListener('mouseenter', this.handleHold);
|
|
48
104
|
this.addEventListener('mouseleave', this.handleRelease);
|
|
49
105
|
this.addEventListener('focusin', this.handleHold);
|
|
50
106
|
this.addEventListener('focusout', this.handleRelease);
|
|
51
|
-
document.addEventListener('
|
|
107
|
+
document.addEventListener('keydown', this.handleHotkey);
|
|
52
108
|
if (this.hasUpdated) {
|
|
53
109
|
this.countdown.resume();
|
|
54
|
-
this.handleVisibility();
|
|
55
110
|
}
|
|
56
111
|
}
|
|
57
112
|
disconnectedCallback() {
|
|
@@ -60,13 +115,26 @@ let HelixHeadlessAnnouncement = class HelixHeadlessAnnouncement extends MixinDis
|
|
|
60
115
|
this.removeEventListener('mouseleave', this.handleRelease);
|
|
61
116
|
this.removeEventListener('focusin', this.handleHold);
|
|
62
117
|
this.removeEventListener('focusout', this.handleRelease);
|
|
63
|
-
document.removeEventListener('
|
|
64
|
-
this.
|
|
118
|
+
document.removeEventListener('keydown', this.handleHotkey);
|
|
119
|
+
this.queue?.disconnect();
|
|
120
|
+
const origin = this.focused ? this.origin : undefined;
|
|
121
|
+
this.focused = false;
|
|
122
|
+
this.origin = undefined;
|
|
123
|
+
if (origin) {
|
|
124
|
+
requestAnimationFrame(() => {
|
|
125
|
+
const active = deepActiveElement();
|
|
126
|
+
if (origin.isConnected && (active === null || active === document.body)) {
|
|
127
|
+
origin.focus({ preventScroll: true });
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
}
|
|
65
131
|
}
|
|
66
132
|
firstUpdated(changed) {
|
|
67
133
|
super.firstUpdated(changed);
|
|
68
|
-
this.
|
|
69
|
-
this.
|
|
134
|
+
this.watchQueue();
|
|
135
|
+
if (!this.waiting) {
|
|
136
|
+
this.countdown.start(this.duration);
|
|
137
|
+
}
|
|
70
138
|
if (this.querySelector(':scope > [slot="icon"]')) {
|
|
71
139
|
reportRuntime({
|
|
72
140
|
kind: 'deprecated',
|
|
@@ -78,6 +146,26 @@ let HelixHeadlessAnnouncement = class HelixHeadlessAnnouncement extends MixinDis
|
|
|
78
146
|
updated(changed) {
|
|
79
147
|
super.updated(changed);
|
|
80
148
|
this.markEmpty();
|
|
149
|
+
if (changed.has('queued')) {
|
|
150
|
+
this.watchQueue();
|
|
151
|
+
}
|
|
152
|
+
this.toggleState('waiting', this.waiting);
|
|
153
|
+
if (changed.has('waiting') && this.waiting) {
|
|
154
|
+
this.arrival.say();
|
|
155
|
+
}
|
|
156
|
+
else if (changed.get('waiting') === true) {
|
|
157
|
+
this.countdown.start(this.duration);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
watchQueue() {
|
|
161
|
+
this.queue?.disconnect();
|
|
162
|
+
this.waiting = this.behind();
|
|
163
|
+
if (this.queued && this.parentElement && typeof MutationObserver !== 'undefined') {
|
|
164
|
+
this.queue ??= new MutationObserver(() => {
|
|
165
|
+
this.waiting = this.behind();
|
|
166
|
+
});
|
|
167
|
+
this.queue.observe(this.parentElement, { childList: true });
|
|
168
|
+
}
|
|
81
169
|
}
|
|
82
170
|
markEmpty() {
|
|
83
171
|
markRegion(this.renderRoot, 'actions', ['actions']);
|
|
@@ -92,6 +180,9 @@ let HelixHeadlessAnnouncement = class HelixHeadlessAnnouncement extends MixinDis
|
|
|
92
180
|
>`;
|
|
93
181
|
}
|
|
94
182
|
render() {
|
|
183
|
+
if (this.waiting) {
|
|
184
|
+
return nothing;
|
|
185
|
+
}
|
|
95
186
|
return html `<div aria-hidden=${this.arrival.said === '' ? nothing : 'true'} part="content"
|
|
96
187
|
><slot @slotchange=${() => this.arrival.say()}></slot
|
|
97
188
|
></div>
|
|
@@ -112,10 +203,22 @@ let HelixHeadlessAnnouncement = class HelixHeadlessAnnouncement extends MixinDis
|
|
|
112
203
|
__decorate([
|
|
113
204
|
property({ type: Number })
|
|
114
205
|
], HelixHeadlessAnnouncement.prototype, "duration", void 0);
|
|
206
|
+
__decorate([
|
|
207
|
+
property()
|
|
208
|
+
], HelixHeadlessAnnouncement.prototype, "hotkey", void 0);
|
|
209
|
+
__decorate([
|
|
210
|
+
property()
|
|
211
|
+
], HelixHeadlessAnnouncement.prototype, "label", void 0);
|
|
212
|
+
__decorate([
|
|
213
|
+
property({ reflect: true, type: Boolean })
|
|
214
|
+
], HelixHeadlessAnnouncement.prototype, "queued", void 0);
|
|
115
215
|
__decorate([
|
|
116
216
|
property({ reflect: true })
|
|
117
217
|
], HelixHeadlessAnnouncement.prototype, "tone", void 0);
|
|
118
|
-
|
|
218
|
+
__decorate([
|
|
219
|
+
state()
|
|
220
|
+
], HelixHeadlessAnnouncement.prototype, "waiting", void 0);
|
|
221
|
+
HelixHeadlessAnnouncement = HelixHeadlessAnnouncement_1 = __decorate([
|
|
119
222
|
customElement('helix-headless-announcement')
|
|
120
223
|
], HelixHeadlessAnnouncement);
|
|
121
224
|
export { HelixHeadlessAnnouncement };
|
|
@@ -37,7 +37,7 @@ let HelixHeadlessAreaChart = class HelixHeadlessAreaChart extends MixinChartSour
|
|
|
37
37
|
get stack() {
|
|
38
38
|
const rows = this.sourceRows;
|
|
39
39
|
const columns = this.plottedColumns;
|
|
40
|
-
const names = columns.map((column) =>
|
|
40
|
+
const names = columns.map((column) => this.columnName(column));
|
|
41
41
|
const values = columns.map((column, depth) => rows.map((row) => {
|
|
42
42
|
if (!this.showsSeries(names[depth] ?? '')) {
|
|
43
43
|
return 0;
|
|
@@ -186,9 +186,8 @@ let HelixHeadlessAreaChart = class HelixHeadlessAreaChart extends MixinChartSour
|
|
|
186
186
|
]
|
|
187
187
|
: []);
|
|
188
188
|
const named = this.named ? `${this.named}, ` : '';
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
</p>`;
|
|
189
|
+
const words = `${named}${rows[last]?.label ?? ''}: ${this.totalLabel} ${format(total)}; ${shares.join(', ')}.`;
|
|
190
|
+
return html `<p id="helix-chart-summary" part="summary">${words}</p>`;
|
|
192
191
|
}
|
|
193
192
|
tallied(rows, stack) {
|
|
194
193
|
const format = formatNumbers(this.locale);
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/** Whether the value counts nothing, in which case there is no mark. */
|
|
2
|
+
export declare const badgeBlank: (given: string) => boolean;
|
|
3
|
+
/** Whether the count is past `max`, which the mark shows as `{max}+`. */
|
|
4
|
+
export declare const badgeCapped: (given: string, max: number | undefined) => boolean;
|
|
5
|
+
/**
|
|
6
|
+
* What the mark reads once the cap has been applied: the value as given, or
|
|
7
|
+
* written with `options` in `locale` where they are set and it is a number.
|
|
8
|
+
*/
|
|
9
|
+
export declare const badgeText: (given: string, max: number | undefined, locale: string, options: Intl.NumberFormatOptions | undefined) => string;
|
|
10
|
+
/**
|
|
11
|
+
* What a reader hears for the mark: `label` with `{count}` filled, or the text
|
|
12
|
+
* alone, and nothing for a blank mark or an unnamed dot.
|
|
13
|
+
*/
|
|
14
|
+
export declare const badgeWords: (text: string, label: string, blank: boolean) => string;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { fillTemplate, formatNumber } from '@fusion.dev/helix-core';
|
|
2
|
+
export const badgeBlank = (given) => given.trim() !== '' && Number(given) === 0;
|
|
3
|
+
export const badgeCapped = (given, max) => {
|
|
4
|
+
const count = Number(given);
|
|
5
|
+
return max !== undefined && Number.isFinite(count) && count > max;
|
|
6
|
+
};
|
|
7
|
+
export const badgeText = (given, max, locale, options) => {
|
|
8
|
+
const write = (count) => options ? formatNumber(count, locale, options) : String(count);
|
|
9
|
+
if (badgeCapped(given, max)) {
|
|
10
|
+
return `${write(max)}+`;
|
|
11
|
+
}
|
|
12
|
+
const count = Number(given);
|
|
13
|
+
return options && given.trim() !== '' && Number.isFinite(count) ? write(count) : given;
|
|
14
|
+
};
|
|
15
|
+
export const badgeWords = (text, label, blank) => {
|
|
16
|
+
if (blank || (text === '' && label === '')) {
|
|
17
|
+
return '';
|
|
18
|
+
}
|
|
19
|
+
return fillTemplate(label, { count: text }) || text;
|
|
20
|
+
};
|
|
@@ -4,9 +4,10 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
4
4
|
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
5
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
6
|
};
|
|
7
|
-
import { customElement,
|
|
7
|
+
import { customElement, focusTarget, HelixElement } from '@fusion.dev/helix-core';
|
|
8
8
|
import { html, nothing } from 'lit';
|
|
9
9
|
import { property } from 'lit/decorators.js';
|
|
10
|
+
import { badgeBlank, badgeCapped, badgeText, badgeWords } from '../count.js';
|
|
10
11
|
let HelixHeadlessBadge = class HelixHeadlessBadge extends HelixElement {
|
|
11
12
|
constructor() {
|
|
12
13
|
super(...arguments);
|
|
@@ -21,28 +22,16 @@ let HelixHeadlessBadge = class HelixHeadlessBadge extends HelixElement {
|
|
|
21
22
|
return String(this.value ?? '');
|
|
22
23
|
}
|
|
23
24
|
get blank() {
|
|
24
|
-
return
|
|
25
|
+
return badgeBlank(this.given);
|
|
25
26
|
}
|
|
26
27
|
get capped() {
|
|
27
|
-
|
|
28
|
-
return this.max !== undefined && Number.isFinite(count) && count > this.max;
|
|
28
|
+
return badgeCapped(this.given, this.max);
|
|
29
29
|
}
|
|
30
30
|
get text() {
|
|
31
|
-
|
|
32
|
-
if (this.capped) {
|
|
33
|
-
return `${write(this.max)}+`;
|
|
34
|
-
}
|
|
35
|
-
const count = Number(this.given);
|
|
36
|
-
return this.formatOptions && this.given.trim() !== '' && Number.isFinite(count)
|
|
37
|
-
? write(count)
|
|
38
|
-
: this.given;
|
|
31
|
+
return badgeText(this.given, this.max, this.locale, this.formatOptions);
|
|
39
32
|
}
|
|
40
33
|
get words() {
|
|
41
|
-
|
|
42
|
-
if (this.blank || (text === '' && this.label === '')) {
|
|
43
|
-
return '';
|
|
44
|
-
}
|
|
45
|
-
return fillTemplate(this.label, { count: text }) || text;
|
|
34
|
+
return badgeWords(this.text, this.label, this.blank);
|
|
46
35
|
}
|
|
47
36
|
wrapped() {
|
|
48
37
|
const found = this.renderRoot
|
|
@@ -50,10 +39,7 @@ let HelixHeadlessBadge = class HelixHeadlessBadge extends HelixElement {
|
|
|
50
39
|
?.assignedElements({ flatten: true })
|
|
51
40
|
.find((element) => element instanceof HTMLElement &&
|
|
52
41
|
(element.tabIndex >= 0 || element.shadowRoot?.delegatesFocus === true));
|
|
53
|
-
|
|
54
|
-
? found.shadowRoot.querySelector('a[href], button, input, select, textarea')
|
|
55
|
-
: null;
|
|
56
|
-
return inner ?? found;
|
|
42
|
+
return found && focusTarget(found);
|
|
57
43
|
}
|
|
58
44
|
firstUpdated(changed) {
|
|
59
45
|
super.firstUpdated(changed);
|
|
@@ -113,7 +99,7 @@ let HelixHeadlessBadge = class HelixHeadlessBadge extends HelixElement {
|
|
|
113
99
|
data-blank=${blank ? '' : nothing}
|
|
114
100
|
data-single=${text.length <= 1 ? '' : nothing}
|
|
115
101
|
role="status"
|
|
116
|
-
aria-label=${announced ?
|
|
102
|
+
aria-label=${announced ? this.words : nothing}
|
|
117
103
|
>${blank ? html `<span aria-hidden="true">${text}</span>` : text}</span
|
|
118
104
|
>`;
|
|
119
105
|
}
|
|
@@ -1,16 +1,36 @@
|
|
|
1
1
|
import { HelixElement } from '@fusion.dev/helix-core';
|
|
2
|
-
import { type PropertyValues, type TemplateResult } from 'lit';
|
|
2
|
+
import { type CSSResultGroup, type PropertyValues, type TemplateResult } from 'lit';
|
|
3
3
|
/**
|
|
4
4
|
* @summary A passage set off from the prose around it.
|
|
5
5
|
*
|
|
6
6
|
* @description
|
|
7
|
-
* Renders a
|
|
7
|
+
* Renders a figure holding the `<blockquote>` and a caption for the
|
|
8
8
|
* attribution, the structure the HTML spec describes. The attribution sits
|
|
9
9
|
* outside the quotation because it is about the quotation, and it names the
|
|
10
10
|
* figure, so a screen reader reads the source as the figure's caption.
|
|
11
11
|
*
|
|
12
|
-
* The figure
|
|
13
|
-
*
|
|
12
|
+
* **The figure is a `<div role="figure">`**, so the headless tier draws no
|
|
13
|
+
* space around the quotation. A `<figure>` takes a 40px indent and a line of
|
|
14
|
+
* space above and below from the browser's own sheet, which every page had to
|
|
15
|
+
* reset. The `<blockquote>` keeps its own margins, as a bare one does. A
|
|
16
|
+
* screen reader meets the same figure, named by its caption.
|
|
17
|
+
*
|
|
18
|
+
* **A figure only while there is an attribution.** With nothing in the caption
|
|
19
|
+
* the figure has no name and nothing to say, so the role and its name are
|
|
20
|
+
* dropped and a reader meets the quotation alone. A server cannot see what is
|
|
21
|
+
* slotted, so it serves the figure, and the client drops it after hydration;
|
|
22
|
+
* an `attribution` written as words is served exactly.
|
|
23
|
+
*
|
|
24
|
+
* **Quotation marks follow the language**, under `marks`. The opening mark is
|
|
25
|
+
* the one the browser sets for a `<q>` in the element's language, „ under
|
|
26
|
+
* `lang="de"` and « under `lang="fr"`, and a `Quote` inside the passage takes
|
|
27
|
+
* the inner marks. The mark is drawn, not read: a screen reader hears the
|
|
28
|
+
* passage. A passage under `marks` that starts with a mark typed into the text
|
|
29
|
+
* would show two, and warns once, with the runtime kind `typed-marks`.
|
|
30
|
+
*
|
|
31
|
+
* **`cite` is the source's URL**, which the browser keeps and does not show.
|
|
32
|
+
* Words in it, such as the author's name, warn once, with the runtime kind
|
|
33
|
+
* `cite-not-url`.
|
|
14
34
|
*
|
|
15
35
|
* For a quotation inside a sentence, use `Quote`.
|
|
16
36
|
*
|
|
@@ -21,17 +41,37 @@ import { type PropertyValues, type TemplateResult } from 'lit';
|
|
|
21
41
|
* @documentation https://fusion.dev/helix/components/blockquote
|
|
22
42
|
*
|
|
23
43
|
* @slot - The quoted passage.
|
|
24
|
-
* @slot attribution - Who said it, and where.
|
|
44
|
+
* @slot attribution - Who said it, and where. Replaces the words of `attribution`.
|
|
25
45
|
*
|
|
26
|
-
* @csspart attribution - The
|
|
46
|
+
* @csspart attribution - The caption carrying the source.
|
|
27
47
|
* @csspart blockquote - The quotation.
|
|
28
|
-
* @csspart figure - The
|
|
48
|
+
* @csspart figure - The figure holding the quotation and its attribution, a `<div>` with `role="figure"` while the attribution has words.
|
|
29
49
|
*/
|
|
30
50
|
export declare class HelixHeadlessBlockquote extends HelixElement {
|
|
51
|
+
static styles: CSSResultGroup;
|
|
31
52
|
/**
|
|
32
|
-
*
|
|
53
|
+
* Who said it, and where, as words, such as "Maria Lopez, operations
|
|
54
|
+
* manager". The `attribution` slot replaces them, for markup such as a link.
|
|
55
|
+
*
|
|
56
|
+
* Words are served with the figure they name, where a server cannot see a
|
|
57
|
+
* slotted attribution.
|
|
58
|
+
*/
|
|
59
|
+
attribution: string;
|
|
60
|
+
/**
|
|
61
|
+
* The URL of the source the passage came from. Who said it goes in
|
|
62
|
+
* `attribution`: a `cite` with words in it warns once, with the runtime kind
|
|
63
|
+
* `cite-not-url`.
|
|
33
64
|
*/
|
|
34
65
|
cite: string;
|
|
66
|
+
/**
|
|
67
|
+
* Whether to draw an opening quotation mark before the passage, in the
|
|
68
|
+
* element's language. Leave the marks out of the text.
|
|
69
|
+
*/
|
|
70
|
+
marks: boolean;
|
|
71
|
+
/** Whether this blockquote has said its passage starts with a typed mark, so it says so once. */
|
|
72
|
+
private warned;
|
|
73
|
+
/** Whether this blockquote has said its `cite` holds words, so it says so once. */
|
|
74
|
+
private citeWarned;
|
|
35
75
|
/**
|
|
36
76
|
* Says on the caption whether there is an attribution in it.
|
|
37
77
|
*
|
|
@@ -44,8 +84,26 @@ export declare class HelixHeadlessBlockquote extends HelixElement {
|
|
|
44
84
|
* rendered, which React reports on hydration.
|
|
45
85
|
*/
|
|
46
86
|
private markEmpty;
|
|
87
|
+
/**
|
|
88
|
+
* Reads the passage for a mark typed at its start, which `marks` would
|
|
89
|
+
* double. Only the start: the mark drawn is an opening one.
|
|
90
|
+
*/
|
|
91
|
+
private readonly readPassage;
|
|
92
|
+
/**
|
|
93
|
+
* Reads `cite` for words where the source's URL belongs, the common mistake
|
|
94
|
+
* of naming the author there. A URL holds no spaces, so words with a space
|
|
95
|
+
* between them are enough to tell.
|
|
96
|
+
*/
|
|
97
|
+
private readCite;
|
|
98
|
+
protected firstUpdated(changed: PropertyValues): void;
|
|
47
99
|
protected updated(changed: PropertyValues): void;
|
|
48
100
|
protected render(): TemplateResult;
|
|
101
|
+
/**
|
|
102
|
+
* The caption, with the words of `attribution` as its slot's fallback,
|
|
103
|
+
* behind a seam a tier can move. The words are in an element, so the region
|
|
104
|
+
* counts them as its own.
|
|
105
|
+
*/
|
|
106
|
+
protected renderAttribution(): TemplateResult;
|
|
49
107
|
}
|
|
50
108
|
declare global {
|
|
51
109
|
interface HTMLElementTagNameMap {
|