@studiocms/ui 1.0.0-beta.3 → 1.0.0-beta.5
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/dist/components/Button/button.css +2 -2
- package/dist/components/Input/input.css +1 -0
- package/dist/components/SearchSelect/SearchSelect.astro +2 -2
- package/dist/components/SearchSelect/searchselect.d.ts +1 -19
- package/dist/components/SearchSelect/searchselect.js +147 -201
- package/dist/components/Select/Select.astro +4 -5
- package/dist/components/Select/select.d.ts +52 -11
- package/dist/components/Select/select.js +140 -150
- package/dist/index.d.ts +2 -2
- package/dist/index.js +20 -18
- package/dist/utils/headers.d.ts +1 -1
- package/dist/virtuals.d.ts +24 -1
- package/package.json +3 -2
|
@@ -267,7 +267,7 @@
|
|
|
267
267
|
color: hsl(259, 84%, 45%);
|
|
268
268
|
}
|
|
269
269
|
.sui-button.success.flat {
|
|
270
|
-
background-color: var(--success-flat
|
|
270
|
+
background-color: var(--success-flat);
|
|
271
271
|
color: hsl(143, 59%, 20%);
|
|
272
272
|
}
|
|
273
273
|
.sui-button.success.flat:hover {
|
|
@@ -277,7 +277,7 @@
|
|
|
277
277
|
background-color: var(--success-flat-active);
|
|
278
278
|
}
|
|
279
279
|
.sui-button.warning.flat {
|
|
280
|
-
background-color: var(--warning-flat
|
|
280
|
+
background-color: var(--warning-flat);
|
|
281
281
|
color: hsl(48, 78%, 20%);
|
|
282
282
|
}
|
|
283
283
|
.sui-button.warning.flat:hover {
|
|
@@ -132,7 +132,7 @@ const defaultLabel = selected
|
|
|
132
132
|
: placeholder;
|
|
133
133
|
---
|
|
134
134
|
|
|
135
|
-
<
|
|
135
|
+
<sui-combobox
|
|
136
136
|
id={`${name}-container`}
|
|
137
137
|
class="sui-search-select-label"
|
|
138
138
|
class:list={[disabled && "disabled", className, fullWidth && "full"]}
|
|
@@ -222,7 +222,7 @@ const defaultLabel = selected
|
|
|
222
222
|
</div>
|
|
223
223
|
)
|
|
224
224
|
}
|
|
225
|
-
</
|
|
225
|
+
</sui-combobox>
|
|
226
226
|
<script>
|
|
227
227
|
import "studiocms:ui/scripts/searchselect"
|
|
228
228
|
</script>
|
|
@@ -1,19 +1 @@
|
|
|
1
|
-
|
|
2
|
-
label: string;
|
|
3
|
-
value: string;
|
|
4
|
-
disabled?: boolean;
|
|
5
|
-
};
|
|
6
|
-
type SearchSelectContainer = HTMLDivElement & {
|
|
7
|
-
input: HTMLInputElement | null;
|
|
8
|
-
dropdown: Element | null;
|
|
9
|
-
select: HTMLSelectElement | null;
|
|
10
|
-
};
|
|
11
|
-
type SearchSelectState = {
|
|
12
|
-
optionsMap: Record<string, SelectOption[]>;
|
|
13
|
-
isMultipleMap: Record<string, boolean>;
|
|
14
|
-
selectedOptionsMap: Record<string, string[]>;
|
|
15
|
-
placeholderMap: Record<string, string>;
|
|
16
|
-
focusIndex: number;
|
|
17
|
-
isSelectingOption: boolean;
|
|
18
|
-
};
|
|
19
|
-
declare function loadSearchSelects(): void;
|
|
1
|
+
export {};
|
|
@@ -1,50 +1,50 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
const customRect = {
|
|
12
|
-
top: rect.bottom + CONSTANTS.MARGIN,
|
|
13
|
-
bottom: rect.bottom + CONSTANTS.MARGIN + dropdownHeight,
|
|
14
|
-
left: rect.left,
|
|
15
|
-
right: rect.right,
|
|
16
|
-
width: rect.width,
|
|
17
|
-
x: rect.x,
|
|
18
|
-
y: rect.y + rect.height + CONSTANTS.MARGIN,
|
|
19
|
-
height: dropdownHeight
|
|
20
|
-
};
|
|
21
|
-
return {
|
|
22
|
-
isAbove: customRect.top >= 0 && customRect.left >= 0 && customRect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && customRect.right <= (window.innerWidth || document.documentElement.clientWidth),
|
|
23
|
-
customRect
|
|
24
|
-
};
|
|
1
|
+
import { SUISelectElement } from "studiocms:ui/components/select/script";
|
|
2
|
+
class SUIComboboxElement extends SUISelectElement {
|
|
3
|
+
input;
|
|
4
|
+
state = {
|
|
5
|
+
options: [],
|
|
6
|
+
isMultiple: false,
|
|
7
|
+
focusIndex: -1,
|
|
8
|
+
placeholder: "",
|
|
9
|
+
selectedOptions: [],
|
|
10
|
+
isSelectingOption: false
|
|
25
11
|
};
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
"
|
|
34
|
-
"sui-search-select-badge"
|
|
12
|
+
constructor() {
|
|
13
|
+
super();
|
|
14
|
+
}
|
|
15
|
+
connectedCallback() {
|
|
16
|
+
this.input = this.querySelector("input");
|
|
17
|
+
this.dropdown = this.querySelector(".sui-search-select-dropdown-list");
|
|
18
|
+
const selectedOptions = Array.from(
|
|
19
|
+
this.dropdown?.querySelectorAll(".sui-search-select-option.selected") ?? []
|
|
35
20
|
);
|
|
36
|
-
|
|
37
|
-
|
|
21
|
+
this.state.placeholder = this.input?.placeholder ?? "";
|
|
22
|
+
this.state.options = JSON.parse(this.dataset.options ?? "{}");
|
|
23
|
+
this.state.isMultiple = this.dataset.multiple === "true";
|
|
24
|
+
this.state.selectedOptions = selectedOptions.map((x) => x.getAttribute("data-value") ?? "");
|
|
25
|
+
this.input?.addEventListener("focusin", () => this.handleContainerFocusIn());
|
|
26
|
+
this.addEventListener("focusout", () => this.handleContainerFocusOut());
|
|
27
|
+
this.addEventListener("keydown", (e) => this.handleSelectKeyDown(e));
|
|
28
|
+
this.input?.addEventListener("keyup", (e) => this.handleInputKeyup(e));
|
|
29
|
+
this.addEventListener("mousedown", (e) => this.handleContainerMouseDown(e));
|
|
30
|
+
if (this.state.isMultiple) {
|
|
31
|
+
this.recalculateBadges();
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
createSearchSelectBadge = (value, label) => {
|
|
35
|
+
const badge = this.createSelectBadge(value, label);
|
|
36
|
+
badge.classList.remove("sui-select-badge");
|
|
37
|
+
badge.classList.add("sui-search-select-badge");
|
|
38
38
|
return badge;
|
|
39
39
|
};
|
|
40
|
-
|
|
41
|
-
const badgeContainer =
|
|
42
|
-
if (!badgeContainer || !
|
|
40
|
+
recalculateBadges = () => {
|
|
41
|
+
const badgeContainer = this.querySelector(".sui-search-select-badge-container");
|
|
42
|
+
if (!badgeContainer || !this.input) return;
|
|
43
43
|
badgeContainer.innerHTML = "";
|
|
44
|
-
const selectedValues =
|
|
45
|
-
const allOptions =
|
|
44
|
+
const selectedValues = this.state.selectedOptions;
|
|
45
|
+
const allOptions = this.state.options;
|
|
46
46
|
if (selectedValues.length === 0) {
|
|
47
|
-
|
|
47
|
+
this.input.placeholder = this.state.placeholder ?? "";
|
|
48
48
|
return;
|
|
49
49
|
}
|
|
50
50
|
for (const value of selectedValues.sort((a, b) => {
|
|
@@ -54,46 +54,44 @@ function loadSearchSelects() {
|
|
|
54
54
|
})) {
|
|
55
55
|
const option = allOptions.find((opt) => opt.value === value);
|
|
56
56
|
if (option) {
|
|
57
|
-
const newBadge =
|
|
57
|
+
const newBadge = this.createSearchSelectBadge(value, option.label);
|
|
58
58
|
badgeContainer.appendChild(newBadge);
|
|
59
59
|
}
|
|
60
60
|
}
|
|
61
61
|
};
|
|
62
|
-
|
|
63
|
-
const selectedInput =
|
|
64
|
-
if (isMultiple) {
|
|
65
|
-
recalculateBadges(
|
|
62
|
+
updateLabel = () => {
|
|
63
|
+
const selectedInput = this.input;
|
|
64
|
+
if (this.state.isMultiple) {
|
|
65
|
+
this.recalculateBadges();
|
|
66
66
|
if (selectedInput) {
|
|
67
|
-
selectedInput.placeholder =
|
|
67
|
+
selectedInput.placeholder = this.state.placeholder;
|
|
68
68
|
}
|
|
69
69
|
} else {
|
|
70
|
-
const selected =
|
|
71
|
-
".sui-search-select-option.selected"
|
|
72
|
-
);
|
|
70
|
+
const selected = this.querySelector(".sui-search-select-option.selected");
|
|
73
71
|
if (selected && selectedInput) {
|
|
74
72
|
selectedInput.placeholder = selected.innerText.trim();
|
|
75
73
|
}
|
|
76
74
|
}
|
|
77
75
|
};
|
|
78
|
-
|
|
79
|
-
const currentSelected =
|
|
76
|
+
updateOptionSelection = (value, forceState) => {
|
|
77
|
+
const currentSelected = this.state.selectedOptions;
|
|
80
78
|
const isCurrentlySelected = currentSelected.includes(value);
|
|
81
|
-
const max = Number.parseInt(
|
|
79
|
+
const max = Number.parseInt(this.dataset.multipleMax, 10);
|
|
82
80
|
if (!isCurrentlySelected && !Number.isNaN(max) && currentSelected.length >= max) {
|
|
83
81
|
return false;
|
|
84
82
|
}
|
|
85
83
|
const newSelected = isCurrentlySelected ? currentSelected.filter((v) => v !== value) : [...currentSelected, value];
|
|
86
|
-
|
|
87
|
-
const option =
|
|
84
|
+
this.state.selectedOptions = newSelected;
|
|
85
|
+
const option = this.dropdown?.querySelector(
|
|
88
86
|
`.sui-search-select-option[data-value='${value}']`
|
|
89
87
|
);
|
|
90
88
|
if (option) {
|
|
91
89
|
option.classList.toggle("selected", forceState ?? !isCurrentlySelected);
|
|
92
|
-
if (
|
|
93
|
-
|
|
90
|
+
if (this.select) {
|
|
91
|
+
this.select.value = option.getAttribute("value");
|
|
94
92
|
}
|
|
95
93
|
}
|
|
96
|
-
const selectedCountEl =
|
|
94
|
+
const selectedCountEl = this.querySelector(
|
|
97
95
|
".sui-search-select-max-span .sui-search-select-select-count"
|
|
98
96
|
);
|
|
99
97
|
if (selectedCountEl) {
|
|
@@ -101,29 +99,29 @@ function loadSearchSelects() {
|
|
|
101
99
|
}
|
|
102
100
|
return true;
|
|
103
101
|
};
|
|
104
|
-
|
|
105
|
-
const success = updateOptionSelection(id
|
|
102
|
+
toggleMultiOption = (id) => {
|
|
103
|
+
const success = this.updateOptionSelection(id);
|
|
106
104
|
if (success) {
|
|
107
|
-
recalculateBadges(
|
|
105
|
+
this.recalculateBadges();
|
|
108
106
|
}
|
|
109
107
|
};
|
|
110
|
-
|
|
111
|
-
const optionElements =
|
|
108
|
+
recomputeOptions = () => {
|
|
109
|
+
const optionElements = this.dropdown?.querySelectorAll(
|
|
112
110
|
".sui-search-select-option"
|
|
113
111
|
);
|
|
114
112
|
for (const entry of optionElements) {
|
|
115
|
-
if (Number.parseInt(entry.dataset.optionIndex, 10) ===
|
|
113
|
+
if (Number.parseInt(entry.dataset.optionIndex, 10) === this.state.focusIndex) {
|
|
116
114
|
entry.classList.add("focused");
|
|
117
115
|
} else {
|
|
118
116
|
entry.classList.remove("focused");
|
|
119
117
|
}
|
|
120
118
|
}
|
|
121
119
|
};
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
const selectedValues =
|
|
120
|
+
reconstructOptions = (filteredOptions) => {
|
|
121
|
+
this.dropdown.innerHTML = "";
|
|
122
|
+
const selectedValues = this.state.selectedOptions;
|
|
125
123
|
if (filteredOptions.length === 0) {
|
|
126
|
-
|
|
124
|
+
this.dropdown.innerHTML = '<li class="empty-search-results">No results found</li>';
|
|
127
125
|
return;
|
|
128
126
|
}
|
|
129
127
|
let i = 0;
|
|
@@ -140,101 +138,101 @@ function loadSearchSelects() {
|
|
|
140
138
|
element.dataset.optionIndex = i.toString();
|
|
141
139
|
element.dataset.value = option.value;
|
|
142
140
|
element.textContent = option.label;
|
|
143
|
-
|
|
141
|
+
this.dropdown?.appendChild(element);
|
|
144
142
|
i++;
|
|
145
143
|
}
|
|
146
144
|
};
|
|
147
|
-
|
|
148
|
-
const allOptions =
|
|
145
|
+
getInteractiveOptions = () => {
|
|
146
|
+
const allOptions = this.dropdown?.querySelectorAll(
|
|
149
147
|
".sui-search-select-option"
|
|
150
148
|
);
|
|
151
149
|
return Array.from(allOptions).filter(
|
|
152
150
|
(option) => !option.classList.contains("hidden") && !option.classList.contains("disabled") && !option.hasAttribute("disabled")
|
|
153
151
|
);
|
|
154
152
|
};
|
|
155
|
-
|
|
153
|
+
handleContainerMouseDown = (e) => {
|
|
156
154
|
const target = e.target;
|
|
157
155
|
if (!target.closest("input")) {
|
|
158
156
|
e.preventDefault();
|
|
159
157
|
}
|
|
160
|
-
if (
|
|
161
|
-
reconstructOptions(
|
|
158
|
+
if (this.input?.value.length === 0) {
|
|
159
|
+
this.reconstructOptions(this.state.options);
|
|
162
160
|
}
|
|
163
161
|
if (target.closest(".sui-search-select-indicator")) {
|
|
164
|
-
if (
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
162
|
+
if (this.dropdown?.parentElement?.classList.contains("active")) {
|
|
163
|
+
this.dropdown?.parentElement?.classList.remove("active", "above");
|
|
164
|
+
this.input?.blur();
|
|
165
|
+
this.input.value = "";
|
|
168
166
|
} else {
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
167
|
+
this.dropdown?.parentElement?.classList.add("active");
|
|
168
|
+
this.input?.focus();
|
|
169
|
+
this.input.value = "";
|
|
172
170
|
}
|
|
173
171
|
return;
|
|
174
172
|
}
|
|
175
173
|
if (target.closest(".sui-search-select-badge-container")) {
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
174
|
+
this.dropdown?.parentElement?.classList.remove("active", "above");
|
|
175
|
+
this.input?.blur();
|
|
176
|
+
this.input.value = "";
|
|
179
177
|
}
|
|
180
|
-
|
|
178
|
+
this.state.isSelectingOption = true;
|
|
181
179
|
setTimeout(() => {
|
|
182
|
-
|
|
180
|
+
this.state.isSelectingOption = false;
|
|
183
181
|
}, 0);
|
|
184
182
|
if (target.closest(".sui-search-select-badge svg")) {
|
|
185
183
|
const value = target.closest(".sui-search-select-badge")?.getAttribute("data-value");
|
|
186
|
-
const success = updateOptionSelection(value
|
|
184
|
+
const success = this.updateOptionSelection(value);
|
|
187
185
|
if (success) {
|
|
188
|
-
recalculateBadges(
|
|
186
|
+
this.recalculateBadges();
|
|
189
187
|
}
|
|
190
188
|
return;
|
|
191
189
|
}
|
|
192
190
|
const opt = target.closest(".sui-search-select-option");
|
|
193
191
|
if (!opt?.dataset.value) return;
|
|
194
192
|
if (opt.classList.contains("disabled") || opt.hasAttribute("disabled")) {
|
|
195
|
-
|
|
193
|
+
this.input?.focus();
|
|
196
194
|
return;
|
|
197
195
|
}
|
|
198
|
-
const isMultiple =
|
|
196
|
+
const isMultiple = this.state.isMultiple;
|
|
199
197
|
if (isMultiple) {
|
|
200
|
-
const success = updateOptionSelection(opt.dataset.value
|
|
198
|
+
const success = this.updateOptionSelection(opt.dataset.value);
|
|
201
199
|
if (success) {
|
|
202
|
-
updateLabel(
|
|
203
|
-
recalculateBadges(
|
|
200
|
+
this.updateLabel();
|
|
201
|
+
this.recalculateBadges();
|
|
204
202
|
}
|
|
205
203
|
} else {
|
|
206
|
-
const currentSelected =
|
|
204
|
+
const currentSelected = this.state.selectedOptions;
|
|
207
205
|
for (const value of currentSelected) {
|
|
208
|
-
updateOptionSelection(value,
|
|
206
|
+
this.updateOptionSelection(value, false);
|
|
209
207
|
}
|
|
210
|
-
updateOptionSelection(opt.dataset.value,
|
|
211
|
-
updateLabel(
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
208
|
+
this.updateOptionSelection(opt.dataset.value, true);
|
|
209
|
+
this.updateLabel();
|
|
210
|
+
this.dropdown?.parentElement?.classList.remove("active", "above");
|
|
211
|
+
this.input?.blur();
|
|
212
|
+
this.input.value = "";
|
|
215
213
|
}
|
|
216
214
|
};
|
|
217
|
-
|
|
215
|
+
handleSelectKeyDown = (e) => {
|
|
218
216
|
const focusedElement = document.activeElement;
|
|
219
217
|
if (e.key === "Escape" || e.key === "Tab") {
|
|
220
|
-
|
|
221
|
-
|
|
218
|
+
this.input?.blur();
|
|
219
|
+
this.dropdown?.parentElement?.classList.remove("active", "above");
|
|
222
220
|
return;
|
|
223
221
|
}
|
|
224
222
|
if ((e.key === "Enter" || e.key === " ") && focusedElement?.tagName.toLowerCase() === "svg") {
|
|
225
223
|
const badgeElement = focusedElement?.closest(".sui-search-select-badge");
|
|
226
|
-
if (badgeElement &&
|
|
224
|
+
if (badgeElement && this.state.isMultiple) {
|
|
227
225
|
const badgeValue = badgeElement.getAttribute("data-value");
|
|
228
226
|
let nextBadge = badgeElement.previousElementSibling;
|
|
229
227
|
if (!nextBadge) {
|
|
230
228
|
nextBadge = badgeElement.nextElementSibling;
|
|
231
229
|
}
|
|
232
230
|
const nextBadgeValue = nextBadge?.getAttribute("data-value");
|
|
233
|
-
toggleMultiOption(badgeValue
|
|
234
|
-
recalculateBadges(
|
|
231
|
+
this.toggleMultiOption(badgeValue);
|
|
232
|
+
this.recalculateBadges();
|
|
235
233
|
setTimeout(() => {
|
|
236
234
|
if (nextBadgeValue) {
|
|
237
|
-
const badgeToFocus =
|
|
235
|
+
const badgeToFocus = this.querySelector(
|
|
238
236
|
`.sui-search-select-badge[data-value="${nextBadgeValue}"] svg`
|
|
239
237
|
);
|
|
240
238
|
if (badgeToFocus) {
|
|
@@ -247,150 +245,98 @@ function loadSearchSelects() {
|
|
|
247
245
|
return;
|
|
248
246
|
}
|
|
249
247
|
}
|
|
250
|
-
const interactiveOptions = getInteractiveOptions(
|
|
248
|
+
const interactiveOptions = this.getInteractiveOptions();
|
|
251
249
|
const currentInteractiveIndex = interactiveOptions.findIndex(
|
|
252
250
|
(option) => option.classList.contains("focused")
|
|
253
251
|
);
|
|
254
252
|
if (e.key === "ArrowUp" && currentInteractiveIndex > 0) {
|
|
255
|
-
|
|
256
|
-
|
|
253
|
+
this.state.focusIndex = Array.from(
|
|
254
|
+
this.dropdown?.querySelectorAll(".sui-search-select-option") || []
|
|
257
255
|
).indexOf(interactiveOptions[currentInteractiveIndex - 1]);
|
|
258
|
-
recomputeOptions(
|
|
256
|
+
this.recomputeOptions();
|
|
259
257
|
return;
|
|
260
258
|
}
|
|
261
259
|
if (e.key === "ArrowDown" && currentInteractiveIndex < interactiveOptions.length - 1) {
|
|
262
|
-
|
|
263
|
-
|
|
260
|
+
this.state.focusIndex = Array.from(
|
|
261
|
+
this.dropdown?.querySelectorAll(".sui-search-select-option") || []
|
|
264
262
|
).indexOf(interactiveOptions[currentInteractiveIndex + 1]);
|
|
265
|
-
recomputeOptions(
|
|
263
|
+
this.recomputeOptions();
|
|
266
264
|
return;
|
|
267
265
|
}
|
|
268
266
|
if (e.key === "PageUp") {
|
|
269
|
-
|
|
270
|
-
|
|
267
|
+
this.state.focusIndex = Array.from(
|
|
268
|
+
this.dropdown?.querySelectorAll(".sui-search-select-option") || []
|
|
271
269
|
).indexOf(interactiveOptions[0]);
|
|
272
|
-
recomputeOptions(
|
|
270
|
+
this.recomputeOptions();
|
|
273
271
|
return;
|
|
274
272
|
}
|
|
275
273
|
if (e.key === "PageDown") {
|
|
276
|
-
|
|
277
|
-
|
|
274
|
+
this.state.focusIndex = Array.from(
|
|
275
|
+
this.dropdown?.querySelectorAll(".sui-search-select-option") || []
|
|
278
276
|
).indexOf(interactiveOptions[interactiveOptions.length - 1]);
|
|
279
|
-
recomputeOptions(
|
|
277
|
+
this.recomputeOptions();
|
|
280
278
|
return;
|
|
281
279
|
}
|
|
282
280
|
if (e.key === "Enter") {
|
|
283
281
|
e.preventDefault();
|
|
284
282
|
e.stopImmediatePropagation();
|
|
285
|
-
const optionElements =
|
|
283
|
+
const optionElements = this.dropdown?.querySelectorAll(
|
|
286
284
|
".sui-search-select-option"
|
|
287
285
|
);
|
|
288
286
|
const focusedOption = Array.from(optionElements).find(
|
|
289
|
-
(entry) => Number.parseInt(entry.dataset.optionIndex, 10) ===
|
|
287
|
+
(entry) => Number.parseInt(entry.dataset.optionIndex, 10) === this.state.focusIndex
|
|
290
288
|
);
|
|
291
289
|
if (focusedOption && !focusedOption.classList.contains("disabled") && !focusedOption.hasAttribute("disabled")) {
|
|
292
290
|
const value = focusedOption.dataset.value;
|
|
293
291
|
if (!value) return;
|
|
294
|
-
const isMultiple =
|
|
292
|
+
const isMultiple = this.state.isMultiple;
|
|
295
293
|
if (isMultiple) {
|
|
296
|
-
const success = updateOptionSelection(value
|
|
294
|
+
const success = this.updateOptionSelection(value);
|
|
297
295
|
if (success) {
|
|
298
|
-
updateLabel(
|
|
299
|
-
recalculateBadges(
|
|
296
|
+
this.updateLabel();
|
|
297
|
+
this.recalculateBadges();
|
|
300
298
|
}
|
|
301
299
|
} else {
|
|
302
|
-
const currentSelected =
|
|
300
|
+
const currentSelected = this.state.selectedOptions;
|
|
303
301
|
for (const existingValue of currentSelected) {
|
|
304
|
-
updateOptionSelection(existingValue,
|
|
302
|
+
this.updateOptionSelection(existingValue, false);
|
|
305
303
|
}
|
|
306
|
-
updateOptionSelection(value,
|
|
307
|
-
updateLabel(
|
|
308
|
-
|
|
309
|
-
|
|
304
|
+
this.updateOptionSelection(value, true);
|
|
305
|
+
this.updateLabel();
|
|
306
|
+
this.dropdown?.parentElement?.classList.remove("active", "above");
|
|
307
|
+
this.input.value = "";
|
|
310
308
|
}
|
|
311
309
|
}
|
|
312
310
|
return;
|
|
313
311
|
}
|
|
314
312
|
};
|
|
315
|
-
|
|
313
|
+
handleInputKeyup = (e) => {
|
|
316
314
|
if (["Enter", "ArrowUp", "ArrowDown"].includes(e.key)) return;
|
|
317
|
-
const value =
|
|
318
|
-
const allOptions =
|
|
315
|
+
const value = this.input.value.trim().toLowerCase();
|
|
316
|
+
const allOptions = this.state.options;
|
|
319
317
|
if (value.length === 0) {
|
|
320
|
-
reconstructOptions(allOptions
|
|
318
|
+
this.reconstructOptions(allOptions);
|
|
321
319
|
return;
|
|
322
320
|
}
|
|
323
|
-
const filteredOptions = allOptions
|
|
324
|
-
|
|
325
|
-
reconstructOptions(filteredOptions
|
|
321
|
+
const filteredOptions = allOptions.filter((option) => option.label.toLowerCase().includes(value)) ?? [];
|
|
322
|
+
this.state.focusIndex = 0;
|
|
323
|
+
this.reconstructOptions(filteredOptions);
|
|
326
324
|
};
|
|
327
|
-
|
|
328
|
-
if (
|
|
329
|
-
|
|
330
|
-
reconstructOptions(
|
|
331
|
-
|
|
325
|
+
handleContainerFocusOut = () => {
|
|
326
|
+
if (this.state.isSelectingOption) return;
|
|
327
|
+
this.input.value = "";
|
|
328
|
+
this.reconstructOptions(this.state.options);
|
|
329
|
+
this.dropdown?.parentElement?.classList.remove("active", "above");
|
|
332
330
|
};
|
|
333
|
-
|
|
331
|
+
handleContainerFocusIn = () => {
|
|
334
332
|
const allDropdowns = document.querySelectorAll(".sui-search-select-dropdown-list");
|
|
335
333
|
for (const dropdown of allDropdowns) {
|
|
336
|
-
if (dropdown !==
|
|
334
|
+
if (dropdown !== this.dropdown) {
|
|
337
335
|
dropdown.parentElement?.classList.remove("active", "above");
|
|
338
336
|
}
|
|
339
337
|
}
|
|
340
|
-
const { isAbove } = getDropdownPosition(
|
|
341
|
-
|
|
342
|
-
state2.optionsMap[container.dataset.id]?.length ?? 0
|
|
343
|
-
);
|
|
344
|
-
container.dropdown?.parentElement?.classList.add("active", ...isAbove ? [] : ["above"]);
|
|
345
|
-
};
|
|
346
|
-
const state = {
|
|
347
|
-
optionsMap: {},
|
|
348
|
-
isMultipleMap: {},
|
|
349
|
-
placeholderMap: {},
|
|
350
|
-
selectedOptionsMap: {},
|
|
351
|
-
focusIndex: 0,
|
|
352
|
-
isSelectingOption: false
|
|
338
|
+
const { isAbove } = this.getDropdownPosition(this.input);
|
|
339
|
+
this.dropdown?.parentElement?.classList.add("active", ...isAbove ? [] : ["above"]);
|
|
353
340
|
};
|
|
354
|
-
const selects = document.querySelectorAll(".sui-search-select-label");
|
|
355
|
-
for (const container of selects) {
|
|
356
|
-
if (container.dataset.initialized === "true") continue;
|
|
357
|
-
const id = container.dataset.id;
|
|
358
|
-
const specialContainer = Object.assign(container, {
|
|
359
|
-
input: container.querySelector("input"),
|
|
360
|
-
dropdown: container.querySelector(".sui-search-select-dropdown-list"),
|
|
361
|
-
select: container.querySelector("select")
|
|
362
|
-
});
|
|
363
|
-
const selectedOptions = Array.from(
|
|
364
|
-
specialContainer.dropdown?.querySelectorAll(".sui-search-select-option.selected") ?? []
|
|
365
|
-
);
|
|
366
|
-
state.placeholderMap[id] = specialContainer.input?.placeholder ?? "";
|
|
367
|
-
state.optionsMap[id] = JSON.parse(container.dataset.options ?? "{}");
|
|
368
|
-
state.isMultipleMap[id] = container.dataset.multiple === "true";
|
|
369
|
-
state.selectedOptionsMap[id] = selectedOptions.map((x) => x.getAttribute("data-value") ?? "");
|
|
370
|
-
specialContainer.input?.addEventListener(
|
|
371
|
-
"focusin",
|
|
372
|
-
() => handleContainerFocusIn(state, specialContainer)
|
|
373
|
-
);
|
|
374
|
-
specialContainer.addEventListener(
|
|
375
|
-
"focusout",
|
|
376
|
-
() => handleContainerFocusOut(state, specialContainer)
|
|
377
|
-
);
|
|
378
|
-
specialContainer.addEventListener(
|
|
379
|
-
"keydown",
|
|
380
|
-
(e) => handleSelectKeyDown(e, state, specialContainer)
|
|
381
|
-
);
|
|
382
|
-
specialContainer.input?.addEventListener(
|
|
383
|
-
"keyup",
|
|
384
|
-
(e) => handleInputKeyup(e, state, specialContainer)
|
|
385
|
-
);
|
|
386
|
-
specialContainer.addEventListener(
|
|
387
|
-
"mousedown",
|
|
388
|
-
(e) => handleContainerMouseDown(e, state, specialContainer)
|
|
389
|
-
);
|
|
390
|
-
if (state.isMultipleMap[id]) {
|
|
391
|
-
recalculateBadges(state, specialContainer);
|
|
392
|
-
}
|
|
393
|
-
container.dataset.initialized = "true";
|
|
394
|
-
}
|
|
395
341
|
}
|
|
396
|
-
|
|
342
|
+
customElements.define("sui-combobox", SUIComboboxElement);
|
|
@@ -131,14 +131,13 @@ const defaultLabel = selected
|
|
|
131
131
|
: placeholder;
|
|
132
132
|
---
|
|
133
133
|
|
|
134
|
-
<
|
|
134
|
+
<sui-select
|
|
135
135
|
id={`${name}-container`}
|
|
136
136
|
class="sui-select-label"
|
|
137
137
|
class:list={[disabled && "disabled", className, fullWidth && "full"]}
|
|
138
138
|
data-options={JSON.stringify(options)}
|
|
139
139
|
data-multiple={multiple ? "true" : undefined}
|
|
140
140
|
data-multiple-max={multiple && max !== undefined ? max : undefined}
|
|
141
|
-
data-id={name}
|
|
142
141
|
>
|
|
143
142
|
{label && (
|
|
144
143
|
<label class="label" for={`${name}-select-btn`}>
|
|
@@ -200,7 +199,7 @@ const defaultLabel = selected
|
|
|
200
199
|
const isSelected = Array.isArray(selected)
|
|
201
200
|
? selected.map((y) => y && y.value).includes(x.value)
|
|
202
201
|
: selected?.value === x.value;
|
|
203
|
-
|
|
202
|
+
|
|
204
203
|
return (
|
|
205
204
|
<option
|
|
206
205
|
value={x.value}
|
|
@@ -219,11 +218,11 @@ const defaultLabel = selected
|
|
|
219
218
|
</span>
|
|
220
219
|
)}
|
|
221
220
|
{
|
|
222
|
-
multiple && Array.isArray(selected ?? []) && (
|
|
221
|
+
multiple && Array.isArray(selected ?? []) && (
|
|
223
222
|
<div class="sui-select-badge-container-below"></div>
|
|
224
223
|
)
|
|
225
224
|
}
|
|
226
|
-
</
|
|
225
|
+
</sui-select>
|
|
227
226
|
<script>
|
|
228
227
|
import "studiocms:ui/scripts/select"
|
|
229
228
|
</script>
|