forge-select 0.2.0 → 0.4.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.
@@ -1,901 +1,2 @@
1
- "use strict";
2
- var ForgeSelectBundle = (() => {
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
- ForgeSelect: () => ForgeSelect,
25
- default: () => ForgeSelect
26
- });
27
-
28
- // src/emitter.ts
29
- var Emitter = class {
30
- constructor() {
31
- this.handlers = /* @__PURE__ */ new Map();
32
- }
33
- on(event, handler) {
34
- let set = this.handlers.get(event);
35
- if (!set) {
36
- set = /* @__PURE__ */ new Set();
37
- this.handlers.set(event, set);
38
- }
39
- set.add(handler);
40
- }
41
- off(event, handler) {
42
- this.handlers.get(event)?.delete(handler);
43
- }
44
- emit(event, ...args) {
45
- const set = this.handlers.get(event);
46
- if (!set) return;
47
- for (const handler of [...set]) handler(...args);
48
- }
49
- clear() {
50
- this.handlers.clear();
51
- }
52
- };
53
-
54
- // src/i18n.ts
55
- var locales = {
56
- en: {
57
- noResults: "No results found",
58
- loading: "Loading\u2026",
59
- loadingMore: "Loading more\u2026",
60
- createOption: 'Create "{query}"',
61
- clearSelection: "Clear selection",
62
- removeItem: "Remove {label}",
63
- search: "Search"
64
- },
65
- vi: {
66
- noResults: "Kh\xF4ng t\xECm th\u1EA5y k\u1EBFt qu\u1EA3",
67
- loading: "\u0110ang t\u1EA3i\u2026",
68
- loadingMore: "\u0110ang t\u1EA3i th\xEAm\u2026",
69
- createOption: 'T\u1EA1o "{query}"',
70
- clearSelection: "X\xF3a l\u1EF1a ch\u1ECDn",
71
- removeItem: "X\xF3a {label}",
72
- search: "T\xECm ki\u1EBFm"
73
- }
74
- };
75
- function getStrings(language) {
76
- if (typeof language === "string") {
77
- return locales[language] ?? locales.en;
78
- }
79
- return { ...locales.en, ...language };
80
- }
81
- function format(template, vars) {
82
- return template.replace(/\{(\w+)\}/g, (match, key) => vars[key] ?? match);
83
- }
84
-
85
- // src/ForgeSelect.ts
86
- var DEFAULT_ITEM_HEIGHT = 36;
87
- var VIRTUAL_BUFFER = 5;
88
- var VIRTUAL_THRESHOLD = 100;
89
- var ROW_CACHE_LIMIT = 2e3;
90
- var uidCounter = 0;
91
- function isGroup(item) {
92
- return item.options !== void 0;
93
- }
94
- function collectDescendantValues(option) {
95
- if (!option.children) return [];
96
- const values = [];
97
- for (const child of option.children) {
98
- values.push(child.value, ...collectDescendantValues(child));
99
- }
100
- return values;
101
- }
102
- function computeCheckState(option, selected) {
103
- if (!option.children || option.children.length === 0) {
104
- return selected.includes(option.value) ? "all" : "none";
105
- }
106
- const states = option.children.map((child) => computeCheckState(child, selected));
107
- if (states.every((s) => s === "all")) return "all";
108
- if (states.every((s) => s === "none")) return "none";
109
- return "some";
110
- }
111
- var ForgeSelect = class {
112
- constructor(target, options = {}) {
113
- this.selected = [];
114
- this.selectedOptions = /* @__PURE__ */ new Map();
115
- this.emitter = new Emitter();
116
- this.uid = `forge-select-${++uidCounter}`;
117
- this.searchInput = null;
118
- this.isOpen = false;
119
- this.isDisabled = false;
120
- this.destroyed = false;
121
- this.query = "";
122
- this.rows = [];
123
- this.navItems = [];
124
- this.highlightedIndex = -1;
125
- this.rowContentCache = /* @__PURE__ */ new Map();
126
- this.expandedValues = /* @__PURE__ */ new Set();
127
- this.loading = false;
128
- this.loadingMore = false;
129
- this.page = 0;
130
- this.hasMore = true;
131
- this.ajaxTimer = null;
132
- this.ajaxRequestId = 0;
133
- this.remoteLoaded = false;
134
- this.onDocumentMouseDown = (event) => {
135
- if (!this.root.contains(event.target)) this.close();
136
- };
137
- const el = typeof target === "string" ? document.querySelector(target) : target;
138
- if (!el) {
139
- throw new Error(`ForgeSelect: target element not found: ${String(target)}`);
140
- }
141
- this.el = el;
142
- const nativeSelect = el instanceof HTMLSelectElement ? el : null;
143
- this.opts = {
144
- placeholder: options.placeholder ?? "",
145
- searchable: options.searchable ?? true,
146
- multiple: options.multiple ?? nativeSelect?.multiple ?? false,
147
- clearable: options.clearable ?? false,
148
- allowCreate: options.allowCreate ?? false,
149
- theme: options.theme ?? "default",
150
- disabled: options.disabled ?? false,
151
- data: options.data,
152
- ajax: options.ajax,
153
- templateResult: options.templateResult,
154
- templateSelection: options.templateSelection,
155
- virtualScroll: options.virtualScroll,
156
- itemHeight: options.itemHeight ?? DEFAULT_ITEM_HEIGHT,
157
- language: options.language ?? "en",
158
- plugins: options.plugins ?? []
159
- };
160
- this.strings = getStrings(this.opts.language);
161
- this.plugins = this.opts.plugins;
162
- this.data = this.opts.data ?? (nativeSelect ? parseNativeOptions(nativeSelect) : []);
163
- if (nativeSelect && !this.opts.data) {
164
- for (const option of Array.from(nativeSelect.querySelectorAll("option"))) {
165
- if (option.hasAttribute("selected")) this.selectValue(option.value, false);
166
- }
167
- }
168
- this.buildDom();
169
- this.renderValue();
170
- if (this.opts.disabled) this.disable();
171
- for (const plugin of this.plugins) plugin.onInit?.(this);
172
- }
173
- // ---------------------------------------------------------------- public API
174
- open() {
175
- if (this.isOpen || this.isDisabled || this.destroyed) return;
176
- this.isOpen = true;
177
- this.dropdown.hidden = false;
178
- this.root.classList.add("forge-select--open");
179
- this.control.setAttribute("aria-expanded", "true");
180
- document.addEventListener("mousedown", this.onDocumentMouseDown);
181
- if (this.opts.ajax && !this.remoteLoaded) {
182
- this.scheduleRemoteLoad(this.query, 0);
183
- }
184
- this.renderList();
185
- if (this.searchInput) this.searchInput.focus();
186
- this.emitter.emit("open");
187
- for (const plugin of this.plugins) plugin.onOpen?.(this);
188
- }
189
- close() {
190
- if (!this.isOpen) return;
191
- this.isOpen = false;
192
- this.dropdown.hidden = true;
193
- this.root.classList.remove("forge-select--open");
194
- this.control.setAttribute("aria-expanded", "false");
195
- document.removeEventListener("mousedown", this.onDocumentMouseDown);
196
- this.highlightedIndex = -1;
197
- if (this.searchInput) {
198
- this.searchInput.value = "";
199
- this.query = "";
200
- }
201
- this.emitter.emit("close");
202
- for (const plugin of this.plugins) plugin.onClose?.(this);
203
- }
204
- destroy() {
205
- if (this.destroyed) return;
206
- this.close();
207
- for (const plugin of this.plugins) plugin.onDestroy?.(this);
208
- this.destroyed = true;
209
- if (this.ajaxTimer) clearTimeout(this.ajaxTimer);
210
- this.rowContentCache.clear();
211
- this.root.remove();
212
- this.el.style.display = "";
213
- this.emitter.clear();
214
- }
215
- getValue() {
216
- if (this.opts.multiple) return [...this.selected];
217
- return this.selected[0] ?? null;
218
- }
219
- setValue(value) {
220
- const values = value == null ? [] : Array.isArray(value) ? value : [value];
221
- const next = this.opts.multiple ? values : values.slice(0, 1);
222
- if (arraysEqual(next, this.selected)) return;
223
- this.selected = [];
224
- for (const v of next) this.selectValue(v, false);
225
- this.afterSelectionChange();
226
- }
227
- enable() {
228
- this.isDisabled = false;
229
- this.root.classList.remove("forge-select--disabled");
230
- this.control.tabIndex = 0;
231
- this.control.setAttribute("aria-disabled", "false");
232
- }
233
- disable() {
234
- this.close();
235
- this.isDisabled = true;
236
- this.root.classList.add("forge-select--disabled");
237
- this.control.tabIndex = -1;
238
- this.control.setAttribute("aria-disabled", "true");
239
- }
240
- on(event, handler) {
241
- this.emitter.on(event, handler);
242
- }
243
- off(event, handler) {
244
- this.emitter.off(event, handler);
245
- }
246
- // ---------------------------------------------------------------- DOM setup
247
- buildDom() {
248
- this.root = document.createElement("div");
249
- this.root.className = "forge-select";
250
- this.root.dataset.theme = this.opts.theme;
251
- this.root.style.setProperty("--fs-item-height", `${this.opts.itemHeight}px`);
252
- this.control = document.createElement("div");
253
- this.control.className = "forge-select__control";
254
- this.control.setAttribute("role", "combobox");
255
- this.control.setAttribute("aria-haspopup", "listbox");
256
- this.control.setAttribute("aria-expanded", "false");
257
- this.control.setAttribute("aria-controls", `${this.uid}-list`);
258
- this.control.tabIndex = 0;
259
- this.valueEl = document.createElement("div");
260
- this.valueEl.className = "forge-select__value";
261
- this.clearBtn = document.createElement("button");
262
- this.clearBtn.type = "button";
263
- this.clearBtn.className = "forge-select__clear";
264
- this.clearBtn.setAttribute("aria-label", this.strings.clearSelection);
265
- this.clearBtn.textContent = "\xD7";
266
- this.clearBtn.hidden = true;
267
- const arrow = document.createElement("span");
268
- arrow.className = "forge-select__arrow";
269
- arrow.setAttribute("aria-hidden", "true");
270
- this.control.append(this.valueEl, this.clearBtn, arrow);
271
- this.dropdown = document.createElement("div");
272
- this.dropdown.className = "forge-select__dropdown";
273
- this.dropdown.hidden = true;
274
- if (this.opts.searchable) {
275
- this.searchInput = document.createElement("input");
276
- this.searchInput.type = "search";
277
- this.searchInput.className = "forge-select__search";
278
- this.searchInput.setAttribute("aria-label", this.strings.search);
279
- this.searchInput.setAttribute("aria-autocomplete", "list");
280
- this.searchInput.setAttribute("aria-controls", `${this.uid}-list`);
281
- this.dropdown.append(this.searchInput);
282
- }
283
- this.list = document.createElement("ul");
284
- this.list.className = "forge-select__list";
285
- this.list.id = `${this.uid}-list`;
286
- this.list.setAttribute("role", "listbox");
287
- if (this.opts.multiple) this.list.setAttribute("aria-multiselectable", "true");
288
- this.dropdown.append(this.list);
289
- this.root.append(this.control, this.dropdown);
290
- this.el.style.display = "none";
291
- this.el.insertAdjacentElement("afterend", this.root);
292
- this.bindEvents();
293
- }
294
- bindEvents() {
295
- this.control.addEventListener("click", (event) => {
296
- if (event.target === this.clearBtn) return;
297
- if (this.isDisabled) return;
298
- this.isOpen ? this.close() : this.open();
299
- });
300
- this.control.addEventListener("keydown", (event) => this.handleKeydown(event));
301
- this.clearBtn.addEventListener("click", (event) => {
302
- event.stopPropagation();
303
- this.clearSelection();
304
- });
305
- if (this.searchInput) {
306
- this.searchInput.addEventListener("input", () => {
307
- this.query = this.searchInput.value;
308
- this.highlightedIndex = -1;
309
- this.list.scrollTop = 0;
310
- this.emitter.emit("search", this.query);
311
- if (this.opts.ajax) {
312
- this.scheduleRemoteLoad(this.query, this.opts.ajax.debounce ?? 250);
313
- } else {
314
- this.renderList();
315
- }
316
- });
317
- this.searchInput.addEventListener("keydown", (event) => this.handleKeydown(event));
318
- }
319
- this.list.addEventListener("click", (event) => {
320
- const target = event.target;
321
- const twisty = target.closest("[data-twisty]");
322
- if (twisty) {
323
- const value = twisty.dataset.twisty;
324
- if (this.expandedValues.has(value)) this.expandedValues.delete(value);
325
- else this.expandedValues.add(value);
326
- this.renderList();
327
- return;
328
- }
329
- const li = target.closest("li[data-nav-index]");
330
- if (!li) return;
331
- const navIndex = Number(li.dataset.navIndex);
332
- this.activateNavItem(navIndex);
333
- });
334
- this.list.addEventListener("scroll", () => {
335
- if (this.usesVirtualScroll()) this.renderRows();
336
- this.maybeLoadNextPage();
337
- });
338
- }
339
- handleKeydown(event) {
340
- if (this.isDisabled) return;
341
- switch (event.key) {
342
- case "Enter":
343
- event.preventDefault();
344
- if (!this.isOpen) this.open();
345
- else if (this.highlightedIndex >= 0) this.activateNavItem(this.highlightedIndex);
346
- break;
347
- case " ":
348
- if (event.target === this.control) {
349
- event.preventDefault();
350
- if (!this.isOpen) this.open();
351
- }
352
- break;
353
- case "ArrowDown":
354
- event.preventDefault();
355
- if (!this.isOpen) this.open();
356
- else this.moveHighlight(1);
357
- break;
358
- case "ArrowUp":
359
- event.preventDefault();
360
- if (this.isOpen) this.moveHighlight(-1);
361
- break;
362
- case "Escape":
363
- if (this.isOpen) {
364
- event.preventDefault();
365
- this.close();
366
- this.control.focus();
367
- }
368
- break;
369
- case "Tab":
370
- this.close();
371
- break;
372
- }
373
- }
374
- // ---------------------------------------------------------------- selection
375
- selectValue(value, notify) {
376
- if (this.selected.includes(value)) return;
377
- const option = this.findOption(value) ?? this.selectedOptions.get(value) ?? { value, label: value };
378
- this.selectedOptions.set(value, option);
379
- if (this.opts.multiple) {
380
- this.selected.push(value);
381
- for (const v of collectDescendantValues(option)) {
382
- if (!this.selected.includes(v)) this.selected.push(v);
383
- }
384
- this.syncTreeAncestors();
385
- } else {
386
- this.selected = [value];
387
- }
388
- if (notify) this.afterSelectionChange();
389
- }
390
- deselectValue(value, notify) {
391
- const index = this.selected.indexOf(value);
392
- if (index === -1) return;
393
- this.selected.splice(index, 1);
394
- if (this.opts.multiple) {
395
- const option = this.findOption(value) ?? this.selectedOptions.get(value);
396
- if (option) {
397
- for (const v of collectDescendantValues(option)) {
398
- const i = this.selected.indexOf(v);
399
- if (i !== -1) this.selected.splice(i, 1);
400
- }
401
- }
402
- this.syncTreeAncestors();
403
- }
404
- if (notify) this.afterSelectionChange();
405
- }
406
- /**
407
- * Keeps every tree parent's own membership in `selected` consistent with
408
- * its descendants (post-order, so parents see already-corrected children):
409
- * a parent counts as selected only when `computeCheckState` says "all".
410
- * No-op for data with no `children` anywhere.
411
- */
412
- syncTreeAncestors() {
413
- const sync = (option) => {
414
- if (!option.children || option.children.length === 0) return;
415
- for (const child of option.children) sync(child);
416
- const state = computeCheckState(option, this.selected);
417
- const index = this.selected.indexOf(option.value);
418
- if (state === "all" && index === -1) this.selected.push(option.value);
419
- else if (state !== "all" && index !== -1) this.selected.splice(index, 1);
420
- };
421
- for (const item of this.data) {
422
- const options = isGroup(item) ? item.options : [item];
423
- options.forEach(sync);
424
- }
425
- }
426
- clearSelection() {
427
- if (this.selected.length === 0) return;
428
- this.selected = [];
429
- this.emitter.emit("clear");
430
- this.afterSelectionChange();
431
- }
432
- afterSelectionChange() {
433
- this.renderValue();
434
- this.syncNativeSelect();
435
- if (this.isOpen) this.renderList();
436
- this.emitter.emit("change", this.getValue());
437
- }
438
- syncNativeSelect() {
439
- if (!(this.el instanceof HTMLSelectElement)) return;
440
- const existing = /* @__PURE__ */ new Set();
441
- for (const option of Array.from(this.el.options)) {
442
- existing.add(option.value);
443
- option.selected = this.selected.includes(option.value);
444
- }
445
- for (const value of this.selected) {
446
- if (existing.has(value)) continue;
447
- const option = document.createElement("option");
448
- option.value = value;
449
- option.textContent = this.selectedOptions.get(value)?.label ?? value;
450
- option.selected = true;
451
- this.el.append(option);
452
- }
453
- this.el.dispatchEvent(new Event("change", { bubbles: true }));
454
- }
455
- findOption(value) {
456
- const search = (options) => {
457
- for (const option of options) {
458
- if (option.value === value) return option;
459
- if (option.children) {
460
- const found = search(option.children);
461
- if (found) return found;
462
- }
463
- }
464
- return void 0;
465
- };
466
- for (const item of this.data) {
467
- const found = search(isGroup(item) ? item.options : [item]);
468
- if (found) return found;
469
- }
470
- return void 0;
471
- }
472
- createFromQuery() {
473
- const label = this.query.trim();
474
- if (!label) return;
475
- const option = { value: label, label };
476
- this.data.push(option);
477
- if (this.searchInput) {
478
- this.searchInput.value = "";
479
- this.query = "";
480
- }
481
- this.selectValue(option.value, true);
482
- if (!this.opts.multiple) this.close();
483
- }
484
- activateNavItem(navIndex) {
485
- const item = this.navItems[navIndex];
486
- if (!item) return;
487
- if (item.kind === "create") {
488
- this.createFromQuery();
489
- return;
490
- }
491
- const { value } = item.option;
492
- if (this.opts.multiple) {
493
- this.selected.includes(value) ? this.deselectValue(value, true) : this.selectValue(value, true);
494
- } else {
495
- this.selectValue(value, true);
496
- this.close();
497
- this.control.focus();
498
- }
499
- }
500
- // ---------------------------------------------------------------- rendering
501
- renderValue() {
502
- this.valueEl.textContent = "";
503
- const hasValue = this.selected.length > 0;
504
- this.clearBtn.hidden = !(this.opts.clearable && hasValue);
505
- if (!hasValue) {
506
- const placeholder = document.createElement("span");
507
- placeholder.className = "forge-select__placeholder";
508
- placeholder.textContent = this.opts.placeholder;
509
- this.valueEl.append(placeholder);
510
- return;
511
- }
512
- if (this.opts.multiple) {
513
- for (const value of this.selected) {
514
- const option = this.selectedOptions.get(value) ?? { value, label: value };
515
- const tag = document.createElement("span");
516
- tag.className = "forge-select__tag";
517
- const label = document.createElement("span");
518
- label.className = "forge-select__tag-label";
519
- this.renderTemplate(label, option, this.opts.templateSelection, "inline");
520
- const remove = document.createElement("button");
521
- remove.type = "button";
522
- remove.className = "forge-select__tag-remove";
523
- remove.setAttribute("aria-label", format(this.strings.removeItem, { label: option.label }));
524
- remove.textContent = "\xD7";
525
- remove.addEventListener("click", (event) => {
526
- event.stopPropagation();
527
- if (!this.isDisabled) this.deselectValue(value, true);
528
- });
529
- tag.append(label, remove);
530
- this.valueEl.append(tag);
531
- }
532
- } else {
533
- const option = this.selectedOptions.get(this.selected[0]) ?? {
534
- value: this.selected[0],
535
- label: this.selected[0]
536
- };
537
- const span = document.createElement("span");
538
- span.className = "forge-select__single-value";
539
- this.renderTemplate(span, option, this.opts.templateSelection, "inline");
540
- this.valueEl.append(span);
541
- }
542
- }
543
- renderTemplate(container, option, template, variant = "row") {
544
- if (template) {
545
- const result = template(option);
546
- if (typeof result === "string") container.innerHTML = result;
547
- else container.append(result);
548
- return;
549
- }
550
- if (!option.avatar && !option.description) {
551
- container.textContent = option.label;
552
- return;
553
- }
554
- if (option.avatar) {
555
- const avatar = document.createElement("img");
556
- avatar.className = variant === "row" ? "forge-select__option-avatar" : "forge-select__inline-avatar";
557
- avatar.src = option.avatar;
558
- avatar.alt = "";
559
- avatar.setAttribute("loading", "lazy");
560
- avatar.setAttribute("decoding", "async");
561
- container.append(avatar);
562
- }
563
- if (variant === "row" && option.description) {
564
- const body = document.createElement("span");
565
- body.className = "forge-select__option-body";
566
- const label = document.createElement("span");
567
- label.className = "forge-select__option-label";
568
- label.textContent = option.label;
569
- const desc = document.createElement("span");
570
- desc.className = "forge-select__option-desc";
571
- desc.textContent = option.description;
572
- body.append(label, desc);
573
- container.append(body);
574
- } else {
575
- const label = document.createElement("span");
576
- label.className = "forge-select__option-label";
577
- label.textContent = option.label;
578
- container.append(label);
579
- }
580
- }
581
- buildRows() {
582
- this.rows = [];
583
- this.navItems = [];
584
- const query = this.query.trim().toLowerCase();
585
- const matches = (option) => query === "" || option.label.toLowerCase().includes(query) || (option.description?.toLowerCase().includes(query) ?? false);
586
- const subtreeMatches = (option) => query === "" || matches(option) || (option.children ?? []).some(subtreeMatches);
587
- const pushOption = (option, depth) => {
588
- let navIndex = -1;
589
- if (!option.disabled) {
590
- navIndex = this.navItems.length;
591
- this.navItems.push({ kind: "option", option });
592
- }
593
- const hasChildren = !!option.children && option.children.length > 0;
594
- this.rows.push({ kind: "option", option, navIndex, depth, hasChildren });
595
- if (hasChildren) {
596
- const expanded = query !== "" || this.expandedValues.has(option.value);
597
- if (expanded) {
598
- for (const child of option.children) {
599
- if (subtreeMatches(child)) pushOption(child, depth + 1);
600
- }
601
- }
602
- }
603
- };
604
- if (this.loading) {
605
- this.rows.push({ kind: "loading" });
606
- return;
607
- }
608
- for (const item of this.data) {
609
- if (isGroup(item)) {
610
- const visible = item.options.filter(subtreeMatches);
611
- if (visible.length === 0) continue;
612
- this.rows.push({ kind: "group", label: item.label });
613
- visible.forEach((o) => pushOption(o, 0));
614
- } else if (subtreeMatches(item)) {
615
- pushOption(item, 0);
616
- }
617
- }
618
- if (this.opts.allowCreate && query !== "" && !this.hasExactMatch(query)) {
619
- const navIndex = this.navItems.length;
620
- this.navItems.push({ kind: "create" });
621
- this.rows.push({ kind: "create", navIndex });
622
- }
623
- if (this.rows.length === 0) this.rows.push({ kind: "empty" });
624
- else if (this.loadingMore) this.rows.push({ kind: "loading-more" });
625
- }
626
- hasExactMatch(lowerQuery) {
627
- const matchesExactly = (option) => option.label.toLowerCase() === lowerQuery || (option.children ?? []).some(matchesExactly);
628
- for (const item of this.data) {
629
- const options = isGroup(item) ? item.options : [item];
630
- if (options.some(matchesExactly)) return true;
631
- }
632
- return false;
633
- }
634
- usesVirtualScroll() {
635
- return this.opts.virtualScroll !== false && this.rows.length > VIRTUAL_THRESHOLD;
636
- }
637
- renderList() {
638
- this.buildRows();
639
- this.renderRows();
640
- }
641
- renderRows() {
642
- const scrollTop = this.list.scrollTop;
643
- const clientHeight = this.list.clientHeight;
644
- const virtual = this.usesVirtualScroll();
645
- this.list.textContent = "";
646
- const rowHeight = this.opts.itemHeight;
647
- let start = 0;
648
- let end = this.rows.length;
649
- if (virtual) {
650
- const viewport = clientHeight || rowHeight * 8;
651
- start = Math.max(0, Math.floor(scrollTop / rowHeight) - VIRTUAL_BUFFER);
652
- end = Math.min(this.rows.length, start + Math.ceil(viewport / rowHeight) + VIRTUAL_BUFFER * 2);
653
- const topSpacer = document.createElement("li");
654
- topSpacer.className = "forge-select__spacer";
655
- topSpacer.setAttribute("aria-hidden", "true");
656
- topSpacer.style.height = `${start * rowHeight}px`;
657
- this.list.append(topSpacer);
658
- }
659
- for (let i = start; i < end; i++) {
660
- this.list.append(this.renderRow(this.rows[i]));
661
- }
662
- if (virtual) {
663
- const bottomSpacer = document.createElement("li");
664
- bottomSpacer.className = "forge-select__spacer";
665
- bottomSpacer.setAttribute("aria-hidden", "true");
666
- bottomSpacer.style.height = `${(this.rows.length - end) * rowHeight}px`;
667
- this.list.append(bottomSpacer);
668
- if (this.list.scrollTop !== scrollTop) {
669
- this.list.scrollTop = scrollTop;
670
- }
671
- }
672
- this.updateActiveDescendant();
673
- }
674
- renderRow(row) {
675
- const li = document.createElement("li");
676
- switch (row.kind) {
677
- case "group":
678
- li.className = "forge-select__group-label";
679
- li.setAttribute("role", "presentation");
680
- li.textContent = row.label;
681
- break;
682
- case "empty":
683
- li.className = "forge-select__empty";
684
- li.textContent = this.strings.noResults;
685
- break;
686
- case "loading":
687
- li.className = "forge-select__loading";
688
- li.textContent = this.strings.loading;
689
- break;
690
- case "loading-more":
691
- li.className = "forge-select__loading-more";
692
- li.setAttribute("aria-hidden", "true");
693
- li.textContent = this.strings.loadingMore;
694
- break;
695
- case "create":
696
- li.className = "forge-select__option forge-select__option--create";
697
- li.setAttribute("role", "option");
698
- li.id = `${this.uid}-nav-${row.navIndex}`;
699
- li.dataset.navIndex = String(row.navIndex);
700
- li.textContent = format(this.strings.createOption, { query: this.query.trim() });
701
- if (row.navIndex === this.highlightedIndex) li.classList.add("forge-select__option--highlighted");
702
- break;
703
- case "option": {
704
- li.className = "forge-select__option";
705
- li.setAttribute("role", "option");
706
- const isSelected = this.selected.includes(row.option.value);
707
- li.setAttribute("aria-selected", String(isSelected));
708
- if (isSelected) li.classList.add("forge-select__option--selected");
709
- if (this.opts.multiple && row.hasChildren && computeCheckState(row.option, this.selected) === "some") {
710
- li.classList.add("forge-select__option--indeterminate");
711
- }
712
- if (row.depth > 0) {
713
- li.style.paddingLeft = `calc(12px + ${row.depth} * var(--fs-tree-indent, 18px))`;
714
- }
715
- if (row.option.disabled) {
716
- li.classList.add("forge-select__option--disabled");
717
- li.setAttribute("aria-disabled", "true");
718
- } else {
719
- li.id = `${this.uid}-nav-${row.navIndex}`;
720
- li.dataset.navIndex = String(row.navIndex);
721
- if (row.navIndex === this.highlightedIndex) li.classList.add("forge-select__option--highlighted");
722
- }
723
- if (row.hasChildren) {
724
- const twisty = document.createElement("span");
725
- twisty.className = "forge-select__twisty";
726
- twisty.dataset.twisty = row.option.value;
727
- twisty.setAttribute("aria-hidden", "true");
728
- twisty.textContent = this.expandedValues.has(row.option.value) ? "\u25BC" : "\u25B6";
729
- li.append(twisty);
730
- }
731
- li.append(this.optionContent(row.option));
732
- break;
733
- }
734
- }
735
- return li;
736
- }
737
- /**
738
- * Rendered row content is cached per option value and cloned on each render,
739
- * so templates run once per option instead of once per scroll frame.
740
- * State classes (selected/highlighted/disabled) live on the <li>, keeping the
741
- * cached content state-free.
742
- */
743
- optionContent(option) {
744
- let cached = this.rowContentCache.get(option.value);
745
- if (!cached) {
746
- const holder = document.createElement("span");
747
- holder.className = "forge-select__option-content";
748
- this.renderTemplate(holder, option, this.opts.templateResult);
749
- if (this.rowContentCache.size >= ROW_CACHE_LIMIT) {
750
- const oldest = this.rowContentCache.keys().next().value;
751
- this.rowContentCache.delete(oldest);
752
- }
753
- this.rowContentCache.set(option.value, holder);
754
- cached = holder;
755
- }
756
- return cached.cloneNode(true);
757
- }
758
- moveHighlight(delta) {
759
- if (this.navItems.length === 0) return;
760
- const next = this.highlightedIndex === -1 && delta > 0 ? 0 : (this.highlightedIndex + delta + this.navItems.length) % this.navItems.length;
761
- this.highlightedIndex = next;
762
- if (this.usesVirtualScroll()) {
763
- const rowIndex = this.rows.findIndex(
764
- (row) => (row.kind === "option" || row.kind === "create") && row.navIndex === next
765
- );
766
- if (rowIndex >= 0) {
767
- const rowHeight = this.opts.itemHeight;
768
- const top = rowIndex * rowHeight;
769
- const viewport = this.list.clientHeight || rowHeight * 8;
770
- let target = this.list.scrollTop;
771
- if (top < target) target = top;
772
- else if (top + rowHeight > target + viewport) target = top + rowHeight - viewport;
773
- if (target !== this.list.scrollTop) this.list.scrollTop = target;
774
- }
775
- this.renderRows();
776
- } else {
777
- this.renderRows();
778
- const highlighted = this.list.querySelector(".forge-select__option--highlighted");
779
- highlighted?.scrollIntoView?.({ block: "nearest" });
780
- }
781
- }
782
- updateActiveDescendant() {
783
- const target = this.searchInput ?? this.control;
784
- if (this.highlightedIndex >= 0) {
785
- target.setAttribute("aria-activedescendant", `${this.uid}-nav-${this.highlightedIndex}`);
786
- } else {
787
- target.removeAttribute("aria-activedescendant");
788
- }
789
- }
790
- // ---------------------------------------------------------------- remote data
791
- scheduleRemoteLoad(query, delay) {
792
- if (this.ajaxTimer) clearTimeout(this.ajaxTimer);
793
- this.page = 0;
794
- this.hasMore = true;
795
- this.loading = true;
796
- this.renderList();
797
- this.ajaxTimer = setTimeout(() => {
798
- void this.loadRemote(query);
799
- }, delay);
800
- }
801
- /**
802
- * Fires on every list scroll. Only acts when pagination is opted into via
803
- * `ajax.pagination`; reads real scroll geometry rather than row counts so
804
- * it works whether or not virtual scrolling is active for this list.
805
- */
806
- maybeLoadNextPage() {
807
- const ajax = this.opts.ajax;
808
- if (!ajax?.pagination || !this.hasMore || this.loading || this.loadingMore) return;
809
- const { scrollHeight, scrollTop, clientHeight } = this.list;
810
- const threshold = this.opts.itemHeight * 2;
811
- if (scrollHeight - scrollTop - clientHeight >= threshold) return;
812
- this.loadingMore = true;
813
- this.renderList();
814
- void this.loadRemote(this.query, { append: true });
815
- }
816
- async loadRemote(query, { append = false } = {}) {
817
- const ajax = this.opts.ajax;
818
- const requestId = ++this.ajaxRequestId;
819
- const page = append ? this.page + 1 : 0;
820
- try {
821
- const url = buildUrl(ajax, query, page);
822
- const response = await fetch(url);
823
- const json = await response.json();
824
- if (requestId !== this.ajaxRequestId || this.destroyed) return;
825
- const result = ajax.transform ? ajax.transform(json) : json;
826
- const options = Array.isArray(result) ? result : result.options;
827
- const hasMore = ajax.pagination ? Array.isArray(result) ? false : result.hasMore : false;
828
- if (append) {
829
- const existing = collectValues(this.data);
830
- this.data = [...this.data, ...options.filter((o) => !existing.has(o.value))];
831
- } else {
832
- this.data = options;
833
- this.rowContentCache.clear();
834
- }
835
- this.page = page;
836
- this.hasMore = hasMore;
837
- this.remoteLoaded = true;
838
- } catch {
839
- if (requestId !== this.ajaxRequestId || this.destroyed) return;
840
- if (!append) {
841
- this.data = [];
842
- this.rowContentCache.clear();
843
- }
844
- this.hasMore = false;
845
- } finally {
846
- if (requestId === this.ajaxRequestId && !this.destroyed) {
847
- this.loading = false;
848
- this.loadingMore = false;
849
- if (this.isOpen) this.renderList();
850
- }
851
- }
852
- }
853
- };
854
- function parseNativeOptions(select) {
855
- const data = [];
856
- for (const child of Array.from(select.children)) {
857
- if (child instanceof HTMLOptGroupElement) {
858
- data.push({
859
- label: child.label,
860
- options: Array.from(child.querySelectorAll("option")).map(parseOption)
861
- });
862
- } else if (child instanceof HTMLOptionElement) {
863
- data.push(parseOption(child));
864
- }
865
- }
866
- return data;
867
- }
868
- function parseOption(option) {
869
- return {
870
- value: option.value,
871
- label: option.textContent?.trim() ?? option.value,
872
- disabled: option.disabled || void 0
873
- };
874
- }
875
- function buildUrl(ajax, query, page) {
876
- if (typeof ajax.url === "function") return ajax.url(query);
877
- if (!ajax.params) return ajax.url;
878
- const params = new URLSearchParams();
879
- for (const [key, value] of Object.entries(ajax.params(query, page))) {
880
- params.set(key, String(value));
881
- }
882
- const separator = ajax.url.includes("?") ? "&" : "?";
883
- return `${ajax.url}${separator}${params.toString()}`;
884
- }
885
- function collectValues(items) {
886
- const values = /* @__PURE__ */ new Set();
887
- for (const item of items) {
888
- if (isGroup(item)) {
889
- for (const option of item.options) values.add(option.value);
890
- } else {
891
- values.add(item.value);
892
- }
893
- }
894
- return values;
895
- }
896
- function arraysEqual(a, b) {
897
- return a.length === b.length && a.every((value, index) => value === b[index]);
898
- }
899
- return __toCommonJS(index_exports);
900
- })();
1
+ "use strict";var ForgeSelectBundle=(()=>{var E=Object.defineProperty;var F=Object.getOwnPropertyDescriptor;var j=Object.getOwnPropertyNames;var P=Object.prototype.hasOwnProperty;var B=(o,t)=>{for(var e in t)E(o,e,{get:t[e],enumerable:!0})},$=(o,t,e,s)=>{if(t&&typeof t=="object"||typeof t=="function")for(let i of j(t))!P.call(o,i)&&i!==e&&E(o,i,{get:()=>t[i],enumerable:!(s=F(t,i))||s.enumerable});return o};var U=o=>$(E({},"__esModule",{value:!0}),o);var W={};B(W,{ForgeSelect:()=>f,default:()=>f});var b=class{constructor(){this.handlers=new Map}on(t,e){let s=this.handlers.get(t);s||(s=new Set,this.handlers.set(t,s)),s.add(e)}off(t,e){this.handlers.get(t)?.delete(e)}emit(t,...e){let s=this.handlers.get(t);if(s)for(let i of[...s])i(...e)}clear(){this.handlers.clear()}};function D(o,t,e,s=4){let i=e-o.bottom,r=o.top,n=t>i&&r>i;return{dropUp:n,top:n?o.top-t-s:o.bottom+s}}var O={en:{noResults:"No results found",loading:"Loading\u2026",loadingMore:"Loading more\u2026",errorLoading:"Could not load options",createOption:'Create "{query}"',clearSelection:"Clear selection",removeItem:"Remove {label}",search:"Search",reorderHint:"{label}. Press Alt+Left or Alt+Right to reorder.",minSearchLength:"Type {count} or more characters to search",maximumSelected:"Maximum of {count} selections reached"},vi:{noResults:"Kh\xF4ng t\xECm th\u1EA5y k\u1EBFt qu\u1EA3",loading:"\u0110ang t\u1EA3i\u2026",loadingMore:"\u0110ang t\u1EA3i th\xEAm\u2026",errorLoading:"Kh\xF4ng th\u1EC3 t\u1EA3i t\xF9y ch\u1ECDn",createOption:'T\u1EA1o "{query}"',clearSelection:"X\xF3a l\u1EF1a ch\u1ECDn",removeItem:"X\xF3a {label}",search:"T\xECm ki\u1EBFm",reorderHint:"{label}. Nh\u1EA5n Alt+Tr\xE1i ho\u1EB7c Alt+Ph\u1EA3i \u0111\u1EC3 s\u1EAFp x\u1EBFp l\u1EA1i.",minSearchLength:"Nh\u1EADp th\xEAm {count} k\xFD t\u1EF1 \u0111\u1EC3 t\xECm ki\u1EBFm",maximumSelected:"\u0110\xE3 \u0111\u1EA1t t\u1ED1i \u0111a {count} l\u1EF1a ch\u1ECDn"}};function _(o){return typeof o=="string"?O[o]??O.en:{...O.en,...o}}function u(o,t){return o.replace(/\{(\w+)\}/g,(e,s)=>t[s]??e)}function H(o){let t=[];for(let e of Array.from(o.children))e instanceof HTMLOptGroupElement?t.push({label:e.label,options:Array.from(e.querySelectorAll("option")).map(M)}):e instanceof HTMLOptionElement&&t.push(M(e));return t}function M(o){let t=o.parentElement instanceof HTMLOptGroupElement&&o.parentElement.disabled;return{value:o.value,label:o.textContent?.trim()??o.value,disabled:o.disabled||t||void 0}}function x(o,t,e,s="row"){if(e){let i=e(t);typeof i=="string"?o.innerHTML=i:o.append(i);return}if(!t.avatar&&!t.description){o.textContent=t.label;return}if(t.avatar){let i=document.createElement("img");i.className=s==="row"?"forge-select__option-avatar":"forge-select__inline-avatar",i.src=t.avatar,i.alt="",i.setAttribute("loading","lazy"),i.setAttribute("decoding","async"),o.append(i)}if(s==="row"&&t.description){let i=document.createElement("span");i.className="forge-select__option-body";let r=document.createElement("span");r.className="forge-select__option-label",r.textContent=t.label;let n=document.createElement("span");n.className="forge-select__option-desc",n.textContent=t.description,i.append(r,n),o.append(i)}else{let i=document.createElement("span");i.className="forge-select__option-label",i.textContent=t.label,o.append(i)}}function R(o,t,e){if(!o.url)throw new Error("ForgeSelect: ajax requires either url or request.");if(typeof o.url=="function")return o.url(t,e);if(!o.params)return o.url;let s=new URLSearchParams;for(let[r,n]of Object.entries(o.params(t,e)))s.set(r,String(n));let i=o.url.includes("?")?"&":"?";return`${o.url}${i}${s.toString()}`}function k(o,t){let e=o.transform?o.transform(t):t;if(Array.isArray(e))return{options:e,hasMore:!1};if(!e||!Array.isArray(e.options))throw new Error("ForgeSelect: ajax.transform must return an array of options, or an object shaped like { options: Option[], hasMore?: boolean }.");return{options:e.options,hasMore:o.pagination?!!e.hasMore:!1}}function p(o){return o.options!==void 0}var w=o=>!!o.disabled;function v(o,t=w){if(!o.children)return[];let e=[];for(let s of o.children)t(s)||e.push(s.value),e.push(...v(s,t));return e}function y(o,t,e=w){if(!o.children?.length)return t.includes(o.value)?"all":"none";let s=o.children.filter(i=>!e(i)).map(i=>y(i,t,e));return s.length===0?"none":s.every(i=>i==="all")?"all":s.every(i=>i==="none")?"none":"some"}function N(o,t){let e=s=>{for(let i of s){if(i.value===t)return i;let r=i.children?e(i.children):void 0;if(r)return r}};for(let s of o){let i=e(p(s)?s.options:[s]);if(i)return i}}function L(o,t,e=w){let s=i=>{if(!i.children?.length)return;for(let a of i.children)s(a);let r=y(i,t,e),n=t.indexOf(i.value);r==="all"&&n===-1?t.push(i.value):r!=="all"&&n!==-1&&t.splice(n,1)};for(let i of o)(p(i)?i.options:[i]).forEach(s)}function I(o){let t=new Set,e=s=>{t.add(s.value),s.children?.forEach(e)};for(let s of o)(p(s)?s.options:[s]).forEach(e);return t}function V(o,t){return o.length===t.length&&o.every((e,s)=>e===t[s])}var G=36,q=5,z=100,K=2e3,X=0,f=class{constructor(t,e={}){this.selected=[];this.selectedOptions=new Map;this.suppressNextTagClick=!1;this.emitter=new b;this.uid=`forge-select-${++X}`;this.searchInput=null;this.portalHost=null;this.isOpen=!1;this.isDisabled=!1;this.destroyed=!1;this.query="";this.rows=[];this.navItems=[];this.highlightedIndex=-1;this.rowContentCache=new Map;this.expandedValues=new Set;this.loading=!1;this.loadingMore=!1;this.page=0;this.hasMore=!0;this.ajaxTimer=null;this.ajaxRequestId=0;this.ajaxController=null;this.remoteLoaded=!1;this.loadError=null;this.originalDisplay="";this.originalDisabled=!1;this.nativeSelect=null;this.nativeForm=null;this.syncingNative=!1;this.isOptionDisabled=t=>t.disabled===!0||(this.opts.isOptionDisabled?.(t)??!1);this.pointerDownOnControl=!1;this.onDocumentMouseDown=t=>{let e=t.target;!this.root.contains(e)&&!this.portalHost?.contains(e)&&this.close()};this.onWindowResize=()=>{this.positionDropdown()};this.onAncestorScroll=()=>{this.portalHost&&this.positionDropdown()};this.onNativeInvalid=t=>{t.preventDefault(),this.control.classList.add("forge-select__control--invalid"),this.control.setAttribute("aria-invalid","true"),this.isOpen||this.open(),this.control.focus()};this.onNativeChange=()=>{if(!this.nativeSelect||this.destroyed||this.syncingNative)return;let t=Array.from(this.nativeSelect.selectedOptions,e=>e.value);this.applyNativeValues(t)};this.onFormReset=()=>{if(!this.nativeSelect||this.destroyed)return;let t=Array.from(this.nativeSelect.options).filter(e=>e.defaultSelected).map(e=>e.value);this.applyNativeValues(t)};let s=typeof t=="string"?document.querySelector(t):t;if(!s)throw new Error(`ForgeSelect: target element not found: ${String(t)}`);this.el=s;let i=s instanceof HTMLSelectElement?s:null;if(this.nativeSelect=i,this.nativeForm=i?.form??null,this.originalDisplay=s.style.display,this.originalDisabled=i?.disabled??!1,this.opts={placeholder:e.placeholder??"",searchable:e.searchable??!0,multiple:e.multiple??i?.multiple??!1,clearable:e.clearable??!1,allowCreate:e.allowCreate??!1,sortable:e.sortable??!1,closeOnSelect:e.closeOnSelect??!1,maxSelections:e.maxSelections==null||!Number.isFinite(e.maxSelections)?void 0:Math.max(0,Math.floor(e.maxSelections)),theme:e.theme??"default",disabled:e.disabled??i?.disabled??!1,required:e.required??i?.required??!1,data:e.data,ajax:e.ajax,templateResult:e.templateResult,templateSelection:e.templateSelection,filterOption:e.filterOption,minSearchLength:Math.max(0,Math.floor(e.minSearchLength??0)),minResultsForSearch:Math.max(0,Math.floor(e.minResultsForSearch??0)),isOptionDisabled:e.isOptionDisabled,virtualScroll:e.virtualScroll,itemHeight:e.itemHeight??G,language:e.language??"en",plugins:e.plugins??[],openOnFocus:e.openOnFocus??!1,dropdownParent:e.dropdownParent},this.strings=_(this.opts.language),this.plugins=this.opts.plugins,i&&(i.required=this.opts.required),this.data=this.opts.data??(i?H(i):[]),i&&!this.opts.data){let r=Array.from(i.options),n=i.multiple||i.selectedIndex>0||r.some(a=>a.defaultSelected);for(let a of r)n&&a.selected&&this.selectValue(a.value,!1)}this.buildDom(),this.renderValue(),this.opts.disabled&&this.disable(),i?.addEventListener("change",this.onNativeChange),i?.addEventListener("invalid",this.onNativeInvalid),this.nativeForm?.addEventListener("reset",this.onFormReset);for(let r of this.plugins)r.onInit?.(this)}applyNativeValues(t){this.selected=[];for(let e of this.opts.multiple?t:t.slice(0,1))this.selectValue(e,!1);this.renderValue(),this.isOpen&&this.renderList(),this.emitter.emit("change",this.getValue())}open(){if(!(this.isOpen||this.isDisabled||this.destroyed)){this.isOpen=!0,this.dropdown.hidden=!1,this.root.classList.add("forge-select--open"),this.control.setAttribute("aria-expanded","true"),document.addEventListener("mousedown",this.onDocumentMouseDown),this.opts.ajax&&!this.remoteLoaded&&this.scheduleRemoteLoad(this.query,0),this.renderList(),this.positionDropdown(),window.addEventListener("resize",this.onWindowResize),document.addEventListener("scroll",this.onAncestorScroll,!0),this.searchInput&&!this.searchInput.hidden&&this.searchInput.focus(),this.emitter.emit("open");for(let t of this.plugins)t.onOpen?.(this)}}close(){if(this.isOpen){this.isOpen=!1,this.dropdown.hidden=!0,this.root.classList.remove("forge-select--open"),this.root.classList.remove("forge-select--drop-up"),this.control.setAttribute("aria-expanded","false"),document.removeEventListener("mousedown",this.onDocumentMouseDown),window.removeEventListener("resize",this.onWindowResize),document.removeEventListener("scroll",this.onAncestorScroll,!0),this.highlightedIndex=-1,this.searchInput&&(this.searchInput.value="",this.query=""),this.emitter.emit("close");for(let t of this.plugins)t.onClose?.(this)}}positionDropdown(){let t=this.control.getBoundingClientRect(),e=D(t,this.dropdown.offsetHeight,window.innerHeight);this.root.classList.toggle("forge-select--drop-up",e.dropUp),this.portalHost&&(this.portalHost.style.top=`${e.top}px`,this.portalHost.style.left=`${t.left}px`,this.portalHost.style.width=`${t.width}px`)}destroy(){if(!this.destroyed){this.close();for(let t of this.plugins)t.onDestroy?.(this);this.destroyed=!0,this.ajaxTimer&&clearTimeout(this.ajaxTimer),this.ajaxController?.abort(),this.nativeSelect?.removeEventListener("change",this.onNativeChange),this.nativeSelect?.removeEventListener("invalid",this.onNativeInvalid),this.nativeForm?.removeEventListener("reset",this.onFormReset),this.rowContentCache.clear(),this.portalHost?.remove(),this.root.remove(),this.el.style.display=this.originalDisplay,this.nativeSelect&&(this.nativeSelect.disabled=this.originalDisabled),this.emitter.clear()}}getValue(){return this.opts.multiple?[...this.selected]:this.selected[0]??null}setValue(t,e={}){let s=t==null?[]:Array.isArray(t)?t:[t],i=this.opts.multiple?s:s.slice(0,1);if(!V(i,this.selected)){this.selected=[];for(let r of i)this.selectValue(r,!1);this.afterSelectionChange(e.emitChange??!0)}}setData(t){this.ajaxTimer&&(clearTimeout(this.ajaxTimer),this.ajaxTimer=null),this.ajaxController?.abort(),this.ajaxController=null,this.ajaxRequestId+=1,this.loading=!1,this.loadingMore=!1,this.loadError=null,this.remoteLoaded=!0,this.page=0,this.hasMore=!1,this.data=t,this.opts.data=t,this.updateSearchVisibility(),this.rowContentCache.clear(),this.highlightedIndex=-1,this.isOpen&&this.renderList()}selectAll(){if(this.opts.multiple){this.selected=[];for(let t of this.allSelectableValues()){let e=this.findOption(t);e&&this.canSelectOption(e)&&this.selectValue(t,!1)}this.afterSelectionChange()}}clearAll(){this.clearSelection()}enable(){this.isDisabled=!1,this.root.classList.remove("forge-select--disabled"),this.control.tabIndex=0,this.control.setAttribute("aria-disabled","false"),this.nativeSelect&&(this.nativeSelect.disabled=!1)}disable(){this.close(),this.isDisabled=!0,this.root.classList.add("forge-select--disabled"),this.control.tabIndex=-1,this.control.setAttribute("aria-disabled","true"),this.nativeSelect&&(this.nativeSelect.disabled=!0)}on(t,e){this.emitter.on(t,e)}off(t,e){this.emitter.off(t,e)}applyAccessibleName(){let t=this.el.getAttribute("aria-labelledby"),e=this.el.getAttribute("aria-label");if(t)this.control.setAttribute("aria-labelledby",t);else if(e)this.control.setAttribute("aria-label",e);else if(this.el.id){let s=Array.from(document.getElementsByTagName("label")).find(i=>i.htmlFor===this.el.id);s&&(s.id||(s.id=`${this.uid}-label`),this.control.setAttribute("aria-labelledby",s.id))}}shouldShowSearch(){return this.opts.searchable&&(this.opts.ajax!=null||I(this.data).size>=this.opts.minResultsForSearch)}updateSearchVisibility(){this.searchInput&&(this.searchInput.hidden=!this.shouldShowSearch(),this.searchInput.hidden&&(this.searchInput.value="",this.query=""))}buildDom(){let t=typeof this.opts.dropdownParent=="string"?document.querySelector(this.opts.dropdownParent):this.opts.dropdownParent;if(this.opts.dropdownParent&&!t)throw new Error(`ForgeSelect: dropdown parent not found: ${String(this.opts.dropdownParent)}`);this.root=document.createElement("div"),this.root.className="forge-select",this.root.dataset.theme=this.opts.theme,this.root.style.setProperty("--fs-item-height",`${this.opts.itemHeight}px`),this.opts.sortable&&this.opts.multiple&&this.root.classList.add("forge-select--sortable"),this.control=document.createElement("div"),this.control.className="forge-select__control",this.control.setAttribute("role","combobox"),this.control.setAttribute("aria-haspopup","listbox"),this.control.setAttribute("aria-expanded","false"),this.control.setAttribute("aria-controls",`${this.uid}-list`),this.opts.required&&this.control.setAttribute("aria-required","true"),this.control.tabIndex=0,this.applyAccessibleName(),this.valueEl=document.createElement("div"),this.valueEl.className="forge-select__value",this.clearBtn=document.createElement("button"),this.clearBtn.type="button",this.clearBtn.className="forge-select__clear",this.clearBtn.setAttribute("aria-label",this.strings.clearSelection),this.clearBtn.textContent="\xD7",this.clearBtn.hidden=!0;let e=document.createElement("span");e.className="forge-select__arrow",e.setAttribute("aria-hidden","true"),this.control.append(this.valueEl,this.clearBtn,e),this.dropdown=document.createElement("div"),this.dropdown.className="forge-select__dropdown",this.dropdown.hidden=!0,this.opts.searchable&&(this.searchInput=document.createElement("input"),this.searchInput.type="search",this.searchInput.className="forge-select__search",this.searchInput.setAttribute("aria-label",this.strings.search),this.searchInput.setAttribute("aria-autocomplete","list"),this.searchInput.setAttribute("aria-controls",`${this.uid}-list`),this.searchInput.hidden=!this.shouldShowSearch(),this.dropdown.append(this.searchInput)),this.list=document.createElement("ul"),this.list.className="forge-select__list",this.list.id=`${this.uid}-list`,this.list.setAttribute("role","listbox"),this.opts.multiple&&this.list.setAttribute("aria-multiselectable","true"),this.dropdown.append(this.list),this.liveRegion=document.createElement("div"),this.liveRegion.className="forge-select__sr-only",this.liveRegion.setAttribute("role","status"),this.liveRegion.setAttribute("aria-live","polite"),this.root.append(this.control,this.liveRegion),t||this.root.append(this.dropdown),this.el.style.display="none",this.el.insertAdjacentElement("afterend",this.root),t&&(this.portalHost=document.createElement("div"),this.portalHost.className="forge-select forge-select--portal-host",this.portalHost.dataset.theme=this.opts.theme,this.portalHost.style.setProperty("--fs-item-height",`${this.opts.itemHeight}px`),this.portalHost.append(this.dropdown),t.append(this.portalHost)),this.bindEvents()}bindEvents(){this.control.addEventListener("click",t=>{if(t.target!==this.clearBtn){if(this.suppressNextTagClick){this.suppressNextTagClick=!1;return}this.isDisabled||(this.isOpen?this.close():this.open())}}),this.control.addEventListener("keydown",t=>this.handleKeydown(t)),this.control.addEventListener("mousedown",()=>{this.pointerDownOnControl=!0}),this.control.addEventListener("focus",()=>{this.opts.openOnFocus&&!this.pointerDownOnControl&&!this.isOpen&&!this.isDisabled&&this.open(),this.pointerDownOnControl=!1}),this.clearBtn.addEventListener("click",t=>{t.stopPropagation(),this.clearSelection()}),this.searchInput&&(this.searchInput.addEventListener("input",()=>{this.query=this.searchInput.value,this.highlightedIndex=-1,this.list.scrollTop=0,this.emitter.emit("search",this.query);let t=this.query.trim(),e=t!==""&&t.length<this.opts.minSearchLength;this.opts.ajax&&!e?this.scheduleRemoteLoad(this.query,this.opts.ajax.debounce??250):(e&&(this.ajaxTimer&&(clearTimeout(this.ajaxTimer),this.ajaxTimer=null),this.ajaxController?.abort(),this.loading=!1),this.renderList())}),this.searchInput.addEventListener("keydown",t=>this.handleKeydown(t)),this.searchInput.addEventListener("paste",t=>{if(!this.opts.multiple||!this.opts.allowCreate)return;let s=(t.clipboardData?.getData("text")??"").split(/[,\n]+/).map(r=>r.trim()).filter(Boolean);if(s.length<2)return;t.preventDefault();let i=[];for(let r of s){let n=this.createTag(r);n&&i.push(n)}if(i.length!==0){this.searchInput.value="",this.query="",this.afterSelectionChange();for(let r of i)r.created&&this.emitter.emit("create",r.option),this.emitter.emit("select",r.option);this.opts.closeOnSelect?this.close():this.renderList()}})),this.list.addEventListener("click",t=>{let e=t.target,s=e.closest("[data-twisty]");if(s){let n=s.dataset.twisty;this.expandedValues.has(n)?this.expandedValues.delete(n):this.expandedValues.add(n),this.renderList();return}let i=e.closest("li[data-nav-index]");if(!i){let n=e.closest("li[data-option-value]"),a=n?this.findOption(n.dataset.optionValue):void 0;a&&this.hasReachedMaximum()&&!this.selected.includes(a.value)&&this.announceMaximum(a);return}let r=Number(i.dataset.navIndex);this.activateNavItem(r)}),this.list.addEventListener("scroll",()=>{this.usesVirtualScroll()&&this.renderRows(),this.maybeLoadNextPage()})}handleKeydown(t){if(!this.isDisabled)switch(t.key){case"Enter":t.preventDefault(),this.isOpen?this.highlightedIndex>=0&&this.activateNavItem(this.highlightedIndex):this.open();break;case" ":t.target===this.control&&(t.preventDefault(),this.isOpen||this.open());break;case"ArrowDown":t.preventDefault(),this.isOpen?this.moveHighlight(1):this.open();break;case"ArrowUp":t.preventDefault(),this.isOpen&&this.moveHighlight(-1);break;case"Escape":this.isOpen&&(t.preventDefault(),this.close(),this.control.focus());break;case"ArrowRight":this.isOpen&&this.navigateTree("right")&&t.preventDefault();break;case"ArrowLeft":this.isOpen&&this.navigateTree("left")&&t.preventDefault();break;case"Tab":this.close();break}}canSelectOption(t){if(this.opts.maxSelections==null)return!0;let e=[...this.selected];e.includes(t.value)||e.push(t.value);for(let s of v(t,this.isOptionDisabled))e.includes(s)||e.push(s);return L(this.data,e,this.isOptionDisabled),e.length<=this.opts.maxSelections}hasReachedMaximum(){return this.opts.maxSelections!=null&&this.selected.length>=this.opts.maxSelections}announceMaximum(t){let e=this.opts.maxSelections;e!=null&&(this.liveRegion.textContent=u(this.strings.maximumSelected,{count:String(e)}),this.emitter.emit("maximum",{limit:e,option:t}))}selectValue(t,e){if(this.selected.includes(t))return;let s=this.findOption(t)??this.selectedOptions.get(t)??{value:t,label:t};if(this.selectedOptions.set(t,s),this.opts.multiple){this.selected.push(t);for(let i of v(s,this.isOptionDisabled))this.selected.includes(i)||this.selected.push(i);this.syncTreeAncestors()}else this.selected=[t];e&&(this.afterSelectionChange(),this.emitter.emit("select",s))}deselectValue(t,e){let s=this.selected.indexOf(t);if(s===-1)return;let i=this.findOption(t)??this.selectedOptions.get(t);if(this.selected.splice(s,1),this.opts.multiple){if(i)for(let r of v(i,this.isOptionDisabled)){let n=this.selected.indexOf(r);n!==-1&&this.selected.splice(n,1)}this.syncTreeAncestors()}e&&(this.afterSelectionChange(),this.emitter.emit("unselect",i??{value:t,label:t}))}syncTreeAncestors(){L(this.data,this.selected,this.isOptionDisabled)}clearSelection(){this.selected.length!==0&&(this.selected=[],this.emitter.emit("clear"),this.afterSelectionChange())}allSelectableValues(){let t=[],e=s=>{this.isOptionDisabled(s)||t.push(s.value),s.children?.forEach(e)};for(let s of this.data)(p(s)?s.options:[s]).forEach(e);return t}afterSelectionChange(t=!0){this.renderValue(),this.syncNativeSelect(t),(!this.opts.required||this.selected.length>0)&&(this.control.classList.remove("forge-select__control--invalid"),this.control.removeAttribute("aria-invalid")),this.isOpen&&this.renderList(),t&&this.emitter.emit("change",this.getValue())}syncNativeSelect(t=!0){if(!(this.el instanceof HTMLSelectElement))return;let e=new Set;for(let s of Array.from(this.el.options))e.add(s.value),s.selected=this.selected.includes(s.value);for(let s of this.selected){if(e.has(s))continue;let i=document.createElement("option");i.value=s,i.textContent=this.selectedOptions.get(s)?.label??s,i.selected=!0,this.el.append(i)}if(this.opts.sortable&&this.opts.multiple)for(let s of this.selected){let i=Array.from(this.el.options).find(r=>r.value===s);i&&this.el.append(i)}if(t){this.syncingNative=!0;try{this.el.dispatchEvent(new Event("change",{bubbles:!0}))}finally{this.syncingNative=!1}}}findOption(t){return N(this.data,t)}findOptionByLabel(t){let e=t.toLowerCase(),s=i=>{for(let r of i){if(r.label.toLowerCase()===e)return r;let n=r.children?s(r.children):void 0;if(n)return n}};for(let i of this.data){let r=s(p(i)?i.options:[i]);if(r)return r}}createTag(t){let e=t.trim();if(!e)return;let s=this.findOptionByLabel(e);if(s){if(this.selected.includes(s.value))return;if(this.opts.multiple&&!this.canSelectOption(s)){this.announceMaximum(s);return}return this.selectValue(s.value,!1),{option:s,created:!1}}let i={value:e,label:e};if(this.opts.multiple&&!this.canSelectOption(i)){this.announceMaximum(i);return}return this.data.push(i),this.selectValue(i.value,!1),{option:i,created:!0}}createFromQuery(){let t=this.query.trim();if(!t)return;let e=this.createTag(t);e&&(this.searchInput&&(this.searchInput.value="",this.query=""),this.afterSelectionChange(),e.created&&this.emitter.emit("create",e.option),this.emitter.emit("select",e.option),(!this.opts.multiple||this.opts.closeOnSelect)&&this.close())}activateNavItem(t){let e=this.navItems[t];if(!e)return;if(e.kind==="create"){this.createFromQuery();return}let{value:s}=e.option;if(this.opts.multiple){let i=!1;this.selected.includes(s)?(this.deselectValue(s,!0),i=!0):this.canSelectOption(e.option)?(this.selectValue(s,!0),i=!0):this.announceMaximum(e.option),i&&this.opts.closeOnSelect&&this.close()}else this.selectValue(s,!0),this.close(),this.control.focus()}renderValue(){this.valueEl.textContent="";let t=this.selected.length>0;if(this.clearBtn.hidden=!(this.opts.clearable&&t),!t){let e=document.createElement("span");e.className="forge-select__placeholder",e.textContent=this.opts.placeholder,this.valueEl.append(e);return}if(this.opts.multiple)for(let e of this.selected){let s=this.selectedOptions.get(e)??{value:e,label:e},i=document.createElement("span");i.className="forge-select__tag";let r=document.createElement("span");r.className="forge-select__tag-label",x(r,s,this.opts.templateSelection,"inline");let n=document.createElement("button");n.type="button",n.className="forge-select__tag-remove",n.setAttribute("aria-label",u(this.strings.removeItem,{label:s.label})),n.textContent="\xD7",n.addEventListener("click",a=>{a.stopPropagation(),this.isDisabled||this.deselectValue(e,!0)}),i.append(r,n),this.opts.sortable&&(i.dataset.value=e,i.tabIndex=0,i.setAttribute("aria-roledescription","draggable item"),i.setAttribute("aria-label",u(this.strings.reorderHint,{label:s.label})),i.addEventListener("keydown",a=>this.handleTagKeydown(a,e)),this.bindTagDrag(i,e)),this.valueEl.append(i)}else{let e=this.selectedOptions.get(this.selected[0])??{value:this.selected[0],label:this.selected[0]},s=document.createElement("span");s.className="forge-select__single-value",x(s,e,this.opts.templateSelection,"inline"),this.valueEl.append(s)}}bindTagDrag(t,e){let i=0,r=!1,n=[],a=h=>{if(!r){if(Math.abs(h.clientX-i)<4)return;r=!0,n=[...this.selected],typeof this.valueEl.setPointerCapture=="function"&&this.valueEl.setPointerCapture(h.pointerId),t.classList.add("forge-select__tag--dragging")}h.preventDefault();let m=n.indexOf(e),d=Array.from(this.valueEl.querySelectorAll(".forge-select__tag"));for(let c of d){if(c===t)continue;let g=c.dataset.value;if(!g)continue;let S=n.indexOf(g);if(S===-1)continue;let C=c.getBoundingClientRect(),A=C.left+C.width/2,T=m<S;if(T?h.clientX>A:h.clientX<A){n.splice(m,1),n.splice(S,0,e),T?this.valueEl.insertBefore(t,c.nextSibling):this.valueEl.insertBefore(t,c);break}}},l=h=>{this.valueEl.removeEventListener("pointermove",a),this.valueEl.removeEventListener("pointerup",l),this.valueEl.removeEventListener("pointercancel",l),r&&(typeof this.valueEl.releasePointerCapture=="function"&&this.valueEl.releasePointerCapture(h.pointerId),t.classList.remove("forge-select__tag--dragging"),this.selected=n,this.suppressNextTagClick=!0,this.afterSelectionChange(),this.emitter.emit("reorder",[...this.selected]))};t.addEventListener("pointerdown",h=>{this.isDisabled||h.button!==0||h.target.closest(".forge-select__tag-remove")||(i=h.clientX,r=!1,this.valueEl.addEventListener("pointermove",a),this.valueEl.addEventListener("pointerup",l),this.valueEl.addEventListener("pointercancel",l))})}handleTagKeydown(t,e){if(!t.altKey||t.key!=="ArrowLeft"&&t.key!=="ArrowRight")return;let s=this.selected.indexOf(e),i=t.key==="ArrowLeft"?s-1:s+1;if(s===-1||i<0||i>=this.selected.length)return;t.preventDefault(),t.stopPropagation();let r=[...this.selected];[r[s],r[i]]=[r[i],r[s]],this.selected=r,this.afterSelectionChange(),this.emitter.emit("reorder",[...this.selected]),this.focusTagByValue(e)}focusTagByValue(t){for(let e of Array.from(this.valueEl.querySelectorAll(".forge-select__tag")))if(e.dataset.value===t){e.focus();return}}buildRows(){this.rows=[],this.navItems=[];let t=this.query.trim(),e=t.toLowerCase(),s=n=>e===""||(this.opts.filterOption?this.opts.filterOption(n,t):n.label.toLowerCase().includes(e)||(n.description?.toLowerCase().includes(e)??!1)),i=n=>e===""||s(n)||(n.children??[]).some(i),r=(n,a,l)=>{let h=-1;this.isOptionDisabled(n)||this.hasReachedMaximum()&&!this.selected.includes(n.value)||(h=this.navItems.length,this.navItems.push({kind:"option",option:n,parentValue:l}));let d=!!n.children&&n.children.length>0;if(this.rows.push({kind:"option",option:n,navIndex:h,depth:a,hasChildren:d}),d&&(e!==""||this.expandedValues.has(n.value)))for(let g of n.children)i(g)&&r(g,a+1,n.value)};if(t!==""&&t.length<this.opts.minSearchLength){this.rows.push({kind:"min-length"});return}if(this.loading){this.rows.push({kind:"loading"});return}if(this.loadError){this.rows.push({kind:"error"});return}for(let n of this.data)if(p(n)){let a=n.options.filter(i);if(a.length===0)continue;this.rows.push({kind:"group",label:n.label}),a.forEach(l=>r(l,0))}else i(n)&&r(n,0);if(this.opts.allowCreate&&e!==""&&!this.hasExactMatch(e)){let n=this.navItems.length;this.navItems.push({kind:"create"}),this.rows.push({kind:"create",navIndex:n})}this.rows.length===0?this.rows.push({kind:"empty"}):this.loadingMore&&this.rows.push({kind:"loading-more"})}hasExactMatch(t){return!!this.findOptionByLabel(t)}usesVirtualScroll(){return this.opts.virtualScroll!==!1&&this.rows.length>z}renderList(){this.buildRows(),this.renderRows(),this.announceStatus()}announceStatus(){let t=this.rows[0],e=this.hasReachedMaximum()?u(this.strings.maximumSelected,{count:String(this.opts.maxSelections)}):t?.kind==="loading"?this.strings.loading:t?.kind==="error"?this.strings.errorLoading:t?.kind==="empty"?this.strings.noResults:t?.kind==="min-length"?u(this.strings.minSearchLength,{count:String(this.opts.minSearchLength)}):"";this.liveRegion.textContent!==e&&(this.liveRegion.textContent=e)}renderRows(){let t=this.list.scrollTop,e=this.list.clientHeight,s=this.usesVirtualScroll();this.list.textContent="";let i=this.opts.itemHeight,r=0,n=this.rows.length;if(s){let a=e||i*8;r=Math.max(0,Math.floor(t/i)-q),n=Math.min(this.rows.length,r+Math.ceil(a/i)+q*2);let l=document.createElement("li");l.className="forge-select__spacer",l.setAttribute("aria-hidden","true"),l.style.height=`${r*i}px`,this.list.append(l)}for(let a=r;a<n;a++)this.list.append(this.renderRow(this.rows[a]));if(s){let a=document.createElement("li");a.className="forge-select__spacer",a.setAttribute("aria-hidden","true"),a.style.height=`${(this.rows.length-n)*i}px`,this.list.append(a),this.list.scrollTop!==t&&(this.list.scrollTop=t)}this.updateActiveDescendant()}renderRow(t){let e=document.createElement("li");switch(t.kind){case"group":e.className="forge-select__group-label",e.setAttribute("role","presentation"),e.textContent=t.label;break;case"empty":e.className="forge-select__empty",e.setAttribute("role","option"),e.setAttribute("aria-disabled","true"),e.setAttribute("aria-selected","false"),e.textContent=this.strings.noResults;break;case"min-length":e.className="forge-select__min-length",e.setAttribute("role","option"),e.setAttribute("aria-disabled","true"),e.setAttribute("aria-selected","false"),e.textContent=u(this.strings.minSearchLength,{count:String(this.opts.minSearchLength)});break;case"error":e.className="forge-select__error",e.setAttribute("role","option"),e.setAttribute("aria-disabled","true"),e.setAttribute("aria-selected","false"),e.textContent=this.strings.errorLoading;break;case"loading":e.className="forge-select__loading",e.setAttribute("role","option"),e.setAttribute("aria-disabled","true"),e.setAttribute("aria-selected","false"),e.textContent=this.strings.loading;break;case"loading-more":e.className="forge-select__loading-more",e.setAttribute("aria-hidden","true"),e.textContent=this.strings.loadingMore;break;case"create":e.className="forge-select__option forge-select__option--create",e.setAttribute("role","option"),e.id=`${this.uid}-nav-${t.navIndex}`,e.dataset.navIndex=String(t.navIndex),e.textContent=u(this.strings.createOption,{query:this.query.trim()}),t.navIndex===this.highlightedIndex&&e.classList.add("forge-select__option--highlighted");break;case"option":{e.className="forge-select__option",e.dataset.optionValue=t.option.value,t.option.className&&e.classList.add(...t.option.className.trim().split(/\s+/).filter(Boolean)),e.setAttribute("role","option");let s=this.selected.includes(t.option.value);if(e.setAttribute("aria-selected",String(s)),s&&e.classList.add("forge-select__option--selected"),this.opts.multiple&&t.hasChildren&&y(t.option,this.selected,this.isOptionDisabled)==="some"&&e.classList.add("forge-select__option--indeterminate"),t.depth>0&&(e.style.paddingLeft=`calc(12px + ${t.depth} * var(--fs-tree-indent, 18px))`),this.isOptionDisabled(t.option)||this.hasReachedMaximum()&&!this.selected.includes(t.option.value)?(e.classList.add("forge-select__option--disabled"),e.setAttribute("aria-disabled","true")):(e.id=`${this.uid}-nav-${t.navIndex}`,e.dataset.navIndex=String(t.navIndex),t.navIndex===this.highlightedIndex&&e.classList.add("forge-select__option--highlighted")),t.hasChildren){let i=this.query!==""||this.expandedValues.has(t.option.value);e.setAttribute("aria-expanded",String(i));let r=document.createElement("span");r.className="forge-select__twisty",r.dataset.twisty=t.option.value,r.setAttribute("aria-hidden","true"),r.textContent=i?"\u25BC":"\u25B6",e.append(r)}e.append(this.optionContent(t.option));break}}return e}optionContent(t){let e=this.rowContentCache.get(t.value);if(!e){let s=document.createElement("span");if(s.className="forge-select__option-content",x(s,t,this.opts.templateResult),this.rowContentCache.size>=K){let i=this.rowContentCache.keys().next().value;this.rowContentCache.delete(i)}this.rowContentCache.set(t.value,s),e=s}return e.cloneNode(!0)}moveHighlight(t){if(this.navItems.length===0)return;let e=this.highlightedIndex===-1?t>0?0:this.navItems.length-1:(this.highlightedIndex+t+this.navItems.length)%this.navItems.length;this.focusNavIndex(e)}focusNavIndex(t){if(this.highlightedIndex=t,this.usesVirtualScroll()){let e=this.rows.findIndex(s=>(s.kind==="option"||s.kind==="create")&&s.navIndex===t);if(e>=0){let s=this.opts.itemHeight,i=e*s,r=this.list.clientHeight||s*8,n=this.list.scrollTop;i<n?n=i:i+s>n+r&&(n=i+s-r),n!==this.list.scrollTop&&(this.list.scrollTop=n)}this.renderRows()}else this.renderRows(),this.list.querySelector(".forge-select__option--highlighted")?.scrollIntoView?.({block:"nearest"})}navigateTree(t){let e=this.navItems[this.highlightedIndex];if(!e||e.kind!=="option")return!1;let{option:s,parentValue:i}=e,r=!!s.children?.length,n=this.query!==""||this.expandedValues.has(s.value);if(t==="right"){if(r&&!n)return this.expandedValues.add(s.value),this.renderList(),!0;if(r){let a=this.navItems.findIndex(l=>l.kind==="option"&&l.parentValue===s.value);if(a>=0)return this.focusNavIndex(a),!0}return!1}if(r&&n&&this.query==="")return this.expandedValues.delete(s.value),this.renderList(),!0;if(i){let a=this.navItems.findIndex(l=>l.kind==="option"&&l.option.value===i);if(a>=0)return this.focusNavIndex(a),!0}return!1}updateActiveDescendant(){let t=this.searchInput??this.control;this.highlightedIndex>=0?t.setAttribute("aria-activedescendant",`${this.uid}-nav-${this.highlightedIndex}`):t.removeAttribute("aria-activedescendant")}scheduleRemoteLoad(t,e){this.ajaxTimer&&clearTimeout(this.ajaxTimer);let s=++this.ajaxRequestId;this.ajaxController?.abort(),this.ajaxController=null,this.page=0,this.hasMore=!0,this.loading=!0,this.loadingMore=!1,this.loadError=null,this.renderList(),this.ajaxTimer=setTimeout(()=>{this.ajaxTimer=null,this.loadRemote(t,{requestId:s})},e)}maybeLoadNextPage(){if(!this.opts.ajax?.pagination||!this.hasMore||this.loading||this.loadingMore)return;let{scrollHeight:e,scrollTop:s,clientHeight:i}=this.list,r=this.opts.itemHeight*2;e-s-i>=r||(this.loadingMore=!0,this.renderList(),this.loadRemote(this.query,{append:!0}))}async loadRemote(t,{append:e=!1,requestId:s}={}){let i=this.opts.ajax,r=s??++this.ajaxRequestId;if(r!==this.ajaxRequestId)return;this.ajaxController?.abort();let n=new AbortController;this.ajaxController=n;let a=e?this.page+1:0;try{let l;if(i.request)l=await i.request(t,a,n.signal);else{let d=R(i,t,a),c=await fetch(d,{signal:n.signal});if(c.ok===!1)throw new Error(`ForgeSelect: remote request failed with HTTP ${c.status}`);l=await c.json()}if(r!==this.ajaxRequestId||this.destroyed)return;let{options:h,hasMore:m}=k(i,l);if(e){let d=I(this.data);this.data=[...this.data,...h.filter(c=>!d.has(c.value))]}else this.data=h,this.rowContentCache.clear();this.page=a,this.hasMore=m,this.remoteLoaded=!0,this.loadError=null}catch(l){if(r!==this.ajaxRequestId||this.destroyed||n.signal.aborted)return;let h=l instanceof Error?l:new Error(String(l));e||(this.data=[],this.rowContentCache.clear()),this.hasMore=!1,this.loadError=h,this.emitter.emit("error",h)}finally{r===this.ajaxRequestId&&!this.destroyed&&(this.ajaxController=null,this.loading=!1,this.loadingMore=!1,this.isOpen&&this.renderList())}}};return U(W);})();
901
2
  //# sourceMappingURL=index.global.js.map