@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.
@@ -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-base);
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-base);
280
+ background-color: var(--warning-flat);
281
281
  color: hsl(48, 78%, 20%);
282
282
  }
283
283
  .sui-button.warning.flat:hover {
@@ -45,6 +45,7 @@
45
45
  position: absolute;
46
46
  top: 50%;
47
47
  transform: translateY(-50%);
48
+ color: var(--text-muted);
48
49
  }
49
50
  .input-icon.icon-left {
50
51
  left: 0.5rem;
@@ -132,7 +132,7 @@ const defaultLabel = selected
132
132
  : placeholder;
133
133
  ---
134
134
 
135
- <div
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
- </div>
225
+ </sui-combobox>
226
226
  <script>
227
227
  import "studiocms:ui/scripts/searchselect"
228
228
  </script>
@@ -1,19 +1 @@
1
- type SearchSelectOption = {
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
- function loadSearchSelects() {
2
- const CONSTANTS = {
3
- OPTION_HEIGHT: 36,
4
- BORDER_SIZE: 2,
5
- MARGIN: 4,
6
- BADGE_PADDING: 80
7
- };
8
- const getDropdownPosition = (input, optionsCount) => {
9
- const rect = input.getBoundingClientRect();
10
- const dropdownHeight = optionsCount * CONSTANTS.OPTION_HEIGHT + CONSTANTS.BORDER_SIZE + CONSTANTS.MARGIN;
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
- const createSelectBadge = (value, label) => {
27
- const badge = document.createElement("span");
28
- badge.classList.add(
29
- "sui-badge",
30
- "primary",
31
- "sm",
32
- "outlined",
33
- "full",
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
- badge.setAttribute("data-value", value);
37
- badge.innerHTML = `${label} <svg style='min-width: 8px' xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 24 24' role="button" tabindex="0"><path fill='none' stroke='currentColor' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 18L18 6M6 6l12 12'></path></svg>`;
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
- const recalculateBadges = (state2, container) => {
41
- const badgeContainer = container.querySelector(".sui-search-select-badge-container");
42
- if (!badgeContainer || !container.input) return;
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 = state2.selectedOptionsMap[container.dataset.id] || [];
45
- const allOptions = state2.optionsMap[container.dataset.id] || [];
44
+ const selectedValues = this.state.selectedOptions;
45
+ const allOptions = this.state.options;
46
46
  if (selectedValues.length === 0) {
47
- container.input.placeholder = state2.placeholderMap[container.dataset.id] ?? "";
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 = createSelectBadge(value, option.label);
57
+ const newBadge = this.createSearchSelectBadge(value, option.label);
58
58
  badgeContainer.appendChild(newBadge);
59
59
  }
60
60
  }
61
61
  };
62
- const updateLabel = (isMultiple, state2, container) => {
63
- const selectedInput = container?.input;
64
- if (isMultiple) {
65
- recalculateBadges(state2, container);
62
+ updateLabel = () => {
63
+ const selectedInput = this.input;
64
+ if (this.state.isMultiple) {
65
+ this.recalculateBadges();
66
66
  if (selectedInput) {
67
- selectedInput.placeholder = state2.placeholderMap[container.dataset.id] ?? "";
67
+ selectedInput.placeholder = this.state.placeholder;
68
68
  }
69
69
  } else {
70
- const selected = container.querySelector(
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
- const updateOptionSelection = (value, container, state2, forceState) => {
79
- const currentSelected = state2.selectedOptionsMap[container.dataset.id] || [];
76
+ updateOptionSelection = (value, forceState) => {
77
+ const currentSelected = this.state.selectedOptions;
80
78
  const isCurrentlySelected = currentSelected.includes(value);
81
- const max = Number.parseInt(container.dataset.multipleMax, 10);
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
- state2.selectedOptionsMap[container.dataset.id] = newSelected;
87
- const option = container.dropdown?.querySelector(
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 (container?.select) {
93
- container.select.value = option.getAttribute("value");
90
+ if (this.select) {
91
+ this.select.value = option.getAttribute("value");
94
92
  }
95
93
  }
96
- const selectedCountEl = container.querySelector(
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
- const toggleMultiOption = (id, container, state2) => {
105
- const success = updateOptionSelection(id, container, state2);
102
+ toggleMultiOption = (id) => {
103
+ const success = this.updateOptionSelection(id);
106
104
  if (success) {
107
- recalculateBadges(state2, container);
105
+ this.recalculateBadges();
108
106
  }
109
107
  };
110
- const recomputeOptions = (state2, container) => {
111
- const optionElements = container?.dropdown?.querySelectorAll(
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) === state2.focusIndex) {
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
- const reconstructOptions = (filteredOptions, state2, container) => {
123
- container.dropdown.innerHTML = "";
124
- const selectedValues = state2.selectedOptionsMap[container.dataset.id] || [];
120
+ reconstructOptions = (filteredOptions) => {
121
+ this.dropdown.innerHTML = "";
122
+ const selectedValues = this.state.selectedOptions;
125
123
  if (filteredOptions.length === 0) {
126
- container.dropdown.innerHTML = '<li class="empty-search-results">No results found</li>';
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
- container.dropdown?.appendChild(element);
141
+ this.dropdown?.appendChild(element);
144
142
  i++;
145
143
  }
146
144
  };
147
- const getInteractiveOptions = (container) => {
148
- const allOptions = container?.dropdown?.querySelectorAll(
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
- const handleContainerMouseDown = (e, state2, container) => {
153
+ handleContainerMouseDown = (e) => {
156
154
  const target = e.target;
157
155
  if (!target.closest("input")) {
158
156
  e.preventDefault();
159
157
  }
160
- if (container.input?.value.length === 0) {
161
- reconstructOptions(state2.optionsMap[container.dataset.id] ?? [], state2, container);
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 (container.dropdown?.parentElement?.classList.contains("active")) {
165
- container.dropdown?.parentElement?.classList.remove("active", "above");
166
- container.input?.blur();
167
- container.input.value = "";
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
- container.dropdown?.parentElement?.classList.add("active");
170
- container.input?.focus();
171
- container.input.value = "";
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
- container.dropdown?.parentElement?.classList.remove("active", "above");
177
- container.input?.blur();
178
- container.input.value = "";
174
+ this.dropdown?.parentElement?.classList.remove("active", "above");
175
+ this.input?.blur();
176
+ this.input.value = "";
179
177
  }
180
- state2.isSelectingOption = true;
178
+ this.state.isSelectingOption = true;
181
179
  setTimeout(() => {
182
- state2.isSelectingOption = false;
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, container, state2);
184
+ const success = this.updateOptionSelection(value);
187
185
  if (success) {
188
- recalculateBadges(state2, container);
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
- container.input?.focus();
193
+ this.input?.focus();
196
194
  return;
197
195
  }
198
- const isMultiple = state2.isMultipleMap[container.dataset.id];
196
+ const isMultiple = this.state.isMultiple;
199
197
  if (isMultiple) {
200
- const success = updateOptionSelection(opt.dataset.value, container, state2);
198
+ const success = this.updateOptionSelection(opt.dataset.value);
201
199
  if (success) {
202
- updateLabel(true, state2, container);
203
- recalculateBadges(state2, container);
200
+ this.updateLabel();
201
+ this.recalculateBadges();
204
202
  }
205
203
  } else {
206
- const currentSelected = state2.selectedOptionsMap[container.dataset.id] || [];
204
+ const currentSelected = this.state.selectedOptions;
207
205
  for (const value of currentSelected) {
208
- updateOptionSelection(value, container, state2, false);
206
+ this.updateOptionSelection(value, false);
209
207
  }
210
- updateOptionSelection(opt.dataset.value, container, state2, true);
211
- updateLabel(false, state2, container);
212
- container.dropdown?.parentElement?.classList.remove("active", "above");
213
- container.input?.blur();
214
- container.input.value = "";
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
- const handleSelectKeyDown = (e, state2, container) => {
215
+ handleSelectKeyDown = (e) => {
218
216
  const focusedElement = document.activeElement;
219
217
  if (e.key === "Escape" || e.key === "Tab") {
220
- container.input?.blur();
221
- container.dropdown?.parentElement?.classList.remove("active", "above");
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 && state2.isMultipleMap[container?.dataset.id]) {
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, container, state2);
234
- recalculateBadges(state2, container);
231
+ this.toggleMultiOption(badgeValue);
232
+ this.recalculateBadges();
235
233
  setTimeout(() => {
236
234
  if (nextBadgeValue) {
237
- const badgeToFocus = container?.querySelector(
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(container);
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
- state2.focusIndex = Array.from(
256
- container?.dropdown?.querySelectorAll(".sui-search-select-option") || []
253
+ this.state.focusIndex = Array.from(
254
+ this.dropdown?.querySelectorAll(".sui-search-select-option") || []
257
255
  ).indexOf(interactiveOptions[currentInteractiveIndex - 1]);
258
- recomputeOptions(state2, container);
256
+ this.recomputeOptions();
259
257
  return;
260
258
  }
261
259
  if (e.key === "ArrowDown" && currentInteractiveIndex < interactiveOptions.length - 1) {
262
- state2.focusIndex = Array.from(
263
- container?.dropdown?.querySelectorAll(".sui-search-select-option") || []
260
+ this.state.focusIndex = Array.from(
261
+ this.dropdown?.querySelectorAll(".sui-search-select-option") || []
264
262
  ).indexOf(interactiveOptions[currentInteractiveIndex + 1]);
265
- recomputeOptions(state2, container);
263
+ this.recomputeOptions();
266
264
  return;
267
265
  }
268
266
  if (e.key === "PageUp") {
269
- state2.focusIndex = Array.from(
270
- container?.dropdown?.querySelectorAll(".sui-search-select-option") || []
267
+ this.state.focusIndex = Array.from(
268
+ this.dropdown?.querySelectorAll(".sui-search-select-option") || []
271
269
  ).indexOf(interactiveOptions[0]);
272
- recomputeOptions(state2, container);
270
+ this.recomputeOptions();
273
271
  return;
274
272
  }
275
273
  if (e.key === "PageDown") {
276
- state2.focusIndex = Array.from(
277
- container?.dropdown?.querySelectorAll(".sui-search-select-option") || []
274
+ this.state.focusIndex = Array.from(
275
+ this.dropdown?.querySelectorAll(".sui-search-select-option") || []
278
276
  ).indexOf(interactiveOptions[interactiveOptions.length - 1]);
279
- recomputeOptions(state2, container);
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 = container?.dropdown?.querySelectorAll(
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) === state2.focusIndex
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 = state2.isMultipleMap[container.dataset.id];
292
+ const isMultiple = this.state.isMultiple;
295
293
  if (isMultiple) {
296
- const success = updateOptionSelection(value, container, state2);
294
+ const success = this.updateOptionSelection(value);
297
295
  if (success) {
298
- updateLabel(true, state2, container);
299
- recalculateBadges(state2, container);
296
+ this.updateLabel();
297
+ this.recalculateBadges();
300
298
  }
301
299
  } else {
302
- const currentSelected = state2.selectedOptionsMap[container.dataset.id] || [];
300
+ const currentSelected = this.state.selectedOptions;
303
301
  for (const existingValue of currentSelected) {
304
- updateOptionSelection(existingValue, container, state2, false);
302
+ this.updateOptionSelection(existingValue, false);
305
303
  }
306
- updateOptionSelection(value, container, state2, true);
307
- updateLabel(false, state2, container);
308
- container.dropdown?.parentElement?.classList.remove("active", "above");
309
- container.input.value = "";
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
- const handleInputKeyup = (e, state2, container) => {
313
+ handleInputKeyup = (e) => {
316
314
  if (["Enter", "ArrowUp", "ArrowDown"].includes(e.key)) return;
317
- const value = container.input.value.trim().toLowerCase();
318
- const allOptions = state2.optionsMap[container.dataset.id];
315
+ const value = this.input.value.trim().toLowerCase();
316
+ const allOptions = this.state.options;
319
317
  if (value.length === 0) {
320
- reconstructOptions(allOptions, state2, container);
318
+ this.reconstructOptions(allOptions);
321
319
  return;
322
320
  }
323
- const filteredOptions = allOptions?.filter((option) => option.label.toLowerCase().includes(value)) ?? [];
324
- state2.focusIndex = 0;
325
- reconstructOptions(filteredOptions, state2, container);
321
+ const filteredOptions = allOptions.filter((option) => option.label.toLowerCase().includes(value)) ?? [];
322
+ this.state.focusIndex = 0;
323
+ this.reconstructOptions(filteredOptions);
326
324
  };
327
- const handleContainerFocusOut = (state2, container) => {
328
- if (state2.isSelectingOption) return;
329
- container.input.value = "";
330
- reconstructOptions(state2.optionsMap[container.dataset.id] ?? [], state2, container);
331
- container.dropdown?.parentElement?.classList.remove("active", "above");
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
- const handleContainerFocusIn = (state2, container) => {
331
+ handleContainerFocusIn = () => {
334
332
  const allDropdowns = document.querySelectorAll(".sui-search-select-dropdown-list");
335
333
  for (const dropdown of allDropdowns) {
336
- if (dropdown !== container.dropdown) {
334
+ if (dropdown !== this.dropdown) {
337
335
  dropdown.parentElement?.classList.remove("active", "above");
338
336
  }
339
337
  }
340
- const { isAbove } = getDropdownPosition(
341
- container.input,
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
- document.addEventListener("astro:page-load", loadSearchSelects);
342
+ customElements.define("sui-combobox", SUIComboboxElement);
@@ -131,14 +131,13 @@ const defaultLabel = selected
131
131
  : placeholder;
132
132
  ---
133
133
 
134
- <div
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
- </div>
225
+ </sui-select>
227
226
  <script>
228
227
  import "studiocms:ui/scripts/select"
229
228
  </script>