@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.
@@ -1,18 +1,59 @@
1
- type SelectOption = {
1
+ export type SelectOption = {
2
2
  value: string;
3
3
  label: string;
4
4
  disabled?: boolean;
5
5
  };
6
- type SelectContainer = HTMLDivElement & {
7
- button: HTMLButtonElement | null;
8
- dropdown: HTMLUListElement | null;
9
- select: HTMLSelectElement | null;
10
- };
11
- type SelectState = {
12
- optionsMap: Record<string, SelectOption[]>;
13
- isMultipleMap: Record<string, boolean>;
6
+ interface SelectState {
7
+ options: SelectOption[];
8
+ isMultiple: boolean;
14
9
  focusIndex: number;
15
10
  placeholder: string;
16
- };
11
+ }
17
12
  type ResizeCallback = (width: number, height: number, element: Element) => void;
18
- declare function loadSelects(): void;
13
+ export declare class SUISelectElement extends HTMLElement {
14
+ readonly CONSTANTS: {
15
+ OPTION_HEIGHT: number;
16
+ BORDER_SIZE: number;
17
+ MARGIN: number;
18
+ BADGE_PADDING: number;
19
+ };
20
+ observerMap: WeakMap<Element, {
21
+ observer: ResizeObserver;
22
+ callback: ResizeCallback;
23
+ }>;
24
+ state: SelectState;
25
+ button: HTMLButtonElement | undefined;
26
+ dropdown: HTMLDivElement | undefined;
27
+ select: HTMLSelectElement | undefined;
28
+ constructor();
29
+ connectedCallback(): void;
30
+ private observeResize;
31
+ private unobserveResize;
32
+ private isVisible;
33
+ getDropdownPosition: (element: HTMLElement) => {
34
+ isAbove: boolean;
35
+ customRect: {
36
+ top: number;
37
+ bottom: number;
38
+ left: number;
39
+ right: number;
40
+ width: number;
41
+ x: number;
42
+ y: number;
43
+ height: number;
44
+ };
45
+ };
46
+ private closeDropdown;
47
+ private openDropdown;
48
+ createSelectBadge: (value: string, label: string) => HTMLSpanElement;
49
+ private measureBadgesWidth;
50
+ private handleBadgeOverflow;
51
+ updateLabel: () => void;
52
+ private deselectMultiOption;
53
+ recomputeOptions: () => void;
54
+ getInteractiveOptions: () => HTMLLIElement[];
55
+ private handleOptionSelect;
56
+ private handleContainerClick;
57
+ handleSelectKeyDown: (e: KeyboardEvent) => void;
58
+ }
59
+ export {};
@@ -1,14 +1,50 @@
1
- function loadSelects() {
2
- const CONSTANTS = {
1
+ class SUISelectElement extends HTMLElement {
2
+ CONSTANTS = {
3
3
  OPTION_HEIGHT: 36,
4
4
  BORDER_SIZE: 2,
5
5
  MARGIN: 4,
6
6
  BADGE_PADDING: 80
7
7
  };
8
- const observerMap = /* @__PURE__ */ new WeakMap();
9
- function observeResize(element, callback) {
10
- if (observerMap.has(element)) {
11
- unobserveResize(element);
8
+ observerMap = /* @__PURE__ */ new WeakMap();
9
+ state = {
10
+ options: [],
11
+ isMultiple: false,
12
+ focusIndex: -1,
13
+ placeholder: ""
14
+ };
15
+ button;
16
+ dropdown;
17
+ select;
18
+ constructor() {
19
+ super();
20
+ }
21
+ connectedCallback() {
22
+ this.button = this.querySelector(".sui-select-button");
23
+ this.dropdown = this.querySelector(".sui-select-dropdown");
24
+ this.select = this.querySelector("select");
25
+ document.addEventListener("click", ({ target }) => {
26
+ if (this.dropdown?.classList.contains("active") || !target) {
27
+ return;
28
+ }
29
+ if (!this.contains(target) && this.isVisible(this)) {
30
+ this.closeDropdown();
31
+ }
32
+ });
33
+ this.state.placeholder = this.button?.querySelector(".sui-select-value-span")?.innerText ?? "";
34
+ this.state.options = JSON.parse(this.dataset.options);
35
+ this.state.isMultiple = this.dataset.multiple === "true";
36
+ this.addEventListener("click", (e) => this.handleContainerClick(e));
37
+ this.addEventListener("keydown", (e) => this.handleSelectKeyDown(e));
38
+ if (this.state.isMultiple) {
39
+ this.observeResize(this.button, () => {
40
+ this.handleBadgeOverflow();
41
+ });
42
+ this.handleBadgeOverflow();
43
+ }
44
+ }
45
+ observeResize = (element, callback) => {
46
+ if (this.observerMap.has(element)) {
47
+ this.unobserveResize(element);
12
48
  }
13
49
  const observer = new ResizeObserver((entries) => {
14
50
  for (const entry of entries) {
@@ -17,28 +53,30 @@ function loadSelects() {
17
53
  }
18
54
  });
19
55
  observer.observe(element);
20
- observerMap.set(element, { observer, callback });
21
- return () => unobserveResize(element);
22
- }
23
- function unobserveResize(element) {
24
- const data = observerMap.get(element);
56
+ this.observerMap.set(element, { observer, callback });
57
+ return () => this.unobserveResize(element);
58
+ };
59
+ unobserveResize = (element) => {
60
+ const data = this.observerMap.get(element);
25
61
  if (data) {
26
62
  data.observer.disconnect();
27
- observerMap.delete(element);
63
+ this.observerMap.delete(element);
28
64
  }
29
- }
30
- const isVisible = (elem) => elem.offsetWidth > 0 || elem.offsetHeight > 0 || elem.getClientRects().length > 0;
31
- const getDropdownPosition = (button, optionsCount) => {
32
- const rect = button.getBoundingClientRect();
33
- const dropdownHeight = optionsCount * CONSTANTS.OPTION_HEIGHT + CONSTANTS.BORDER_SIZE + CONSTANTS.MARGIN;
65
+ };
66
+ isVisible = (elem) => elem.offsetWidth > 0 || elem.offsetHeight > 0 || elem.getClientRects().length > 0;
67
+ getDropdownPosition = (element) => {
68
+ const rect = element.getBoundingClientRect();
69
+ const optionsCount = this.state.options?.length ?? 0;
70
+ const { OPTION_HEIGHT, BORDER_SIZE, MARGIN } = this.CONSTANTS;
71
+ const dropdownHeight = optionsCount * OPTION_HEIGHT + BORDER_SIZE + MARGIN;
34
72
  const customRect = {
35
- top: rect.bottom + CONSTANTS.MARGIN,
36
- bottom: rect.bottom + CONSTANTS.MARGIN + dropdownHeight,
73
+ top: rect.bottom + MARGIN,
74
+ bottom: rect.bottom + MARGIN + dropdownHeight,
37
75
  left: rect.left,
38
76
  right: rect.right,
39
77
  width: rect.width,
40
78
  x: rect.x,
41
- y: rect.y + rect.height + CONSTANTS.MARGIN,
79
+ y: rect.y + rect.height + this.CONSTANTS.MARGIN,
42
80
  height: dropdownHeight
43
81
  };
44
82
  return {
@@ -46,35 +84,32 @@ function loadSelects() {
46
84
  customRect
47
85
  };
48
86
  };
49
- const closeDropdown = (container) => {
50
- if (!container?.button || !container?.dropdown) return;
51
- container.dropdown.classList.remove("active", "above");
52
- container.button.ariaExpanded = "false";
87
+ closeDropdown = () => {
88
+ if (!this?.button || !this?.dropdown) return;
89
+ this.dropdown.classList.remove("active", "above");
90
+ this.button.ariaExpanded = "false";
53
91
  };
54
- const openDropdown = (state2, container) => {
55
- if (!container?.button || !container?.dropdown) return;
56
- const { isAbove } = getDropdownPosition(
57
- container.button,
58
- state2.optionsMap[container.dataset.id]?.length ?? 0
59
- );
60
- container.button.ariaExpanded = "true";
61
- container.dropdown.classList.add("active", ...isAbove ? [] : ["above"]);
92
+ openDropdown = () => {
93
+ if (!this.button || !this.dropdown) return;
94
+ const { isAbove } = this.getDropdownPosition(this.button);
95
+ this.button.ariaExpanded = "true";
96
+ this.dropdown.classList.add("active", ...isAbove ? [] : ["above"]);
62
97
  };
63
- const createSelectBadge = (value, label) => {
98
+ createSelectBadge = (value, label) => {
64
99
  const badge = document.createElement("span");
65
100
  badge.classList.add("sui-badge", "primary", "sm", "outlined", "full", "sui-select-badge");
66
101
  badge.setAttribute("data-value", value);
67
102
  badge.innerHTML = `${label} <svg style='min-width: 8px' xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 24 24'><path fill='none' stroke='currentColor' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 18L18 6M6 6l12 12'></path></svg>`;
68
103
  return badge;
69
104
  };
70
- const measureBadgesWidth = (activeSelects) => {
105
+ measureBadgesWidth = (activeSelects) => {
71
106
  const tempContainer = document.createElement("div");
72
107
  tempContainer.classList.add("sui-select-badge-container");
73
108
  tempContainer.style.position = "absolute";
74
109
  tempContainer.style.visibility = "hidden";
75
110
  document.body.appendChild(tempContainer);
76
111
  const badges = Array.from(activeSelects).map(
77
- (select) => createSelectBadge(
112
+ (select) => this.createSelectBadge(
78
113
  select.getAttribute("value") ?? "",
79
114
  select.innerText.trim()
80
115
  )
@@ -88,26 +123,24 @@ function loadSelects() {
88
123
  }, 0);
89
124
  return { totalWidth, badges, tempContainer };
90
125
  };
91
- const handleBadgeOverflow = (state2, container) => {
92
- const buttonContainer = container?.button?.parentElement?.parentElement;
93
- const buttonValueSpan = container?.button?.querySelector(
94
- ".sui-select-value-span"
95
- );
96
- const activeSelects = container?.dropdown?.querySelectorAll(".sui-select-option.selected");
97
- const overflowContainer = container?.querySelector(".sui-select-badge-container-below");
98
- if (!buttonContainer || !overflowContainer || !container?.button || !activeSelects) return;
126
+ handleBadgeOverflow = () => {
127
+ const buttonContainer = this.button?.parentElement?.parentElement;
128
+ const buttonValueSpan = this.button?.querySelector(".sui-select-value-span");
129
+ const activeSelects = this.dropdown?.querySelectorAll(".sui-select-option.selected");
130
+ const overflowContainer = this.querySelector(".sui-select-badge-container-below");
131
+ if (!buttonContainer || !overflowContainer || !this.button || !activeSelects) return;
99
132
  const parentContainer = buttonContainer.parentElement;
100
133
  if (!parentContainer) return;
101
134
  overflowContainer.innerHTML = "";
102
135
  buttonValueSpan.innerHTML = "";
103
136
  if (activeSelects.length === 0) {
104
- buttonValueSpan.innerText = state2.placeholder;
137
+ buttonValueSpan.innerText = this.state.placeholder;
105
138
  return;
106
139
  }
107
- const { totalWidth, badges, tempContainer } = measureBadgesWidth(activeSelects);
140
+ const { totalWidth, badges, tempContainer } = this.measureBadgesWidth(activeSelects);
108
141
  const parentStyles = window.getComputedStyle(parentContainer);
109
142
  const availableWidth = parentContainer.clientWidth - (Number.parseFloat(parentStyles.paddingLeft) || 0) - (Number.parseFloat(parentStyles.paddingRight) || 0);
110
- const effectiveAvailableWidth = availableWidth - CONSTANTS.BADGE_PADDING;
143
+ const effectiveAvailableWidth = availableWidth - this.CONSTANTS.BADGE_PADDING;
111
144
  document.body.removeChild(tempContainer);
112
145
  const finalBadgeContainer = document.createElement("div");
113
146
  finalBadgeContainer.classList.add("sui-select-badge-container");
@@ -121,13 +154,12 @@ function loadSelects() {
121
154
  buttonValueSpan.appendChild(finalBadgeContainer);
122
155
  }
123
156
  };
124
- const updateLabel = (state2, container) => {
125
- const isMultiple = state2.isMultipleMap[container?.dataset.id];
126
- if (isMultiple) {
127
- handleBadgeOverflow(state2, container);
157
+ updateLabel = () => {
158
+ if (this.state.isMultiple) {
159
+ this.handleBadgeOverflow();
128
160
  } else {
129
- const selected = container?.querySelector(".sui-select-option.selected");
130
- const selectedButtonSpan = container?.button?.querySelector(
161
+ const selected = this.querySelector(".sui-select-option.selected");
162
+ const selectedButtonSpan = this.button?.querySelector(
131
163
  ".sui-select-value-span"
132
164
  );
133
165
  if (selected && selectedButtonSpan) {
@@ -135,19 +167,19 @@ function loadSelects() {
135
167
  }
136
168
  }
137
169
  };
138
- const deselectMultiOption = (state2, id, container) => {
139
- const selectOpt = container?.dropdown?.querySelector(
170
+ deselectMultiOption = (id) => {
171
+ const selectOpt = this.dropdown?.querySelector(
140
172
  `.sui-select-option[value='${id}']`
141
173
  );
142
- const max = Number.parseInt(container?.dataset.multipleMax, 10);
143
- const selectedCount = container?.querySelectorAll(".sui-select-option.selected").length ?? 0;
144
- const selectedCountEl = container?.querySelector(
174
+ const max = Number.parseInt(this.dataset.multipleMax, 10);
175
+ const selectedCount = this.querySelectorAll(".sui-select-option.selected").length ?? 0;
176
+ const selectedCountEl = this.querySelector(
145
177
  ".sui-select-max-span .sui-select-select-count"
146
178
  );
147
179
  const isSelected = selectOpt?.classList.contains("selected");
148
180
  if (selectOpt && (isSelected || Number.isNaN(max) || selectedCount < max)) {
149
181
  selectOpt.classList.toggle("selected");
150
- const selectOptEl = container?.select?.querySelector(
182
+ const selectOptEl = this.select?.querySelector(
151
183
  `option[value='${selectOpt.getAttribute("value")}']`
152
184
  );
153
185
  if (selectOptEl) {
@@ -156,99 +188,97 @@ function loadSelects() {
156
188
  if (selectedCountEl) {
157
189
  selectedCountEl.innerText = String(selectedCount + (isSelected ? -1 : 1));
158
190
  }
159
- updateLabel(state2, container);
191
+ this.updateLabel();
160
192
  }
161
193
  };
162
- const recomputeOptions = (state2, container) => {
163
- const optionElements = container?.dropdown?.querySelectorAll(
194
+ recomputeOptions = () => {
195
+ const optionElements = this.dropdown?.querySelectorAll(
164
196
  ".sui-select-option"
165
197
  );
166
198
  for (const entry of optionElements) {
167
- if (Number.parseInt(entry.dataset.optionIndex, 10) === state2.focusIndex) {
199
+ if (Number.parseInt(entry.dataset.optionIndex, 10) === this.state.focusIndex) {
168
200
  entry.classList.add("focused");
169
201
  } else {
170
202
  entry.classList.remove("focused");
171
203
  }
172
204
  }
173
205
  };
174
- const getInteractiveOptions = (container) => {
175
- const allOptions = container?.dropdown?.querySelectorAll(
206
+ getInteractiveOptions = () => {
207
+ const allOptions = this.dropdown?.querySelectorAll(
176
208
  ".sui-select-option"
177
209
  );
178
210
  return Array.from(allOptions).filter(
179
211
  (option) => !option.classList.contains("hidden") && !option.classList.contains("disabled") && !option.hasAttribute("disabled")
180
212
  );
181
213
  };
182
- const handleOptionSelect = (target, state2, container) => {
214
+ handleOptionSelect = (target) => {
183
215
  const option = target.closest(".sui-select-option");
184
- const lastActive = container?.dropdown?.querySelector(".sui-select-option.selected");
185
- const isMultiple = state2.isMultipleMap[container.dataset.id];
216
+ const lastActive = this.dropdown?.querySelector(".sui-select-option.selected");
217
+ const isMultiple = this.state.isMultiple;
186
218
  if (isMultiple) {
187
- deselectMultiOption(state2, option?.getAttribute("value"), container);
219
+ this.deselectMultiOption(option?.getAttribute("value"));
188
220
  } else {
189
221
  if (lastActive) {
190
222
  lastActive.classList.remove("selected");
191
223
  }
192
224
  if (option) {
193
225
  option.classList.add("selected");
194
- if (container?.select) {
195
- container.select.value = option.getAttribute("value");
226
+ if (this.select) {
227
+ this.select.value = option.getAttribute("value");
196
228
  }
197
- updateLabel(state2, container);
229
+ this.updateLabel();
198
230
  }
199
- closeDropdown(container);
231
+ this.closeDropdown();
200
232
  }
201
233
  };
202
- const handleContainerClick = (e, state2, container) => {
234
+ handleContainerClick = (e) => {
203
235
  const target = e.target;
204
236
  if (target.closest(".sui-select-badge svg")) {
205
- deselectMultiOption(
206
- state2,
207
- target.closest(".sui-select-badge")?.getAttribute("data-value"),
208
- container
237
+ this.deselectMultiOption(
238
+ target.closest(".sui-select-badge")?.getAttribute("data-value")
209
239
  );
210
- handleBadgeOverflow(state2, container);
240
+ this.handleBadgeOverflow();
211
241
  }
212
242
  if (target.closest(".sui-select-button")) {
213
- const container2 = target.closest(".sui-select-label");
214
- if (container2.dropdown?.classList.contains("active")) {
215
- closeDropdown(container2);
243
+ const container = target.closest(".sui-select-label");
244
+ if (container.dropdown?.classList.contains("active")) {
245
+ this.closeDropdown();
216
246
  } else {
217
- openDropdown(state2, container2);
247
+ this.openDropdown();
218
248
  }
219
249
  }
220
250
  if (target.closest(".sui-select-dropdown.active")) {
221
- handleOptionSelect(target, state2, container);
251
+ this.handleOptionSelect(target);
222
252
  }
223
253
  };
224
- const handleSelectKeyDown = (e, state2, container) => {
225
- const active = !!container.dropdown?.classList.contains("active");
254
+ handleSelectKeyDown = (e) => {
255
+ const active = !!this.dropdown?.classList.contains("active");
226
256
  const focusedElement = document.activeElement;
227
257
  if (e.key === "Tab" || e.key === "Escape") {
228
- closeDropdown(container);
258
+ this.closeDropdown();
229
259
  return;
230
260
  }
231
261
  if ((e.key === "Enter" || e.key === " ") && focusedElement?.tagName.toLowerCase() === "svg") {
232
262
  const badgeElement = focusedElement?.closest(".sui-select-badge");
233
- if (badgeElement && state2.isMultipleMap[container?.dataset.id]) {
263
+ if (badgeElement && this.state.isMultiple) {
234
264
  const badgeValue = badgeElement.getAttribute("data-value");
235
265
  let nextBadge = badgeElement.previousElementSibling;
236
266
  if (!nextBadge) {
237
267
  nextBadge = badgeElement.nextElementSibling;
238
268
  }
239
269
  const nextBadgeValue = nextBadge?.getAttribute("data-value");
240
- deselectMultiOption(state2, badgeValue, container);
241
- handleBadgeOverflow(state2, container);
270
+ this.deselectMultiOption(badgeValue);
271
+ this.handleBadgeOverflow();
242
272
  setTimeout(() => {
243
273
  if (nextBadgeValue) {
244
- const badgeToFocus = container?.querySelector(
274
+ const badgeToFocus = this.querySelector(
245
275
  `.sui-select-badge[data-value="${nextBadgeValue}"] svg`
246
276
  );
247
277
  if (badgeToFocus) {
248
278
  badgeToFocus.focus();
249
279
  }
250
280
  } else {
251
- container?.button?.focus();
281
+ this.button?.focus();
252
282
  }
253
283
  }, 0);
254
284
  e.preventDefault();
@@ -257,13 +287,13 @@ function loadSelects() {
257
287
  }
258
288
  }
259
289
  if ((e.key === " " || e.key === "Enter") && !active) {
260
- openDropdown(state2, container);
290
+ this.openDropdown();
261
291
  e.preventDefault();
262
292
  e.stopImmediatePropagation();
263
293
  return;
264
294
  }
265
295
  if (e.key === "Enter" && active) {
266
- const currentlyFocused = container?.querySelector(".sui-select-option.focused");
296
+ const currentlyFocused = this.querySelector(".sui-select-option.focused");
267
297
  if (currentlyFocused) {
268
298
  currentlyFocused.click();
269
299
  e.preventDefault();
@@ -273,77 +303,37 @@ function loadSelects() {
273
303
  }
274
304
  e.preventDefault();
275
305
  e.stopImmediatePropagation();
276
- const interactiveOptions = getInteractiveOptions(container);
306
+ const interactiveOptions = this.getInteractiveOptions();
277
307
  const currentInteractiveIndex = interactiveOptions.findIndex(
278
308
  (option) => option.classList.contains("focused")
279
309
  );
280
310
  if (e.key === "ArrowUp" && currentInteractiveIndex > 0) {
281
- state2.focusIndex = Array.from(
282
- container?.dropdown?.querySelectorAll(".sui-select-option") || []
311
+ this.state.focusIndex = Array.from(
312
+ this.dropdown?.querySelectorAll(".sui-select-option") || []
283
313
  ).indexOf(interactiveOptions[currentInteractiveIndex - 1]);
284
- recomputeOptions(state2, container);
314
+ this.recomputeOptions();
285
315
  }
286
316
  if (e.key === "ArrowDown" && currentInteractiveIndex < interactiveOptions.length - 1) {
287
- state2.focusIndex = Array.from(
288
- container?.dropdown?.querySelectorAll(".sui-select-option") || []
317
+ this.state.focusIndex = Array.from(
318
+ this.dropdown?.querySelectorAll(".sui-select-option") || []
289
319
  ).indexOf(interactiveOptions[currentInteractiveIndex + 1]);
290
- recomputeOptions(state2, container);
320
+ this.recomputeOptions();
291
321
  }
292
322
  if (e.key === "PageUp") {
293
- state2.focusIndex = Array.from(
294
- container?.dropdown?.querySelectorAll(".sui-select-option") || []
323
+ this.state.focusIndex = Array.from(
324
+ this.dropdown?.querySelectorAll(".sui-select-option") || []
295
325
  ).indexOf(interactiveOptions[0]);
296
- recomputeOptions(state2, container);
326
+ this.recomputeOptions();
297
327
  }
298
328
  if (e.key === "PageDown") {
299
- state2.focusIndex = Array.from(
300
- container?.dropdown?.querySelectorAll(".sui-select-option") || []
329
+ this.state.focusIndex = Array.from(
330
+ this.dropdown?.querySelectorAll(".sui-select-option") || []
301
331
  ).indexOf(interactiveOptions[interactiveOptions.length - 1]);
302
- recomputeOptions(state2, container);
332
+ this.recomputeOptions();
303
333
  }
304
334
  };
305
- const state = {
306
- optionsMap: {},
307
- isMultipleMap: {},
308
- focusIndex: -1,
309
- placeholder: ""
310
- };
311
- const selects = document.querySelectorAll(".sui-select-label");
312
- document.addEventListener("click", ({ target }) => {
313
- for (const container of selects) {
314
- if (!container.dropdown?.classList.contains("active") || !target)
315
- continue;
316
- if (!container.contains(target) && isVisible(container)) {
317
- closeDropdown(container);
318
- }
319
- }
320
- });
321
- for (const container of selects) {
322
- if (container.dataset.initialized === "true") continue;
323
- const id = container.dataset.id;
324
- const specialContainer = Object.assign(container, {
325
- button: container.querySelector("button"),
326
- dropdown: container.querySelector(".sui-select-dropdown"),
327
- select: container.querySelector("select")
328
- });
329
- state.placeholder = specialContainer.button?.querySelector(".sui-select-value-span")?.innerText ?? "";
330
- state.optionsMap[id] = JSON.parse(container.dataset.options);
331
- state.isMultipleMap[id] = container.dataset.multiple === "true";
332
- specialContainer.addEventListener(
333
- "click",
334
- (e) => handleContainerClick(e, state, specialContainer)
335
- );
336
- specialContainer.addEventListener(
337
- "keydown",
338
- (e) => handleSelectKeyDown(e, state, specialContainer)
339
- );
340
- if (state.isMultipleMap[id]) {
341
- observeResize(specialContainer.button, () => {
342
- handleBadgeOverflow(state, specialContainer);
343
- });
344
- handleBadgeOverflow(state, specialContainer);
345
- }
346
- container.dataset.initialized = "true";
347
- }
348
335
  }
349
- document.addEventListener("astro:page-load", loadSelects);
336
+ customElements.define("sui-select", SUISelectElement);
337
+ export {
338
+ SUISelectElement
339
+ };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- /// <reference path="../src/events.d.ts" preserve="true" />
2
- /// <reference path="../src/virtuals.d.ts" preserve="true" />
1
+ /// <reference path="events.d.ts" preserve="true" />
2
+ /// <reference path="virtuals.d.ts" preserve="true" />
3
3
  import type { IconifyJSON } from '@iconify/types';
4
4
  import type { AstroIntegration } from 'astro';
5
5
  type Options = {
package/dist/index.js CHANGED
@@ -62,23 +62,17 @@ function integration(options = {}) {
62
62
  "studiocms:ui/components/checkbox": `export { default as Checkbox } from '${resolve("./components/Checkbox/Checkbox.astro")}';`,
63
63
  "studiocms:ui/components/toggle": `export { default as Toggle } from '${resolve("./components/Toggle/Toggle.astro")}';`,
64
64
  "studiocms:ui/components/radiogroup": `export { default as RadioGroup } from '${resolve("./components/RadioGroup/RadioGroup.astro")}';`,
65
- "studiocms:ui/components/toaster": `
66
- export { default as Toaster } from '${resolve("./components/Toast/Toaster.astro")}';
67
- export { toast } from '${resolve("./components/Toast/toast.js")}';
68
- `,
65
+ "studiocms:ui/components/toaster": `export { default as Toaster } from '${resolve("./components/Toast/Toaster.astro")}';`,
66
+ "studiocms:ui/components/toaster/client": `export { toast } from '${resolve("./components/Toast/toast.js")}';`,
69
67
  "studiocms:ui/components/card": `export { default as Card } from '${resolve("./components/Card/Card.astro")}';`,
70
- "studiocms:ui/components/modal": `
71
- export { default as Modal } from '${resolve("./components/Modal/Modal.astro")}';
72
- export { ModalHelper } from '${resolve("./components/Modal/modal.js")}';
73
- `,
68
+ "studiocms:ui/components/modal": `export { default as Modal } from '${resolve("./components/Modal/Modal.astro")}';`,
69
+ "studiocms:ui/components/modal/client": `export { ModalHelper } from '${resolve("./components/Modal/modal.js")}';`,
74
70
  "studiocms:ui/components/select": `
75
71
  export { default as Select } from '${resolve("./components/Select/Select.astro")}';
76
72
  export { default as SearchSelect } from '${resolve("./components/SearchSelect/SearchSelect.astro")}';
77
73
  `,
78
- "studiocms:ui/components/dropdown": `
79
- export { default as Dropdown } from '${resolve("./components/Dropdown/Dropdown.astro")}';
80
- export { DropdownHelper } from '${resolve("./components/Dropdown/dropdown.js")}';
81
- `,
74
+ "studiocms:ui/components/dropdown": `export { default as Dropdown } from '${resolve("./components/Dropdown/Dropdown.astro")}';`,
75
+ "studiocms:ui/components/dropdown/client": `export { DropdownHelper } from '${resolve("./components/Dropdown/dropdown.js")}';`,
82
76
  "studiocms:ui/components/user": `export { default as User } from '${resolve("./components/User/User.astro")}';`,
83
77
  "studiocms:ui/components/tabs": `
84
78
  export { default as Tabs } from '${resolve("./components/Tabs/Tabs.astro")}';
@@ -89,15 +83,13 @@ function integration(options = {}) {
89
83
  export { default as AccordionItem } from '${resolve("./components/Accordion/Item.astro")}';
90
84
  `,
91
85
  "studiocms:ui/components/footer": `export { default as Footer } from '${resolve("./components/Footer/Footer.astro")}';`,
92
- "studiocms:ui/components/progress": `
93
- export { default as Progress } from '${resolve("./components/Progress/Progress.astro")}';
94
- export { ProgressHelper } from '${resolve("./components/Progress/helper.js")}';
95
- `,
86
+ "studiocms:ui/components/progress": `export { default as Progress } from '${resolve("./components/Progress/Progress.astro")}';`,
87
+ "studiocms:ui/components/progress/client": `export { ProgressHelper } from '${resolve("./components/Progress/helper.js")}';`,
96
88
  "studiocms:ui/components/sidebar": `
97
89
  export { default as Sidebar } from '${resolve("./components/Sidebar/Single.astro")}';
98
90
  export { default as DoubleSidebar } from '${resolve("./components/Sidebar/Double.astro")}';
99
- export { SingleSidebarHelper, DoubleSidebarHelper } from '${resolve("./components/Sidebar/helpers.js")}';
100
91
  `,
92
+ "studiocms:ui/components/sidebar/client": `export { SingleSidebarHelper, DoubleSidebarHelper } from '${resolve("./components/Sidebar/helpers.js")}';`,
101
93
  "studiocms:ui/components/breadcrumbs": `export { default as Breadcrumbs } from '${resolve("./components/Breadcrumbs/Breadcrumbs.astro")}';`,
102
94
  "studiocms:ui/components/group": `export { default as Group } from '${resolve("./components/Group/Group.astro")}';`,
103
95
  "studiocms:ui/components/badge": `export { default as Badge } from '${resolve("./components/Badge/Badge.astro")}';`,
@@ -108,9 +100,16 @@ function integration(options = {}) {
108
100
  "studiocms:ui/components/skeleton": `export { default as Skeleton } from '${resolve("./components/Skeleton/Skeleton.astro")}';`,
109
101
  "studiocms:ui/components/tooltip": `export { default as Tooltip } from '${resolve("./components/Tooltip/Tooltip.astro")}';`
110
102
  };
103
+ const ServerComponents = Object.entries(componentMap).filter(
104
+ ([key]) => !key.endsWith("/client")
105
+ );
106
+ const ClientComponents = Object.entries(componentMap).filter(
107
+ ([key]) => key.endsWith("/client")
108
+ );
111
109
  const virtualComponents = {
112
110
  ...componentMap,
113
- "studiocms:ui/components": Object.values(componentMap).join("\n")
111
+ "studiocms:ui/components": ServerComponents.map(([_, value]) => value).join("\n"),
112
+ "studiocms:ui/components/client": ClientComponents.map(([_, value]) => value).join("\n")
114
113
  };
115
114
  addVirtualImports(params, {
116
115
  name: "@studiocms/ui",
@@ -134,6 +133,9 @@ function integration(options = {}) {
134
133
  "studiocms:ui/scripts/tooltip": `import '${resolve("./components/Tooltip/tooltip.js")}';`,
135
134
  "studiocms:ui/scripts/accordion": `import '${resolve("./components/Accordion/accordion.js")}';`,
136
135
  "studiocms:ui/scripts/progress": `import '${resolve("./components/Progress/progress.js")}';`,
136
+ "studiocms:ui/components/select/script": `
137
+ export { SUISelectElement } from '${resolve("./components/Select/select.js")}';
138
+ `,
137
139
  // Components
138
140
  ...virtualComponents,
139
141
  "studiocms:ui/utils": `
@@ -80,7 +80,7 @@ export declare function sortHead(head: HeadConfig): {
80
80
  content: string;
81
81
  }[];
82
82
  /** Get the relative importance of a specific head tag. */
83
- export declare function getImportance(entry: HeadConfig[number]): 0 | 100 | 80 | 90 | 70;
83
+ export declare function getImportance(entry: HeadConfig[number]): 0 | 80 | 100 | 90 | 70;
84
84
  /** Create a fully parsed, merged, and sorted head entry array from multiple sources. */
85
85
  export declare function createHead(defaultHeaders: HeadUserConfig, ...heads: HeadConfig[]): {
86
86
  tag: "link" | "base" | "meta" | "noscript" | "script" | "style" | "template" | "title";