@getflip/swirl-components 0.87.2 → 0.87.3
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/components.json +1 -1
- package/dist/cjs/loader.cjs.js +1 -1
- package/dist/cjs/swirl-autocomplete.cjs.entry.js +8 -3
- package/dist/cjs/swirl-components.cjs.js +1 -1
- package/dist/cjs/swirl-icon-check-small_3.cjs.entry.js +8 -1
- package/dist/cjs/swirl-option-list_2.cjs.entry.js +19 -16
- package/dist/collection/components/swirl-autocomplete/swirl-autocomplete.js +8 -3
- package/dist/collection/components/swirl-autocomplete/swirl-autocomplete.stories.js +190 -0
- package/dist/collection/components/swirl-option-list/swirl-option-list.js +19 -16
- package/dist/collection/components/swirl-option-list/swirl-option-list.spec.js +1 -1
- package/dist/collection/components/swirl-option-list/swirl-option-list.stories.js +5 -5
- package/dist/collection/components/swirl-option-list-item/swirl-option-list-item.js +10 -2
- package/dist/components/swirl-autocomplete.js +8 -3
- package/dist/components/swirl-option-list-item2.js +10 -2
- package/dist/components/swirl-option-list2.js +19 -16
- package/dist/esm/loader.js +1 -1
- package/dist/esm/swirl-autocomplete.entry.js +8 -3
- package/dist/esm/swirl-components.js +1 -1
- package/dist/esm/swirl-icon-check-small_3.entry.js +8 -1
- package/dist/esm/swirl-option-list_2.entry.js +19 -16
- package/dist/swirl-components/p-1f3ce187.entry.js +1 -0
- package/dist/swirl-components/p-4bec5f4c.entry.js +1 -0
- package/dist/swirl-components/p-89898ac6.entry.js +1 -0
- package/dist/swirl-components/swirl-components.esm.js +1 -1
- package/dist/types/components/swirl-option-list/swirl-option-list.d.ts +1 -1
- package/dist/types/components/swirl-option-list-item/swirl-option-list-item.d.ts +3 -0
- package/package.json +1 -1
- package/dist/swirl-components/p-07957b9a.entry.js +0 -1
- package/dist/swirl-components/p-7dafac36.entry.js +0 -1
- package/dist/swirl-components/p-f7f2f11c.entry.js +0 -1
|
@@ -53,6 +53,12 @@ const SwirlOptionListItem = class {
|
|
|
53
53
|
this.toggleDrag.emit(this.el);
|
|
54
54
|
}
|
|
55
55
|
};
|
|
56
|
+
this.onBlur = () => {
|
|
57
|
+
this.focused = false;
|
|
58
|
+
};
|
|
59
|
+
this.onFocus = () => {
|
|
60
|
+
this.focused = true;
|
|
61
|
+
};
|
|
56
62
|
this.allowDrag = undefined;
|
|
57
63
|
this.context = "single-select";
|
|
58
64
|
this.disabled = undefined;
|
|
@@ -65,6 +71,7 @@ const SwirlOptionListItem = class {
|
|
|
65
71
|
this.swirlAriaRole = "option";
|
|
66
72
|
this.value = undefined;
|
|
67
73
|
this.iconSize = 24;
|
|
74
|
+
this.focused = undefined;
|
|
68
75
|
}
|
|
69
76
|
componentDidLoad() {
|
|
70
77
|
this.forceIconProps(this.desktopMediaQuery.matches);
|
|
@@ -93,7 +100,7 @@ const SwirlOptionListItem = class {
|
|
|
93
100
|
"option-list-item--dragging": this.dragging,
|
|
94
101
|
"option-list-item--selected": this.selected,
|
|
95
102
|
});
|
|
96
|
-
return (index.h(index.Host, null, index.h("div", { "aria-checked": this.swirlAriaRole === "menuitemradio" ? ariaSelected : undefined, "aria-disabled": ariaDisabled, "aria-selected": this.swirlAriaRole === "option" ? ariaSelected : undefined, class: className, part: "option-list-item", role: this.swirlAriaRole }, showIcon && (index.h("span", { class: "option-list-item__icon", innerHTML: this.icon, ref: (el) => (this.iconEl = el) })), showCheckbox && (index.h("span", { class: "option-list-item__checkbox" }, index.h("span", { class: "option-list-item__checkbox-box" }, this.selected && (index.h("swirl-icon-check-strong", { class: "option-list-item__checkbox-icon", size: 16 }))))), index.h("span", { class: "option-list-item__label", part: "option-list-item__label" }, this.label), showSelectionIcon && (index.h("span", { class: "option-list-item__selection-icon" }, index.h("swirl-icon-check-small", { size: this.iconSize })))), this.allowDrag && (index.h("button", { "aria-describedby": this.dragHandleDescription, "aria-label": `${this.dragHandleLabel} "${this.label}"`, class: "option-list-item__drag-handle", onKeyDown: this.onDragHandleKeyDown, type: "button" }, index.h("swirl-icon-drag-handle", { size: this.iconSize })))));
|
|
103
|
+
return (index.h(index.Host, null, index.h("div", { "aria-checked": this.swirlAriaRole === "menuitemradio" ? ariaSelected : undefined, "aria-disabled": ariaDisabled, "aria-selected": this.swirlAriaRole === "option" ? ariaSelected : undefined, class: className, onBlur: this.onBlur, onFocus: this.onFocus, part: "option-list-item", role: this.swirlAriaRole }, showIcon && (index.h("span", { class: "option-list-item__icon", innerHTML: this.icon, ref: (el) => (this.iconEl = el) })), showCheckbox && (index.h("span", { class: "option-list-item__checkbox" }, index.h("span", { class: "option-list-item__checkbox-box" }, this.selected && (index.h("swirl-icon-check-strong", { class: "option-list-item__checkbox-icon", size: 16 }))))), index.h("span", { class: "option-list-item__label", part: "option-list-item__label" }, this.label), showSelectionIcon && (index.h("span", { class: "option-list-item__selection-icon" }, index.h("swirl-icon-check-small", { size: this.iconSize })))), this.allowDrag && (index.h("button", { "aria-describedby": this.dragHandleDescription, "aria-label": `${this.dragHandleLabel} "${this.label}"`, class: "option-list-item__drag-handle", onKeyDown: this.onDragHandleKeyDown, tabIndex: this.focused ? 0 : -1, type: "button" }, index.h("swirl-icon-drag-handle", { size: this.iconSize })))));
|
|
97
104
|
}
|
|
98
105
|
get el() { return index.getElement(this); }
|
|
99
106
|
};
|
|
@@ -14,19 +14,6 @@ const SwirlOptionList = class {
|
|
|
14
14
|
index.registerInstance(this, hostRef);
|
|
15
15
|
this.itemDrop = index.createEvent(this, "itemDrop", 7);
|
|
16
16
|
this.valueChange = index.createEvent(this, "valueChange", 7);
|
|
17
|
-
this.onFocus = async (event) => {
|
|
18
|
-
if (this.listboxEl.contains(event.relatedTarget)) {
|
|
19
|
-
return;
|
|
20
|
-
}
|
|
21
|
-
// prevent focus from canceling the drag event in Safari
|
|
22
|
-
await new Promise((resolve) => setTimeout(resolve));
|
|
23
|
-
if (Boolean(this.focusedItem)) {
|
|
24
|
-
this.focusItem(this.getActiveItemIndex());
|
|
25
|
-
}
|
|
26
|
-
else {
|
|
27
|
-
this.focusItem(0);
|
|
28
|
-
}
|
|
29
|
-
};
|
|
30
17
|
this.onClick = (event) => {
|
|
31
18
|
event.preventDefault();
|
|
32
19
|
const target = event.target;
|
|
@@ -59,6 +46,12 @@ const SwirlOptionList = class {
|
|
|
59
46
|
}
|
|
60
47
|
}
|
|
61
48
|
else if (event.code === "Space" || event.code === "Enter") {
|
|
49
|
+
const startingDrag = event.target.classList.contains("option-list-item__drag-handle");
|
|
50
|
+
if (!startingDrag && Boolean(this.dragging)) {
|
|
51
|
+
event.preventDefault();
|
|
52
|
+
this.stopDrag(this.dragging);
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
62
55
|
const target = event.composedPath()[0];
|
|
63
56
|
const optionFocused = Boolean(utils.closestPassShadow(target, '[role="option"]'));
|
|
64
57
|
if (!optionFocused) {
|
|
@@ -116,6 +109,7 @@ const SwirlOptionList = class {
|
|
|
116
109
|
this.assistiveText = `${this.assistiveTextItemMoved} ${newIndex + 1}`;
|
|
117
110
|
this.itemDrop.emit({ item, oldIndex: this.draggingStartIndex, newIndex });
|
|
118
111
|
this.draggingStartIndex = undefined;
|
|
112
|
+
this.focusItem(newIndex);
|
|
119
113
|
};
|
|
120
114
|
this.allowDeselect = true;
|
|
121
115
|
this.allowDrag = undefined;
|
|
@@ -138,6 +132,9 @@ const SwirlOptionList = class {
|
|
|
138
132
|
this.syncItemsWithValue();
|
|
139
133
|
this.setupDragDrop();
|
|
140
134
|
}
|
|
135
|
+
componentDidRender() {
|
|
136
|
+
this.setupDragDrop();
|
|
137
|
+
}
|
|
141
138
|
disconnectedCallback() {
|
|
142
139
|
this.observer?.disconnect();
|
|
143
140
|
this.sortable?.destroy();
|
|
@@ -162,12 +159,17 @@ const SwirlOptionList = class {
|
|
|
162
159
|
this.setItemDisabledState();
|
|
163
160
|
this.setItemContext();
|
|
164
161
|
this.syncItemsWithValue();
|
|
165
|
-
this.setupDragDrop();
|
|
166
162
|
});
|
|
167
163
|
this.observer.observe(this.listboxEl, { childList: true });
|
|
168
164
|
}
|
|
169
165
|
updateItems() {
|
|
170
166
|
this.items = utils.querySelectorAllDeep(this.el, "swirl-option-list-item");
|
|
167
|
+
this.items.forEach((item) => item.querySelector('[role="option"]')?.removeAttribute("tabIndex"));
|
|
168
|
+
const item = this.items[0]?.querySelector('[role="option"]');
|
|
169
|
+
if (!Boolean(item)) {
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
item.setAttribute("tabIndex", "0");
|
|
171
173
|
}
|
|
172
174
|
setItemDisabledState() {
|
|
173
175
|
if (this.disabled) {
|
|
@@ -249,6 +251,7 @@ const SwirlOptionList = class {
|
|
|
249
251
|
else {
|
|
250
252
|
this.updateValue(this.value.filter((v) => v !== item.value));
|
|
251
253
|
}
|
|
254
|
+
this.focusItem(index);
|
|
252
255
|
}
|
|
253
256
|
updateValue(value) {
|
|
254
257
|
this.value = value;
|
|
@@ -331,8 +334,8 @@ const SwirlOptionList = class {
|
|
|
331
334
|
}
|
|
332
335
|
render() {
|
|
333
336
|
const ariaMultiselectable = this.multiSelect ? "true" : undefined;
|
|
334
|
-
const tabIndex = this.
|
|
335
|
-
return (index.h(index.Host, null, index.h("swirl-visually-hidden", { role: "alert" }, this.assistiveText), index.h("div", { "aria-label": this.label, "aria-multiselectable": ariaMultiselectable, class: "option-list", id: this.optionListId, onClick: this.onClick,
|
|
337
|
+
const tabIndex = Boolean(this.dragging) ? 0 : undefined;
|
|
338
|
+
return (index.h(index.Host, null, index.h("swirl-visually-hidden", { role: "alert" }, this.assistiveText), index.h("div", { "aria-label": this.label, "aria-multiselectable": ariaMultiselectable, class: "option-list", id: this.optionListId, onClick: this.onClick, onKeyDown: this.onKeyDown, ref: (el) => (this.listboxEl = el), role: "listbox", tabIndex: tabIndex }, index.h("slot", null))));
|
|
336
339
|
}
|
|
337
340
|
get el() { return index.getElement(this); }
|
|
338
341
|
static get watchers() { return {
|
|
@@ -44,8 +44,10 @@ export class SwirlAutocomplete {
|
|
|
44
44
|
}
|
|
45
45
|
this.value = suggestion;
|
|
46
46
|
this.valueChange.emit(this.value);
|
|
47
|
-
|
|
48
|
-
|
|
47
|
+
queueMicrotask(() => {
|
|
48
|
+
this.inputEl.querySelector("input")?.focus();
|
|
49
|
+
this.close();
|
|
50
|
+
});
|
|
49
51
|
}
|
|
50
52
|
}
|
|
51
53
|
};
|
|
@@ -77,7 +79,10 @@ export class SwirlAutocomplete {
|
|
|
77
79
|
this.onInputKeyDown = (event) => {
|
|
78
80
|
if (event.code === "ArrowDown") {
|
|
79
81
|
event.preventDefault();
|
|
80
|
-
this.listboxEl
|
|
82
|
+
this.listboxEl
|
|
83
|
+
.querySelector('[role="listbox"]')
|
|
84
|
+
.querySelector('[tabIndex="0"]')
|
|
85
|
+
?.focus();
|
|
81
86
|
}
|
|
82
87
|
};
|
|
83
88
|
this.autoSelect = undefined;
|
|
@@ -72,6 +72,196 @@ const Template = (args) => {
|
|
|
72
72
|
id: "item-3",
|
|
73
73
|
label: "Item #3 with a longer label",
|
|
74
74
|
},
|
|
75
|
+
{
|
|
76
|
+
id: "item-4",
|
|
77
|
+
label: "Item #4",
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
id: "item-5",
|
|
81
|
+
label: "Item #5",
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
id: "item-6",
|
|
85
|
+
label: "Item #6 with some additional information",
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
id: "item-7",
|
|
89
|
+
label: "Item #7",
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
disabled: true,
|
|
93
|
+
id: "item-8",
|
|
94
|
+
label: "Item #8",
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
id: "item-9",
|
|
98
|
+
label: "Item #9 with a lengthy label to test wrapping",
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
id: "item-10",
|
|
102
|
+
label: "Item #10",
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
id: "item-11",
|
|
106
|
+
label: "Item #11",
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
id: "item-12",
|
|
110
|
+
label: "Item #12",
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
id: "item-13",
|
|
114
|
+
label: "Item #13",
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
id: "item-14",
|
|
118
|
+
label: "Item #14 with a descriptive label",
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
id: "item-15",
|
|
122
|
+
label: "Item #15",
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
id: "item-16",
|
|
126
|
+
label: "Item #16",
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
id: "item-17",
|
|
130
|
+
label: "Item #17",
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
disabled: true,
|
|
134
|
+
id: "item-18",
|
|
135
|
+
label: "Item #18 with special considerations",
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
id: "item-19",
|
|
139
|
+
label: "Item #19",
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
id: "item-20",
|
|
143
|
+
label: "Item #20",
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
id: "item-21",
|
|
147
|
+
label: "Item #21",
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
id: "item-22",
|
|
151
|
+
label: "Item #22",
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
id: "item-23",
|
|
155
|
+
label: "Item #23 with an extended label",
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
id: "item-24",
|
|
159
|
+
label: "Item #24",
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
id: "item-25",
|
|
163
|
+
label: "Item #25",
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
id: "item-26",
|
|
167
|
+
label: "Item #26",
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
id: "item-27",
|
|
171
|
+
label: "Item #27",
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
id: "item-28",
|
|
175
|
+
label: "Item #28 with unique properties",
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
id: "item-29",
|
|
179
|
+
label: "Item #29",
|
|
180
|
+
},
|
|
181
|
+
{
|
|
182
|
+
id: "item-30",
|
|
183
|
+
label: "Item #30",
|
|
184
|
+
},
|
|
185
|
+
{
|
|
186
|
+
id: "item-31",
|
|
187
|
+
label: "Item #31",
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
id: "item-32",
|
|
191
|
+
label: "Item #32",
|
|
192
|
+
},
|
|
193
|
+
{
|
|
194
|
+
id: "item-33",
|
|
195
|
+
label: "Item #33 with additional notes",
|
|
196
|
+
},
|
|
197
|
+
{
|
|
198
|
+
id: "item-34",
|
|
199
|
+
label: "Item #34",
|
|
200
|
+
},
|
|
201
|
+
{
|
|
202
|
+
id: "item-35",
|
|
203
|
+
label: "Item #35",
|
|
204
|
+
},
|
|
205
|
+
{
|
|
206
|
+
id: "item-36",
|
|
207
|
+
label: "Item #36",
|
|
208
|
+
},
|
|
209
|
+
{
|
|
210
|
+
id: "item-37",
|
|
211
|
+
label: "Item #37",
|
|
212
|
+
},
|
|
213
|
+
{
|
|
214
|
+
id: "item-38",
|
|
215
|
+
label: "Item #38 with a customized label",
|
|
216
|
+
},
|
|
217
|
+
{
|
|
218
|
+
id: "item-39",
|
|
219
|
+
label: "Item #39",
|
|
220
|
+
},
|
|
221
|
+
{
|
|
222
|
+
id: "item-40",
|
|
223
|
+
label: "Item #40",
|
|
224
|
+
},
|
|
225
|
+
{
|
|
226
|
+
id: "item-41",
|
|
227
|
+
label: "Item #41",
|
|
228
|
+
},
|
|
229
|
+
{
|
|
230
|
+
id: "item-42",
|
|
231
|
+
label: "Item #42",
|
|
232
|
+
},
|
|
233
|
+
{
|
|
234
|
+
id: "item-43",
|
|
235
|
+
label: "Item #43 with specific details",
|
|
236
|
+
},
|
|
237
|
+
{
|
|
238
|
+
id: "item-44",
|
|
239
|
+
label: "Item #44",
|
|
240
|
+
},
|
|
241
|
+
{
|
|
242
|
+
id: "item-45",
|
|
243
|
+
label: "Item #45",
|
|
244
|
+
},
|
|
245
|
+
{
|
|
246
|
+
id: "item-46",
|
|
247
|
+
label: "Item #46",
|
|
248
|
+
},
|
|
249
|
+
{
|
|
250
|
+
id: "item-47",
|
|
251
|
+
label: "Item #47",
|
|
252
|
+
},
|
|
253
|
+
{
|
|
254
|
+
id: "item-48",
|
|
255
|
+
label: "Item #48 with optional settings",
|
|
256
|
+
},
|
|
257
|
+
{
|
|
258
|
+
id: "item-49",
|
|
259
|
+
label: "Item #49",
|
|
260
|
+
},
|
|
261
|
+
{
|
|
262
|
+
id: "item-50",
|
|
263
|
+
label: "Item #50",
|
|
264
|
+
},
|
|
75
265
|
];
|
|
76
266
|
element.generateSuggestions = async (value) => {
|
|
77
267
|
if (!Boolean(value)) {
|
|
@@ -3,19 +3,6 @@ import { closestPassShadow, querySelectorAllDeep, } from "../../utils";
|
|
|
3
3
|
import Sortable from "sortablejs";
|
|
4
4
|
export class SwirlOptionList {
|
|
5
5
|
constructor() {
|
|
6
|
-
this.onFocus = async (event) => {
|
|
7
|
-
if (this.listboxEl.contains(event.relatedTarget)) {
|
|
8
|
-
return;
|
|
9
|
-
}
|
|
10
|
-
// prevent focus from canceling the drag event in Safari
|
|
11
|
-
await new Promise((resolve) => setTimeout(resolve));
|
|
12
|
-
if (Boolean(this.focusedItem)) {
|
|
13
|
-
this.focusItem(this.getActiveItemIndex());
|
|
14
|
-
}
|
|
15
|
-
else {
|
|
16
|
-
this.focusItem(0);
|
|
17
|
-
}
|
|
18
|
-
};
|
|
19
6
|
this.onClick = (event) => {
|
|
20
7
|
event.preventDefault();
|
|
21
8
|
const target = event.target;
|
|
@@ -48,6 +35,12 @@ export class SwirlOptionList {
|
|
|
48
35
|
}
|
|
49
36
|
}
|
|
50
37
|
else if (event.code === "Space" || event.code === "Enter") {
|
|
38
|
+
const startingDrag = event.target.classList.contains("option-list-item__drag-handle");
|
|
39
|
+
if (!startingDrag && Boolean(this.dragging)) {
|
|
40
|
+
event.preventDefault();
|
|
41
|
+
this.stopDrag(this.dragging);
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
51
44
|
const target = event.composedPath()[0];
|
|
52
45
|
const optionFocused = Boolean(closestPassShadow(target, '[role="option"]'));
|
|
53
46
|
if (!optionFocused) {
|
|
@@ -105,6 +98,7 @@ export class SwirlOptionList {
|
|
|
105
98
|
this.assistiveText = `${this.assistiveTextItemMoved} ${newIndex + 1}`;
|
|
106
99
|
this.itemDrop.emit({ item, oldIndex: this.draggingStartIndex, newIndex });
|
|
107
100
|
this.draggingStartIndex = undefined;
|
|
101
|
+
this.focusItem(newIndex);
|
|
108
102
|
};
|
|
109
103
|
this.allowDeselect = true;
|
|
110
104
|
this.allowDrag = undefined;
|
|
@@ -127,6 +121,9 @@ export class SwirlOptionList {
|
|
|
127
121
|
this.syncItemsWithValue();
|
|
128
122
|
this.setupDragDrop();
|
|
129
123
|
}
|
|
124
|
+
componentDidRender() {
|
|
125
|
+
this.setupDragDrop();
|
|
126
|
+
}
|
|
130
127
|
disconnectedCallback() {
|
|
131
128
|
this.observer?.disconnect();
|
|
132
129
|
this.sortable?.destroy();
|
|
@@ -151,12 +148,17 @@ export class SwirlOptionList {
|
|
|
151
148
|
this.setItemDisabledState();
|
|
152
149
|
this.setItemContext();
|
|
153
150
|
this.syncItemsWithValue();
|
|
154
|
-
this.setupDragDrop();
|
|
155
151
|
});
|
|
156
152
|
this.observer.observe(this.listboxEl, { childList: true });
|
|
157
153
|
}
|
|
158
154
|
updateItems() {
|
|
159
155
|
this.items = querySelectorAllDeep(this.el, "swirl-option-list-item");
|
|
156
|
+
this.items.forEach((item) => item.querySelector('[role="option"]')?.removeAttribute("tabIndex"));
|
|
157
|
+
const item = this.items[0]?.querySelector('[role="option"]');
|
|
158
|
+
if (!Boolean(item)) {
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
item.setAttribute("tabIndex", "0");
|
|
160
162
|
}
|
|
161
163
|
setItemDisabledState() {
|
|
162
164
|
if (this.disabled) {
|
|
@@ -238,6 +240,7 @@ export class SwirlOptionList {
|
|
|
238
240
|
else {
|
|
239
241
|
this.updateValue(this.value.filter((v) => v !== item.value));
|
|
240
242
|
}
|
|
243
|
+
this.focusItem(index);
|
|
241
244
|
}
|
|
242
245
|
updateValue(value) {
|
|
243
246
|
this.value = value;
|
|
@@ -320,8 +323,8 @@ export class SwirlOptionList {
|
|
|
320
323
|
}
|
|
321
324
|
render() {
|
|
322
325
|
const ariaMultiselectable = this.multiSelect ? "true" : undefined;
|
|
323
|
-
const tabIndex = this.
|
|
324
|
-
return (h(Host, null, h("swirl-visually-hidden", { role: "alert" }, this.assistiveText), h("div", { "aria-label": this.label, "aria-multiselectable": ariaMultiselectable, class: "option-list", id: this.optionListId, onClick: this.onClick,
|
|
326
|
+
const tabIndex = Boolean(this.dragging) ? 0 : undefined;
|
|
327
|
+
return (h(Host, null, h("swirl-visually-hidden", { role: "alert" }, this.assistiveText), h("div", { "aria-label": this.label, "aria-multiselectable": ariaMultiselectable, class: "option-list", id: this.optionListId, onClick: this.onClick, onKeyDown: this.onKeyDown, ref: (el) => (this.listboxEl = el), role: "listbox", tabIndex: tabIndex }, h("slot", null))));
|
|
325
328
|
}
|
|
326
329
|
static get is() { return "swirl-option-list"; }
|
|
327
330
|
static get encapsulation() { return "scoped"; }
|
|
@@ -43,7 +43,7 @@ describe("swirl-option-list", () => {
|
|
|
43
43
|
expect(page.root).toEqualHtml(`
|
|
44
44
|
<swirl-option-list label="Option List" multi-select="true">
|
|
45
45
|
<swirl-visually-hidden role="alert"></swirl-visually-hidden>
|
|
46
|
-
<div aria-label="Option List" aria-multiselectable="true" class="option-list" role="listbox"
|
|
46
|
+
<div aria-label="Option List" aria-multiselectable="true" class="option-list" role="listbox">
|
|
47
47
|
<swirl-option-list-item label="This is an option" value="1"></swirl-option-list-item>
|
|
48
48
|
<swirl-option-list-item label="This is an option" value="2"></swirl-option-list-item>
|
|
49
49
|
<swirl-option-list-item label="This is an option" value="3"></swirl-option-list-item>
|
|
@@ -13,11 +13,11 @@ export default {
|
|
|
13
13
|
const Template = (args) => {
|
|
14
14
|
const element = generateStoryElement("swirl-option-list", args);
|
|
15
15
|
element.innerHTML = `
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
16
|
+
<swirl-option-list-item label="This is option 1" value="1"></swirl-option-list-item>
|
|
17
|
+
<swirl-option-list-item label="This is option 2" value="2"></swirl-option-list-item>
|
|
18
|
+
<swirl-option-list-item label="This is option 3" value="3"></swirl-option-list-item>
|
|
19
|
+
<swirl-option-list-item label="This is option 4" value="4"></swirl-option-list-item>
|
|
20
|
+
`;
|
|
21
21
|
return element;
|
|
22
22
|
};
|
|
23
23
|
export const SwirlOptionList = Template.bind({});
|
|
@@ -14,6 +14,12 @@ export class SwirlOptionListItem {
|
|
|
14
14
|
this.toggleDrag.emit(this.el);
|
|
15
15
|
}
|
|
16
16
|
};
|
|
17
|
+
this.onBlur = () => {
|
|
18
|
+
this.focused = false;
|
|
19
|
+
};
|
|
20
|
+
this.onFocus = () => {
|
|
21
|
+
this.focused = true;
|
|
22
|
+
};
|
|
17
23
|
this.allowDrag = undefined;
|
|
18
24
|
this.context = "single-select";
|
|
19
25
|
this.disabled = undefined;
|
|
@@ -26,6 +32,7 @@ export class SwirlOptionListItem {
|
|
|
26
32
|
this.swirlAriaRole = "option";
|
|
27
33
|
this.value = undefined;
|
|
28
34
|
this.iconSize = 24;
|
|
35
|
+
this.focused = undefined;
|
|
29
36
|
}
|
|
30
37
|
componentDidLoad() {
|
|
31
38
|
this.forceIconProps(this.desktopMediaQuery.matches);
|
|
@@ -54,7 +61,7 @@ export class SwirlOptionListItem {
|
|
|
54
61
|
"option-list-item--dragging": this.dragging,
|
|
55
62
|
"option-list-item--selected": this.selected,
|
|
56
63
|
});
|
|
57
|
-
return (h(Host, null, h("div", { "aria-checked": this.swirlAriaRole === "menuitemradio" ? ariaSelected : undefined, "aria-disabled": ariaDisabled, "aria-selected": this.swirlAriaRole === "option" ? ariaSelected : undefined, class: className, part: "option-list-item", role: this.swirlAriaRole }, showIcon && (h("span", { class: "option-list-item__icon", innerHTML: this.icon, ref: (el) => (this.iconEl = el) })), showCheckbox && (h("span", { class: "option-list-item__checkbox" }, h("span", { class: "option-list-item__checkbox-box" }, this.selected && (h("swirl-icon-check-strong", { class: "option-list-item__checkbox-icon", size: 16 }))))), h("span", { class: "option-list-item__label", part: "option-list-item__label" }, this.label), showSelectionIcon && (h("span", { class: "option-list-item__selection-icon" }, h("swirl-icon-check-small", { size: this.iconSize })))), this.allowDrag && (h("button", { "aria-describedby": this.dragHandleDescription, "aria-label": `${this.dragHandleLabel} "${this.label}"`, class: "option-list-item__drag-handle", onKeyDown: this.onDragHandleKeyDown, type: "button" }, h("swirl-icon-drag-handle", { size: this.iconSize })))));
|
|
64
|
+
return (h(Host, null, h("div", { "aria-checked": this.swirlAriaRole === "menuitemradio" ? ariaSelected : undefined, "aria-disabled": ariaDisabled, "aria-selected": this.swirlAriaRole === "option" ? ariaSelected : undefined, class: className, onBlur: this.onBlur, onFocus: this.onFocus, part: "option-list-item", role: this.swirlAriaRole }, showIcon && (h("span", { class: "option-list-item__icon", innerHTML: this.icon, ref: (el) => (this.iconEl = el) })), showCheckbox && (h("span", { class: "option-list-item__checkbox" }, h("span", { class: "option-list-item__checkbox-box" }, this.selected && (h("swirl-icon-check-strong", { class: "option-list-item__checkbox-icon", size: 16 }))))), h("span", { class: "option-list-item__label", part: "option-list-item__label" }, this.label), showSelectionIcon && (h("span", { class: "option-list-item__selection-icon" }, h("swirl-icon-check-small", { size: this.iconSize })))), this.allowDrag && (h("button", { "aria-describedby": this.dragHandleDescription, "aria-label": `${this.dragHandleLabel} "${this.label}"`, class: "option-list-item__drag-handle", onKeyDown: this.onDragHandleKeyDown, tabIndex: this.focused ? 0 : -1, type: "button" }, h("swirl-icon-drag-handle", { size: this.iconSize })))));
|
|
58
65
|
}
|
|
59
66
|
static get is() { return "swirl-option-list-item"; }
|
|
60
67
|
static get encapsulation() { return "scoped"; }
|
|
@@ -276,7 +283,8 @@ export class SwirlOptionListItem {
|
|
|
276
283
|
}
|
|
277
284
|
static get states() {
|
|
278
285
|
return {
|
|
279
|
-
"iconSize": {}
|
|
286
|
+
"iconSize": {},
|
|
287
|
+
"focused": {}
|
|
280
288
|
};
|
|
281
289
|
}
|
|
282
290
|
static get events() {
|
|
@@ -66,8 +66,10 @@ const SwirlAutocomplete$1 = /*@__PURE__*/ proxyCustomElement(class SwirlAutocomp
|
|
|
66
66
|
}
|
|
67
67
|
this.value = suggestion;
|
|
68
68
|
this.valueChange.emit(this.value);
|
|
69
|
-
|
|
70
|
-
|
|
69
|
+
queueMicrotask(() => {
|
|
70
|
+
this.inputEl.querySelector("input")?.focus();
|
|
71
|
+
this.close();
|
|
72
|
+
});
|
|
71
73
|
}
|
|
72
74
|
}
|
|
73
75
|
};
|
|
@@ -99,7 +101,10 @@ const SwirlAutocomplete$1 = /*@__PURE__*/ proxyCustomElement(class SwirlAutocomp
|
|
|
99
101
|
this.onInputKeyDown = (event) => {
|
|
100
102
|
if (event.code === "ArrowDown") {
|
|
101
103
|
event.preventDefault();
|
|
102
|
-
this.listboxEl
|
|
104
|
+
this.listboxEl
|
|
105
|
+
.querySelector('[role="listbox"]')
|
|
106
|
+
.querySelector('[tabIndex="0"]')
|
|
107
|
+
?.focus();
|
|
103
108
|
}
|
|
104
109
|
};
|
|
105
110
|
this.autoSelect = undefined;
|
|
@@ -23,6 +23,12 @@ const SwirlOptionListItem = /*@__PURE__*/ proxyCustomElement(class SwirlOptionLi
|
|
|
23
23
|
this.toggleDrag.emit(this.el);
|
|
24
24
|
}
|
|
25
25
|
};
|
|
26
|
+
this.onBlur = () => {
|
|
27
|
+
this.focused = false;
|
|
28
|
+
};
|
|
29
|
+
this.onFocus = () => {
|
|
30
|
+
this.focused = true;
|
|
31
|
+
};
|
|
26
32
|
this.allowDrag = undefined;
|
|
27
33
|
this.context = "single-select";
|
|
28
34
|
this.disabled = undefined;
|
|
@@ -35,6 +41,7 @@ const SwirlOptionListItem = /*@__PURE__*/ proxyCustomElement(class SwirlOptionLi
|
|
|
35
41
|
this.swirlAriaRole = "option";
|
|
36
42
|
this.value = undefined;
|
|
37
43
|
this.iconSize = 24;
|
|
44
|
+
this.focused = undefined;
|
|
38
45
|
}
|
|
39
46
|
componentDidLoad() {
|
|
40
47
|
this.forceIconProps(this.desktopMediaQuery.matches);
|
|
@@ -63,7 +70,7 @@ const SwirlOptionListItem = /*@__PURE__*/ proxyCustomElement(class SwirlOptionLi
|
|
|
63
70
|
"option-list-item--dragging": this.dragging,
|
|
64
71
|
"option-list-item--selected": this.selected,
|
|
65
72
|
});
|
|
66
|
-
return (h(Host, null, h("div", { "aria-checked": this.swirlAriaRole === "menuitemradio" ? ariaSelected : undefined, "aria-disabled": ariaDisabled, "aria-selected": this.swirlAriaRole === "option" ? ariaSelected : undefined, class: className, part: "option-list-item", role: this.swirlAriaRole }, showIcon && (h("span", { class: "option-list-item__icon", innerHTML: this.icon, ref: (el) => (this.iconEl = el) })), showCheckbox && (h("span", { class: "option-list-item__checkbox" }, h("span", { class: "option-list-item__checkbox-box" }, this.selected && (h("swirl-icon-check-strong", { class: "option-list-item__checkbox-icon", size: 16 }))))), h("span", { class: "option-list-item__label", part: "option-list-item__label" }, this.label), showSelectionIcon && (h("span", { class: "option-list-item__selection-icon" }, h("swirl-icon-check-small", { size: this.iconSize })))), this.allowDrag && (h("button", { "aria-describedby": this.dragHandleDescription, "aria-label": `${this.dragHandleLabel} "${this.label}"`, class: "option-list-item__drag-handle", onKeyDown: this.onDragHandleKeyDown, type: "button" }, h("swirl-icon-drag-handle", { size: this.iconSize })))));
|
|
73
|
+
return (h(Host, null, h("div", { "aria-checked": this.swirlAriaRole === "menuitemradio" ? ariaSelected : undefined, "aria-disabled": ariaDisabled, "aria-selected": this.swirlAriaRole === "option" ? ariaSelected : undefined, class: className, onBlur: this.onBlur, onFocus: this.onFocus, part: "option-list-item", role: this.swirlAriaRole }, showIcon && (h("span", { class: "option-list-item__icon", innerHTML: this.icon, ref: (el) => (this.iconEl = el) })), showCheckbox && (h("span", { class: "option-list-item__checkbox" }, h("span", { class: "option-list-item__checkbox-box" }, this.selected && (h("swirl-icon-check-strong", { class: "option-list-item__checkbox-icon", size: 16 }))))), h("span", { class: "option-list-item__label", part: "option-list-item__label" }, this.label), showSelectionIcon && (h("span", { class: "option-list-item__selection-icon" }, h("swirl-icon-check-small", { size: this.iconSize })))), this.allowDrag && (h("button", { "aria-describedby": this.dragHandleDescription, "aria-label": `${this.dragHandleLabel} "${this.label}"`, class: "option-list-item__drag-handle", onKeyDown: this.onDragHandleKeyDown, tabIndex: this.focused ? 0 : -1, type: "button" }, h("swirl-icon-drag-handle", { size: this.iconSize })))));
|
|
67
74
|
}
|
|
68
75
|
get el() { return this; }
|
|
69
76
|
static get style() { return swirlOptionListItemCss; }
|
|
@@ -79,7 +86,8 @@ const SwirlOptionListItem = /*@__PURE__*/ proxyCustomElement(class SwirlOptionLi
|
|
|
79
86
|
"selected": [1028],
|
|
80
87
|
"swirlAriaRole": [1, "swirl-aria-role"],
|
|
81
88
|
"value": [1],
|
|
82
|
-
"iconSize": [32]
|
|
89
|
+
"iconSize": [32],
|
|
90
|
+
"focused": [32]
|
|
83
91
|
}]);
|
|
84
92
|
function defineCustomElement() {
|
|
85
93
|
if (typeof customElements === "undefined") {
|
|
@@ -11,19 +11,6 @@ const SwirlOptionList = /*@__PURE__*/ proxyCustomElement(class SwirlOptionList e
|
|
|
11
11
|
this.__registerHost();
|
|
12
12
|
this.itemDrop = createEvent(this, "itemDrop", 7);
|
|
13
13
|
this.valueChange = createEvent(this, "valueChange", 7);
|
|
14
|
-
this.onFocus = async (event) => {
|
|
15
|
-
if (this.listboxEl.contains(event.relatedTarget)) {
|
|
16
|
-
return;
|
|
17
|
-
}
|
|
18
|
-
// prevent focus from canceling the drag event in Safari
|
|
19
|
-
await new Promise((resolve) => setTimeout(resolve));
|
|
20
|
-
if (Boolean(this.focusedItem)) {
|
|
21
|
-
this.focusItem(this.getActiveItemIndex());
|
|
22
|
-
}
|
|
23
|
-
else {
|
|
24
|
-
this.focusItem(0);
|
|
25
|
-
}
|
|
26
|
-
};
|
|
27
14
|
this.onClick = (event) => {
|
|
28
15
|
event.preventDefault();
|
|
29
16
|
const target = event.target;
|
|
@@ -56,6 +43,12 @@ const SwirlOptionList = /*@__PURE__*/ proxyCustomElement(class SwirlOptionList e
|
|
|
56
43
|
}
|
|
57
44
|
}
|
|
58
45
|
else if (event.code === "Space" || event.code === "Enter") {
|
|
46
|
+
const startingDrag = event.target.classList.contains("option-list-item__drag-handle");
|
|
47
|
+
if (!startingDrag && Boolean(this.dragging)) {
|
|
48
|
+
event.preventDefault();
|
|
49
|
+
this.stopDrag(this.dragging);
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
59
52
|
const target = event.composedPath()[0];
|
|
60
53
|
const optionFocused = Boolean(closestPassShadow(target, '[role="option"]'));
|
|
61
54
|
if (!optionFocused) {
|
|
@@ -113,6 +106,7 @@ const SwirlOptionList = /*@__PURE__*/ proxyCustomElement(class SwirlOptionList e
|
|
|
113
106
|
this.assistiveText = `${this.assistiveTextItemMoved} ${newIndex + 1}`;
|
|
114
107
|
this.itemDrop.emit({ item, oldIndex: this.draggingStartIndex, newIndex });
|
|
115
108
|
this.draggingStartIndex = undefined;
|
|
109
|
+
this.focusItem(newIndex);
|
|
116
110
|
};
|
|
117
111
|
this.allowDeselect = true;
|
|
118
112
|
this.allowDrag = undefined;
|
|
@@ -135,6 +129,9 @@ const SwirlOptionList = /*@__PURE__*/ proxyCustomElement(class SwirlOptionList e
|
|
|
135
129
|
this.syncItemsWithValue();
|
|
136
130
|
this.setupDragDrop();
|
|
137
131
|
}
|
|
132
|
+
componentDidRender() {
|
|
133
|
+
this.setupDragDrop();
|
|
134
|
+
}
|
|
138
135
|
disconnectedCallback() {
|
|
139
136
|
this.observer?.disconnect();
|
|
140
137
|
this.sortable?.destroy();
|
|
@@ -159,12 +156,17 @@ const SwirlOptionList = /*@__PURE__*/ proxyCustomElement(class SwirlOptionList e
|
|
|
159
156
|
this.setItemDisabledState();
|
|
160
157
|
this.setItemContext();
|
|
161
158
|
this.syncItemsWithValue();
|
|
162
|
-
this.setupDragDrop();
|
|
163
159
|
});
|
|
164
160
|
this.observer.observe(this.listboxEl, { childList: true });
|
|
165
161
|
}
|
|
166
162
|
updateItems() {
|
|
167
163
|
this.items = querySelectorAllDeep(this.el, "swirl-option-list-item");
|
|
164
|
+
this.items.forEach((item) => item.querySelector('[role="option"]')?.removeAttribute("tabIndex"));
|
|
165
|
+
const item = this.items[0]?.querySelector('[role="option"]');
|
|
166
|
+
if (!Boolean(item)) {
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
item.setAttribute("tabIndex", "0");
|
|
168
170
|
}
|
|
169
171
|
setItemDisabledState() {
|
|
170
172
|
if (this.disabled) {
|
|
@@ -246,6 +248,7 @@ const SwirlOptionList = /*@__PURE__*/ proxyCustomElement(class SwirlOptionList e
|
|
|
246
248
|
else {
|
|
247
249
|
this.updateValue(this.value.filter((v) => v !== item.value));
|
|
248
250
|
}
|
|
251
|
+
this.focusItem(index);
|
|
249
252
|
}
|
|
250
253
|
updateValue(value) {
|
|
251
254
|
this.value = value;
|
|
@@ -328,8 +331,8 @@ const SwirlOptionList = /*@__PURE__*/ proxyCustomElement(class SwirlOptionList e
|
|
|
328
331
|
}
|
|
329
332
|
render() {
|
|
330
333
|
const ariaMultiselectable = this.multiSelect ? "true" : undefined;
|
|
331
|
-
const tabIndex = this.
|
|
332
|
-
return (h(Host, null, h("swirl-visually-hidden", { role: "alert" }, this.assistiveText), h("div", { "aria-label": this.label, "aria-multiselectable": ariaMultiselectable, class: "option-list", id: this.optionListId, onClick: this.onClick,
|
|
334
|
+
const tabIndex = Boolean(this.dragging) ? 0 : undefined;
|
|
335
|
+
return (h(Host, null, h("swirl-visually-hidden", { role: "alert" }, this.assistiveText), h("div", { "aria-label": this.label, "aria-multiselectable": ariaMultiselectable, class: "option-list", id: this.optionListId, onClick: this.onClick, onKeyDown: this.onKeyDown, ref: (el) => (this.listboxEl = el), role: "listbox", tabIndex: tabIndex }, h("slot", null))));
|
|
333
336
|
}
|
|
334
337
|
get el() { return this; }
|
|
335
338
|
static get watchers() { return {
|