@fusion.dev/helix-headless 0.149.1 → 0.150.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 +471 -45
- package/dist/button/root/button-root.d.ts +5 -0
- package/dist/button/root/button-root.js +3 -0
- package/dist/color-input/root/color-input-root.d.ts +1 -0
- package/dist/color-picker/root/color-picker-root.js +1 -0
- package/dist/combobox/root/combobox-root.d.ts +12 -1
- package/dist/combobox/root/combobox-root.js +8 -0
- package/dist/copy-button/root/copy-button-root.d.ts +4 -3
- package/dist/copy-button/root/copy-button-root.js +5 -1
- package/dist/date-input/root/date-input-root.d.ts +1 -0
- package/dist/element-events.generated.js +1 -1
- package/dist/filter-input/chip/filter-chip.d.ts +2 -1
- package/dist/filter-input/root/filter-input-root.d.ts +5 -0
- package/dist/filter-input/root/filter-input-root.js +7 -0
- package/dist/number-input/root/number-input-root.d.ts +6 -0
- package/dist/number-input/root/number-input-root.js +14 -3
- package/dist/option/root/option-root.d.ts +17 -8
- package/dist/option/root/option-root.js +18 -4
- package/dist/radio/group/radio-group.d.ts +8 -1
- package/dist/radio/group/radio-group.js +15 -1
- package/dist/slider/root/slider-root.d.ts +8 -0
- package/dist/slider/root/slider-root.js +6 -3
- package/dist/tag/group/tag-group.d.ts +22 -1
- package/dist/tag/group/tag-group.js +63 -3
- package/dist/tag/root/tag-root.d.ts +9 -0
- package/dist/tag/root/tag-root.js +5 -1
- package/dist/time-input/root/time-input-root.d.ts +1 -0
- package/dist/toggle/group/toggle-group.d.ts +6 -0
- package/dist/toggle/group/toggle-group.js +16 -1
- package/dist/toggle/root/toggle-root.d.ts +21 -3
- package/dist/toggle/root/toggle-root.js +22 -5
- package/lint.json +1 -1
- package/package.json +5 -5
- package/types/jsx.d.ts +160 -58
- package/types/vue.d.ts +28 -16
|
@@ -70,6 +70,15 @@ export declare class HelixHeadlessTag extends HelixHeadlessTag_base {
|
|
|
70
70
|
* tag's words in it, so a translation puts them where its language does.
|
|
71
71
|
*/
|
|
72
72
|
dismissLabel: string;
|
|
73
|
+
/**
|
|
74
|
+
* Whether the remove button is a tab stop of its own, outside a `Tag.Group`.
|
|
75
|
+
*
|
|
76
|
+
* A field that holds tags as its tokens, such as a `multiple` Combobox,
|
|
77
|
+
* writes `false`: the field's text is its one tab stop, and the arrow keys
|
|
78
|
+
* move from the text to the tags. No attribute: it is coordination between
|
|
79
|
+
* elements, as `focusable` is.
|
|
80
|
+
*/
|
|
81
|
+
dismissTabStop: boolean;
|
|
73
82
|
/**
|
|
74
83
|
* Whether the tag holds its group's tab stop.
|
|
75
84
|
*
|
|
@@ -12,6 +12,7 @@ let HelixHeadlessTag = class HelixHeadlessTag extends MixinDismissible(HelixElem
|
|
|
12
12
|
super(...arguments);
|
|
13
13
|
this.disabled = false;
|
|
14
14
|
this.dismissLabel = 'Remove {label}';
|
|
15
|
+
this.dismissTabStop = true;
|
|
15
16
|
this.href = '';
|
|
16
17
|
this.value = '';
|
|
17
18
|
}
|
|
@@ -87,7 +88,7 @@ let HelixHeadlessTag = class HelixHeadlessTag extends MixinDismissible(HelixElem
|
|
|
87
88
|
label: this.removeName,
|
|
88
89
|
mark: html `<slot name="dismiss-icon">${drawDismissGlyph()}</slot>`,
|
|
89
90
|
onDismiss: () => this.dismiss(),
|
|
90
|
-
tabindex: this.grouped ? '-1' : undefined,
|
|
91
|
+
tabindex: this.grouped || !this.dismissTabStop ? '-1' : undefined,
|
|
91
92
|
});
|
|
92
93
|
}
|
|
93
94
|
};
|
|
@@ -98,6 +99,9 @@ __decorate([
|
|
|
98
99
|
words,
|
|
99
100
|
property({ attribute: 'dismiss-label' })
|
|
100
101
|
], HelixHeadlessTag.prototype, "dismissLabel", void 0);
|
|
102
|
+
__decorate([
|
|
103
|
+
property({ attribute: false })
|
|
104
|
+
], HelixHeadlessTag.prototype, "dismissTabStop", void 0);
|
|
101
105
|
__decorate([
|
|
102
106
|
property({ attribute: false })
|
|
103
107
|
], HelixHeadlessTag.prototype, "focusable", void 0);
|
|
@@ -42,6 +42,7 @@ declare const HelixHeadlessTimeInput_base: (new (...args: any[]) => import("@fus
|
|
|
42
42
|
*
|
|
43
43
|
* @slot feedback - The message that says what is wrong with the value.
|
|
44
44
|
* @slot hint - Help under the field.
|
|
45
|
+
* @slot label - The label, for content richer than the `label` text.
|
|
45
46
|
* @slot panel-footer - Content in the panel under the times, such as a note or an action.
|
|
46
47
|
* @slot panel-header - Content in the panel above the times, such as a heading or a shortcut.
|
|
47
48
|
* @slot trigger - The icon on the field that shows a list opens.
|
|
@@ -191,7 +191,13 @@ export declare class HelixHeadlessToggleGroup extends HelixHeadlessToggleGroup_b
|
|
|
191
191
|
private readonly indicator;
|
|
192
192
|
protected readonly keys: KeyboardNavigationController;
|
|
193
193
|
protected readonly cascade: DisabledCascadeController;
|
|
194
|
+
/**
|
|
195
|
+
* The toggles, in light-DOM order, at any depth, so a toggle inside a layout
|
|
196
|
+
* wrapper of yours is one of the group's. A toggle held by a group nested
|
|
197
|
+
* inside this one is that group's.
|
|
198
|
+
*/
|
|
194
199
|
private get items();
|
|
200
|
+
private observer;
|
|
195
201
|
private readonly handleSelection;
|
|
196
202
|
/**
|
|
197
203
|
* Whether the group holds an answer the form can submit: a value an item
|
|
@@ -101,7 +101,10 @@ let HelixHeadlessToggleGroup = class HelixHeadlessToggleGroup extends MixinContr
|
|
|
101
101
|
null);
|
|
102
102
|
}
|
|
103
103
|
get items() {
|
|
104
|
-
|
|
104
|
+
if (this.children === undefined) {
|
|
105
|
+
return [];
|
|
106
|
+
}
|
|
107
|
+
return [...this.querySelectorAll('*')].filter((element) => element instanceof HelixHeadlessToggle && nearestGroup(element) === this);
|
|
105
108
|
}
|
|
106
109
|
get answered() {
|
|
107
110
|
if (this.value === '') {
|
|
@@ -118,11 +121,15 @@ let HelixHeadlessToggleGroup = class HelixHeadlessToggleGroup extends MixinContr
|
|
|
118
121
|
super.connectedCallback();
|
|
119
122
|
this.addEventListener('helix-select', this.handleSelection);
|
|
120
123
|
this.addEventListener('focusin', this.noteFocus);
|
|
124
|
+
this.observer = new MutationObserver(this.handleItems);
|
|
125
|
+
this.observer.observe(this, { childList: true, subtree: true });
|
|
121
126
|
}
|
|
122
127
|
disconnectedCallback() {
|
|
123
128
|
super.disconnectedCallback();
|
|
124
129
|
this.removeEventListener('helix-select', this.handleSelection);
|
|
125
130
|
this.removeEventListener('focusin', this.noteFocus);
|
|
131
|
+
this.observer?.disconnect();
|
|
132
|
+
this.observer = undefined;
|
|
126
133
|
}
|
|
127
134
|
firstUpdated(changed) {
|
|
128
135
|
super.firstUpdated(changed);
|
|
@@ -146,6 +153,7 @@ let HelixHeadlessToggleGroup = class HelixHeadlessToggleGroup extends MixinContr
|
|
|
146
153
|
this.adopt();
|
|
147
154
|
hostAria(this.internals, { label: this.label, role: this.groupRole });
|
|
148
155
|
this.internals.ariaOrientation = this.orientation ?? null;
|
|
156
|
+
this.internals.ariaRequired = this.required && !this.multiple ? 'true' : null;
|
|
149
157
|
if (!this.answered) {
|
|
150
158
|
this.internals.setFormValue(null);
|
|
151
159
|
}
|
|
@@ -203,3 +211,10 @@ HelixHeadlessToggleGroup = __decorate([
|
|
|
203
211
|
customElement('helix-headless-toggle-group')
|
|
204
212
|
], HelixHeadlessToggleGroup);
|
|
205
213
|
export { HelixHeadlessToggleGroup };
|
|
214
|
+
function nearestGroup(toggle) {
|
|
215
|
+
let parent = toggle.parentElement;
|
|
216
|
+
while (parent && !(parent instanceof HelixHeadlessToggleGroup)) {
|
|
217
|
+
parent = parent.parentElement;
|
|
218
|
+
}
|
|
219
|
+
return parent;
|
|
220
|
+
}
|
|
@@ -2,13 +2,18 @@ import { HelixElement, HostClickController } from '@fusion.dev/helix-core';
|
|
|
2
2
|
import { type PropertyValues, type TemplateResult } from 'lit';
|
|
3
3
|
declare const HelixHeadlessToggle_base: (new (...args: any[]) => import("@fusion.dev/helix-core").MixinSelectableItemInterface) & typeof HelixElement;
|
|
4
4
|
/**
|
|
5
|
-
* @summary One option in a toggle group.
|
|
5
|
+
* @summary One option in a toggle group, or a toggle button on its own.
|
|
6
6
|
*
|
|
7
7
|
* @description
|
|
8
8
|
* Renders a `<button role="radio">` for use as a form control. In a group with
|
|
9
9
|
* `selection-mode="multiple"` it is a `<button>` that says whether it is
|
|
10
10
|
* pressed with `aria-pressed`.
|
|
11
11
|
*
|
|
12
|
+
* **On its own, outside a `Toggle.Group`, it is a toggle button**, as React
|
|
13
|
+
* Aria's `ToggleButton`: a `<button>` with `aria-pressed`. A press, Enter or
|
|
14
|
+
* Space sends `helix-select`, which you can cancel, then flips `selected` and
|
|
15
|
+
* sends `change`. Write `selected` in the markup to start it pressed.
|
|
16
|
+
*
|
|
12
17
|
* Do not use it for navigation. For navigation, use links marked with
|
|
13
18
|
* `aria-current` and styled by the `data-navigation-item` recipe in the base
|
|
14
19
|
* layer. The recipe has the same look, and links are not announced as choices.
|
|
@@ -21,6 +26,7 @@ declare const HelixHeadlessToggle_base: (new (...args: any[]) => import("@fusion
|
|
|
21
26
|
*
|
|
22
27
|
* @slot - The item's label.
|
|
23
28
|
*
|
|
29
|
+
* @event {Event} change - Fired after a toggle button on its own is pressed or released.
|
|
24
30
|
* @event {HelixSelectEvent} helix-select - Fired when the item is chosen. Cancelable: call `preventDefault()` to keep the current choice.
|
|
25
31
|
*
|
|
26
32
|
* @csspart item - The radio button.
|
|
@@ -61,8 +67,8 @@ export declare class HelixHeadlessToggle extends HelixHeadlessToggle_base {
|
|
|
61
67
|
get defaultSelected(): boolean;
|
|
62
68
|
set defaultSelected(next: boolean);
|
|
63
69
|
/**
|
|
64
|
-
* Whether the item holds the group's tab stop. Unset,
|
|
65
|
-
* it.
|
|
70
|
+
* Whether the item holds the group's tab stop. Unset, no group holds the
|
|
71
|
+
* item, and it is a toggle button with a tab stop of its own.
|
|
66
72
|
*
|
|
67
73
|
* Written by the group on every item: `true` for the chosen item, or for the
|
|
68
74
|
* first item that can be chosen while there is no selection to carry the
|
|
@@ -79,6 +85,18 @@ export declare class HelixHeadlessToggle extends HelixHeadlessToggle_base {
|
|
|
79
85
|
* Written by the group, like `selected`, so no attribute.
|
|
80
86
|
*/
|
|
81
87
|
pressed?: boolean | undefined;
|
|
88
|
+
/**
|
|
89
|
+
* Whether no `Toggle.Group` holds the item, which makes it a toggle button
|
|
90
|
+
* of its own. A group writes `focusable` on every item it holds. A server,
|
|
91
|
+
* and the render that hydrates its markup, cannot tell, and draw the item a
|
|
92
|
+
* group holds.
|
|
93
|
+
*/
|
|
94
|
+
private get alone();
|
|
95
|
+
/**
|
|
96
|
+
* Chooses the item in its group, or, on its own, flips `selected` once
|
|
97
|
+
* `helix-select` stands and sends `change`.
|
|
98
|
+
*/
|
|
99
|
+
select(): boolean;
|
|
82
100
|
protected willUpdate(changed: PropertyValues): void;
|
|
83
101
|
/**
|
|
84
102
|
* Asks for the render that follows a hydration, so the stop stops being every
|
|
@@ -18,23 +18,40 @@ let HelixHeadlessToggle = class HelixHeadlessToggle extends MixinSelectableItem(
|
|
|
18
18
|
set defaultSelected(next) {
|
|
19
19
|
this.toggleAttribute('selected', next);
|
|
20
20
|
}
|
|
21
|
+
get alone() {
|
|
22
|
+
return this.focusable === undefined && this.childrenKnown;
|
|
23
|
+
}
|
|
24
|
+
select() {
|
|
25
|
+
if (!this.alone) {
|
|
26
|
+
return super.select();
|
|
27
|
+
}
|
|
28
|
+
if (!super.select()) {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
this.selected = !this.selected;
|
|
32
|
+
this.dispatchEvent(new Event('change', { bubbles: true }));
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
21
35
|
willUpdate(changed) {
|
|
22
36
|
super.willUpdate(changed);
|
|
23
|
-
this.toggleState('pressed', this.pressed === true);
|
|
37
|
+
this.toggleState('pressed', this.alone ? this.selected : this.pressed === true);
|
|
24
38
|
}
|
|
25
39
|
firstUpdated(changed) {
|
|
26
40
|
super.firstUpdated(changed);
|
|
27
41
|
this.finishHydration();
|
|
28
42
|
}
|
|
29
43
|
render() {
|
|
44
|
+
const alone = this.alone;
|
|
45
|
+
const radio = !alone && this.pressed === undefined;
|
|
46
|
+
const pressed = alone ? this.selected : this.pressed;
|
|
30
47
|
return html `<button
|
|
31
48
|
part="item"
|
|
32
49
|
type="button"
|
|
33
|
-
role=${
|
|
34
|
-
aria-checked=${
|
|
35
|
-
aria-pressed=${
|
|
50
|
+
role=${radio ? 'radio' : nothing}
|
|
51
|
+
aria-checked=${radio ? String(this.selected) : nothing}
|
|
52
|
+
aria-pressed=${radio || pressed === undefined ? nothing : String(pressed)}
|
|
36
53
|
?disabled=${this.disabled}
|
|
37
|
-
tabindex=${(this.focusable ?? this.selected) || !this.childrenKnown ? '0' : '-1'}
|
|
54
|
+
tabindex=${alone || (this.focusable ?? this.selected) || !this.childrenKnown ? '0' : '-1'}
|
|
38
55
|
@click=${() => this.select()}
|
|
39
56
|
><slot></slot></button>`;
|
|
40
57
|
}
|