@getflip/swirl-components 0.87.2 → 0.87.4
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 +10 -5
- 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 +10 -5
- 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 +10 -5
- 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 +10 -5
- 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-0067f11b.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;
|
|
@@ -187,13 +192,13 @@ export class SwirlAutocomplete {
|
|
|
187
192
|
const className = classnames("autocomplete", {
|
|
188
193
|
"autocomplete--inactive": !this.active,
|
|
189
194
|
});
|
|
190
|
-
return (h(Host, null, h("div", { class: className,
|
|
195
|
+
return (h(Host, null, h("div", { class: className, onKeyDown: this.onKeyDown }, this.multiSelect &&
|
|
191
196
|
Array.isArray(this.value) &&
|
|
192
197
|
this.value.length > 0 && (h("div", { class: "autocomplete__multi-select-tags" }, h("swirl-stack", { orientation: "horizontal", spacing: "8", wrap: true }, this.value.map((item) => {
|
|
193
198
|
return (h("swirl-tag", { key: item.id, label: item.label,
|
|
194
199
|
// eslint-disable-next-line react/jsx-no-bind
|
|
195
200
|
onRemove: () => this.onRemoveValue(item.id), removable: !this.disabled }));
|
|
196
|
-
})))), h("swirl-text-input", { autoSelect: this.autoSelect, class: "autocomplete__input", clearable: clearable, clearButtonLabel: this.clearButtonLabel, disabled: this.disabled, disableDynamicWidth: true, id: this.id, inline: this.inline, invalid: this.invalid, onInputFocus: this.onFocus, onKeyDown: this.onInputKeyDown, onValueChange: this.onChange, maxLength: this.maxLength, mode: this.mode, placeholder: this.placeholder, ref: (el) => (this.inputEl = el), required: this.required, spellCheck: this.spellCheck, swirlAriaAutocomplete: "list", swirlAriaControls: suggestionsMenuId, swirlAriaDescribedby: this.swirlAriaDescribedby, swirlAriaExpanded: String(this.active), swirlRole: "combobox", value: valueLabel }), h("div", { class: "autocomplete__listbox-container", ref: (el) => (this.listboxContainerEl = el), style: {
|
|
201
|
+
})))), h("swirl-text-input", { autoSelect: this.autoSelect, class: "autocomplete__input", clearable: clearable, clearButtonLabel: this.clearButtonLabel, disabled: this.disabled, disableDynamicWidth: true, id: this.id, inline: this.inline, invalid: this.invalid, onInputFocus: this.onFocus, onKeyDown: this.onInputKeyDown, onValueChange: this.onChange, maxLength: this.maxLength, mode: this.mode, placeholder: this.placeholder, ref: (el) => (this.inputEl = el), required: this.required, spellCheck: this.spellCheck, swirlAriaAutocomplete: "list", swirlAriaControls: suggestionsMenuId, swirlAriaDescribedby: this.swirlAriaDescribedby, swirlAriaExpanded: String(this.active), swirlRole: "combobox", value: valueLabel }), h("div", { class: "autocomplete__listbox-container", onFocusout: this.onFocusOut, ref: (el) => (this.listboxContainerEl = el), style: {
|
|
197
202
|
top: Boolean(this.position) ? `${this.position?.y}px` : "",
|
|
198
203
|
left: Boolean(this.position) ? `${this.position?.x}px` : "",
|
|
199
204
|
width: `${this.inputEl?.getBoundingClientRect().width + 32}px`,
|
|
@@ -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;
|
|
@@ -209,13 +214,13 @@ const SwirlAutocomplete$1 = /*@__PURE__*/ proxyCustomElement(class SwirlAutocomp
|
|
|
209
214
|
const className = classnames("autocomplete", {
|
|
210
215
|
"autocomplete--inactive": !this.active,
|
|
211
216
|
});
|
|
212
|
-
return (h(Host, null, h("div", { class: className,
|
|
217
|
+
return (h(Host, null, h("div", { class: className, onKeyDown: this.onKeyDown }, this.multiSelect &&
|
|
213
218
|
Array.isArray(this.value) &&
|
|
214
219
|
this.value.length > 0 && (h("div", { class: "autocomplete__multi-select-tags" }, h("swirl-stack", { orientation: "horizontal", spacing: "8", wrap: true }, this.value.map((item) => {
|
|
215
220
|
return (h("swirl-tag", { key: item.id, label: item.label,
|
|
216
221
|
// eslint-disable-next-line react/jsx-no-bind
|
|
217
222
|
onRemove: () => this.onRemoveValue(item.id), removable: !this.disabled }));
|
|
218
|
-
})))), h("swirl-text-input", { autoSelect: this.autoSelect, class: "autocomplete__input", clearable: clearable, clearButtonLabel: this.clearButtonLabel, disabled: this.disabled, disableDynamicWidth: true, id: this.id, inline: this.inline, invalid: this.invalid, onInputFocus: this.onFocus, onKeyDown: this.onInputKeyDown, onValueChange: this.onChange, maxLength: this.maxLength, mode: this.mode, placeholder: this.placeholder, ref: (el) => (this.inputEl = el), required: this.required, spellCheck: this.spellCheck, swirlAriaAutocomplete: "list", swirlAriaControls: suggestionsMenuId, swirlAriaDescribedby: this.swirlAriaDescribedby, swirlAriaExpanded: String(this.active), swirlRole: "combobox", value: valueLabel }), h("div", { class: "autocomplete__listbox-container", ref: (el) => (this.listboxContainerEl = el), style: {
|
|
223
|
+
})))), h("swirl-text-input", { autoSelect: this.autoSelect, class: "autocomplete__input", clearable: clearable, clearButtonLabel: this.clearButtonLabel, disabled: this.disabled, disableDynamicWidth: true, id: this.id, inline: this.inline, invalid: this.invalid, onInputFocus: this.onFocus, onKeyDown: this.onInputKeyDown, onValueChange: this.onChange, maxLength: this.maxLength, mode: this.mode, placeholder: this.placeholder, ref: (el) => (this.inputEl = el), required: this.required, spellCheck: this.spellCheck, swirlAriaAutocomplete: "list", swirlAriaControls: suggestionsMenuId, swirlAriaDescribedby: this.swirlAriaDescribedby, swirlAriaExpanded: String(this.active), swirlRole: "combobox", value: valueLabel }), h("div", { class: "autocomplete__listbox-container", onFocusout: this.onFocusOut, ref: (el) => (this.listboxContainerEl = el), style: {
|
|
219
224
|
top: Boolean(this.position) ? `${this.position?.y}px` : "",
|
|
220
225
|
left: Boolean(this.position) ? `${this.position?.x}px` : "",
|
|
221
226
|
width: `${this.inputEl?.getBoundingClientRect().width + 32}px`,
|
|
@@ -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") {
|