@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
|
@@ -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 { AriaForwardController, afterLinkFollowed, customElement, HelixFormElement, hostAria, hostWords, isRtl, MixinControllerOpen, MixinControllerValue, MixinField, PopoverController, reportValues, typesCharacter, } from '@fusion.dev/helix-core';
|
|
7
|
+
import { AriaForwardController, afterLinkFollowed, customElement, drawAutofill, drawStatus, HelixFormElement, hostAria, hostWords, isRtl, MixinControllerOpen, MixinControllerValue, MixinField, onApple, PopoverController, reportValues, StatusController, typesCharacter, } from '@fusion.dev/helix-core';
|
|
8
8
|
import { caretDown } from '@fusion.dev/helix-icons';
|
|
9
9
|
import { nothing } from 'lit';
|
|
10
10
|
import { property } from 'lit/decorators.js';
|
|
@@ -23,6 +23,8 @@ let HelixHeadlessSelect = class HelixHeadlessSelect extends MixinControllerOpen(
|
|
|
23
23
|
},
|
|
24
24
|
target: () => this.partNamed('trigger'),
|
|
25
25
|
});
|
|
26
|
+
this.status = new StatusController(this);
|
|
27
|
+
this.autocomplete = '';
|
|
26
28
|
this.emptyText = 'No matches';
|
|
27
29
|
this.filterable = false;
|
|
28
30
|
this.focusable = true;
|
|
@@ -114,7 +116,17 @@ let HelixHeadlessSelect = class HelixHeadlessSelect extends MixinControllerOpen(
|
|
|
114
116
|
this.requestOpen(true);
|
|
115
117
|
}
|
|
116
118
|
else {
|
|
117
|
-
this.
|
|
119
|
+
this.choose(match);
|
|
120
|
+
}
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
const step = event.key === 'ArrowRight' ? 1 : event.key === 'ArrowLeft' ? -1 : 0;
|
|
124
|
+
if (step !== 0 && !this.multiple && !(event.altKey || event.ctrlKey || event.metaKey)) {
|
|
125
|
+
event.preventDefault();
|
|
126
|
+
const reachable = listbox?.options.filter((option) => !option.hidden && !option.disabled) ?? [];
|
|
127
|
+
const next = reachable[Math.max(0, reachable.findIndex((one) => one.value === this.value) + step)];
|
|
128
|
+
if (next) {
|
|
129
|
+
this.choose(next.value);
|
|
118
130
|
}
|
|
119
131
|
return;
|
|
120
132
|
}
|
|
@@ -151,12 +163,11 @@ let HelixHeadlessSelect = class HelixHeadlessSelect extends MixinControllerOpen(
|
|
|
151
163
|
return this.partNamed('listbox');
|
|
152
164
|
}
|
|
153
165
|
get display() {
|
|
154
|
-
const chosen = new Set(this.value === '' ? [] : this.value.split(','));
|
|
155
166
|
const words = this.chosenOptions.map((option) => option.text);
|
|
156
167
|
if (words.length > 0) {
|
|
157
168
|
return new Intl.ListFormat(this.lang || undefined, { style: 'short', type: 'unit' }).format(words);
|
|
158
169
|
}
|
|
159
|
-
return
|
|
170
|
+
return this.value !== '' && !this.childrenKnown ? nothing : this.placeholder;
|
|
160
171
|
}
|
|
161
172
|
get chosenOptions() {
|
|
162
173
|
const chosen = new Set(this.value === '' ? [] : this.value.split(','));
|
|
@@ -207,6 +218,11 @@ let HelixHeadlessSelect = class HelixHeadlessSelect extends MixinControllerOpen(
|
|
|
207
218
|
this.requestOpen(false);
|
|
208
219
|
this.partNamed('trigger')?.focus();
|
|
209
220
|
}
|
|
221
|
+
choose(value) {
|
|
222
|
+
if (this.requestValue(value) && onApple()) {
|
|
223
|
+
this.status.say(String(this.display));
|
|
224
|
+
}
|
|
225
|
+
}
|
|
210
226
|
reconcileOptions() {
|
|
211
227
|
const options = optionsAmong(this.knownChildren);
|
|
212
228
|
if (this.defaultValue === '' && !this.hasAttribute('default-value')) {
|
|
@@ -283,12 +299,11 @@ let HelixHeadlessSelect = class HelixHeadlessSelect extends MixinControllerOpen(
|
|
|
283
299
|
manual
|
|
284
300
|
aria-label=${this.listName || this.listLabel || nothing}
|
|
285
301
|
@keydown=${this.handleListKeydown}
|
|
302
|
+
.chosenAgain=${() => this.close()}
|
|
286
303
|
@helix-before-change=${this.handleChange}
|
|
287
304
|
@change=${this.stopInner}
|
|
288
305
|
@input=${this.stopInner}
|
|
289
306
|
>
|
|
290
|
-
<!-- An option arriving or leaving changes no property, so this is what
|
|
291
|
-
schedules the update that rereads the trigger's words. -->
|
|
292
307
|
<slot @slotchange=${() => this.requestUpdate()}></slot>
|
|
293
308
|
</${this.tag('listbox')}>`;
|
|
294
309
|
}
|
|
@@ -327,18 +342,27 @@ let HelixHeadlessSelect = class HelixHeadlessSelect extends MixinControllerOpen(
|
|
|
327
342
|
>
|
|
328
343
|
<slot name="start"></slot>
|
|
329
344
|
<span part="value" id="value">${this.renderValue()}</span>
|
|
330
|
-
${''}
|
|
331
345
|
<slot name="end"></slot>
|
|
332
|
-
${''}
|
|
333
346
|
${this.renderIndicator()}
|
|
334
347
|
</button>
|
|
335
348
|
|
|
336
349
|
<div part="surface" popover="manual" ?hidden=${!this.open}>
|
|
337
350
|
${this.renderListbox()}
|
|
338
351
|
</div>
|
|
339
|
-
${this.renderHint()}${this.renderFeedback()}
|
|
352
|
+
${this.renderHint()}${this.renderFeedback()}${drawStatus(this.status)}
|
|
353
|
+
${drawAutofill({
|
|
354
|
+
autocomplete: this.autocomplete,
|
|
355
|
+
disabled: this.isDisabled,
|
|
356
|
+
multiple: this.multiple,
|
|
357
|
+
onFill: (value) => this.requestValue(value),
|
|
358
|
+
options: optionsAmong(this.knownChildren),
|
|
359
|
+
value: this.value,
|
|
360
|
+
})}`;
|
|
340
361
|
}
|
|
341
362
|
};
|
|
363
|
+
__decorate([
|
|
364
|
+
property()
|
|
365
|
+
], HelixHeadlessSelect.prototype, "autocomplete", void 0);
|
|
342
366
|
__decorate([
|
|
343
367
|
property({ attribute: 'empty-text' })
|
|
344
368
|
], HelixHeadlessSelect.prototype, "emptyText", void 0);
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { HelixElement } from '@fusion.dev/helix-core';
|
|
2
|
+
import { type PropertyValues, type TemplateResult } from 'lit';
|
|
3
|
+
/**
|
|
4
|
+
* @summary A named set of actions inside a speed dial, set apart from the rest.
|
|
5
|
+
*
|
|
6
|
+
* @description
|
|
7
|
+
* A `group` of the dial's menu, named by its label, as React Aria's menu
|
|
8
|
+
* sections and Menu's groups are: a reader hears the name on entering the set,
|
|
9
|
+
* and the arrow keys walk through every group's actions in order, from the
|
|
10
|
+
* last action of one group to the first of the next.
|
|
11
|
+
*
|
|
12
|
+
* Put actions straight into the group, and the group straight into the dial.
|
|
13
|
+
* Anything else slotted here is drawn and is not an action.
|
|
14
|
+
*
|
|
15
|
+
* @since 0.141.0
|
|
16
|
+
* @status stable
|
|
17
|
+
* @category actions
|
|
18
|
+
* @aliases Speed dial section, Action group
|
|
19
|
+
* @documentation https://fusion.dev/helix/components/speed-dial
|
|
20
|
+
*
|
|
21
|
+
* @slot - The group's actions.
|
|
22
|
+
* @slot label - The group's name, for content richer than the `label` text.
|
|
23
|
+
*
|
|
24
|
+
* @csspart group - The set, which carries the `group` role.
|
|
25
|
+
* @csspart label - The group's name, drawn above its actions while there is one.
|
|
26
|
+
*/
|
|
27
|
+
export declare class HelixHeadlessSpeedDialGroup extends HelixElement {
|
|
28
|
+
/**
|
|
29
|
+
* The group's name, drawn above its actions and read by screen readers.
|
|
30
|
+
*/
|
|
31
|
+
label: string;
|
|
32
|
+
/**
|
|
33
|
+
* @internal Read by the dial to find the actions inside a group, which is
|
|
34
|
+
* cheaper than `instanceof` and is why this is public rather than protected.
|
|
35
|
+
* Not a consumer's to read or set.
|
|
36
|
+
*/
|
|
37
|
+
readonly isSpeedDialGroup = true;
|
|
38
|
+
/** Whether anything is in the `label` slot, so the name is drawn from it. */
|
|
39
|
+
private labelSlotted;
|
|
40
|
+
private readonly readLabel;
|
|
41
|
+
protected firstUpdated(changed: PropertyValues): void;
|
|
42
|
+
protected render(): TemplateResult;
|
|
43
|
+
}
|
|
44
|
+
declare global {
|
|
45
|
+
interface HTMLElementTagNameMap {
|
|
46
|
+
'helix-headless-speed-dial-group': HelixHeadlessSpeedDialGroup;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
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
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
import { customElement, HelixElement, slotFilled } from '@fusion.dev/helix-core';
|
|
8
|
+
import { html, nothing } from 'lit';
|
|
9
|
+
import { property, state } from 'lit/decorators.js';
|
|
10
|
+
let HelixHeadlessSpeedDialGroup = class HelixHeadlessSpeedDialGroup extends HelixElement {
|
|
11
|
+
constructor() {
|
|
12
|
+
super(...arguments);
|
|
13
|
+
this.label = '';
|
|
14
|
+
this.isSpeedDialGroup = true;
|
|
15
|
+
this.labelSlotted = false;
|
|
16
|
+
this.readLabel = () => {
|
|
17
|
+
this.labelSlotted = slotFilled(this.renderRoot.querySelector('slot[name="label"]'));
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
firstUpdated(changed) {
|
|
21
|
+
super.firstUpdated(changed);
|
|
22
|
+
this.readLabel();
|
|
23
|
+
}
|
|
24
|
+
render() {
|
|
25
|
+
const named = this.label !== '' || this.labelSlotted;
|
|
26
|
+
return html `<div part="group" role="group" aria-labelledby=${named ? 'label' : nothing}>
|
|
27
|
+
<span id="label" part="label" ?hidden=${!named}
|
|
28
|
+
><slot name="label" @slotchange=${this.readLabel}>${this.label}</slot></span
|
|
29
|
+
>
|
|
30
|
+
<slot></slot>
|
|
31
|
+
</div>`;
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
__decorate([
|
|
35
|
+
property()
|
|
36
|
+
], HelixHeadlessSpeedDialGroup.prototype, "label", void 0);
|
|
37
|
+
__decorate([
|
|
38
|
+
state()
|
|
39
|
+
], HelixHeadlessSpeedDialGroup.prototype, "labelSlotted", void 0);
|
|
40
|
+
HelixHeadlessSpeedDialGroup = __decorate([
|
|
41
|
+
customElement('helix-headless-speed-dial-group')
|
|
42
|
+
], HelixHeadlessSpeedDialGroup);
|
|
43
|
+
export { HelixHeadlessSpeedDialGroup };
|
package/dist/speed-dial/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { HelixElement } from '@fusion.dev/helix-core';
|
|
2
|
-
import { type TemplateResult } from 'lit';
|
|
2
|
+
import { type PropertyValues, type TemplateResult } from 'lit';
|
|
3
3
|
/**
|
|
4
4
|
* @summary One action in a speed dial: an icon and a name beside it.
|
|
5
5
|
*
|
|
@@ -12,6 +12,13 @@ import { type TemplateResult } from 'lit';
|
|
|
12
12
|
* `title` on an icon shows only on hover. Visible text is read, searched, and
|
|
13
13
|
* translated like other text.
|
|
14
14
|
*
|
|
15
|
+
* `shortcut` gives the action's keyboard shortcut as a chord, such as `mod+s`,
|
|
16
|
+
* the grammar Menu's rows use. The action carries it as `aria-keyshortcuts`
|
|
17
|
+
* and draws it after the name in the platform's glyphs or words, `⌘S` on an
|
|
18
|
+
* Apple keyboard and `Ctrl+S` elsewhere, as Menu draws its rows' shortcuts.
|
|
19
|
+
* Put a `Kbd` in the `shortcut` slot to draw it as keys instead; the dial
|
|
20
|
+
* imports no `Kbd` of its own. Showing a shortcut does not bind it.
|
|
21
|
+
*
|
|
15
22
|
* @since 0.1.0
|
|
16
23
|
* @status stable
|
|
17
24
|
* @category actions
|
|
@@ -20,18 +27,26 @@ import { type TemplateResult } from 'lit';
|
|
|
20
27
|
*
|
|
21
28
|
* @slot - The action's name.
|
|
22
29
|
* @slot icon - The action's icon, the main way the action is recognized.
|
|
30
|
+
* @slot shortcut - The action's keyboard shortcut, drawn after its name, such as a `Kbd`. It replaces the text drawn for `shortcut`, and is hidden from the name, which `aria-keyshortcuts` carries instead.
|
|
23
31
|
*
|
|
24
32
|
* @event {HelixSelectEvent} helix-select - The action was chosen. Cancelable: cancel it to keep the dial open.
|
|
25
33
|
*
|
|
26
34
|
* @csspart icon - The box that holds the icon.
|
|
27
35
|
* @csspart item - The control for the action.
|
|
28
36
|
* @csspart label - The action's name, beside the icon.
|
|
37
|
+
* @csspart shortcut - The box after the name that holds the shortcut, shown while `shortcut` is set or something is in the `shortcut` slot.
|
|
29
38
|
*/
|
|
30
39
|
export declare class HelixHeadlessSpeedDialItem extends HelixElement {
|
|
31
40
|
/**
|
|
32
41
|
* Whether the action is disabled, so it cannot be chosen.
|
|
33
42
|
*/
|
|
34
43
|
disabled: boolean;
|
|
44
|
+
/**
|
|
45
|
+
* The action's keyboard shortcut as a chord, such as `mod+s`, carried as
|
|
46
|
+
* `aria-keyshortcuts` and drawn after the name. `mod` is Command on an Apple
|
|
47
|
+
* keyboard and Control elsewhere. Showing a shortcut does not bind it.
|
|
48
|
+
*/
|
|
49
|
+
shortcut: string;
|
|
35
50
|
/**
|
|
36
51
|
* The action's value, sent in `helix-select` when the action is chosen.
|
|
37
52
|
*/
|
|
@@ -66,9 +81,19 @@ export declare class HelixHeadlessSpeedDialItem extends HelixElement {
|
|
|
66
81
|
* chosen out of a set, and a speed dial's action is never chosen that way.
|
|
67
82
|
*/
|
|
68
83
|
get selected(): boolean;
|
|
84
|
+
/** Whether anything is in the `shortcut` slot, so its box is drawn. */
|
|
85
|
+
private hasShortcut;
|
|
86
|
+
private readonly readShortcut;
|
|
69
87
|
private choose;
|
|
70
88
|
private readonly handleClick;
|
|
71
89
|
protected render(): TemplateResult;
|
|
90
|
+
/**
|
|
91
|
+
* The `shortcut` slot, holding the shortcut drawn after the name as Kbd
|
|
92
|
+
* draws a chord and from the same table: the slot's fallback, so a `Kbd`
|
|
93
|
+
* slotted in replaces it.
|
|
94
|
+
*/
|
|
95
|
+
protected renderShortcut(): TemplateResult;
|
|
96
|
+
protected firstUpdated(changed: PropertyValues): void;
|
|
72
97
|
}
|
|
73
98
|
declare global {
|
|
74
99
|
interface HTMLElementTagNameMap {
|
|
@@ -4,15 +4,21 @@ 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, deepActiveElement, HelixElement, HelixSelectEvent, } from '@fusion.dev/helix-core';
|
|
7
|
+
import { ariaChord, chordParts, customElement, deepActiveElement, HelixElement, HelixSelectEvent, keyNames, onApple, reportRuntime, slotFilled, } from '@fusion.dev/helix-core';
|
|
8
8
|
import { html, nothing } from 'lit';
|
|
9
|
-
import { property } from 'lit/decorators.js';
|
|
9
|
+
import { property, state } from 'lit/decorators.js';
|
|
10
|
+
let toldSelected = false;
|
|
10
11
|
let HelixHeadlessSpeedDialItem = class HelixHeadlessSpeedDialItem extends HelixElement {
|
|
11
12
|
constructor() {
|
|
12
13
|
super(...arguments);
|
|
13
14
|
this.disabled = false;
|
|
15
|
+
this.shortcut = '';
|
|
14
16
|
this.value = '';
|
|
15
17
|
this.isSpeedDialItem = true;
|
|
18
|
+
this.hasShortcut = false;
|
|
19
|
+
this.readShortcut = () => {
|
|
20
|
+
this.hasShortcut = slotFilled(this.renderRoot.querySelector('slot[name="shortcut"]'));
|
|
21
|
+
};
|
|
16
22
|
this.handleClick = () => {
|
|
17
23
|
this.choose();
|
|
18
24
|
};
|
|
@@ -31,6 +37,14 @@ let HelixHeadlessSpeedDialItem = class HelixHeadlessSpeedDialItem extends HelixE
|
|
|
31
37
|
return active !== null && active.getRootNode() === this.renderRoot;
|
|
32
38
|
}
|
|
33
39
|
get selected() {
|
|
40
|
+
if (!toldSelected) {
|
|
41
|
+
toldSelected = true;
|
|
42
|
+
reportRuntime({
|
|
43
|
+
kind: 'deprecated',
|
|
44
|
+
message: `<${this.localName}>.selected is deprecated: read \`active\`, which says the same.`,
|
|
45
|
+
tag: this.localName,
|
|
46
|
+
});
|
|
47
|
+
}
|
|
34
48
|
return this.active;
|
|
35
49
|
}
|
|
36
50
|
choose() {
|
|
@@ -40,26 +54,50 @@ let HelixHeadlessSpeedDialItem = class HelixHeadlessSpeedDialItem extends HelixE
|
|
|
40
54
|
this.dispatchEvent(new HelixSelectEvent({ value: this.value }));
|
|
41
55
|
}
|
|
42
56
|
render() {
|
|
57
|
+
const keys = this.shortcut === '' ? nothing : ariaChord(this.shortcut, !this.hydrating && onApple());
|
|
43
58
|
return html `<button
|
|
44
59
|
part="item"
|
|
45
60
|
role="menuitem"
|
|
46
61
|
type="button"
|
|
47
62
|
aria-disabled=${this.disabled ? 'true' : nothing}
|
|
63
|
+
aria-keyshortcuts=${keys}
|
|
48
64
|
?disabled=${this.disabled}
|
|
49
65
|
tabindex="-1"
|
|
50
66
|
@click=${this.handleClick}
|
|
51
67
|
>
|
|
52
68
|
<span part="icon"><slot name="icon"></slot></span>
|
|
53
|
-
<span part="label"><slot></slot
|
|
69
|
+
<span part="label"><slot></slot><span
|
|
70
|
+
part="shortcut"
|
|
71
|
+
aria-hidden="true"
|
|
72
|
+
?hidden=${!this.hasShortcut && this.shortcut === ''}
|
|
73
|
+
>${this.renderShortcut()}</span
|
|
74
|
+
></span>
|
|
54
75
|
</button>`;
|
|
55
76
|
}
|
|
77
|
+
renderShortcut() {
|
|
78
|
+
const drawn = keyNames(chordParts(this.shortcut), !this.hydrating && onApple());
|
|
79
|
+
return html `<slot name="shortcut" @slotchange=${this.readShortcut}>${drawn.keys.join(drawn.separator)}</slot>`;
|
|
80
|
+
}
|
|
81
|
+
firstUpdated(changed) {
|
|
82
|
+
super.firstUpdated(changed);
|
|
83
|
+
this.readShortcut();
|
|
84
|
+
if (this.shortcut !== '') {
|
|
85
|
+
this.finishHydration();
|
|
86
|
+
}
|
|
87
|
+
}
|
|
56
88
|
};
|
|
57
89
|
__decorate([
|
|
58
90
|
property({ reflect: true, type: Boolean })
|
|
59
91
|
], HelixHeadlessSpeedDialItem.prototype, "disabled", void 0);
|
|
92
|
+
__decorate([
|
|
93
|
+
property()
|
|
94
|
+
], HelixHeadlessSpeedDialItem.prototype, "shortcut", void 0);
|
|
60
95
|
__decorate([
|
|
61
96
|
property()
|
|
62
97
|
], HelixHeadlessSpeedDialItem.prototype, "value", void 0);
|
|
98
|
+
__decorate([
|
|
99
|
+
state()
|
|
100
|
+
], HelixHeadlessSpeedDialItem.prototype, "hasShortcut", void 0);
|
|
63
101
|
HelixHeadlessSpeedDialItem = __decorate([
|
|
64
102
|
customElement('helix-headless-speed-dial-item')
|
|
65
103
|
], HelixHeadlessSpeedDialItem);
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { ReactiveControllerHost } from 'lit';
|
|
2
|
+
/** How long a press is held before it counts, in milliseconds, as React Aria waits. */
|
|
3
|
+
export declare const LONG_PRESS_DELAY = 500;
|
|
4
|
+
/**
|
|
5
|
+
* How far a held pointer may drift and still be the same press, in pixels. A
|
|
6
|
+
* finger is never still, and one that moves further is scrolling or dragging.
|
|
7
|
+
*/
|
|
8
|
+
export declare const LONG_PRESS_SLOP = 10;
|
|
9
|
+
/** What a long press reads and calls. */
|
|
10
|
+
export interface LongPressOptions {
|
|
11
|
+
/** Called once a press has been held for the delay without drifting. */
|
|
12
|
+
onLongPress: () => void;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Watches one control's presses for a held one.
|
|
16
|
+
*
|
|
17
|
+
* The control's own handlers call `down`, `move` and `up`, so a press counts
|
|
18
|
+
* only where it started on the control, and `cancel` drops it. The click a
|
|
19
|
+
* held press ends in is the long press's, and `takeClick` says so once, so the
|
|
20
|
+
* control's own action does not run as well.
|
|
21
|
+
*/
|
|
22
|
+
export declare class LongPress {
|
|
23
|
+
private readonly options;
|
|
24
|
+
private readonly wait;
|
|
25
|
+
/** Where the press began, while one is held and still counts. */
|
|
26
|
+
private from;
|
|
27
|
+
/** Whether the press that just ended was a long one, until its click is taken. */
|
|
28
|
+
private held;
|
|
29
|
+
constructor(host: ReactiveControllerHost, options: LongPressOptions);
|
|
30
|
+
/** Starts the wait for a press of the primary button. */
|
|
31
|
+
down(event: PointerEvent): void;
|
|
32
|
+
/** Drops the press once the pointer drifts past the slop. */
|
|
33
|
+
move(event: PointerEvent): void;
|
|
34
|
+
/**
|
|
35
|
+
* Ends the press. A press released before the delay is an ordinary one.
|
|
36
|
+
*
|
|
37
|
+
* A held one keeps its claim on a click only until the task ends: the click
|
|
38
|
+
* a release makes comes in the same task, and a release that makes none, off
|
|
39
|
+
* the control or after a touch's own menu, must not leave the next Enter on
|
|
40
|
+
* the control swallowed.
|
|
41
|
+
*/
|
|
42
|
+
up(): void;
|
|
43
|
+
/** Drops the press, which then neither opens anything nor swallows a click. */
|
|
44
|
+
cancel(): void;
|
|
45
|
+
/**
|
|
46
|
+
* Whether a click belongs to the long press that just ended, and so is not
|
|
47
|
+
* the control's. True once.
|
|
48
|
+
*/
|
|
49
|
+
takeClick(): boolean;
|
|
50
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { DelayController } from '@fusion.dev/helix-core';
|
|
2
|
+
export const LONG_PRESS_DELAY = 500;
|
|
3
|
+
export const LONG_PRESS_SLOP = 10;
|
|
4
|
+
export class LongPress {
|
|
5
|
+
constructor(host, options) {
|
|
6
|
+
this.options = options;
|
|
7
|
+
this.from = undefined;
|
|
8
|
+
this.held = false;
|
|
9
|
+
this.wait = new DelayController(host);
|
|
10
|
+
}
|
|
11
|
+
down(event) {
|
|
12
|
+
this.held = false;
|
|
13
|
+
if (event.button !== 0) {
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
this.from = { x: event.clientX, y: event.clientY };
|
|
17
|
+
this.wait.start(LONG_PRESS_DELAY, () => {
|
|
18
|
+
this.from = undefined;
|
|
19
|
+
this.held = true;
|
|
20
|
+
this.options.onLongPress();
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
move(event) {
|
|
24
|
+
const from = this.from;
|
|
25
|
+
if (from && Math.hypot(event.clientX - from.x, event.clientY - from.y) > LONG_PRESS_SLOP) {
|
|
26
|
+
this.cancel();
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
up() {
|
|
30
|
+
this.from = undefined;
|
|
31
|
+
this.wait.cancel();
|
|
32
|
+
if (this.held) {
|
|
33
|
+
setTimeout(() => {
|
|
34
|
+
this.held = false;
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
cancel() {
|
|
39
|
+
this.from = undefined;
|
|
40
|
+
this.held = false;
|
|
41
|
+
this.wait.cancel();
|
|
42
|
+
}
|
|
43
|
+
takeClick() {
|
|
44
|
+
const held = this.held;
|
|
45
|
+
this.held = false;
|
|
46
|
+
return held;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -25,6 +25,31 @@ declare const HelixHeadlessSpeedDial_base: (new (...args: any[]) => import("@fus
|
|
|
25
25
|
* action whose name starts with it. The actions are one tab stop, and Tab
|
|
26
26
|
* closes the dial and moves on.
|
|
27
27
|
*
|
|
28
|
+
* **Keyboard.** On the trigger, Enter, Space or a press opens the dial on its
|
|
29
|
+
* first action. With the dial open:
|
|
30
|
+
*
|
|
31
|
+
* | Key | What it does |
|
|
32
|
+
* |---|---|
|
|
33
|
+
* | Down, Right | Moves to the next action, from the last back to the first. |
|
|
34
|
+
* | Up, Left | Moves to the action before, from the first round to the last. |
|
|
35
|
+
* | Home, End | Moves to the first or the last action. |
|
|
36
|
+
* | A letter | Moves to the next action whose name starts with it. |
|
|
37
|
+
* | Enter, Space | Chooses the action, and the dial closes. |
|
|
38
|
+
* | Escape | Closes the dial and puts focus back on the trigger. |
|
|
39
|
+
* | Tab | Closes the dial and moves on. |
|
|
40
|
+
*
|
|
41
|
+
* **Groups.** Put actions in `Speed Dial.Group`, named by its `label`, to set
|
|
42
|
+
* them apart as Menu's sections do: each is a `group` of the menu, and the
|
|
43
|
+
* arrow keys walk through every group's actions in order.
|
|
44
|
+
*
|
|
45
|
+
* **A trigger with its own action.** With `open-on="long-press"`, a press on
|
|
46
|
+
* the trigger is the trigger's own, such as composing a message, and holding it
|
|
47
|
+
* for half a second opens the dial, as React Aria's menu trigger does with
|
|
48
|
+
* `trigger="longPress"`. Alt+Down or Alt+Up opens it from the keyboard, and
|
|
49
|
+
* the trigger says so in its description, `long-press-label`, the drawn one
|
|
50
|
+
* and a slotted one alike. A press that has opened the dial does not also do
|
|
51
|
+
* the trigger's action.
|
|
52
|
+
*
|
|
28
53
|
* @since 0.1.0
|
|
29
54
|
* @status stable
|
|
30
55
|
* @category actions
|
|
@@ -49,10 +74,26 @@ export declare class HelixHeadlessSpeedDial extends HelixHeadlessSpeedDial_base
|
|
|
49
74
|
* Accessible name for the trigger and the set of actions it opens.
|
|
50
75
|
*/
|
|
51
76
|
label: string;
|
|
77
|
+
/**
|
|
78
|
+
* The description the trigger gives under `open-on="long-press"`, saying how
|
|
79
|
+
* its actions are reached.
|
|
80
|
+
*
|
|
81
|
+
* A slotted trigger is described by the dial, which carries the words as
|
|
82
|
+
* its own label for that: a Helix button's inner control at once, and a
|
|
83
|
+
* plain element of yours once a reader reaches it, after hydration, unless
|
|
84
|
+
* you described or labeled it yourself.
|
|
85
|
+
*/
|
|
86
|
+
longPressLabel: string;
|
|
52
87
|
/**
|
|
53
88
|
* Space between the trigger and the actions, in pixels.
|
|
54
89
|
*/
|
|
55
90
|
offset: number;
|
|
91
|
+
/**
|
|
92
|
+
* What opens the dial. `click` opens it on a press of the trigger. Under
|
|
93
|
+
* `long-press`, a press is the trigger's own action, and the dial opens on a
|
|
94
|
+
* press held for half a second, or on Alt+Down or Alt+Up.
|
|
95
|
+
*/
|
|
96
|
+
openOn: 'click' | 'long-press';
|
|
56
97
|
/**
|
|
57
98
|
* The direction the actions open in. They move to another side if that side
|
|
58
99
|
* does not fit.
|
|
@@ -74,8 +115,14 @@ export declare class HelixHeadlessSpeedDial extends HelixHeadlessSpeedDial_base
|
|
|
74
115
|
protected readonly panel: PopoverController;
|
|
75
116
|
/** Whatever the consumer slotted, or the button rendered in its place. */
|
|
76
117
|
private get trigger();
|
|
77
|
-
/**
|
|
118
|
+
/**
|
|
119
|
+
* The actions, in order: this element's own rows, and the rows of each group
|
|
120
|
+
* slotted beside them, and nothing else slotted.
|
|
121
|
+
*/
|
|
78
122
|
private get items();
|
|
123
|
+
/** Watches the trigger for a held press, under `open-on="long-press"`. */
|
|
124
|
+
private readonly longPress;
|
|
125
|
+
private get pressHeld();
|
|
79
126
|
/**
|
|
80
127
|
* Puts focus on the trigger, the slotted one or the button drawn in its
|
|
81
128
|
* place, as a `focus()` on a native control would. The host itself takes no
|
|
@@ -85,6 +132,17 @@ export declare class HelixHeadlessSpeedDial extends HelixHeadlessSpeedDial_base
|
|
|
85
132
|
/** Shuts it and puts the reader back on the trigger. */
|
|
86
133
|
private close;
|
|
87
134
|
private readonly handleTriggerClick;
|
|
135
|
+
/**
|
|
136
|
+
* Takes the click a held press ends in, ahead of the trigger's own
|
|
137
|
+
* listeners, so a press that opened the dial does not also do the trigger's
|
|
138
|
+
* action. In capture, on the host, which is above a slotted trigger.
|
|
139
|
+
*/
|
|
140
|
+
private readonly handleHeldClick;
|
|
141
|
+
private readonly handleTriggerPointerDown;
|
|
142
|
+
private readonly handleTriggerPointerMove;
|
|
143
|
+
private readonly handleTriggerPointerUp;
|
|
144
|
+
private readonly handleTriggerPointerCancel;
|
|
145
|
+
private readonly handleTriggerContextMenu;
|
|
88
146
|
private readonly handleSelect;
|
|
89
147
|
private readonly handleKeydown;
|
|
90
148
|
connectedCallback(): void;
|
|
@@ -113,6 +171,27 @@ export declare class HelixHeadlessSpeedDial extends HelixHeadlessSpeedDial_base
|
|
|
113
171
|
* is expanded is already saying it has something to expand.
|
|
114
172
|
*/
|
|
115
173
|
private announce;
|
|
174
|
+
/** The control the dial has described, so the description can be taken back. */
|
|
175
|
+
private described;
|
|
176
|
+
/**
|
|
177
|
+
* Describes a slotted trigger by `long-press-label` under
|
|
178
|
+
* `open-on="long-press"`, as the drawn one is, or takes it back.
|
|
179
|
+
*
|
|
180
|
+
* **By the host, which carries the words as its label.** The drawn
|
|
181
|
+
* trigger's description is in this shadow root, and a reference from the
|
|
182
|
+
* consumer's trigger, or from the button inside a Helix one, cannot point
|
|
183
|
+
* in; it can point out, to the host. Core's `linkDescription` says where
|
|
184
|
+
* that is safe: inside a component's shadow root always, and on the
|
|
185
|
+
* consumer's own element only once `light` says a reader has acted, which
|
|
186
|
+
* is after hydration. React Aria's long press describes whatever trigger it
|
|
187
|
+
* is given.
|
|
188
|
+
*
|
|
189
|
+
* @param light A reader has just reached the trigger.
|
|
190
|
+
*/
|
|
191
|
+
private describeTrigger;
|
|
192
|
+
/** A reader reaching a slotted trigger, which is late enough to describe a plain one. */
|
|
193
|
+
private readonly handleTriggerFocusIn;
|
|
194
|
+
protected firstUpdated(changed: PropertyValues): void;
|
|
116
195
|
protected updated(changed: PropertyValues): void;
|
|
117
196
|
/**
|
|
118
197
|
* The control that opens it, behind a seam the styled tier can move.
|