@studiocms/ui 0.4.15 → 0.4.17

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.
@@ -154,31 +154,33 @@ const defaultLabel = selected
154
154
  />
155
155
  <Icon name="chevron-up-down" class="sui-search-select-indicator" width={24} height={24} />
156
156
  </div>
157
- <ul class="sui-search-select-dropdown" role="listbox" id={`${name}-dropdown`}>
158
- {
159
- options.map((x, i) => {
160
- const isSelected = Array.isArray(selected)
161
- ? selected.map((y) => y && y.value).includes(x.value)
162
- : selected?.value === x.value;
163
- return (
164
- <li
165
- class="sui-search-select-option"
166
- role="option"
167
- value={x.value}
168
- class:list={[
169
- isSelected && `selected`,
170
- x.disabled && "disabled"
171
- ]}
172
- id={isSelected ? `${name}-selected` : ""}
173
- data-option-index={i}
174
- data-value={x.value}
175
- >
176
- {x.label}
177
- </li>
178
- )
179
- })
180
- }
181
- </ul>
157
+ <div class="sui-search-select-dropdown">
158
+ <ul class="sui-search-select-dropdown-list" role="listbox" id={`${name}-dropdown`}>
159
+ {
160
+ options.map((x, i) => {
161
+ const isSelected = Array.isArray(selected)
162
+ ? selected.map((y) => y && y.value).includes(x.value)
163
+ : selected?.value === x.value;
164
+ return (
165
+ <li
166
+ class="sui-search-select-option"
167
+ role="option"
168
+ value={x.value}
169
+ class:list={[
170
+ isSelected && `selected`,
171
+ x.disabled && "disabled"
172
+ ]}
173
+ id={isSelected ? `${name}-selected` : ""}
174
+ data-option-index={i}
175
+ data-value={x.value}
176
+ >
177
+ {x.label}
178
+ </li>
179
+ )
180
+ })
181
+ }
182
+ </ul>
183
+ </div>
182
184
  </div>
183
185
  <select class="sui-hidden-search-select" id={name} name={name} required={isRequired} multiple={multiple ? "" : undefined} hidden tabindex="-1">
184
186
  <option value={""}> Select </option>
@@ -187,7 +189,8 @@ const defaultLabel = selected
187
189
  const isSelected = Array.isArray(selected)
188
190
  ? selected.map((y) => y && y.value).includes(x.value)
189
191
  : selected?.value === x.value;
190
- (
192
+
193
+ return (
191
194
  <option
192
195
  value={x.value}
193
196
  selected={isSelected}
@@ -32,19 +32,25 @@
32
32
  .sui-search-select-dropdown {
33
33
  position: absolute;
34
34
  width: 100%;
35
- border: 1px solid hsl(var(--border));
36
- list-style: none;
37
- margin: 0;
38
- padding: 0;
39
- flex-direction: column;
40
- border-radius: var(--radius-md);
41
- background-color: hsl(var(--background-step-2));
42
- overflow: hidden;
43
35
  top: calc(100% + 0.25rem);
44
36
  left: 0;
45
37
  display: none;
38
+ max-height: 300px;
39
+ border: 1px solid hsl(var(--border));
40
+ border-radius: var(--radius-md);
41
+ background-color: hsl(var(--background-step-2));
46
42
  z-index: 90;
47
43
  box-shadow: 0px 4px 8px hsl(var(--shadow), 0.5);
44
+ overflow: hidden;
45
+ }
46
+ .sui-search-select-dropdown-list {
47
+ width: 100%;
48
+ list-style: none;
49
+ margin: 0;
50
+ padding: 0;
51
+ flex-direction: column;
52
+ overflow-y: auto;
53
+ scrollbar-color: hsl(var(--border)) hsl(var(--background-step-2));
48
54
  }
49
55
  .sui-search-select-dropdown.above {
50
56
  top: auto;
@@ -105,7 +111,6 @@
105
111
  position: absolute;
106
112
  bottom: .675rem;
107
113
  right: .675rem;
108
- pointer-events: none;
109
114
  }
110
115
  .sui-search-input-wrapper:has(input:focus) + .sui-search-select-dropdown {
111
116
  display: flex;
@@ -82,12 +82,9 @@ function loadSearchSelects() {
82
82
  );
83
83
  if (option) {
84
84
  option.classList.toggle("selected", forceState ?? !isCurrentlySelected);
85
- }
86
- const selectOption = container.select?.querySelector(
87
- `option[value='${value}']`
88
- );
89
- if (selectOption) {
90
- selectOption.selected = forceState ?? !isCurrentlySelected;
85
+ if (container?.select) {
86
+ container.select.value = option.getAttribute("value");
87
+ }
91
88
  }
92
89
  const selectedCountEl = container.querySelector(
93
90
  ".sui-search-select-max-span .sui-search-select-select-count"
@@ -117,12 +114,12 @@ function loadSearchSelects() {
117
114
  };
118
115
  const reconstructOptions = (filteredOptions, state2, container) => {
119
116
  container.dropdown.innerHTML = "";
120
- let i = 0;
121
117
  const selectedValues = state2.selectedOptionsMap[container.dataset.id] || [];
122
118
  if (filteredOptions.length === 0) {
123
119
  container.dropdown.innerHTML = '<li class="empty-search-results">No results found</li>';
124
120
  return;
125
121
  }
122
+ let i = 0;
126
123
  for (const option of filteredOptions) {
127
124
  const element = document.createElement("li");
128
125
  element.classList.add("sui-search-select-option");
@@ -153,6 +150,26 @@ function loadSearchSelects() {
153
150
  if (!target.closest("input")) {
154
151
  e.preventDefault();
155
152
  }
153
+ if (container.input?.value.length === 0) {
154
+ reconstructOptions(state2.optionsMap[container.dataset.id] ?? [], state2, container);
155
+ }
156
+ if (target.closest(".sui-search-select-indicator")) {
157
+ if (container.dropdown?.parentElement?.classList.contains("active")) {
158
+ container.dropdown?.parentElement?.classList.remove("active", "above");
159
+ container.input?.blur();
160
+ container.input.value = "";
161
+ } else {
162
+ container.dropdown?.parentElement?.classList.add("active");
163
+ container.input?.focus();
164
+ container.input.value = "";
165
+ }
166
+ return;
167
+ }
168
+ if (target.closest(".sui-search-select-badge-container")) {
169
+ container.dropdown?.parentElement?.classList.remove("active", "above");
170
+ container.input?.blur();
171
+ container.input.value = "";
172
+ }
156
173
  state2.isSelectingOption = true;
157
174
  setTimeout(() => {
158
175
  state2.isSelectingOption = false;
@@ -185,7 +202,7 @@ function loadSearchSelects() {
185
202
  }
186
203
  updateOptionSelection(opt.dataset.value, container, state2, true);
187
204
  updateLabel(false, state2, container);
188
- container.dropdown?.classList.remove("active", "above");
205
+ container.dropdown?.parentElement?.classList.remove("active", "above");
189
206
  container.input?.blur();
190
207
  container.input.value = "";
191
208
  }
@@ -194,7 +211,7 @@ function loadSearchSelects() {
194
211
  const focusedElement = document.activeElement;
195
212
  if (e.key === "Escape" || e.key === "Tab") {
196
213
  container.input?.blur();
197
- container.dropdown?.classList.remove("active", "above");
214
+ container.dropdown?.parentElement?.classList.remove("active", "above");
198
215
  return;
199
216
  }
200
217
  if ((e.key === "Enter" || e.key === " ") && focusedElement?.tagName.toLowerCase() === "svg") {
@@ -281,7 +298,7 @@ function loadSearchSelects() {
281
298
  }
282
299
  updateOptionSelection(value, container, state2, true);
283
300
  updateLabel(false, state2, container);
284
- container.dropdown?.classList.remove("active", "above");
301
+ container.dropdown?.parentElement?.classList.remove("active", "above");
285
302
  container.input.value = "";
286
303
  }
287
304
  }
@@ -304,20 +321,20 @@ function loadSearchSelects() {
304
321
  if (state2.isSelectingOption) return;
305
322
  container.input.value = "";
306
323
  reconstructOptions(state2.optionsMap[container.dataset.id] ?? [], state2, container);
307
- container.dropdown?.classList.remove("active", "above");
324
+ container.dropdown?.parentElement?.classList.remove("active", "above");
308
325
  };
309
326
  const handleContainerFocusIn = (state2, container) => {
310
- const allDropdowns = document.querySelectorAll(".sui-search-select-dropdown");
327
+ const allDropdowns = document.querySelectorAll(".sui-search-select-dropdown-list");
311
328
  for (const dropdown of allDropdowns) {
312
329
  if (dropdown !== container.dropdown) {
313
- dropdown.classList.remove("active", "above");
330
+ dropdown.parentElement?.classList.remove("active", "above");
314
331
  }
315
332
  }
316
333
  const { isAbove } = getDropdownPosition(
317
334
  container.input,
318
335
  state2.optionsMap[container.dataset.id]?.length ?? 0
319
336
  );
320
- container.dropdown?.classList.add("active", ...isAbove ? [] : ["above"]);
337
+ container.dropdown?.parentElement?.classList.add("active", ...isAbove ? [] : ["above"]);
321
338
  };
322
339
  const state = {
323
340
  optionsMap: {},
@@ -333,7 +350,7 @@ function loadSearchSelects() {
333
350
  const id = container.dataset.id;
334
351
  const specialContainer = Object.assign(container, {
335
352
  input: container.querySelector("input"),
336
- dropdown: container.querySelector(".sui-search-select-dropdown"),
353
+ dropdown: container.querySelector(".sui-search-select-dropdown-list"),
337
354
  select: container.querySelector("select")
338
355
  });
339
356
  const selectedOptions = Array.from(
@@ -166,30 +166,32 @@ const defaultLabel = selected
166
166
  </span>
167
167
  <Icon name="chevron-up-down" width={24} height={24} class="sui-select-chevron" />
168
168
  </button>
169
- <ul class="sui-select-dropdown" role="listbox" id={`${name}-dropdown`}>
170
- {
171
- options.map((x, i) => {
172
- const isSelected = Array.isArray(selected)
173
- ? selected.map((y) => y && y.value).includes(x.value)
174
- : selected?.value === x.value;
175
- return (
176
- <li
177
- class="sui-select-option"
178
- role="option"
179
- value={x.value}
180
- class:list={[
181
- isSelected && `selected`,
182
- x.disabled && "disabled",
183
- ]}
184
- id={isSelected ? `${name}-selected` : ""}
185
- data-option-index={i}
186
- >
187
- {x.label}
188
- </li>
189
- )
190
- })
191
- }
192
- </ul>
169
+ <div class="sui-select-dropdown">
170
+ <ul class="sui-select-dropdown-list" role="listbox" id={`${name}-dropdown`}>
171
+ {
172
+ options.map((x, i) => {
173
+ const isSelected = Array.isArray(selected)
174
+ ? selected.map((y) => y && y.value).includes(x.value)
175
+ : selected?.value === x.value;
176
+ return (
177
+ <li
178
+ class="sui-select-option"
179
+ role="option"
180
+ value={x.value}
181
+ class:list={[
182
+ isSelected && `selected`,
183
+ x.disabled && "disabled",
184
+ ]}
185
+ id={isSelected ? `${name}-selected` : ""}
186
+ data-option-index={i}
187
+ >
188
+ {x.label}
189
+ </li>
190
+ )
191
+ })
192
+ }
193
+ </ul>
194
+ </div>
193
195
  </div>
194
196
  <select class="sui-hidden-select" id={name} name={name} required={isRequired} multiple={multiple ? "" : undefined} hidden tabindex="-1">
195
197
  <option value={""}> Select </option>
@@ -198,7 +200,8 @@ const defaultLabel = selected
198
200
  const isSelected = Array.isArray(selected)
199
201
  ? selected.map((y) => y && y.value).includes(x.value)
200
202
  : selected?.value === x.value;
201
- (
203
+
204
+ return (
202
205
  <option
203
206
  value={x.value}
204
207
  selected={isSelected}
@@ -70,19 +70,25 @@
70
70
  .sui-select-dropdown {
71
71
  position: absolute;
72
72
  width: 100%;
73
- border: 1px solid hsl(var(--border));
74
- list-style: none;
75
- margin: 0;
76
- padding: 0;
77
- flex-direction: column;
78
- border-radius: var(--radius-md);
79
- background-color: hsl(var(--background-step-2));
80
- overflow: hidden;
81
73
  top: calc(100% + 0.25rem);
82
74
  left: 0;
83
75
  display: none;
76
+ max-height: 300px;
77
+ border: 1px solid hsl(var(--border));
78
+ border-radius: var(--radius-md);
79
+ background-color: hsl(var(--background-step-2));
84
80
  z-index: 90;
85
81
  box-shadow: 0px 4px 8px hsl(var(--shadow), 0.5);
82
+ overflow: hidden;
83
+ }
84
+ .sui-select-dropdown-list {
85
+ width: 100%;
86
+ list-style: none;
87
+ margin: 0;
88
+ padding: 0;
89
+ flex-direction: column;
90
+ overflow-y: auto;
91
+ scrollbar-color: hsl(var(--border)) hsl(var(--background-step-2));
86
92
  }
87
93
  .sui-select-dropdown.active {
88
94
  display: flex;
@@ -188,20 +188,11 @@ function loadSelects() {
188
188
  } else {
189
189
  if (lastActive) {
190
190
  lastActive.classList.remove("selected");
191
- const lastSelectOpt = container?.select?.querySelector(
192
- `option[value='${lastActive.getAttribute("value")}']`
193
- );
194
- if (lastSelectOpt) {
195
- lastSelectOpt.selected = false;
196
- }
197
191
  }
198
192
  if (option) {
199
193
  option.classList.add("selected");
200
- const selectOpt = container?.select?.querySelector(
201
- `option[value='${option.getAttribute("value")}']`
202
- );
203
- if (selectOpt) {
204
- selectOpt.selected = true;
194
+ if (container?.select) {
195
+ container.select.value = option.getAttribute("value");
205
196
  }
206
197
  updateLabel(state2, container);
207
198
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@studiocms/ui",
3
- "version": "0.4.15",
3
+ "version": "0.4.17",
4
4
  "description": "The UI library for StudioCMS. Includes the layouts & components we use to build StudioCMS.",
5
5
  "repository": {
6
6
  "type": "git",