@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
|
@@ -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
|
-
|
|
7
|
-
|
|
8
|
-
|
|
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
|
|
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
|
-
|
|
2
|
-
|
|
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
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
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
|
-
|
|
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
|
-
|
|
31
|
-
|
|
32
|
-
const rect =
|
|
33
|
-
const
|
|
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 +
|
|
36
|
-
bottom: rect.bottom +
|
|
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
|
-
|
|
50
|
-
if (!
|
|
51
|
-
|
|
52
|
-
|
|
87
|
+
closeDropdown = () => {
|
|
88
|
+
if (!this?.button || !this?.dropdown) return;
|
|
89
|
+
this.dropdown.classList.remove("active", "above");
|
|
90
|
+
this.button.ariaExpanded = "false";
|
|
53
91
|
};
|
|
54
|
-
|
|
55
|
-
if (!
|
|
56
|
-
const { isAbove } = getDropdownPosition(
|
|
57
|
-
|
|
58
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
92
|
-
const buttonContainer =
|
|
93
|
-
const buttonValueSpan =
|
|
94
|
-
|
|
95
|
-
);
|
|
96
|
-
|
|
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 =
|
|
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
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
handleBadgeOverflow(state2, container);
|
|
157
|
+
updateLabel = () => {
|
|
158
|
+
if (this.state.isMultiple) {
|
|
159
|
+
this.handleBadgeOverflow();
|
|
128
160
|
} else {
|
|
129
|
-
const selected =
|
|
130
|
-
const selectedButtonSpan =
|
|
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
|
-
|
|
139
|
-
const selectOpt =
|
|
170
|
+
deselectMultiOption = (id) => {
|
|
171
|
+
const selectOpt = this.dropdown?.querySelector(
|
|
140
172
|
`.sui-select-option[value='${id}']`
|
|
141
173
|
);
|
|
142
|
-
const max = Number.parseInt(
|
|
143
|
-
const selectedCount =
|
|
144
|
-
const selectedCountEl =
|
|
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 =
|
|
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(
|
|
191
|
+
this.updateLabel();
|
|
160
192
|
}
|
|
161
193
|
};
|
|
162
|
-
|
|
163
|
-
const optionElements =
|
|
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) ===
|
|
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
|
-
|
|
175
|
-
const allOptions =
|
|
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
|
-
|
|
214
|
+
handleOptionSelect = (target) => {
|
|
183
215
|
const option = target.closest(".sui-select-option");
|
|
184
|
-
const lastActive =
|
|
185
|
-
const isMultiple =
|
|
216
|
+
const lastActive = this.dropdown?.querySelector(".sui-select-option.selected");
|
|
217
|
+
const isMultiple = this.state.isMultiple;
|
|
186
218
|
if (isMultiple) {
|
|
187
|
-
deselectMultiOption(
|
|
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 (
|
|
195
|
-
|
|
226
|
+
if (this.select) {
|
|
227
|
+
this.select.value = option.getAttribute("value");
|
|
196
228
|
}
|
|
197
|
-
updateLabel(
|
|
229
|
+
this.updateLabel();
|
|
198
230
|
}
|
|
199
|
-
closeDropdown(
|
|
231
|
+
this.closeDropdown();
|
|
200
232
|
}
|
|
201
233
|
};
|
|
202
|
-
|
|
234
|
+
handleContainerClick = (e) => {
|
|
203
235
|
const target = e.target;
|
|
204
236
|
if (target.closest(".sui-select-badge svg")) {
|
|
205
|
-
deselectMultiOption(
|
|
206
|
-
|
|
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(
|
|
240
|
+
this.handleBadgeOverflow();
|
|
211
241
|
}
|
|
212
242
|
if (target.closest(".sui-select-button")) {
|
|
213
|
-
const
|
|
214
|
-
if (
|
|
215
|
-
closeDropdown(
|
|
243
|
+
const container = target.closest(".sui-select-label");
|
|
244
|
+
if (container.dropdown?.classList.contains("active")) {
|
|
245
|
+
this.closeDropdown();
|
|
216
246
|
} else {
|
|
217
|
-
openDropdown(
|
|
247
|
+
this.openDropdown();
|
|
218
248
|
}
|
|
219
249
|
}
|
|
220
250
|
if (target.closest(".sui-select-dropdown.active")) {
|
|
221
|
-
handleOptionSelect(target
|
|
251
|
+
this.handleOptionSelect(target);
|
|
222
252
|
}
|
|
223
253
|
};
|
|
224
|
-
|
|
225
|
-
const 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(
|
|
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 &&
|
|
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(
|
|
241
|
-
handleBadgeOverflow(
|
|
270
|
+
this.deselectMultiOption(badgeValue);
|
|
271
|
+
this.handleBadgeOverflow();
|
|
242
272
|
setTimeout(() => {
|
|
243
273
|
if (nextBadgeValue) {
|
|
244
|
-
const badgeToFocus =
|
|
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
|
-
|
|
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(
|
|
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 =
|
|
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(
|
|
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
|
-
|
|
282
|
-
|
|
311
|
+
this.state.focusIndex = Array.from(
|
|
312
|
+
this.dropdown?.querySelectorAll(".sui-select-option") || []
|
|
283
313
|
).indexOf(interactiveOptions[currentInteractiveIndex - 1]);
|
|
284
|
-
recomputeOptions(
|
|
314
|
+
this.recomputeOptions();
|
|
285
315
|
}
|
|
286
316
|
if (e.key === "ArrowDown" && currentInteractiveIndex < interactiveOptions.length - 1) {
|
|
287
|
-
|
|
288
|
-
|
|
317
|
+
this.state.focusIndex = Array.from(
|
|
318
|
+
this.dropdown?.querySelectorAll(".sui-select-option") || []
|
|
289
319
|
).indexOf(interactiveOptions[currentInteractiveIndex + 1]);
|
|
290
|
-
recomputeOptions(
|
|
320
|
+
this.recomputeOptions();
|
|
291
321
|
}
|
|
292
322
|
if (e.key === "PageUp") {
|
|
293
|
-
|
|
294
|
-
|
|
323
|
+
this.state.focusIndex = Array.from(
|
|
324
|
+
this.dropdown?.querySelectorAll(".sui-select-option") || []
|
|
295
325
|
).indexOf(interactiveOptions[0]);
|
|
296
|
-
recomputeOptions(
|
|
326
|
+
this.recomputeOptions();
|
|
297
327
|
}
|
|
298
328
|
if (e.key === "PageDown") {
|
|
299
|
-
|
|
300
|
-
|
|
329
|
+
this.state.focusIndex = Array.from(
|
|
330
|
+
this.dropdown?.querySelectorAll(".sui-select-option") || []
|
|
301
331
|
).indexOf(interactiveOptions[interactiveOptions.length - 1]);
|
|
302
|
-
recomputeOptions(
|
|
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
|
-
|
|
336
|
+
customElements.define("sui-select", SUISelectElement);
|
|
337
|
+
export {
|
|
338
|
+
SUISelectElement
|
|
339
|
+
};
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
/// <reference path="
|
|
2
|
-
/// <reference path="
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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":
|
|
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": `
|
package/dist/utils/headers.d.ts
CHANGED
|
@@ -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 |
|
|
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";
|