selectamelo 0.1.0
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/LICENSE +21 -0
- package/README.md +261 -0
- package/dist/index.cjs +636 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +64 -0
- package/dist/index.d.ts +64 -0
- package/dist/index.global.js +634 -0
- package/dist/index.global.js.map +1 -0
- package/dist/index.js +609 -0
- package/dist/index.js.map +1 -0
- package/package.json +56 -0
|
@@ -0,0 +1,634 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var ZSelectCore = (() => {
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
+
};
|
|
11
|
+
var __copyProps = (to, from, except, desc) => {
|
|
12
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
13
|
+
for (let key of __getOwnPropNames(from))
|
|
14
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
15
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
16
|
+
}
|
|
17
|
+
return to;
|
|
18
|
+
};
|
|
19
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
20
|
+
|
|
21
|
+
// src/index.ts
|
|
22
|
+
var index_exports = {};
|
|
23
|
+
__export(index_exports, {
|
|
24
|
+
ZSelectCore: () => ZSelectCore
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
// src/dom.ts
|
|
28
|
+
function uid(prefix = "zs-") {
|
|
29
|
+
return prefix + Math.random().toString(36).slice(2);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// src/core.ts
|
|
33
|
+
var instances = /* @__PURE__ */ new WeakMap();
|
|
34
|
+
function dispatch(select, name, detail) {
|
|
35
|
+
select.dispatchEvent(new CustomEvent(name, { bubbles: true, detail }));
|
|
36
|
+
}
|
|
37
|
+
function getSelected(select) {
|
|
38
|
+
if (select.multiple) return Array.from(select.selectedOptions).map((o) => o.value);
|
|
39
|
+
return select.value;
|
|
40
|
+
}
|
|
41
|
+
function setSelected(select, value) {
|
|
42
|
+
if (select.multiple) {
|
|
43
|
+
const arr = Array.isArray(value) ? value.map(String) : value == null ? [] : [String(value)];
|
|
44
|
+
Array.from(select.options).forEach((o) => o.selected = arr.includes(o.value));
|
|
45
|
+
} else {
|
|
46
|
+
select.value = value == null ? "" : String(value);
|
|
47
|
+
}
|
|
48
|
+
select.dispatchEvent(new Event("change", { bubbles: true }));
|
|
49
|
+
}
|
|
50
|
+
var ZSelectCore = {
|
|
51
|
+
create(select, options = {}) {
|
|
52
|
+
const existing = instances.get(select);
|
|
53
|
+
if (existing) return existing;
|
|
54
|
+
const opts = {
|
|
55
|
+
searchable: options.searchable ?? true,
|
|
56
|
+
placeholder: options.placeholder ?? "",
|
|
57
|
+
allowClear: options.allowClear ?? true,
|
|
58
|
+
hideSearch: options.hideSearch ?? false
|
|
59
|
+
};
|
|
60
|
+
function readBoolAttr(name) {
|
|
61
|
+
const v = select.getAttribute(name);
|
|
62
|
+
if (v == null) return null;
|
|
63
|
+
if (v === "" || v === "true" || v === "1") return true;
|
|
64
|
+
if (v === "false" || v === "0") return false;
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
const aAllowClear = readBoolAttr("data-allow-clear");
|
|
68
|
+
if (aAllowClear != null) opts.allowClear = aAllowClear;
|
|
69
|
+
const aHideSearch = readBoolAttr("data-hide-search");
|
|
70
|
+
if (aHideSearch != null) opts.hideSearch = aHideSearch;
|
|
71
|
+
const aSearchable = readBoolAttr("data-searchable");
|
|
72
|
+
if (aSearchable != null) opts.searchable = aSearchable;
|
|
73
|
+
const root = document.createElement("div");
|
|
74
|
+
root.className = "zselect";
|
|
75
|
+
root.setAttribute("data-zselect-core", "1");
|
|
76
|
+
const control = document.createElement("button");
|
|
77
|
+
control.type = "button";
|
|
78
|
+
control.className = "zselect__control";
|
|
79
|
+
control.setAttribute("aria-haspopup", "listbox");
|
|
80
|
+
control.setAttribute("aria-expanded", "false");
|
|
81
|
+
const ds = {
|
|
82
|
+
placeholder: select.getAttribute("data-zselect-placeholder"),
|
|
83
|
+
searchPlaceholder: select.getAttribute("data-zselect-search-placeholder"),
|
|
84
|
+
clearTitle: select.getAttribute("data-zselect-clear-title"),
|
|
85
|
+
clearAriaLabel: select.getAttribute("data-zselect-clear-aria-label"),
|
|
86
|
+
removeChipAriaLabel: select.getAttribute("data-zselect-remove-chip-aria-label"),
|
|
87
|
+
noResults: select.getAttribute("data-zselect-no-results")
|
|
88
|
+
};
|
|
89
|
+
const t = {
|
|
90
|
+
placeholder: options.i18n?.placeholder ?? ds.placeholder ?? options.placeholder ?? "",
|
|
91
|
+
searchPlaceholder: options.i18n?.searchPlaceholder ?? ds.searchPlaceholder ?? "Cerca...",
|
|
92
|
+
clearTitle: options.i18n?.clearTitle ?? ds.clearTitle ?? "Pulisci",
|
|
93
|
+
clearAriaLabel: options.i18n?.clearAriaLabel ?? ds.clearAriaLabel ?? "Pulisci selezione",
|
|
94
|
+
removeChipAriaLabel: options.i18n?.removeChipAriaLabel ?? ds.removeChipAriaLabel ?? "Rimuovi",
|
|
95
|
+
noResults: options.i18n?.noResults ?? ds.noResults ?? "Nessun risultato"
|
|
96
|
+
};
|
|
97
|
+
const valueWrap = document.createElement("span");
|
|
98
|
+
valueWrap.className = "zselect__valuewrap";
|
|
99
|
+
const chipsEl = document.createElement("span");
|
|
100
|
+
chipsEl.className = "zselect__chips";
|
|
101
|
+
chipsEl.hidden = !select.multiple;
|
|
102
|
+
const valueEl = document.createElement("span");
|
|
103
|
+
valueEl.className = "zselect__value";
|
|
104
|
+
valueWrap.append(chipsEl, valueEl);
|
|
105
|
+
const actions = document.createElement("span");
|
|
106
|
+
actions.className = "zselect__actions";
|
|
107
|
+
const clearEl = document.createElement("span");
|
|
108
|
+
clearEl.className = "zselect__clear";
|
|
109
|
+
clearEl.textContent = "\xD7";
|
|
110
|
+
clearEl.title = t.clearTitle;
|
|
111
|
+
clearEl.setAttribute("aria-hidden", "true");
|
|
112
|
+
clearEl.setAttribute("aria-label", t.clearAriaLabel);
|
|
113
|
+
clearEl.style.display = "none";
|
|
114
|
+
const arrow = document.createElement("span");
|
|
115
|
+
arrow.className = "zselect__arrow";
|
|
116
|
+
arrow.textContent = "\u25BE";
|
|
117
|
+
actions.append(clearEl, arrow);
|
|
118
|
+
control.append(valueWrap, actions);
|
|
119
|
+
const dropdown = document.createElement("div");
|
|
120
|
+
dropdown.className = "zselect__dropdown";
|
|
121
|
+
dropdown.hidden = true;
|
|
122
|
+
const searchEl = document.createElement("input");
|
|
123
|
+
searchEl.className = "zselect__search";
|
|
124
|
+
searchEl.type = "text";
|
|
125
|
+
searchEl.autocomplete = "off";
|
|
126
|
+
searchEl.placeholder = t.searchPlaceholder;
|
|
127
|
+
searchEl.hidden = opts.hideSearch || !opts.searchable;
|
|
128
|
+
const list = document.createElement("ul");
|
|
129
|
+
list.className = "zselect__list";
|
|
130
|
+
list.setAttribute("role", "listbox");
|
|
131
|
+
const listId = `zselect-list-${uid()}`;
|
|
132
|
+
list.id = listId;
|
|
133
|
+
control.setAttribute("aria-controls", listId);
|
|
134
|
+
dropdown.append(searchEl, list);
|
|
135
|
+
root.append(control, dropdown);
|
|
136
|
+
const prevStyle = {
|
|
137
|
+
position: select.style.position,
|
|
138
|
+
left: select.style.left,
|
|
139
|
+
width: select.style.width,
|
|
140
|
+
height: select.style.height
|
|
141
|
+
};
|
|
142
|
+
select.style.position = "absolute";
|
|
143
|
+
select.style.left = "-9999px";
|
|
144
|
+
select.style.width = "1px";
|
|
145
|
+
select.style.height = "1px";
|
|
146
|
+
select.insertAdjacentElement("afterend", root);
|
|
147
|
+
let open = false;
|
|
148
|
+
let readonly = !!options.readonly || select.getAttribute("data-readonly") === "true";
|
|
149
|
+
const aDisabled = readBoolAttr("data-disabled");
|
|
150
|
+
if (aDisabled != null) select.disabled = !!aDisabled;
|
|
151
|
+
let activeIndex = -1;
|
|
152
|
+
let filterQuery = "";
|
|
153
|
+
let hoverArmed = false;
|
|
154
|
+
function isDisabled() {
|
|
155
|
+
return select.disabled || select.hasAttribute("disabled");
|
|
156
|
+
}
|
|
157
|
+
function isReadonly() {
|
|
158
|
+
return readonly;
|
|
159
|
+
}
|
|
160
|
+
function applyStateToUI() {
|
|
161
|
+
const disabledNow = isDisabled();
|
|
162
|
+
const readonlyNow = isReadonly();
|
|
163
|
+
root.classList.toggle("is-disabled", disabledNow);
|
|
164
|
+
root.classList.toggle("is-readonly", readonlyNow);
|
|
165
|
+
control.disabled = disabledNow || readonlyNow;
|
|
166
|
+
searchEl.disabled = disabledNow || readonlyNow;
|
|
167
|
+
control.setAttribute("aria-disabled", disabledNow || readonlyNow ? "true" : "false");
|
|
168
|
+
if ((disabledNow || readonlyNow) && open) close();
|
|
169
|
+
}
|
|
170
|
+
function hasValue() {
|
|
171
|
+
if (select.multiple) return Array.from(select.selectedOptions).some((o) => (o.value ?? "") !== "");
|
|
172
|
+
return (select.value ?? "") !== "";
|
|
173
|
+
}
|
|
174
|
+
function updateClearVisibility() {
|
|
175
|
+
if (!opts.allowClear || isDisabled() || isReadonly()) {
|
|
176
|
+
clearEl.style.display = "none";
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
clearEl.style.display = hasValue() ? "" : "none";
|
|
180
|
+
}
|
|
181
|
+
function normalize(s) {
|
|
182
|
+
return s.toLowerCase();
|
|
183
|
+
}
|
|
184
|
+
function getOptionLabel(opt) {
|
|
185
|
+
return (opt.textContent ?? "").trim();
|
|
186
|
+
}
|
|
187
|
+
function allOptions() {
|
|
188
|
+
return Array.from(select.options);
|
|
189
|
+
}
|
|
190
|
+
function selectedOptions() {
|
|
191
|
+
return Array.from(select.selectedOptions);
|
|
192
|
+
}
|
|
193
|
+
function selectedNonEmpty() {
|
|
194
|
+
return selectedOptions().filter((o) => (o.value ?? "") !== "");
|
|
195
|
+
}
|
|
196
|
+
function emitChange() {
|
|
197
|
+
select.dispatchEvent(new Event("change", { bubbles: true }));
|
|
198
|
+
}
|
|
199
|
+
function getFilteredOptions() {
|
|
200
|
+
const q = normalize(filterQuery.trim());
|
|
201
|
+
const all = allOptions();
|
|
202
|
+
if (!q) return all;
|
|
203
|
+
return all.filter((o) => normalize(getOptionLabel(o)).includes(q));
|
|
204
|
+
}
|
|
205
|
+
function clearValue() {
|
|
206
|
+
if (!opts.allowClear) return;
|
|
207
|
+
if (isReadonly()) return;
|
|
208
|
+
if (select.multiple) {
|
|
209
|
+
allOptions().forEach((o) => o.selected = false);
|
|
210
|
+
select.value = "";
|
|
211
|
+
} else {
|
|
212
|
+
const hasEmpty = !!select.querySelector('option[value=""]');
|
|
213
|
+
select.value = hasEmpty ? "" : select.options[0]?.value ?? "";
|
|
214
|
+
}
|
|
215
|
+
emitChange();
|
|
216
|
+
dispatch(select, "zselect:clear");
|
|
217
|
+
}
|
|
218
|
+
function renderChips() {
|
|
219
|
+
chipsEl.innerHTML = "";
|
|
220
|
+
if (!select.multiple) return;
|
|
221
|
+
const selected = selectedNonEmpty();
|
|
222
|
+
if (!selected.length) return;
|
|
223
|
+
selected.forEach((opt) => {
|
|
224
|
+
const chip = document.createElement("span");
|
|
225
|
+
chip.className = "zselect__chip";
|
|
226
|
+
chip.dataset.value = opt.value;
|
|
227
|
+
const label = document.createElement("span");
|
|
228
|
+
label.className = "zselect__chip-label";
|
|
229
|
+
label.textContent = getOptionLabel(opt);
|
|
230
|
+
const x = document.createElement("button");
|
|
231
|
+
x.type = "button";
|
|
232
|
+
x.className = "zselect__chip-remove";
|
|
233
|
+
x.textContent = "\xD7";
|
|
234
|
+
x.title = "Rimuovi";
|
|
235
|
+
x.setAttribute("aria-label", t.removeChipAriaLabel);
|
|
236
|
+
x.addEventListener("mousedown", (e) => {
|
|
237
|
+
e.preventDefault();
|
|
238
|
+
e.stopPropagation();
|
|
239
|
+
if (isReadonly()) return;
|
|
240
|
+
opt.selected = false;
|
|
241
|
+
emitChange();
|
|
242
|
+
});
|
|
243
|
+
chip.append(label, x);
|
|
244
|
+
chipsEl.append(chip);
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
function renderLabel() {
|
|
248
|
+
const selected = selectedOptions();
|
|
249
|
+
const isPlaceholder = selected.length === 0 || selected.length === 1 && (selected[0]?.value ?? "") === "" || select.multiple && selectedNonEmpty().length === 0;
|
|
250
|
+
if (select.multiple) {
|
|
251
|
+
renderChips();
|
|
252
|
+
valueEl.textContent = isPlaceholder ? t.placeholder || "Seleziona..." : "";
|
|
253
|
+
valueEl.classList.toggle("is-placeholder", isPlaceholder);
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
if (isPlaceholder) {
|
|
257
|
+
valueEl.textContent = t.placeholder || "Seleziona...";
|
|
258
|
+
valueEl.classList.add("is-placeholder");
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
const first = selected[0];
|
|
262
|
+
valueEl.textContent = (first?.textContent ?? "").trim();
|
|
263
|
+
valueEl.classList.remove("is-placeholder");
|
|
264
|
+
}
|
|
265
|
+
function getOptionItems(includeDisabled = true) {
|
|
266
|
+
const selector = includeDisabled ? ".zselect__option" : ".zselect__option:not(.is-disabled)";
|
|
267
|
+
return Array.from(list.querySelectorAll(selector));
|
|
268
|
+
}
|
|
269
|
+
function setActiveIndex(items, idx, scroll = true) {
|
|
270
|
+
activeIndex = idx;
|
|
271
|
+
items.forEach((li, i) => li.classList.toggle("is-active", i === activeIndex));
|
|
272
|
+
if (scroll && activeIndex >= 0) items[activeIndex]?.scrollIntoView({ block: "nearest" });
|
|
273
|
+
}
|
|
274
|
+
function clearActive(items) {
|
|
275
|
+
const all = items ?? getOptionItems();
|
|
276
|
+
activeIndex = -1;
|
|
277
|
+
all.forEach((li) => li.classList.remove("is-active"));
|
|
278
|
+
}
|
|
279
|
+
function setActiveByValue(val) {
|
|
280
|
+
const items = getOptionItems();
|
|
281
|
+
const idx = items.findIndex((li) => (li.dataset.value ?? "") === val);
|
|
282
|
+
setActiveIndex(items, idx, true);
|
|
283
|
+
}
|
|
284
|
+
function renderList() {
|
|
285
|
+
const filtered = getFilteredOptions().filter((opt) => (opt.value ?? "") !== "");
|
|
286
|
+
list.innerHTML = "";
|
|
287
|
+
const selectedValues = new Set(selectedOptions().map((o) => o.value));
|
|
288
|
+
if (!filtered.length) {
|
|
289
|
+
const empty = document.createElement("li");
|
|
290
|
+
empty.className = "zselect__empty";
|
|
291
|
+
empty.textContent = t.noResults;
|
|
292
|
+
list.append(empty);
|
|
293
|
+
activeIndex = -1;
|
|
294
|
+
return;
|
|
295
|
+
}
|
|
296
|
+
filtered.forEach((opt, idx) => {
|
|
297
|
+
const li = document.createElement("li");
|
|
298
|
+
li.className = "zselect__option";
|
|
299
|
+
li.setAttribute("role", "option");
|
|
300
|
+
li.dataset.value = opt.value;
|
|
301
|
+
const labelText = getOptionLabel(opt);
|
|
302
|
+
const labelEl = document.createElement("span");
|
|
303
|
+
labelEl.className = "zselect__option-label";
|
|
304
|
+
labelEl.textContent = labelText;
|
|
305
|
+
const checkEl = document.createElement("span");
|
|
306
|
+
checkEl.className = "zselect__option-check";
|
|
307
|
+
checkEl.setAttribute("aria-hidden", "true");
|
|
308
|
+
checkEl.textContent = "\u2713";
|
|
309
|
+
const isSelected = selectedValues.has(opt.value);
|
|
310
|
+
checkEl.hidden = !isSelected;
|
|
311
|
+
li.append(labelEl, checkEl);
|
|
312
|
+
if (opt.disabled) li.classList.add("is-disabled");
|
|
313
|
+
if (isSelected) {
|
|
314
|
+
li.classList.add("is-selected");
|
|
315
|
+
li.setAttribute("aria-selected", "true");
|
|
316
|
+
} else {
|
|
317
|
+
li.setAttribute("aria-selected", "false");
|
|
318
|
+
}
|
|
319
|
+
li.addEventListener("mousedown", (e) => {
|
|
320
|
+
e.preventDefault();
|
|
321
|
+
if (opt.disabled) return;
|
|
322
|
+
if (isReadonly()) return;
|
|
323
|
+
if (select.multiple) {
|
|
324
|
+
opt.selected = !opt.selected;
|
|
325
|
+
emitChange();
|
|
326
|
+
renderList();
|
|
327
|
+
if (!searchEl.hidden) searchEl.focus();
|
|
328
|
+
} else {
|
|
329
|
+
select.value = opt.value;
|
|
330
|
+
emitChange();
|
|
331
|
+
close();
|
|
332
|
+
control.focus();
|
|
333
|
+
}
|
|
334
|
+
});
|
|
335
|
+
li.addEventListener("mouseenter", () => {
|
|
336
|
+
if (!hoverArmed) return;
|
|
337
|
+
if (opt.disabled) return;
|
|
338
|
+
const all = getOptionItems();
|
|
339
|
+
setActiveIndex(all, idx, false);
|
|
340
|
+
});
|
|
341
|
+
list.append(li);
|
|
342
|
+
});
|
|
343
|
+
clearActive();
|
|
344
|
+
}
|
|
345
|
+
function refresh() {
|
|
346
|
+
renderLabel();
|
|
347
|
+
updateClearVisibility();
|
|
348
|
+
if (open) renderList();
|
|
349
|
+
}
|
|
350
|
+
function openDropdown() {
|
|
351
|
+
if (open || isDisabled() || isReadonly()) return;
|
|
352
|
+
open = true;
|
|
353
|
+
dropdown.hidden = false;
|
|
354
|
+
control.setAttribute("aria-expanded", "true");
|
|
355
|
+
root.classList.add("is-open");
|
|
356
|
+
filterQuery = "";
|
|
357
|
+
hoverArmed = false;
|
|
358
|
+
dropdown.addEventListener(
|
|
359
|
+
"mousemove",
|
|
360
|
+
() => {
|
|
361
|
+
hoverArmed = true;
|
|
362
|
+
},
|
|
363
|
+
{ once: true }
|
|
364
|
+
);
|
|
365
|
+
if (!searchEl.hidden) {
|
|
366
|
+
searchEl.value = "";
|
|
367
|
+
renderList();
|
|
368
|
+
queueMicrotask(() => searchEl.focus());
|
|
369
|
+
} else {
|
|
370
|
+
renderList();
|
|
371
|
+
}
|
|
372
|
+
dispatch(select, "zselect:open");
|
|
373
|
+
}
|
|
374
|
+
function close() {
|
|
375
|
+
if (!open) return;
|
|
376
|
+
open = false;
|
|
377
|
+
hoverArmed = false;
|
|
378
|
+
dropdown.hidden = true;
|
|
379
|
+
control.setAttribute("aria-expanded", "false");
|
|
380
|
+
root.classList.remove("is-open");
|
|
381
|
+
dispatch(select, "zselect:close");
|
|
382
|
+
}
|
|
383
|
+
function toggle() {
|
|
384
|
+
open ? close() : openDropdown();
|
|
385
|
+
}
|
|
386
|
+
function moveActive(delta) {
|
|
387
|
+
const items = getOptionItems(false);
|
|
388
|
+
if (!items.length) return;
|
|
389
|
+
let i = activeIndex;
|
|
390
|
+
if (i < 0) i = -1;
|
|
391
|
+
i = Math.min(Math.max(i + delta, 0), items.length - 1);
|
|
392
|
+
setActiveIndex(items, i, true);
|
|
393
|
+
}
|
|
394
|
+
function selectActive() {
|
|
395
|
+
if (isReadonly()) return;
|
|
396
|
+
const items = getOptionItems(false);
|
|
397
|
+
if (!items.length) return;
|
|
398
|
+
const li = items[Math.max(0, Math.min(activeIndex, items.length - 1))];
|
|
399
|
+
if (!li) return;
|
|
400
|
+
const val = li.dataset.value ?? "";
|
|
401
|
+
const opt = Array.from(select.options).find((o) => o.value === val);
|
|
402
|
+
if (!opt || opt.disabled) return;
|
|
403
|
+
if (select.multiple) {
|
|
404
|
+
opt.selected = !opt.selected;
|
|
405
|
+
emitChange();
|
|
406
|
+
renderList();
|
|
407
|
+
if (!searchEl.hidden) searchEl.focus();
|
|
408
|
+
} else {
|
|
409
|
+
select.value = opt.value;
|
|
410
|
+
emitChange();
|
|
411
|
+
close();
|
|
412
|
+
control.focus();
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
function onKeydown(e) {
|
|
416
|
+
if (isDisabled() || isReadonly()) return;
|
|
417
|
+
if (e.key === "ArrowDown") {
|
|
418
|
+
e.preventDefault();
|
|
419
|
+
if (!open) openDropdown();
|
|
420
|
+
moveActive(1);
|
|
421
|
+
return;
|
|
422
|
+
}
|
|
423
|
+
if (e.key === "ArrowUp") {
|
|
424
|
+
e.preventDefault();
|
|
425
|
+
if (!open) openDropdown();
|
|
426
|
+
moveActive(-1);
|
|
427
|
+
return;
|
|
428
|
+
}
|
|
429
|
+
if (e.key === "Enter") {
|
|
430
|
+
if (!open) return;
|
|
431
|
+
e.preventDefault();
|
|
432
|
+
selectActive();
|
|
433
|
+
return;
|
|
434
|
+
}
|
|
435
|
+
if (e.key === "Escape") {
|
|
436
|
+
if (!open) return;
|
|
437
|
+
e.preventDefault();
|
|
438
|
+
close();
|
|
439
|
+
control.focus();
|
|
440
|
+
return;
|
|
441
|
+
}
|
|
442
|
+
if (e.key === "Tab") {
|
|
443
|
+
if (open) close();
|
|
444
|
+
return;
|
|
445
|
+
}
|
|
446
|
+
if (select.multiple && e.key === "Backspace") {
|
|
447
|
+
const searchEmpty = searchEl.hidden || searchEl.value.trim() === "";
|
|
448
|
+
if (searchEmpty) {
|
|
449
|
+
const selected = selectedNonEmpty();
|
|
450
|
+
const last = selected[selected.length - 1];
|
|
451
|
+
if (last) {
|
|
452
|
+
last.selected = false;
|
|
453
|
+
emitChange();
|
|
454
|
+
renderList();
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
const onDocDown = (e) => {
|
|
460
|
+
if (!root.contains(e.target)) close();
|
|
461
|
+
};
|
|
462
|
+
document.addEventListener("mousedown", onDocDown);
|
|
463
|
+
control.addEventListener("click", () => toggle());
|
|
464
|
+
control.addEventListener("keydown", onKeydown);
|
|
465
|
+
searchEl.addEventListener("keydown", onKeydown);
|
|
466
|
+
searchEl.addEventListener("input", () => {
|
|
467
|
+
filterQuery = searchEl.value ?? "";
|
|
468
|
+
renderList();
|
|
469
|
+
dispatch(select, "zselect:search", { q: filterQuery });
|
|
470
|
+
});
|
|
471
|
+
clearEl.addEventListener("mousedown", (e) => {
|
|
472
|
+
e.preventDefault();
|
|
473
|
+
e.stopPropagation();
|
|
474
|
+
clearValue();
|
|
475
|
+
if (open) renderList();
|
|
476
|
+
if (!searchEl.hidden) searchEl.focus();
|
|
477
|
+
});
|
|
478
|
+
select.addEventListener("change", () => {
|
|
479
|
+
renderLabel();
|
|
480
|
+
updateClearVisibility();
|
|
481
|
+
if (open) renderList();
|
|
482
|
+
dispatch(select, "zselect:change", { value: getSelected(select) });
|
|
483
|
+
});
|
|
484
|
+
function clearOptions(keepPlaceholder = true) {
|
|
485
|
+
const placeholderOpt = select.querySelector('option[value=""]');
|
|
486
|
+
select.innerHTML = "";
|
|
487
|
+
if (keepPlaceholder) {
|
|
488
|
+
const opt = document.createElement("option");
|
|
489
|
+
opt.value = "";
|
|
490
|
+
opt.textContent = placeholderOpt?.textContent ?? opts.placeholder ?? "Seleziona...";
|
|
491
|
+
select.append(opt);
|
|
492
|
+
}
|
|
493
|
+
select.value = "";
|
|
494
|
+
emitChange();
|
|
495
|
+
}
|
|
496
|
+
function setOptions(optionsArr, keepPlaceholder = true) {
|
|
497
|
+
const placeholderText = select.querySelector('option[value=""]')?.textContent ?? opts.placeholder ?? "Seleziona...";
|
|
498
|
+
select.innerHTML = "";
|
|
499
|
+
if (keepPlaceholder) {
|
|
500
|
+
const ph = document.createElement("option");
|
|
501
|
+
ph.value = "";
|
|
502
|
+
ph.textContent = placeholderText;
|
|
503
|
+
select.append(ph);
|
|
504
|
+
}
|
|
505
|
+
optionsArr.forEach((o) => {
|
|
506
|
+
const opt = document.createElement("option");
|
|
507
|
+
opt.value = String(o.value);
|
|
508
|
+
opt.textContent = o.label;
|
|
509
|
+
if (o.disabled) opt.disabled = true;
|
|
510
|
+
select.append(opt);
|
|
511
|
+
});
|
|
512
|
+
select.value = "";
|
|
513
|
+
emitChange();
|
|
514
|
+
}
|
|
515
|
+
function setLoading(isLoading) {
|
|
516
|
+
root.classList.toggle("is-loading", !!isLoading);
|
|
517
|
+
if (isLoading) {
|
|
518
|
+
control.disabled = true;
|
|
519
|
+
searchEl.disabled = true;
|
|
520
|
+
return;
|
|
521
|
+
}
|
|
522
|
+
applyStateToUI();
|
|
523
|
+
}
|
|
524
|
+
function findOptionEl(value) {
|
|
525
|
+
const v = String(value);
|
|
526
|
+
return Array.from(select.options).find((o) => o.value === v) ?? null;
|
|
527
|
+
}
|
|
528
|
+
function addOption(option, selectIt = false) {
|
|
529
|
+
const value = String(option.value);
|
|
530
|
+
if (findOptionEl(value)) return;
|
|
531
|
+
const opt = document.createElement("option");
|
|
532
|
+
opt.value = value;
|
|
533
|
+
opt.textContent = option.label;
|
|
534
|
+
if (option.disabled) opt.disabled = true;
|
|
535
|
+
select.append(opt);
|
|
536
|
+
if (selectIt) {
|
|
537
|
+
if (select.multiple) opt.selected = true;
|
|
538
|
+
else select.value = value;
|
|
539
|
+
emitChange();
|
|
540
|
+
} else {
|
|
541
|
+
refresh();
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
function removeOption(value) {
|
|
545
|
+
const opt = findOptionEl(value);
|
|
546
|
+
if (!opt) return;
|
|
547
|
+
const wasSelected = opt.selected;
|
|
548
|
+
opt.remove();
|
|
549
|
+
if (wasSelected) {
|
|
550
|
+
emitChange();
|
|
551
|
+
} else {
|
|
552
|
+
refresh();
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
function updateOption(value, patch) {
|
|
556
|
+
const opt = findOptionEl(value);
|
|
557
|
+
if (!opt) return;
|
|
558
|
+
if (patch.label !== void 0) opt.textContent = patch.label;
|
|
559
|
+
if (patch.disabled !== void 0) opt.disabled = !!patch.disabled;
|
|
560
|
+
refresh();
|
|
561
|
+
}
|
|
562
|
+
function upsertOption(option, selectIt = false) {
|
|
563
|
+
const value = String(option.value);
|
|
564
|
+
const opt = findOptionEl(value);
|
|
565
|
+
if (!opt) {
|
|
566
|
+
addOption(option, selectIt);
|
|
567
|
+
return;
|
|
568
|
+
}
|
|
569
|
+
opt.textContent = option.label;
|
|
570
|
+
opt.disabled = !!option.disabled;
|
|
571
|
+
if (selectIt) {
|
|
572
|
+
if (select.multiple) opt.selected = true;
|
|
573
|
+
else select.value = value;
|
|
574
|
+
select.dispatchEvent(new Event("change", { bubbles: true }));
|
|
575
|
+
} else {
|
|
576
|
+
refresh();
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
renderLabel();
|
|
580
|
+
updateClearVisibility();
|
|
581
|
+
if (options.disabled != null) select.disabled = !!options.disabled;
|
|
582
|
+
applyStateToUI();
|
|
583
|
+
const inst = {
|
|
584
|
+
select,
|
|
585
|
+
destroy() {
|
|
586
|
+
document.removeEventListener("mousedown", onDocDown);
|
|
587
|
+
root.remove();
|
|
588
|
+
select.style.position = prevStyle.position;
|
|
589
|
+
select.style.left = prevStyle.left;
|
|
590
|
+
select.style.width = prevStyle.width;
|
|
591
|
+
select.style.height = prevStyle.height;
|
|
592
|
+
instances.delete(select);
|
|
593
|
+
},
|
|
594
|
+
open: openDropdown,
|
|
595
|
+
close,
|
|
596
|
+
setValue(value) {
|
|
597
|
+
setSelected(select, value);
|
|
598
|
+
},
|
|
599
|
+
getValue() {
|
|
600
|
+
return getSelected(select);
|
|
601
|
+
},
|
|
602
|
+
clearOptions,
|
|
603
|
+
setOptions,
|
|
604
|
+
addOption,
|
|
605
|
+
removeOption,
|
|
606
|
+
updateOption,
|
|
607
|
+
upsertOption,
|
|
608
|
+
refresh,
|
|
609
|
+
setLoading,
|
|
610
|
+
setDisabled(v) {
|
|
611
|
+
select.disabled = !!v;
|
|
612
|
+
applyStateToUI();
|
|
613
|
+
},
|
|
614
|
+
setReadonly(v) {
|
|
615
|
+
readonly = !!v;
|
|
616
|
+
applyStateToUI();
|
|
617
|
+
},
|
|
618
|
+
isDisabled,
|
|
619
|
+
isReadonly
|
|
620
|
+
};
|
|
621
|
+
instances.set(select, inst);
|
|
622
|
+
return inst;
|
|
623
|
+
},
|
|
624
|
+
get(select) {
|
|
625
|
+
return instances.get(select) ?? null;
|
|
626
|
+
},
|
|
627
|
+
destroy(select) {
|
|
628
|
+
const inst = instances.get(select);
|
|
629
|
+
if (inst) inst.destroy();
|
|
630
|
+
}
|
|
631
|
+
};
|
|
632
|
+
return __toCommonJS(index_exports);
|
|
633
|
+
})();
|
|
634
|
+
//# sourceMappingURL=index.global.js.map
|