@fusion.dev/helix-headless 0.156.0 → 0.158.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 +1085 -66
- package/dist/command/root/command-root.d.ts +19 -5
- package/dist/command/root/command-root.js +14 -4
- package/dist/data-table/root/data-table-root.js +5 -2
- package/dist/drilldown/item/drilldown-item.d.ts +49 -1
- package/dist/drilldown/item/drilldown-item.js +77 -17
- package/dist/drilldown/root/drilldown-root.d.ts +45 -5
- package/dist/drilldown/root/drilldown-root.js +51 -5
- package/dist/element-events.generated.js +1 -1
- package/dist/filter-input/menu/filter-menu.d.ts +1 -0
- package/dist/filter-input/menu/filter-menu.js +4 -4
- package/dist/list/item/list-item.d.ts +15 -1
- package/dist/list/item/list-item.js +20 -1
- package/dist/list/root/list-root.d.ts +29 -1
- package/dist/list/root/list-root.js +44 -10
- package/dist/listbox/root/listbox-root.d.ts +19 -2
- package/dist/listbox/root/listbox-root.js +16 -8
- package/dist/map/root/map-root.d.ts +4 -0
- package/dist/map/root/map-root.js +1 -1
- package/dist/megamenu/root/megamenu-root.d.ts +50 -13
- package/dist/megamenu/root/megamenu-root.js +44 -6
- package/dist/menu/item/menu-item.d.ts +24 -1
- package/dist/menu/item/menu-item.js +42 -2
- package/dist/menu/root/menu-root.d.ts +126 -11
- package/dist/menu/root/menu-root.js +336 -26
- package/dist/notifications/root/notifications-root.d.ts +20 -0
- package/dist/notifications/root/notifications-root.js +32 -1
- package/dist/number-input/root/number-input-root.d.ts +7 -4
- package/dist/number-input/root/number-input-root.js +3 -3
- package/dist/progress/root/progress-root.js +1 -1
- package/dist/select/root/select-root.d.ts +8 -1
- package/dist/select/root/select-root.js +8 -5
- package/dist/skeleton/root/skeleton-root.d.ts +2 -1
- package/dist/skeleton/root/skeleton-root.js +1 -1
- package/dist/toast/item/toast-item.d.ts +26 -0
- package/dist/toast/item/toast-item.js +55 -3
- package/dist/toast/root/toast-root.d.ts +32 -0
- package/dist/toast/root/toast-root.js +47 -2
- package/dist/video/root/video-root.d.ts +1 -0
- package/dist/video/root/video-root.js +9 -9
- package/dist/video-playlist/menu/video-playlist-menu.d.ts +1 -0
- package/dist/video-playlist/menu/video-playlist-menu.js +1 -1
- package/lint.json +1 -1
- package/package.json +5 -5
- package/types/jsx.d.ts +653 -74
- package/types/vue.d.ts +133 -16
- package/types/words.d.ts +14 -0
|
@@ -13,8 +13,11 @@ let HelixHeadlessMegamenu = class HelixHeadlessMegamenu extends MixinControllerO
|
|
|
13
13
|
constructor() {
|
|
14
14
|
super(...arguments);
|
|
15
15
|
this.bandBelow = 640;
|
|
16
|
+
this.closeDelay = 220;
|
|
16
17
|
this.label = '';
|
|
18
|
+
this.openDelay = 120;
|
|
17
19
|
this.openOn = 'hover';
|
|
20
|
+
this.placement = 'bottom-start';
|
|
18
21
|
this.narrow = false;
|
|
19
22
|
this.handleWidth = (event) => {
|
|
20
23
|
this.narrow = event.matches;
|
|
@@ -25,9 +28,10 @@ let HelixHeadlessMegamenu = class HelixHeadlessMegamenu extends MixinControllerO
|
|
|
25
28
|
onDismiss: () => this.close(),
|
|
26
29
|
open: () => this.open,
|
|
27
30
|
place: () => !this.banded,
|
|
28
|
-
placement: () =>
|
|
31
|
+
placement: () => this.placement,
|
|
29
32
|
surface: () => this.partNamed('surface'),
|
|
30
33
|
});
|
|
34
|
+
this.settledOnce = false;
|
|
31
35
|
this.keys = new KeyboardNavigationController(this, {
|
|
32
36
|
items: () => !this.parentElement?.shadowRoot
|
|
33
37
|
? HelixHeadlessMegamenu_1.bar
|
|
@@ -72,7 +76,7 @@ let HelixHeadlessMegamenu = class HelixHeadlessMegamenu extends MixinControllerO
|
|
|
72
76
|
return;
|
|
73
77
|
}
|
|
74
78
|
const showing = HelixHeadlessMegamenu_1.bar.showing;
|
|
75
|
-
this.schedule(showing && showing !== this ? 0 :
|
|
79
|
+
this.schedule(showing && showing !== this ? 0 : this.openDelay, () => {
|
|
76
80
|
this.requestOpen(true);
|
|
77
81
|
});
|
|
78
82
|
};
|
|
@@ -80,7 +84,7 @@ let HelixHeadlessMegamenu = class HelixHeadlessMegamenu extends MixinControllerO
|
|
|
80
84
|
if (this.openOn !== 'hover' || event.pointerType !== 'mouse') {
|
|
81
85
|
return;
|
|
82
86
|
}
|
|
83
|
-
this.schedule(
|
|
87
|
+
this.schedule(this.closeDelay, () => {
|
|
84
88
|
this.requestOpen(false);
|
|
85
89
|
});
|
|
86
90
|
};
|
|
@@ -142,9 +146,17 @@ let HelixHeadlessMegamenu = class HelixHeadlessMegamenu extends MixinControllerO
|
|
|
142
146
|
}
|
|
143
147
|
tellTrigger() {
|
|
144
148
|
const slotted = this.slotted;
|
|
145
|
-
if (slotted) {
|
|
149
|
+
if (!slotted || slotted.localName.includes('-')) {
|
|
146
150
|
tellDisclosure(slotted, this.open);
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
if (slotted !== this.told) {
|
|
154
|
+
if (!this.settledOnce || slotted.hasAttribute('aria-expanded')) {
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
this.told = slotted;
|
|
147
158
|
}
|
|
159
|
+
slotted.setAttribute('aria-expanded', String(this.open));
|
|
148
160
|
}
|
|
149
161
|
get slotted() {
|
|
150
162
|
const slot = this.renderRoot?.querySelector('slot[name="trigger"]');
|
|
@@ -156,6 +168,10 @@ let HelixHeadlessMegamenu = class HelixHeadlessMegamenu extends MixinControllerO
|
|
|
156
168
|
get navigationControl() {
|
|
157
169
|
return this.trigger;
|
|
158
170
|
}
|
|
171
|
+
markCurrent() {
|
|
172
|
+
const current = [...this.querySelectorAll('[aria-current="page"]')].some((link) => !link.closest('[slot="trigger"]'));
|
|
173
|
+
this.toggleState('current', current);
|
|
174
|
+
}
|
|
159
175
|
close() {
|
|
160
176
|
if (!this.requestOpen(false)) {
|
|
161
177
|
return;
|
|
@@ -180,8 +196,6 @@ let HelixHeadlessMegamenu = class HelixHeadlessMegamenu extends MixinControllerO
|
|
|
180
196
|
}
|
|
181
197
|
return this.trigger.getBoundingClientRect().bottom;
|
|
182
198
|
}
|
|
183
|
-
static { this.OPEN_DELAY = 120; }
|
|
184
|
-
static { this.CLOSE_DELAY = 220; }
|
|
185
199
|
schedule(delay, then) {
|
|
186
200
|
this.stop();
|
|
187
201
|
this.timer = setTimeout(then, delay);
|
|
@@ -203,6 +217,19 @@ let HelixHeadlessMegamenu = class HelixHeadlessMegamenu extends MixinControllerO
|
|
|
203
217
|
this.addEventListener('pointerleave', this.handlePointerLeave);
|
|
204
218
|
this.addEventListener('focusout', this.handleFocusOut);
|
|
205
219
|
this.addEventListener('keydown', this.handleKeyDown);
|
|
220
|
+
this.markCurrent();
|
|
221
|
+
if (typeof MutationObserver === 'function') {
|
|
222
|
+
this.currentWatch = new MutationObserver(() => this.markCurrent());
|
|
223
|
+
this.currentWatch.observe(this, {
|
|
224
|
+
attributeFilter: ['aria-current'],
|
|
225
|
+
childList: true,
|
|
226
|
+
subtree: true,
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
void this.whenSettled.then(() => {
|
|
230
|
+
this.settledOnce = true;
|
|
231
|
+
this.tellTrigger();
|
|
232
|
+
});
|
|
206
233
|
}
|
|
207
234
|
disconnectedCallback() {
|
|
208
235
|
super.disconnectedCallback();
|
|
@@ -215,6 +242,8 @@ let HelixHeadlessMegamenu = class HelixHeadlessMegamenu extends MixinControllerO
|
|
|
215
242
|
this.removeEventListener('pointerleave', this.handlePointerLeave);
|
|
216
243
|
this.removeEventListener('focusout', this.handleFocusOut);
|
|
217
244
|
this.removeEventListener('keydown', this.handleKeyDown);
|
|
245
|
+
this.currentWatch?.disconnect();
|
|
246
|
+
this.currentWatch = undefined;
|
|
218
247
|
HelixHeadlessMegamenu_1.bar.remove(this);
|
|
219
248
|
this.stop();
|
|
220
249
|
}
|
|
@@ -297,12 +326,21 @@ let HelixHeadlessMegamenu = class HelixHeadlessMegamenu extends MixinControllerO
|
|
|
297
326
|
__decorate([
|
|
298
327
|
property({ attribute: 'band-below', type: Number })
|
|
299
328
|
], HelixHeadlessMegamenu.prototype, "bandBelow", void 0);
|
|
329
|
+
__decorate([
|
|
330
|
+
property({ attribute: 'close-delay', type: Number })
|
|
331
|
+
], HelixHeadlessMegamenu.prototype, "closeDelay", void 0);
|
|
300
332
|
__decorate([
|
|
301
333
|
property()
|
|
302
334
|
], HelixHeadlessMegamenu.prototype, "label", void 0);
|
|
335
|
+
__decorate([
|
|
336
|
+
property({ attribute: 'open-delay', type: Number })
|
|
337
|
+
], HelixHeadlessMegamenu.prototype, "openDelay", void 0);
|
|
303
338
|
__decorate([
|
|
304
339
|
property({ attribute: 'open-on' })
|
|
305
340
|
], HelixHeadlessMegamenu.prototype, "openOn", void 0);
|
|
341
|
+
__decorate([
|
|
342
|
+
property()
|
|
343
|
+
], HelixHeadlessMegamenu.prototype, "placement", void 0);
|
|
306
344
|
__decorate([
|
|
307
345
|
property({ reflect: true })
|
|
308
346
|
], HelixHeadlessMegamenu.prototype, "variant", void 0);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { HelixElement } from '@fusion.dev/helix-core';
|
|
1
|
+
import { HelixElement, PopoverController } from '@fusion.dev/helix-core';
|
|
2
2
|
import { nothing, type PropertyValues, type TemplateResult } from 'lit';
|
|
3
3
|
import '../../kbd/index.js';
|
|
4
4
|
/**
|
|
@@ -34,6 +34,9 @@ import '../../kbd/index.js';
|
|
|
34
34
|
* React Aria's menu item does with its description: a reader hears "Copy",
|
|
35
35
|
* then "Copies the selection".
|
|
36
36
|
*
|
|
37
|
+
* A development build warns when a row has no words to be named by, such as a
|
|
38
|
+
* row holding only an icon with no label.
|
|
39
|
+
*
|
|
37
40
|
* The check mark and the caret are the fallbacks of the `check` and `chevron`
|
|
38
41
|
* slots, so a mark of your own replaces either by being slotted. Neither is
|
|
39
42
|
* read as part of the row's name.
|
|
@@ -63,8 +66,11 @@ import '../../kbd/index.js';
|
|
|
63
66
|
* @csspart shortcut-key - One key of the shortcut's chord, such as `Ctrl`.
|
|
64
67
|
* @csspart shortcut-separator - The `+` between two keys of the shortcut, where the platform writes one.
|
|
65
68
|
* @csspart start - The leading column that holds the `start` slot. It is always rendered.
|
|
69
|
+
* @csspart surface - The panel a submenu opens in beside the row, under the menu's `submenus="flyout"`.
|
|
66
70
|
*
|
|
67
71
|
* @cssstate checked - The row is checked.
|
|
72
|
+
* @cssstate submenu-mark - Under `submenus="flyout"`, a row in this row's submenu can be ticked or opens a submenu, so the submenu keeps its trailing column.
|
|
73
|
+
* @cssstate submenu-start - Under `submenus="flyout"`, a row in this row's submenu has content before its label, so the submenu keeps its leading column.
|
|
68
74
|
*/
|
|
69
75
|
export declare class HelixHeadlessMenuItem extends HelixElement {
|
|
70
76
|
/**
|
|
@@ -114,6 +120,16 @@ export declare class HelixHeadlessMenuItem extends HelixElement {
|
|
|
114
120
|
* one of a set of anything.
|
|
115
121
|
*/
|
|
116
122
|
exclusive: boolean;
|
|
123
|
+
/**
|
|
124
|
+
* @internal Whether this row's submenu is open beside it, under
|
|
125
|
+
* `submenus="flyout"`. Written by the menu.
|
|
126
|
+
*/
|
|
127
|
+
expanded: boolean;
|
|
128
|
+
/**
|
|
129
|
+
* @internal Whether the menu opens submenus beside their rows, under
|
|
130
|
+
* `submenus="flyout"`. Written by the menu.
|
|
131
|
+
*/
|
|
132
|
+
flyout: boolean;
|
|
117
133
|
/**
|
|
118
134
|
* The URL the row links to. Setting it renders the row as a link.
|
|
119
135
|
*
|
|
@@ -228,6 +244,7 @@ export declare class HelixHeadlessMenuItem extends HelixElement {
|
|
|
228
244
|
*/
|
|
229
245
|
protected willUpdate(changed: PropertyValues): void;
|
|
230
246
|
protected firstUpdated(changed: PropertyValues): void;
|
|
247
|
+
protected updated(changed: PropertyValues): void;
|
|
231
248
|
/**
|
|
232
249
|
* Re-reads whether anything is before the label.
|
|
233
250
|
*
|
|
@@ -291,6 +308,12 @@ export declare class HelixHeadlessMenuItem extends HelixElement {
|
|
|
291
308
|
* on the row, which is the whole of what moving here means.
|
|
292
309
|
*/
|
|
293
310
|
focusItem(): void;
|
|
311
|
+
/**
|
|
312
|
+
* The panel a flyout submenu opens in, beside the row and in the top layer.
|
|
313
|
+
* The menu around it says when, and answers a press outside and Escape, so
|
|
314
|
+
* its own dismissal does nothing.
|
|
315
|
+
*/
|
|
316
|
+
protected readonly flyoutPanel: PopoverController;
|
|
294
317
|
/** Chooses the row, the same as pressing it. */
|
|
295
318
|
select(): void;
|
|
296
319
|
/**
|
|
@@ -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 { ariaChord, customElement, deepActiveElement, HelixBeforeToggleEvent, HelixElement, HelixSelectEvent, onApple, requestChange, slotFilled, } from '@fusion.dev/helix-core';
|
|
7
|
+
import { ariaChord, auditName, customElement, deepActiveElement, HelixBeforeToggleEvent, HelixElement, HelixSelectEvent, isRtl, onApple, PopoverController, requestChange, slotFilled, } from '@fusion.dev/helix-core';
|
|
8
8
|
import { caretRight, check } from '@fusion.dev/helix-icons';
|
|
9
9
|
import { html, nothing } from 'lit';
|
|
10
10
|
import { property, state } from 'lit/decorators.js';
|
|
@@ -19,6 +19,8 @@ let HelixHeadlessMenuItem = class HelixHeadlessMenuItem extends HelixElement {
|
|
|
19
19
|
this.disabled = false;
|
|
20
20
|
this.download = false;
|
|
21
21
|
this.exclusive = false;
|
|
22
|
+
this.expanded = false;
|
|
23
|
+
this.flyout = false;
|
|
22
24
|
this.href = '';
|
|
23
25
|
this.onPage = true;
|
|
24
26
|
this.rel = '';
|
|
@@ -47,6 +49,14 @@ let HelixHeadlessMenuItem = class HelixHeadlessMenuItem extends HelixElement {
|
|
|
47
49
|
const slot = this.renderRoot.querySelector('slot[name="submenu"]');
|
|
48
50
|
this.submenu = (slot?.assignedElements().length ?? 0) > 0;
|
|
49
51
|
};
|
|
52
|
+
this.flyoutPanel = new PopoverController(this, {
|
|
53
|
+
anchor: () => this.partNamed('item'),
|
|
54
|
+
boundary: () => this,
|
|
55
|
+
onDismiss: () => { },
|
|
56
|
+
open: () => this.flyout && this.expanded && this.onPage && this.submenu,
|
|
57
|
+
placement: () => (isRtl(this) ? 'left-start' : 'right-start'),
|
|
58
|
+
surface: () => (this.flyout ? this.partNamed('surface') : null),
|
|
59
|
+
});
|
|
50
60
|
this.handleClick = () => {
|
|
51
61
|
this.choose();
|
|
52
62
|
};
|
|
@@ -80,11 +90,23 @@ let HelixHeadlessMenuItem = class HelixHeadlessMenuItem extends HelixElement {
|
|
|
80
90
|
this.start = named('start');
|
|
81
91
|
this.end = named('end');
|
|
82
92
|
this.description = named('description');
|
|
93
|
+
const rows = this.knownChildren.filter((child) => child.slot === 'submenu' && child.isMenuItem);
|
|
94
|
+
this.toggleState('submenu-start', this.flyout && rows.some((row) => row.querySelector(':scope > [slot="start"]') !== null));
|
|
95
|
+
this.toggleState('submenu-mark', this.flyout &&
|
|
96
|
+
rows.some((row) => row.checkable ||
|
|
97
|
+
row.checked ||
|
|
98
|
+
[...row.children].some((child) => child.slot === 'submenu')));
|
|
83
99
|
}
|
|
84
100
|
firstUpdated(changed) {
|
|
85
101
|
super.firstUpdated(changed);
|
|
86
102
|
this.finishHydration();
|
|
87
103
|
}
|
|
104
|
+
updated(changed) {
|
|
105
|
+
super.updated(changed);
|
|
106
|
+
if (this.partNamed('item')) {
|
|
107
|
+
auditName(this, () => this.partNamed('item') ?? undefined, 'Give the row words in its default slot, or a label on the icon it holds.');
|
|
108
|
+
}
|
|
109
|
+
}
|
|
88
110
|
renderDescription() {
|
|
89
111
|
return html `<slot
|
|
90
112
|
id="description"
|
|
@@ -213,6 +235,7 @@ let HelixHeadlessMenuItem = class HelixHeadlessMenuItem extends HelixElement {
|
|
|
213
235
|
return html `${linked
|
|
214
236
|
? html `<a
|
|
215
237
|
part="item"
|
|
238
|
+
id=${this.flyout ? 'row' : nothing}
|
|
216
239
|
href=${this.href}
|
|
217
240
|
target=${this.target || nothing}
|
|
218
241
|
rel=${this.rel || nothing}
|
|
@@ -221,6 +244,7 @@ let HelixHeadlessMenuItem = class HelixHeadlessMenuItem extends HelixElement {
|
|
|
221
244
|
aria-checked=${checkedState}
|
|
222
245
|
aria-describedby=${this.describedBy}
|
|
223
246
|
aria-haspopup=${submenu ? 'menu' : nothing}
|
|
247
|
+
aria-expanded=${this.flyout && submenu ? String(this.expanded) : nothing}
|
|
224
248
|
aria-keyshortcuts=${keys}
|
|
225
249
|
tabindex="-1"
|
|
226
250
|
@click=${this.handleClick}
|
|
@@ -228,17 +252,27 @@ let HelixHeadlessMenuItem = class HelixHeadlessMenuItem extends HelixElement {
|
|
|
228
252
|
>${inside}</a>`
|
|
229
253
|
: html `<div
|
|
230
254
|
part="item"
|
|
255
|
+
id=${this.flyout ? 'row' : nothing}
|
|
231
256
|
role=${role}
|
|
232
257
|
aria-checked=${checkedState}
|
|
233
258
|
aria-describedby=${this.describedBy}
|
|
234
259
|
aria-disabled=${this.disabled ? 'true' : nothing}
|
|
235
260
|
aria-haspopup=${submenu ? 'menu' : nothing}
|
|
261
|
+
aria-expanded=${this.flyout && submenu ? String(this.expanded) : nothing}
|
|
236
262
|
aria-keyshortcuts=${keys}
|
|
237
263
|
tabindex=${this.disabled ? nothing : '-1'}
|
|
238
264
|
@click=${this.handleClick}
|
|
239
265
|
@keydown=${this.handleKeydown}
|
|
240
266
|
>${inside}</div>`}
|
|
241
|
-
|
|
267
|
+
${this.flyout
|
|
268
|
+
? html `<div
|
|
269
|
+
part="surface"
|
|
270
|
+
popover="manual"
|
|
271
|
+
role="menu"
|
|
272
|
+
aria-labelledby="row"
|
|
273
|
+
?hidden=${!(this.expanded && submenu)}
|
|
274
|
+
><slot name="submenu" @slotchange=${this.readSubmenu}></slot></div>`
|
|
275
|
+
: html `<div hidden><slot name="submenu" @slotchange=${this.readSubmenu}></slot></div>`}`;
|
|
242
276
|
}
|
|
243
277
|
};
|
|
244
278
|
__decorate([
|
|
@@ -256,6 +290,12 @@ __decorate([
|
|
|
256
290
|
__decorate([
|
|
257
291
|
property({ reflect: true, type: Boolean })
|
|
258
292
|
], HelixHeadlessMenuItem.prototype, "exclusive", void 0);
|
|
293
|
+
__decorate([
|
|
294
|
+
property({ attribute: false })
|
|
295
|
+
], HelixHeadlessMenuItem.prototype, "expanded", void 0);
|
|
296
|
+
__decorate([
|
|
297
|
+
property({ attribute: false })
|
|
298
|
+
], HelixHeadlessMenuItem.prototype, "flyout", void 0);
|
|
259
299
|
__decorate([
|
|
260
300
|
property()
|
|
261
301
|
], HelixHeadlessMenuItem.prototype, "href", void 0);
|
|
@@ -10,15 +10,24 @@ declare const HelixHeadlessMenu_base: (new (...args: any[]) => import("@fusion.d
|
|
|
10
10
|
* ancestor with `overflow: hidden` does not clip it and it needs no `z-index`.
|
|
11
11
|
*
|
|
12
12
|
* Slot a `helix-button` or `helix-icon-button` as the trigger, and the menu
|
|
13
|
-
* tells it that it opens a menu and whether the menu is open
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
13
|
+
* tells it that it opens a menu and whether the menu is open, inside its own
|
|
14
|
+
* shadow root. On a plain element, such as a `<button>`, the menu writes
|
|
15
|
+
* `aria-haspopup="menu"` and `aria-expanded`, from a frame after its own first
|
|
16
|
+
* render, so a server's markup is never changed while a framework hydrates it.
|
|
17
|
+
* It never writes `aria-expanded` where you wrote it yourself.
|
|
17
18
|
*
|
|
18
|
-
* Submenus drill down. A submenu replaces the panel's content, and a
|
|
19
|
-
* returns to the previous page. One panel
|
|
20
|
-
*
|
|
21
|
-
*
|
|
19
|
+
* Submenus drill down by default. A submenu replaces the panel's content, and a
|
|
20
|
+
* back row returns to the previous page. One panel stays inside a narrow
|
|
21
|
+
* window, and works on touch screens, which have no hover.
|
|
22
|
+
*
|
|
23
|
+
* **`submenus="flyout"` opens each submenu in a panel of its own beside its
|
|
24
|
+
* row**, as React Aria's `SubmenuTrigger` and Radix's `Sub` do. Resting the
|
|
25
|
+
* pointer on the row opens it, and a pointer crossing toward the open panel
|
|
26
|
+
* over the rows between keeps it open, as Radix's grace area does. Right Arrow
|
|
27
|
+
* opens a row's submenu on its first row, and Left Arrow or Escape closes it
|
|
28
|
+
* and returns focus to the row, the two arrows swapping on a right-to-left
|
|
29
|
+
* page. The row says `aria-expanded`, and the panel is a `menu` named by the
|
|
30
|
+
* row.
|
|
22
31
|
*
|
|
23
32
|
* The root holds which page is open, because only one page shows at a time. It
|
|
24
33
|
* sets this on items as a property, never as an attribute. An attribute written
|
|
@@ -33,8 +42,8 @@ declare const HelixHeadlessMenu_base: (new (...args: any[]) => import("@fusion.d
|
|
|
33
42
|
* rows, Home and End go to the first and the last, a typed letter moves to
|
|
34
43
|
* the next row whose name starts with it, Enter and Space choose, Right Arrow
|
|
35
44
|
* opens a row's submenu and Left Arrow goes back, the two swapping on a
|
|
36
|
-
* right-to-left page
|
|
37
|
-
* trigger. Tab closes it and moves on.
|
|
45
|
+
* right-to-left page. Left Arrow on the top page does nothing. Escape closes
|
|
46
|
+
* the menu and returns focus to the trigger. Tab closes it and moves on.
|
|
38
47
|
*
|
|
39
48
|
* While it is open, the surface carries the trigger's width as
|
|
40
49
|
* `--floating-anchor-inline-size` and the room left below it as
|
|
@@ -46,7 +55,14 @@ declare const HelixHeadlessMenu_base: (new (...args: any[]) => import("@fusion.d
|
|
|
46
55
|
* right-click, a long press, Shift+F10 or the menu key over the area opens it
|
|
47
56
|
* at the pointer, or at the area's corner from the keyboard, as Radix's context
|
|
48
57
|
* menu does. A press on the area does not open it, and closing it returns focus
|
|
49
|
-
* to where it was.
|
|
58
|
+
* to where it was. A touch or a pen held still on the area opens it too, on
|
|
59
|
+
* every browser, since iOS sends no `contextmenu` for one.
|
|
60
|
+
*
|
|
61
|
+
* **A trigger with its own action.** With `open-on="long-press"`, a press on
|
|
62
|
+
* the trigger is the trigger's own, and a press held for half a second opens
|
|
63
|
+
* the menu, as React Aria's `trigger="longPress"` does. Alt+Down or Alt+Up
|
|
64
|
+
* opens it from the keyboard, and the trigger is described by
|
|
65
|
+
* `long-press-label`, which says so.
|
|
50
66
|
*
|
|
51
67
|
* @since 0.1.0
|
|
52
68
|
* @status stable
|
|
@@ -77,6 +93,10 @@ declare const HelixHeadlessMenu_base: (new (...args: any[]) => import("@fusion.d
|
|
|
77
93
|
* @cssstate drilled - A submenu's page is showing, so content that is not a row can hide with the top page.
|
|
78
94
|
* @cssstate mark - A row on the current page has a checkbox or a link, so every row keeps the trailing column.
|
|
79
95
|
* @cssstate open - While the menu is open.
|
|
96
|
+
* @cssstate placed-bottom - While the open panel sits below its trigger.
|
|
97
|
+
* @cssstate placed-left - While the open panel sits to the left of its trigger.
|
|
98
|
+
* @cssstate placed-right - While the open panel sits to the right of its trigger.
|
|
99
|
+
* @cssstate placed-top - While the open panel sits above its trigger.
|
|
80
100
|
* @cssstate start - A row on the current page has content in its leading column, so every row keeps that column.
|
|
81
101
|
*/
|
|
82
102
|
export declare class HelixHeadlessMenu extends HelixHeadlessMenu_base {
|
|
@@ -102,16 +122,58 @@ export declare class HelixHeadlessMenu extends HelixHeadlessMenu_base {
|
|
|
102
122
|
* `aria-labelledby` on the host names it too, and wins over both.
|
|
103
123
|
*/
|
|
104
124
|
label: string;
|
|
125
|
+
/** Whether submenus open beside their rows. */
|
|
126
|
+
private get flies();
|
|
127
|
+
/** The rows of the top page, read through each section. */
|
|
128
|
+
private get topRows();
|
|
129
|
+
/** The rows of a submenu, or of the top page for null. */
|
|
130
|
+
private rowsOf;
|
|
131
|
+
/** The row whose submenu holds a row, or null for a row of the top page. */
|
|
132
|
+
private holderOf;
|
|
133
|
+
/** The submenus open, from the top page down to the deepest. */
|
|
134
|
+
private get openPages();
|
|
135
|
+
/**
|
|
136
|
+
* The submenu the keys move in under `flyout`: the one holding the row with
|
|
137
|
+
* focus, or the deepest open one.
|
|
138
|
+
*/
|
|
139
|
+
private get keyPage();
|
|
140
|
+
/** Opens a submenu and closes the ones below it, or closes them all for null. */
|
|
141
|
+
private openPage;
|
|
142
|
+
/**
|
|
143
|
+
* The description a trigger gives under `open-on="long-press"`, saying how
|
|
144
|
+
* to open the menu. Translate it with the rest of your interface.
|
|
145
|
+
*/
|
|
146
|
+
longPressLabel: string;
|
|
105
147
|
/**
|
|
106
148
|
* Space between the trigger and the surface, in pixels.
|
|
107
149
|
*/
|
|
108
150
|
offset: number;
|
|
151
|
+
/** Whether the trigger opens the menu only on a held press. */
|
|
152
|
+
private get pressHeld();
|
|
153
|
+
/**
|
|
154
|
+
* Watches the trigger for a held press: under `open-on="long-press"`, and
|
|
155
|
+
* for a touch or a pen on a context menu's area.
|
|
156
|
+
*/
|
|
157
|
+
private readonly longPress;
|
|
158
|
+
/** Where a held press on a context menu's area is, to open the menu there. */
|
|
159
|
+
private heldAt;
|
|
160
|
+
/**
|
|
161
|
+
* What opens the menu from its trigger. `long-press` leaves a press to the
|
|
162
|
+
* trigger's own action and opens the menu on a press held for half a second,
|
|
163
|
+
* or on Alt+Down or Alt+Up.
|
|
164
|
+
*/
|
|
165
|
+
openOn: 'click' | 'long-press';
|
|
109
166
|
/**
|
|
110
167
|
* The side the menu opens on, when there is room.
|
|
111
168
|
*
|
|
112
169
|
* It moves if that side does not fit.
|
|
113
170
|
*/
|
|
114
171
|
placement: Placement;
|
|
172
|
+
/**
|
|
173
|
+
* How a submenu opens. `drill` shows it in place of the page showing, with
|
|
174
|
+
* a way back. `flyout` opens it in a panel of its own beside its row.
|
|
175
|
+
*/
|
|
176
|
+
submenus: 'drill' | 'flyout';
|
|
115
177
|
/**
|
|
116
178
|
* Which page is showing, and what is on it.
|
|
117
179
|
*
|
|
@@ -155,6 +217,10 @@ export declare class HelixHeadlessMenu extends HelixHeadlessMenu_base {
|
|
|
155
217
|
* hydration mismatch waiting to happen.
|
|
156
218
|
*/
|
|
157
219
|
private tellTrigger;
|
|
220
|
+
/** Whether a frame has passed since the first render, after which a plain trigger may be told. */
|
|
221
|
+
private settledOnce;
|
|
222
|
+
/** The plain trigger this menu writes `aria-expanded` on. */
|
|
223
|
+
private told;
|
|
158
224
|
/** The element the surface opens against. */
|
|
159
225
|
private get trigger();
|
|
160
226
|
/** The items on the page that is showing, which is what the keys move over. */
|
|
@@ -170,6 +236,8 @@ export declare class HelixHeadlessMenu extends HelixHeadlessMenu_base {
|
|
|
170
236
|
* the area's corner where the keyboard asked and the event has no position.
|
|
171
237
|
*/
|
|
172
238
|
private readonly handleContextMenu;
|
|
239
|
+
/** Opens a context menu at a point in the viewport, or moves an open one there. */
|
|
240
|
+
private openContextAt;
|
|
173
241
|
/** Shows an item's submenu in place of the current page. */
|
|
174
242
|
private enter;
|
|
175
243
|
/** Returns to the page above, or closes when there is none. */
|
|
@@ -192,6 +260,53 @@ export declare class HelixHeadlessMenu extends HelixHeadlessMenu_base {
|
|
|
192
260
|
*/
|
|
193
261
|
private handleClosedKeydown;
|
|
194
262
|
private readonly handleTriggerClick;
|
|
263
|
+
/** Whether an event came from the slotted trigger, or the area of a context menu. */
|
|
264
|
+
private fromTrigger;
|
|
265
|
+
/**
|
|
266
|
+
* A press on the trigger starting: the start of a held one under
|
|
267
|
+
* `open-on="long-press"`, or a touch or a pen held on a context menu's area.
|
|
268
|
+
*/
|
|
269
|
+
private readonly handlePointerDown;
|
|
270
|
+
private readonly handlePointerMove;
|
|
271
|
+
private readonly handlePointerUp;
|
|
272
|
+
private readonly handlePointerCancel;
|
|
273
|
+
/**
|
|
274
|
+
* Takes the click a held press ends in, ahead of the trigger's own
|
|
275
|
+
* listeners, so a press that opened the menu does not also do the trigger's
|
|
276
|
+
* action. In capture, on the host, which is above a slotted trigger.
|
|
277
|
+
*/
|
|
278
|
+
private readonly handleHeldClick;
|
|
279
|
+
/** The control the menu has described, so the description can be taken back. */
|
|
280
|
+
private described;
|
|
281
|
+
/**
|
|
282
|
+
* Describes the trigger by `long-press-label` under `open-on="long-press"`,
|
|
283
|
+
* or takes it back, by the host, which carries the words as its label, as
|
|
284
|
+
* `SpeedDial` does. On the consumer's own element only once a reader has
|
|
285
|
+
* reached it, which is after hydration.
|
|
286
|
+
*/
|
|
287
|
+
private describeTrigger;
|
|
288
|
+
/** A reader reaching the trigger, which is late enough to describe a plain one. */
|
|
289
|
+
private readonly handleTriggerFocusIn;
|
|
290
|
+
/** The row the pointer last rested on, under `flyout`. */
|
|
291
|
+
private hovered;
|
|
292
|
+
/** The wait before a rested-on row opens or closes a submenu. */
|
|
293
|
+
private hoverTimer;
|
|
294
|
+
/**
|
|
295
|
+
* The triangle from where the pointer left a row with an open submenu to the
|
|
296
|
+
* near edge of that submenu, and until when it holds.
|
|
297
|
+
*/
|
|
298
|
+
private grace;
|
|
299
|
+
private stopHover;
|
|
300
|
+
/**
|
|
301
|
+
* A mouse resting on a row, under `flyout`: focus follows it, and after a
|
|
302
|
+
* moment the row's submenu opens, or the submenus below the row's own page
|
|
303
|
+
* close. Not while the pointer is crossing toward an open submenu.
|
|
304
|
+
*/
|
|
305
|
+
private readonly handleHover;
|
|
306
|
+
/** The pointer leaving a row whose submenu is open, which starts the grace area. */
|
|
307
|
+
private readonly handleRowOut;
|
|
308
|
+
/** Whether the pointer is inside the grace area, while it holds. */
|
|
309
|
+
private inGrace;
|
|
195
310
|
connectedCallback(): void;
|
|
196
311
|
disconnectedCallback(): void;
|
|
197
312
|
protected updated(changed: PropertyValues): void;
|