forge-select 0.1.0 → 0.2.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/README.md +13 -10
- package/dist/index.cjs +174 -24
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +37 -2
- package/dist/index.d.ts +37 -2
- package/dist/index.global.js +174 -24
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +174 -24
- package/dist/index.js.map +1 -1
- package/package.json +4 -1
- package/styles/forge-select.css +17 -1
package/dist/index.js
CHANGED
|
@@ -29,6 +29,7 @@ var locales = {
|
|
|
29
29
|
en: {
|
|
30
30
|
noResults: "No results found",
|
|
31
31
|
loading: "Loading\u2026",
|
|
32
|
+
loadingMore: "Loading more\u2026",
|
|
32
33
|
createOption: 'Create "{query}"',
|
|
33
34
|
clearSelection: "Clear selection",
|
|
34
35
|
removeItem: "Remove {label}",
|
|
@@ -37,6 +38,7 @@ var locales = {
|
|
|
37
38
|
vi: {
|
|
38
39
|
noResults: "Kh\xF4ng t\xECm th\u1EA5y k\u1EBFt qu\u1EA3",
|
|
39
40
|
loading: "\u0110ang t\u1EA3i\u2026",
|
|
41
|
+
loadingMore: "\u0110ang t\u1EA3i th\xEAm\u2026",
|
|
40
42
|
createOption: 'T\u1EA1o "{query}"',
|
|
41
43
|
clearSelection: "X\xF3a l\u1EF1a ch\u1ECDn",
|
|
42
44
|
removeItem: "X\xF3a {label}",
|
|
@@ -62,6 +64,23 @@ var uidCounter = 0;
|
|
|
62
64
|
function isGroup(item) {
|
|
63
65
|
return item.options !== void 0;
|
|
64
66
|
}
|
|
67
|
+
function collectDescendantValues(option) {
|
|
68
|
+
if (!option.children) return [];
|
|
69
|
+
const values = [];
|
|
70
|
+
for (const child of option.children) {
|
|
71
|
+
values.push(child.value, ...collectDescendantValues(child));
|
|
72
|
+
}
|
|
73
|
+
return values;
|
|
74
|
+
}
|
|
75
|
+
function computeCheckState(option, selected) {
|
|
76
|
+
if (!option.children || option.children.length === 0) {
|
|
77
|
+
return selected.includes(option.value) ? "all" : "none";
|
|
78
|
+
}
|
|
79
|
+
const states = option.children.map((child) => computeCheckState(child, selected));
|
|
80
|
+
if (states.every((s) => s === "all")) return "all";
|
|
81
|
+
if (states.every((s) => s === "none")) return "none";
|
|
82
|
+
return "some";
|
|
83
|
+
}
|
|
65
84
|
var ForgeSelect = class {
|
|
66
85
|
constructor(target, options = {}) {
|
|
67
86
|
this.selected = [];
|
|
@@ -77,7 +96,11 @@ var ForgeSelect = class {
|
|
|
77
96
|
this.navItems = [];
|
|
78
97
|
this.highlightedIndex = -1;
|
|
79
98
|
this.rowContentCache = /* @__PURE__ */ new Map();
|
|
99
|
+
this.expandedValues = /* @__PURE__ */ new Set();
|
|
80
100
|
this.loading = false;
|
|
101
|
+
this.loadingMore = false;
|
|
102
|
+
this.page = 0;
|
|
103
|
+
this.hasMore = true;
|
|
81
104
|
this.ajaxTimer = null;
|
|
82
105
|
this.ajaxRequestId = 0;
|
|
83
106
|
this.remoteLoaded = false;
|
|
@@ -267,13 +290,23 @@ var ForgeSelect = class {
|
|
|
267
290
|
this.searchInput.addEventListener("keydown", (event) => this.handleKeydown(event));
|
|
268
291
|
}
|
|
269
292
|
this.list.addEventListener("click", (event) => {
|
|
270
|
-
const
|
|
293
|
+
const target = event.target;
|
|
294
|
+
const twisty = target.closest("[data-twisty]");
|
|
295
|
+
if (twisty) {
|
|
296
|
+
const value = twisty.dataset.twisty;
|
|
297
|
+
if (this.expandedValues.has(value)) this.expandedValues.delete(value);
|
|
298
|
+
else this.expandedValues.add(value);
|
|
299
|
+
this.renderList();
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
const li = target.closest("li[data-nav-index]");
|
|
271
303
|
if (!li) return;
|
|
272
304
|
const navIndex = Number(li.dataset.navIndex);
|
|
273
305
|
this.activateNavItem(navIndex);
|
|
274
306
|
});
|
|
275
307
|
this.list.addEventListener("scroll", () => {
|
|
276
308
|
if (this.usesVirtualScroll()) this.renderRows();
|
|
309
|
+
this.maybeLoadNextPage();
|
|
277
310
|
});
|
|
278
311
|
}
|
|
279
312
|
handleKeydown(event) {
|
|
@@ -316,16 +349,53 @@ var ForgeSelect = class {
|
|
|
316
349
|
if (this.selected.includes(value)) return;
|
|
317
350
|
const option = this.findOption(value) ?? this.selectedOptions.get(value) ?? { value, label: value };
|
|
318
351
|
this.selectedOptions.set(value, option);
|
|
319
|
-
if (this.opts.multiple)
|
|
320
|
-
|
|
352
|
+
if (this.opts.multiple) {
|
|
353
|
+
this.selected.push(value);
|
|
354
|
+
for (const v of collectDescendantValues(option)) {
|
|
355
|
+
if (!this.selected.includes(v)) this.selected.push(v);
|
|
356
|
+
}
|
|
357
|
+
this.syncTreeAncestors();
|
|
358
|
+
} else {
|
|
359
|
+
this.selected = [value];
|
|
360
|
+
}
|
|
321
361
|
if (notify) this.afterSelectionChange();
|
|
322
362
|
}
|
|
323
363
|
deselectValue(value, notify) {
|
|
324
364
|
const index = this.selected.indexOf(value);
|
|
325
365
|
if (index === -1) return;
|
|
326
366
|
this.selected.splice(index, 1);
|
|
367
|
+
if (this.opts.multiple) {
|
|
368
|
+
const option = this.findOption(value) ?? this.selectedOptions.get(value);
|
|
369
|
+
if (option) {
|
|
370
|
+
for (const v of collectDescendantValues(option)) {
|
|
371
|
+
const i = this.selected.indexOf(v);
|
|
372
|
+
if (i !== -1) this.selected.splice(i, 1);
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
this.syncTreeAncestors();
|
|
376
|
+
}
|
|
327
377
|
if (notify) this.afterSelectionChange();
|
|
328
378
|
}
|
|
379
|
+
/**
|
|
380
|
+
* Keeps every tree parent's own membership in `selected` consistent with
|
|
381
|
+
* its descendants (post-order, so parents see already-corrected children):
|
|
382
|
+
* a parent counts as selected only when `computeCheckState` says "all".
|
|
383
|
+
* No-op for data with no `children` anywhere.
|
|
384
|
+
*/
|
|
385
|
+
syncTreeAncestors() {
|
|
386
|
+
const sync = (option) => {
|
|
387
|
+
if (!option.children || option.children.length === 0) return;
|
|
388
|
+
for (const child of option.children) sync(child);
|
|
389
|
+
const state = computeCheckState(option, this.selected);
|
|
390
|
+
const index = this.selected.indexOf(option.value);
|
|
391
|
+
if (state === "all" && index === -1) this.selected.push(option.value);
|
|
392
|
+
else if (state !== "all" && index !== -1) this.selected.splice(index, 1);
|
|
393
|
+
};
|
|
394
|
+
for (const item of this.data) {
|
|
395
|
+
const options = isGroup(item) ? item.options : [item];
|
|
396
|
+
options.forEach(sync);
|
|
397
|
+
}
|
|
398
|
+
}
|
|
329
399
|
clearSelection() {
|
|
330
400
|
if (this.selected.length === 0) return;
|
|
331
401
|
this.selected = [];
|
|
@@ -356,13 +426,19 @@ var ForgeSelect = class {
|
|
|
356
426
|
this.el.dispatchEvent(new Event("change", { bubbles: true }));
|
|
357
427
|
}
|
|
358
428
|
findOption(value) {
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
if (
|
|
363
|
-
|
|
364
|
-
|
|
429
|
+
const search = (options) => {
|
|
430
|
+
for (const option of options) {
|
|
431
|
+
if (option.value === value) return option;
|
|
432
|
+
if (option.children) {
|
|
433
|
+
const found = search(option.children);
|
|
434
|
+
if (found) return found;
|
|
435
|
+
}
|
|
365
436
|
}
|
|
437
|
+
return void 0;
|
|
438
|
+
};
|
|
439
|
+
for (const item of this.data) {
|
|
440
|
+
const found = search(isGroup(item) ? item.options : [item]);
|
|
441
|
+
if (found) return found;
|
|
366
442
|
}
|
|
367
443
|
return void 0;
|
|
368
444
|
}
|
|
@@ -480,13 +556,23 @@ var ForgeSelect = class {
|
|
|
480
556
|
this.navItems = [];
|
|
481
557
|
const query = this.query.trim().toLowerCase();
|
|
482
558
|
const matches = (option) => query === "" || option.label.toLowerCase().includes(query) || (option.description?.toLowerCase().includes(query) ?? false);
|
|
483
|
-
const
|
|
559
|
+
const subtreeMatches = (option) => query === "" || matches(option) || (option.children ?? []).some(subtreeMatches);
|
|
560
|
+
const pushOption = (option, depth) => {
|
|
484
561
|
let navIndex = -1;
|
|
485
562
|
if (!option.disabled) {
|
|
486
563
|
navIndex = this.navItems.length;
|
|
487
564
|
this.navItems.push({ kind: "option", option });
|
|
488
565
|
}
|
|
489
|
-
|
|
566
|
+
const hasChildren = !!option.children && option.children.length > 0;
|
|
567
|
+
this.rows.push({ kind: "option", option, navIndex, depth, hasChildren });
|
|
568
|
+
if (hasChildren) {
|
|
569
|
+
const expanded = query !== "" || this.expandedValues.has(option.value);
|
|
570
|
+
if (expanded) {
|
|
571
|
+
for (const child of option.children) {
|
|
572
|
+
if (subtreeMatches(child)) pushOption(child, depth + 1);
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
}
|
|
490
576
|
};
|
|
491
577
|
if (this.loading) {
|
|
492
578
|
this.rows.push({ kind: "loading" });
|
|
@@ -494,12 +580,12 @@ var ForgeSelect = class {
|
|
|
494
580
|
}
|
|
495
581
|
for (const item of this.data) {
|
|
496
582
|
if (isGroup(item)) {
|
|
497
|
-
const visible = item.options.filter(
|
|
583
|
+
const visible = item.options.filter(subtreeMatches);
|
|
498
584
|
if (visible.length === 0) continue;
|
|
499
585
|
this.rows.push({ kind: "group", label: item.label });
|
|
500
|
-
visible.forEach(pushOption);
|
|
501
|
-
} else if (
|
|
502
|
-
pushOption(item);
|
|
586
|
+
visible.forEach((o) => pushOption(o, 0));
|
|
587
|
+
} else if (subtreeMatches(item)) {
|
|
588
|
+
pushOption(item, 0);
|
|
503
589
|
}
|
|
504
590
|
}
|
|
505
591
|
if (this.opts.allowCreate && query !== "" && !this.hasExactMatch(query)) {
|
|
@@ -508,11 +594,13 @@ var ForgeSelect = class {
|
|
|
508
594
|
this.rows.push({ kind: "create", navIndex });
|
|
509
595
|
}
|
|
510
596
|
if (this.rows.length === 0) this.rows.push({ kind: "empty" });
|
|
597
|
+
else if (this.loadingMore) this.rows.push({ kind: "loading-more" });
|
|
511
598
|
}
|
|
512
599
|
hasExactMatch(lowerQuery) {
|
|
600
|
+
const matchesExactly = (option) => option.label.toLowerCase() === lowerQuery || (option.children ?? []).some(matchesExactly);
|
|
513
601
|
for (const item of this.data) {
|
|
514
602
|
const options = isGroup(item) ? item.options : [item];
|
|
515
|
-
if (options.some(
|
|
603
|
+
if (options.some(matchesExactly)) return true;
|
|
516
604
|
}
|
|
517
605
|
return false;
|
|
518
606
|
}
|
|
@@ -572,6 +660,11 @@ var ForgeSelect = class {
|
|
|
572
660
|
li.className = "forge-select__loading";
|
|
573
661
|
li.textContent = this.strings.loading;
|
|
574
662
|
break;
|
|
663
|
+
case "loading-more":
|
|
664
|
+
li.className = "forge-select__loading-more";
|
|
665
|
+
li.setAttribute("aria-hidden", "true");
|
|
666
|
+
li.textContent = this.strings.loadingMore;
|
|
667
|
+
break;
|
|
575
668
|
case "create":
|
|
576
669
|
li.className = "forge-select__option forge-select__option--create";
|
|
577
670
|
li.setAttribute("role", "option");
|
|
@@ -586,6 +679,12 @@ var ForgeSelect = class {
|
|
|
586
679
|
const isSelected = this.selected.includes(row.option.value);
|
|
587
680
|
li.setAttribute("aria-selected", String(isSelected));
|
|
588
681
|
if (isSelected) li.classList.add("forge-select__option--selected");
|
|
682
|
+
if (this.opts.multiple && row.hasChildren && computeCheckState(row.option, this.selected) === "some") {
|
|
683
|
+
li.classList.add("forge-select__option--indeterminate");
|
|
684
|
+
}
|
|
685
|
+
if (row.depth > 0) {
|
|
686
|
+
li.style.paddingLeft = `calc(12px + ${row.depth} * var(--fs-tree-indent, 18px))`;
|
|
687
|
+
}
|
|
589
688
|
if (row.option.disabled) {
|
|
590
689
|
li.classList.add("forge-select__option--disabled");
|
|
591
690
|
li.setAttribute("aria-disabled", "true");
|
|
@@ -594,6 +693,14 @@ var ForgeSelect = class {
|
|
|
594
693
|
li.dataset.navIndex = String(row.navIndex);
|
|
595
694
|
if (row.navIndex === this.highlightedIndex) li.classList.add("forge-select__option--highlighted");
|
|
596
695
|
}
|
|
696
|
+
if (row.hasChildren) {
|
|
697
|
+
const twisty = document.createElement("span");
|
|
698
|
+
twisty.className = "forge-select__twisty";
|
|
699
|
+
twisty.dataset.twisty = row.option.value;
|
|
700
|
+
twisty.setAttribute("aria-hidden", "true");
|
|
701
|
+
twisty.textContent = this.expandedValues.has(row.option.value) ? "\u25BC" : "\u25B6";
|
|
702
|
+
li.append(twisty);
|
|
703
|
+
}
|
|
597
704
|
li.append(this.optionContent(row.option));
|
|
598
705
|
break;
|
|
599
706
|
}
|
|
@@ -656,30 +763,62 @@ var ForgeSelect = class {
|
|
|
656
763
|
// ---------------------------------------------------------------- remote data
|
|
657
764
|
scheduleRemoteLoad(query, delay) {
|
|
658
765
|
if (this.ajaxTimer) clearTimeout(this.ajaxTimer);
|
|
766
|
+
this.page = 0;
|
|
767
|
+
this.hasMore = true;
|
|
659
768
|
this.loading = true;
|
|
660
769
|
this.renderList();
|
|
661
770
|
this.ajaxTimer = setTimeout(() => {
|
|
662
771
|
void this.loadRemote(query);
|
|
663
772
|
}, delay);
|
|
664
773
|
}
|
|
665
|
-
|
|
774
|
+
/**
|
|
775
|
+
* Fires on every list scroll. Only acts when pagination is opted into via
|
|
776
|
+
* `ajax.pagination`; reads real scroll geometry rather than row counts so
|
|
777
|
+
* it works whether or not virtual scrolling is active for this list.
|
|
778
|
+
*/
|
|
779
|
+
maybeLoadNextPage() {
|
|
780
|
+
const ajax = this.opts.ajax;
|
|
781
|
+
if (!ajax?.pagination || !this.hasMore || this.loading || this.loadingMore) return;
|
|
782
|
+
const { scrollHeight, scrollTop, clientHeight } = this.list;
|
|
783
|
+
const threshold = this.opts.itemHeight * 2;
|
|
784
|
+
if (scrollHeight - scrollTop - clientHeight >= threshold) return;
|
|
785
|
+
this.loadingMore = true;
|
|
786
|
+
this.renderList();
|
|
787
|
+
void this.loadRemote(this.query, { append: true });
|
|
788
|
+
}
|
|
789
|
+
async loadRemote(query, { append = false } = {}) {
|
|
666
790
|
const ajax = this.opts.ajax;
|
|
667
791
|
const requestId = ++this.ajaxRequestId;
|
|
792
|
+
const page = append ? this.page + 1 : 0;
|
|
668
793
|
try {
|
|
669
|
-
const url = buildUrl(ajax, query);
|
|
794
|
+
const url = buildUrl(ajax, query, page);
|
|
670
795
|
const response = await fetch(url);
|
|
671
796
|
const json = await response.json();
|
|
672
797
|
if (requestId !== this.ajaxRequestId || this.destroyed) return;
|
|
673
|
-
|
|
674
|
-
|
|
798
|
+
const result = ajax.transform ? ajax.transform(json) : json;
|
|
799
|
+
const options = Array.isArray(result) ? result : result.options;
|
|
800
|
+
const hasMore = ajax.pagination ? Array.isArray(result) ? false : result.hasMore : false;
|
|
801
|
+
if (append) {
|
|
802
|
+
const existing = collectValues(this.data);
|
|
803
|
+
this.data = [...this.data, ...options.filter((o) => !existing.has(o.value))];
|
|
804
|
+
} else {
|
|
805
|
+
this.data = options;
|
|
806
|
+
this.rowContentCache.clear();
|
|
807
|
+
}
|
|
808
|
+
this.page = page;
|
|
809
|
+
this.hasMore = hasMore;
|
|
675
810
|
this.remoteLoaded = true;
|
|
676
811
|
} catch {
|
|
677
812
|
if (requestId !== this.ajaxRequestId || this.destroyed) return;
|
|
678
|
-
|
|
679
|
-
|
|
813
|
+
if (!append) {
|
|
814
|
+
this.data = [];
|
|
815
|
+
this.rowContentCache.clear();
|
|
816
|
+
}
|
|
817
|
+
this.hasMore = false;
|
|
680
818
|
} finally {
|
|
681
819
|
if (requestId === this.ajaxRequestId && !this.destroyed) {
|
|
682
820
|
this.loading = false;
|
|
821
|
+
this.loadingMore = false;
|
|
683
822
|
if (this.isOpen) this.renderList();
|
|
684
823
|
}
|
|
685
824
|
}
|
|
@@ -706,16 +845,27 @@ function parseOption(option) {
|
|
|
706
845
|
disabled: option.disabled || void 0
|
|
707
846
|
};
|
|
708
847
|
}
|
|
709
|
-
function buildUrl(ajax, query) {
|
|
848
|
+
function buildUrl(ajax, query, page) {
|
|
710
849
|
if (typeof ajax.url === "function") return ajax.url(query);
|
|
711
850
|
if (!ajax.params) return ajax.url;
|
|
712
851
|
const params = new URLSearchParams();
|
|
713
|
-
for (const [key, value] of Object.entries(ajax.params(query))) {
|
|
852
|
+
for (const [key, value] of Object.entries(ajax.params(query, page))) {
|
|
714
853
|
params.set(key, String(value));
|
|
715
854
|
}
|
|
716
855
|
const separator = ajax.url.includes("?") ? "&" : "?";
|
|
717
856
|
return `${ajax.url}${separator}${params.toString()}`;
|
|
718
857
|
}
|
|
858
|
+
function collectValues(items) {
|
|
859
|
+
const values = /* @__PURE__ */ new Set();
|
|
860
|
+
for (const item of items) {
|
|
861
|
+
if (isGroup(item)) {
|
|
862
|
+
for (const option of item.options) values.add(option.value);
|
|
863
|
+
} else {
|
|
864
|
+
values.add(item.value);
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
return values;
|
|
868
|
+
}
|
|
719
869
|
function arraysEqual(a, b) {
|
|
720
870
|
return a.length === b.length && a.every((value, index) => value === b[index]);
|
|
721
871
|
}
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/emitter.ts","../src/i18n.ts","../src/ForgeSelect.ts"],"sourcesContent":["export type Handler = (...args: unknown[]) => void;\n\nexport class Emitter {\n private handlers = new Map<string, Set<Handler>>();\n\n on(event: string, handler: Handler): void {\n let set = this.handlers.get(event);\n if (!set) {\n set = new Set();\n this.handlers.set(event, set);\n }\n set.add(handler);\n }\n\n off(event: string, handler: Handler): void {\n this.handlers.get(event)?.delete(handler);\n }\n\n emit(event: string, ...args: unknown[]): void {\n const set = this.handlers.get(event);\n if (!set) return;\n for (const handler of [...set]) handler(...args);\n }\n\n clear(): void {\n this.handlers.clear();\n }\n}\n","export type Strings = Record<string, string>;\n\nconst locales: Record<string, Strings> = {\n en: {\n noResults: \"No results found\",\n loading: \"Loading…\",\n createOption: 'Create \"{query}\"',\n clearSelection: \"Clear selection\",\n removeItem: \"Remove {label}\",\n search: \"Search\",\n },\n vi: {\n noResults: \"Không tìm thấy kết quả\",\n loading: \"Đang tải…\",\n createOption: 'Tạo \"{query}\"',\n clearSelection: \"Xóa lựa chọn\",\n removeItem: \"Xóa {label}\",\n search: \"Tìm kiếm\",\n },\n};\n\nexport function getStrings(language: string | Strings): Strings {\n if (typeof language === \"string\") {\n return locales[language] ?? locales.en;\n }\n return { ...locales.en, ...language };\n}\n\nexport function format(template: string, vars: Record<string, string>): string {\n return template.replace(/\\{(\\w+)\\}/g, (match, key: string) => vars[key] ?? match);\n}\n","import { Emitter, type Handler } from \"./emitter\";\nimport { format, getStrings, type Strings } from \"./i18n\";\nimport type {\n AjaxConfig,\n DataItem,\n ForgeSelectEvent,\n ForgeSelectOptions,\n ForgeSelectPlugin,\n ForgeSelectValue,\n Option,\n OptionGroup,\n TemplateFn,\n} from \"./types\";\n\ntype Row =\n | { kind: \"group\"; label: string }\n | { kind: \"option\"; option: Option; navIndex: number }\n | { kind: \"create\"; navIndex: number }\n | { kind: \"empty\" }\n | { kind: \"loading\" };\n\ntype NavItem = { kind: \"option\"; option: Option } | { kind: \"create\" };\n\ninterface ResolvedOptions {\n placeholder: string;\n searchable: boolean;\n multiple: boolean;\n clearable: boolean;\n allowCreate: boolean;\n theme: string;\n disabled: boolean;\n data?: DataItem[];\n ajax?: AjaxConfig;\n templateResult?: TemplateFn;\n templateSelection?: TemplateFn;\n virtualScroll: boolean | undefined;\n itemHeight: number;\n language: string | Record<string, string>;\n plugins: ForgeSelectPlugin[];\n}\n\nconst DEFAULT_ITEM_HEIGHT = 36;\nconst VIRTUAL_BUFFER = 5;\nconst VIRTUAL_THRESHOLD = 100;\nconst ROW_CACHE_LIMIT = 2000;\n\nlet uidCounter = 0;\n\nfunction isGroup(item: DataItem): item is OptionGroup {\n return (item as OptionGroup).options !== undefined;\n}\n\nexport default class ForgeSelect {\n /** The original element ForgeSelect was mounted on. */\n readonly el: HTMLElement;\n\n private opts: ResolvedOptions;\n private strings: Strings;\n private data: DataItem[];\n private selected: string[] = [];\n private selectedOptions = new Map<string, Option>();\n private emitter = new Emitter();\n private plugins: ForgeSelectPlugin[];\n\n private uid = `forge-select-${++uidCounter}`;\n private root!: HTMLDivElement;\n private control!: HTMLDivElement;\n private valueEl!: HTMLDivElement;\n private clearBtn!: HTMLButtonElement;\n private dropdown!: HTMLDivElement;\n private searchInput: HTMLInputElement | null = null;\n private list!: HTMLUListElement;\n\n private isOpen = false;\n private isDisabled = false;\n private destroyed = false;\n private query = \"\";\n private rows: Row[] = [];\n private navItems: NavItem[] = [];\n private highlightedIndex = -1;\n private rowContentCache = new Map<string, Node>();\n\n private loading = false;\n private ajaxTimer: ReturnType<typeof setTimeout> | null = null;\n private ajaxRequestId = 0;\n private remoteLoaded = false;\n\n private onDocumentMouseDown = (event: MouseEvent): void => {\n if (!this.root.contains(event.target as Node)) this.close();\n };\n\n constructor(target: string | HTMLElement, options: ForgeSelectOptions = {}) {\n const el = typeof target === \"string\" ? document.querySelector<HTMLElement>(target) : target;\n if (!el) {\n throw new Error(`ForgeSelect: target element not found: ${String(target)}`);\n }\n this.el = el;\n\n const nativeSelect = el instanceof HTMLSelectElement ? el : null;\n this.opts = {\n placeholder: options.placeholder ?? \"\",\n searchable: options.searchable ?? true,\n multiple: options.multiple ?? nativeSelect?.multiple ?? false,\n clearable: options.clearable ?? false,\n allowCreate: options.allowCreate ?? false,\n theme: options.theme ?? \"default\",\n disabled: options.disabled ?? false,\n data: options.data,\n ajax: options.ajax,\n templateResult: options.templateResult,\n templateSelection: options.templateSelection,\n virtualScroll: options.virtualScroll,\n itemHeight: options.itemHeight ?? DEFAULT_ITEM_HEIGHT,\n language: options.language ?? \"en\",\n plugins: options.plugins ?? [],\n };\n this.strings = getStrings(this.opts.language);\n this.plugins = this.opts.plugins;\n\n this.data = this.opts.data ?? (nativeSelect ? parseNativeOptions(nativeSelect) : []);\n if (nativeSelect && !this.opts.data) {\n for (const option of Array.from(nativeSelect.querySelectorAll(\"option\"))) {\n if (option.hasAttribute(\"selected\")) this.selectValue(option.value, false);\n }\n }\n\n this.buildDom();\n this.renderValue();\n if (this.opts.disabled) this.disable();\n\n for (const plugin of this.plugins) plugin.onInit?.(this);\n }\n\n // ---------------------------------------------------------------- public API\n\n open(): void {\n if (this.isOpen || this.isDisabled || this.destroyed) return;\n this.isOpen = true;\n this.dropdown.hidden = false;\n this.root.classList.add(\"forge-select--open\");\n this.control.setAttribute(\"aria-expanded\", \"true\");\n document.addEventListener(\"mousedown\", this.onDocumentMouseDown);\n\n if (this.opts.ajax && !this.remoteLoaded) {\n this.scheduleRemoteLoad(this.query, 0);\n }\n this.renderList();\n if (this.searchInput) this.searchInput.focus();\n\n this.emitter.emit(\"open\");\n for (const plugin of this.plugins) plugin.onOpen?.(this);\n }\n\n close(): void {\n if (!this.isOpen) return;\n this.isOpen = false;\n this.dropdown.hidden = true;\n this.root.classList.remove(\"forge-select--open\");\n this.control.setAttribute(\"aria-expanded\", \"false\");\n document.removeEventListener(\"mousedown\", this.onDocumentMouseDown);\n this.highlightedIndex = -1;\n if (this.searchInput) {\n this.searchInput.value = \"\";\n this.query = \"\";\n }\n\n this.emitter.emit(\"close\");\n for (const plugin of this.plugins) plugin.onClose?.(this);\n }\n\n destroy(): void {\n if (this.destroyed) return;\n this.close();\n for (const plugin of this.plugins) plugin.onDestroy?.(this);\n this.destroyed = true;\n if (this.ajaxTimer) clearTimeout(this.ajaxTimer);\n this.rowContentCache.clear();\n this.root.remove();\n this.el.style.display = \"\";\n this.emitter.clear();\n }\n\n getValue(): ForgeSelectValue {\n if (this.opts.multiple) return [...this.selected];\n return this.selected[0] ?? null;\n }\n\n setValue(value: ForgeSelectValue): void {\n const values = value == null ? [] : Array.isArray(value) ? value : [value];\n const next = this.opts.multiple ? values : values.slice(0, 1);\n if (arraysEqual(next, this.selected)) return;\n this.selected = [];\n for (const v of next) this.selectValue(v, false);\n this.afterSelectionChange();\n }\n\n enable(): void {\n this.isDisabled = false;\n this.root.classList.remove(\"forge-select--disabled\");\n this.control.tabIndex = 0;\n this.control.setAttribute(\"aria-disabled\", \"false\");\n }\n\n disable(): void {\n this.close();\n this.isDisabled = true;\n this.root.classList.add(\"forge-select--disabled\");\n this.control.tabIndex = -1;\n this.control.setAttribute(\"aria-disabled\", \"true\");\n }\n\n on(event: ForgeSelectEvent, handler: Handler): void {\n this.emitter.on(event, handler);\n }\n\n off(event: ForgeSelectEvent, handler: Handler): void {\n this.emitter.off(event, handler);\n }\n\n // ---------------------------------------------------------------- DOM setup\n\n private buildDom(): void {\n this.root = document.createElement(\"div\");\n this.root.className = \"forge-select\";\n this.root.dataset.theme = this.opts.theme;\n this.root.style.setProperty(\"--fs-item-height\", `${this.opts.itemHeight}px`);\n\n this.control = document.createElement(\"div\");\n this.control.className = \"forge-select__control\";\n this.control.setAttribute(\"role\", \"combobox\");\n this.control.setAttribute(\"aria-haspopup\", \"listbox\");\n this.control.setAttribute(\"aria-expanded\", \"false\");\n this.control.setAttribute(\"aria-controls\", `${this.uid}-list`);\n this.control.tabIndex = 0;\n\n this.valueEl = document.createElement(\"div\");\n this.valueEl.className = \"forge-select__value\";\n\n this.clearBtn = document.createElement(\"button\");\n this.clearBtn.type = \"button\";\n this.clearBtn.className = \"forge-select__clear\";\n this.clearBtn.setAttribute(\"aria-label\", this.strings.clearSelection);\n this.clearBtn.textContent = \"×\";\n this.clearBtn.hidden = true;\n\n const arrow = document.createElement(\"span\");\n arrow.className = \"forge-select__arrow\";\n arrow.setAttribute(\"aria-hidden\", \"true\");\n\n this.control.append(this.valueEl, this.clearBtn, arrow);\n\n this.dropdown = document.createElement(\"div\");\n this.dropdown.className = \"forge-select__dropdown\";\n this.dropdown.hidden = true;\n\n if (this.opts.searchable) {\n this.searchInput = document.createElement(\"input\");\n this.searchInput.type = \"search\";\n this.searchInput.className = \"forge-select__search\";\n this.searchInput.setAttribute(\"aria-label\", this.strings.search);\n this.searchInput.setAttribute(\"aria-autocomplete\", \"list\");\n this.searchInput.setAttribute(\"aria-controls\", `${this.uid}-list`);\n this.dropdown.append(this.searchInput);\n }\n\n this.list = document.createElement(\"ul\");\n this.list.className = \"forge-select__list\";\n this.list.id = `${this.uid}-list`;\n this.list.setAttribute(\"role\", \"listbox\");\n if (this.opts.multiple) this.list.setAttribute(\"aria-multiselectable\", \"true\");\n this.dropdown.append(this.list);\n\n this.root.append(this.control, this.dropdown);\n this.el.style.display = \"none\";\n this.el.insertAdjacentElement(\"afterend\", this.root);\n\n this.bindEvents();\n }\n\n private bindEvents(): void {\n this.control.addEventListener(\"click\", (event) => {\n if (event.target === this.clearBtn) return;\n if (this.isDisabled) return;\n this.isOpen ? this.close() : this.open();\n });\n\n this.control.addEventListener(\"keydown\", (event) => this.handleKeydown(event));\n\n this.clearBtn.addEventListener(\"click\", (event) => {\n event.stopPropagation();\n this.clearSelection();\n });\n\n if (this.searchInput) {\n this.searchInput.addEventListener(\"input\", () => {\n this.query = this.searchInput!.value;\n this.highlightedIndex = -1;\n this.list.scrollTop = 0;\n this.emitter.emit(\"search\", this.query);\n if (this.opts.ajax) {\n this.scheduleRemoteLoad(this.query, this.opts.ajax.debounce ?? 250);\n } else {\n this.renderList();\n }\n });\n this.searchInput.addEventListener(\"keydown\", (event) => this.handleKeydown(event));\n }\n\n this.list.addEventListener(\"click\", (event) => {\n const li = (event.target as HTMLElement).closest<HTMLLIElement>(\"li[data-nav-index]\");\n if (!li) return;\n const navIndex = Number(li.dataset.navIndex);\n this.activateNavItem(navIndex);\n });\n\n this.list.addEventListener(\"scroll\", () => {\n if (this.usesVirtualScroll()) this.renderRows();\n });\n }\n\n private handleKeydown(event: KeyboardEvent): void {\n if (this.isDisabled) return;\n switch (event.key) {\n case \"Enter\":\n event.preventDefault();\n if (!this.isOpen) this.open();\n else if (this.highlightedIndex >= 0) this.activateNavItem(this.highlightedIndex);\n break;\n case \" \":\n if (event.target === this.control) {\n event.preventDefault();\n if (!this.isOpen) this.open();\n }\n break;\n case \"ArrowDown\":\n event.preventDefault();\n if (!this.isOpen) this.open();\n else this.moveHighlight(1);\n break;\n case \"ArrowUp\":\n event.preventDefault();\n if (this.isOpen) this.moveHighlight(-1);\n break;\n case \"Escape\":\n if (this.isOpen) {\n event.preventDefault();\n this.close();\n this.control.focus();\n }\n break;\n case \"Tab\":\n this.close();\n break;\n }\n }\n\n // ---------------------------------------------------------------- selection\n\n private selectValue(value: string, notify: boolean): void {\n if (this.selected.includes(value)) return;\n const option = this.findOption(value) ?? this.selectedOptions.get(value) ?? { value, label: value };\n this.selectedOptions.set(value, option);\n if (this.opts.multiple) this.selected.push(value);\n else this.selected = [value];\n if (notify) this.afterSelectionChange();\n }\n\n private deselectValue(value: string, notify: boolean): void {\n const index = this.selected.indexOf(value);\n if (index === -1) return;\n this.selected.splice(index, 1);\n if (notify) this.afterSelectionChange();\n }\n\n private clearSelection(): void {\n if (this.selected.length === 0) return;\n this.selected = [];\n this.emitter.emit(\"clear\");\n this.afterSelectionChange();\n }\n\n private afterSelectionChange(): void {\n this.renderValue();\n this.syncNativeSelect();\n if (this.isOpen) this.renderList();\n this.emitter.emit(\"change\", this.getValue());\n }\n\n private syncNativeSelect(): void {\n if (!(this.el instanceof HTMLSelectElement)) return;\n const existing = new Set<string>();\n for (const option of Array.from(this.el.options)) {\n existing.add(option.value);\n option.selected = this.selected.includes(option.value);\n }\n for (const value of this.selected) {\n if (existing.has(value)) continue;\n const option = document.createElement(\"option\");\n option.value = value;\n option.textContent = this.selectedOptions.get(value)?.label ?? value;\n option.selected = true;\n this.el.append(option);\n }\n this.el.dispatchEvent(new Event(\"change\", { bubbles: true }));\n }\n\n private findOption(value: string): Option | undefined {\n for (const item of this.data) {\n if (isGroup(item)) {\n const found = item.options.find((o) => o.value === value);\n if (found) return found;\n } else if (item.value === value) {\n return item;\n }\n }\n return undefined;\n }\n\n private createFromQuery(): void {\n const label = this.query.trim();\n if (!label) return;\n const option: Option = { value: label, label };\n this.data.push(option);\n if (this.searchInput) {\n this.searchInput.value = \"\";\n this.query = \"\";\n }\n this.selectValue(option.value, true);\n if (!this.opts.multiple) this.close();\n }\n\n private activateNavItem(navIndex: number): void {\n const item = this.navItems[navIndex];\n if (!item) return;\n if (item.kind === \"create\") {\n this.createFromQuery();\n return;\n }\n const { value } = item.option;\n if (this.opts.multiple) {\n this.selected.includes(value) ? this.deselectValue(value, true) : this.selectValue(value, true);\n } else {\n this.selectValue(value, true);\n this.close();\n this.control.focus();\n }\n }\n\n // ---------------------------------------------------------------- rendering\n\n private renderValue(): void {\n this.valueEl.textContent = \"\";\n const hasValue = this.selected.length > 0;\n this.clearBtn.hidden = !(this.opts.clearable && hasValue);\n\n if (!hasValue) {\n const placeholder = document.createElement(\"span\");\n placeholder.className = \"forge-select__placeholder\";\n placeholder.textContent = this.opts.placeholder;\n this.valueEl.append(placeholder);\n return;\n }\n\n if (this.opts.multiple) {\n for (const value of this.selected) {\n const option = this.selectedOptions.get(value) ?? { value, label: value };\n const tag = document.createElement(\"span\");\n tag.className = \"forge-select__tag\";\n const label = document.createElement(\"span\");\n label.className = \"forge-select__tag-label\";\n this.renderTemplate(label, option, this.opts.templateSelection, \"inline\");\n const remove = document.createElement(\"button\");\n remove.type = \"button\";\n remove.className = \"forge-select__tag-remove\";\n remove.setAttribute(\"aria-label\", format(this.strings.removeItem, { label: option.label }));\n remove.textContent = \"×\";\n remove.addEventListener(\"click\", (event) => {\n event.stopPropagation();\n if (!this.isDisabled) this.deselectValue(value, true);\n });\n tag.append(label, remove);\n this.valueEl.append(tag);\n }\n } else {\n const option = this.selectedOptions.get(this.selected[0]) ?? {\n value: this.selected[0],\n label: this.selected[0],\n };\n const span = document.createElement(\"span\");\n span.className = \"forge-select__single-value\";\n this.renderTemplate(span, option, this.opts.templateSelection, \"inline\");\n this.valueEl.append(span);\n }\n }\n\n private renderTemplate(\n container: HTMLElement,\n option: Option,\n template?: TemplateFn,\n variant: \"row\" | \"inline\" = \"row\",\n ): void {\n if (template) {\n const result = template(option);\n if (typeof result === \"string\") container.innerHTML = result;\n else container.append(result);\n return;\n }\n if (!option.avatar && !option.description) {\n container.textContent = option.label;\n return;\n }\n // Built-in rich renderer: DOM built via textContent, so all fields are XSS-safe.\n if (option.avatar) {\n const avatar = document.createElement(\"img\");\n avatar.className =\n variant === \"row\" ? \"forge-select__option-avatar\" : \"forge-select__inline-avatar\";\n avatar.src = option.avatar;\n avatar.alt = \"\";\n avatar.setAttribute(\"loading\", \"lazy\");\n avatar.setAttribute(\"decoding\", \"async\");\n container.append(avatar);\n }\n if (variant === \"row\" && option.description) {\n const body = document.createElement(\"span\");\n body.className = \"forge-select__option-body\";\n const label = document.createElement(\"span\");\n label.className = \"forge-select__option-label\";\n label.textContent = option.label;\n const desc = document.createElement(\"span\");\n desc.className = \"forge-select__option-desc\";\n desc.textContent = option.description;\n body.append(label, desc);\n container.append(body);\n } else {\n const label = document.createElement(\"span\");\n label.className = \"forge-select__option-label\";\n label.textContent = option.label;\n container.append(label);\n }\n }\n\n private buildRows(): void {\n this.rows = [];\n this.navItems = [];\n const query = this.query.trim().toLowerCase();\n const matches = (option: Option): boolean =>\n query === \"\" ||\n option.label.toLowerCase().includes(query) ||\n (option.description?.toLowerCase().includes(query) ?? false);\n\n const pushOption = (option: Option): void => {\n let navIndex = -1;\n if (!option.disabled) {\n navIndex = this.navItems.length;\n this.navItems.push({ kind: \"option\", option });\n }\n this.rows.push({ kind: \"option\", option, navIndex });\n };\n\n if (this.loading) {\n this.rows.push({ kind: \"loading\" });\n return;\n }\n\n for (const item of this.data) {\n if (isGroup(item)) {\n const visible = item.options.filter(matches);\n if (visible.length === 0) continue;\n this.rows.push({ kind: \"group\", label: item.label });\n visible.forEach(pushOption);\n } else if (matches(item)) {\n pushOption(item);\n }\n }\n\n if (this.opts.allowCreate && query !== \"\" && !this.hasExactMatch(query)) {\n const navIndex = this.navItems.length;\n this.navItems.push({ kind: \"create\" });\n this.rows.push({ kind: \"create\", navIndex });\n }\n\n if (this.rows.length === 0) this.rows.push({ kind: \"empty\" });\n }\n\n private hasExactMatch(lowerQuery: string): boolean {\n for (const item of this.data) {\n const options = isGroup(item) ? item.options : [item];\n if (options.some((o) => o.label.toLowerCase() === lowerQuery)) return true;\n }\n return false;\n }\n\n private usesVirtualScroll(): boolean {\n return this.opts.virtualScroll !== false && this.rows.length > VIRTUAL_THRESHOLD;\n }\n\n private renderList(): void {\n this.buildRows();\n this.renderRows();\n }\n\n private renderRows(): void {\n // Capture the scroll offset and viewport height BEFORE clearing the list:\n // removing the children collapses both scrollHeight (clamping scrollTop to 0)\n // and clientHeight (the list has no explicit height, only max-height, so an\n // empty list reports just its padding instead of the real box height).\n const scrollTop = this.list.scrollTop;\n const clientHeight = this.list.clientHeight;\n const virtual = this.usesVirtualScroll();\n this.list.textContent = \"\";\n\n const rowHeight = this.opts.itemHeight;\n let start = 0;\n let end = this.rows.length;\n if (virtual) {\n const viewport = clientHeight || rowHeight * 8;\n start = Math.max(0, Math.floor(scrollTop / rowHeight) - VIRTUAL_BUFFER);\n end = Math.min(this.rows.length, start + Math.ceil(viewport / rowHeight) + VIRTUAL_BUFFER * 2);\n\n const topSpacer = document.createElement(\"li\");\n topSpacer.className = \"forge-select__spacer\";\n topSpacer.setAttribute(\"aria-hidden\", \"true\");\n topSpacer.style.height = `${start * rowHeight}px`;\n this.list.append(topSpacer);\n }\n\n for (let i = start; i < end; i++) {\n this.list.append(this.renderRow(this.rows[i]));\n }\n\n if (virtual) {\n const bottomSpacer = document.createElement(\"li\");\n bottomSpacer.className = \"forge-select__spacer\";\n bottomSpacer.setAttribute(\"aria-hidden\", \"true\");\n bottomSpacer.style.height = `${(this.rows.length - end) * rowHeight}px`;\n this.list.append(bottomSpacer);\n\n // Restore the offset that clearing clamped away. The net scroll change\n // within this handler is zero, so this does not re-fire the scroll\n // listener; even if it did, the re-render is idempotent and settles.\n if (this.list.scrollTop !== scrollTop) {\n this.list.scrollTop = scrollTop;\n }\n }\n\n this.updateActiveDescendant();\n }\n\n private renderRow(row: Row): HTMLLIElement {\n const li = document.createElement(\"li\");\n switch (row.kind) {\n case \"group\":\n li.className = \"forge-select__group-label\";\n li.setAttribute(\"role\", \"presentation\");\n li.textContent = row.label;\n break;\n case \"empty\":\n li.className = \"forge-select__empty\";\n li.textContent = this.strings.noResults;\n break;\n case \"loading\":\n li.className = \"forge-select__loading\";\n li.textContent = this.strings.loading;\n break;\n case \"create\":\n li.className = \"forge-select__option forge-select__option--create\";\n li.setAttribute(\"role\", \"option\");\n li.id = `${this.uid}-nav-${row.navIndex}`;\n li.dataset.navIndex = String(row.navIndex);\n li.textContent = format(this.strings.createOption, { query: this.query.trim() });\n if (row.navIndex === this.highlightedIndex) li.classList.add(\"forge-select__option--highlighted\");\n break;\n case \"option\": {\n li.className = \"forge-select__option\";\n li.setAttribute(\"role\", \"option\");\n const isSelected = this.selected.includes(row.option.value);\n li.setAttribute(\"aria-selected\", String(isSelected));\n if (isSelected) li.classList.add(\"forge-select__option--selected\");\n if (row.option.disabled) {\n li.classList.add(\"forge-select__option--disabled\");\n li.setAttribute(\"aria-disabled\", \"true\");\n } else {\n li.id = `${this.uid}-nav-${row.navIndex}`;\n li.dataset.navIndex = String(row.navIndex);\n if (row.navIndex === this.highlightedIndex) li.classList.add(\"forge-select__option--highlighted\");\n }\n li.append(this.optionContent(row.option));\n break;\n }\n }\n return li;\n }\n\n /**\n * Rendered row content is cached per option value and cloned on each render,\n * so templates run once per option instead of once per scroll frame.\n * State classes (selected/highlighted/disabled) live on the <li>, keeping the\n * cached content state-free.\n */\n private optionContent(option: Option): Node {\n let cached = this.rowContentCache.get(option.value);\n if (!cached) {\n const holder = document.createElement(\"span\");\n holder.className = \"forge-select__option-content\";\n this.renderTemplate(holder, option, this.opts.templateResult);\n if (this.rowContentCache.size >= ROW_CACHE_LIMIT) {\n // FIFO eviction keeps memory bounded on very large lists.\n const oldest = this.rowContentCache.keys().next().value as string;\n this.rowContentCache.delete(oldest);\n }\n this.rowContentCache.set(option.value, holder);\n cached = holder;\n }\n return cached.cloneNode(true);\n }\n\n private moveHighlight(delta: number): void {\n if (this.navItems.length === 0) return;\n const next =\n this.highlightedIndex === -1 && delta > 0\n ? 0\n : (this.highlightedIndex + delta + this.navItems.length) % this.navItems.length;\n this.highlightedIndex = next;\n\n if (this.usesVirtualScroll()) {\n const rowIndex = this.rows.findIndex(\n (row) => (row.kind === \"option\" || row.kind === \"create\") && row.navIndex === next,\n );\n if (rowIndex >= 0) {\n const rowHeight = this.opts.itemHeight;\n const top = rowIndex * rowHeight;\n const viewport = this.list.clientHeight || rowHeight * 8;\n let target = this.list.scrollTop;\n if (top < target) target = top;\n else if (top + rowHeight > target + viewport) target = top + rowHeight - viewport;\n if (target !== this.list.scrollTop) this.list.scrollTop = target;\n }\n this.renderRows();\n } else {\n this.renderRows();\n const highlighted = this.list.querySelector<HTMLElement>(\".forge-select__option--highlighted\");\n highlighted?.scrollIntoView?.({ block: \"nearest\" });\n }\n }\n\n private updateActiveDescendant(): void {\n const target = this.searchInput ?? this.control;\n if (this.highlightedIndex >= 0) {\n target.setAttribute(\"aria-activedescendant\", `${this.uid}-nav-${this.highlightedIndex}`);\n } else {\n target.removeAttribute(\"aria-activedescendant\");\n }\n }\n\n // ---------------------------------------------------------------- remote data\n\n private scheduleRemoteLoad(query: string, delay: number): void {\n if (this.ajaxTimer) clearTimeout(this.ajaxTimer);\n this.loading = true;\n this.renderList();\n this.ajaxTimer = setTimeout(() => {\n void this.loadRemote(query);\n }, delay);\n }\n\n private async loadRemote(query: string): Promise<void> {\n const ajax = this.opts.ajax!;\n const requestId = ++this.ajaxRequestId;\n try {\n const url = buildUrl(ajax, query);\n const response = await fetch(url);\n const json: unknown = await response.json();\n if (requestId !== this.ajaxRequestId || this.destroyed) return;\n this.data = ajax.transform ? ajax.transform(json) : (json as Option[]);\n this.rowContentCache.clear();\n this.remoteLoaded = true;\n } catch {\n if (requestId !== this.ajaxRequestId || this.destroyed) return;\n this.data = [];\n this.rowContentCache.clear();\n } finally {\n if (requestId === this.ajaxRequestId && !this.destroyed) {\n this.loading = false;\n if (this.isOpen) this.renderList();\n }\n }\n }\n}\n\nfunction parseNativeOptions(select: HTMLSelectElement): DataItem[] {\n const data: DataItem[] = [];\n for (const child of Array.from(select.children)) {\n if (child instanceof HTMLOptGroupElement) {\n data.push({\n label: child.label,\n options: Array.from(child.querySelectorAll(\"option\")).map(parseOption),\n });\n } else if (child instanceof HTMLOptionElement) {\n data.push(parseOption(child));\n }\n }\n return data;\n}\n\nfunction parseOption(option: HTMLOptionElement): Option {\n return {\n value: option.value,\n label: option.textContent?.trim() ?? option.value,\n disabled: option.disabled || undefined,\n };\n}\n\nfunction buildUrl(ajax: AjaxConfig, query: string): string {\n if (typeof ajax.url === \"function\") return ajax.url(query);\n if (!ajax.params) return ajax.url;\n const params = new URLSearchParams();\n for (const [key, value] of Object.entries(ajax.params(query))) {\n params.set(key, String(value));\n }\n const separator = ajax.url.includes(\"?\") ? \"&\" : \"?\";\n return `${ajax.url}${separator}${params.toString()}`;\n}\n\nfunction arraysEqual(a: string[], b: string[]): boolean {\n return a.length === b.length && a.every((value, index) => value === b[index]);\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/emitter.ts","../src/i18n.ts","../src/ForgeSelect.ts"],"sourcesContent":["export type Handler = (...args: unknown[]) => void;\n\nexport class Emitter {\n private handlers = new Map<string, Set<Handler>>();\n\n on(event: string, handler: Handler): void {\n let set = this.handlers.get(event);\n if (!set) {\n set = new Set();\n this.handlers.set(event, set);\n }\n set.add(handler);\n }\n\n off(event: string, handler: Handler): void {\n this.handlers.get(event)?.delete(handler);\n }\n\n emit(event: string, ...args: unknown[]): void {\n const set = this.handlers.get(event);\n if (!set) return;\n for (const handler of [...set]) handler(...args);\n }\n\n clear(): void {\n this.handlers.clear();\n }\n}\n","export type Strings = Record<string, string>;\n\nconst locales: Record<string, Strings> = {\n en: {\n noResults: \"No results found\",\n loading: \"Loading…\",\n loadingMore: \"Loading more…\",\n createOption: 'Create \"{query}\"',\n clearSelection: \"Clear selection\",\n removeItem: \"Remove {label}\",\n search: \"Search\",\n },\n vi: {\n noResults: \"Không tìm thấy kết quả\",\n loading: \"Đang tải…\",\n loadingMore: \"Đang tải thêm…\",\n createOption: 'Tạo \"{query}\"',\n clearSelection: \"Xóa lựa chọn\",\n removeItem: \"Xóa {label}\",\n search: \"Tìm kiếm\",\n },\n};\n\nexport function getStrings(language: string | Strings): Strings {\n if (typeof language === \"string\") {\n return locales[language] ?? locales.en;\n }\n return { ...locales.en, ...language };\n}\n\nexport function format(template: string, vars: Record<string, string>): string {\n return template.replace(/\\{(\\w+)\\}/g, (match, key: string) => vars[key] ?? match);\n}\n","import { Emitter, type Handler } from \"./emitter\";\nimport { format, getStrings, type Strings } from \"./i18n\";\nimport type {\n AjaxConfig,\n DataItem,\n ForgeSelectEvent,\n ForgeSelectOptions,\n ForgeSelectPlugin,\n ForgeSelectValue,\n Option,\n OptionGroup,\n TemplateFn,\n} from \"./types\";\n\ntype Row =\n | { kind: \"group\"; label: string }\n | { kind: \"option\"; option: Option; navIndex: number; depth: number; hasChildren: boolean }\n | { kind: \"create\"; navIndex: number }\n | { kind: \"empty\" }\n | { kind: \"loading\" }\n | { kind: \"loading-more\" };\n\ntype NavItem = { kind: \"option\"; option: Option } | { kind: \"create\" };\n\ninterface ResolvedOptions {\n placeholder: string;\n searchable: boolean;\n multiple: boolean;\n clearable: boolean;\n allowCreate: boolean;\n theme: string;\n disabled: boolean;\n data?: DataItem[];\n ajax?: AjaxConfig;\n templateResult?: TemplateFn;\n templateSelection?: TemplateFn;\n virtualScroll: boolean | undefined;\n itemHeight: number;\n language: string | Record<string, string>;\n plugins: ForgeSelectPlugin[];\n}\n\nconst DEFAULT_ITEM_HEIGHT = 36;\nconst VIRTUAL_BUFFER = 5;\nconst VIRTUAL_THRESHOLD = 100;\nconst ROW_CACHE_LIMIT = 2000;\n\nlet uidCounter = 0;\n\nfunction isGroup(item: DataItem): item is OptionGroup {\n return (item as OptionGroup).options !== undefined;\n}\n\n/** All descendant values under `option` (children, recursively), not including `option` itself. */\nfunction collectDescendantValues(option: Option): string[] {\n if (!option.children) return [];\n const values: string[] = [];\n for (const child of option.children) {\n values.push(child.value, ...collectDescendantValues(child));\n }\n return values;\n}\n\n/**\n * Tri-state checkbox state for a tree node, derived purely from `selected`:\n * \"all\"/\"none\" if every (or no) leaf descendant is selected, \"some\" otherwise.\n * Leaf options (no children) are just \"all\"/\"none\" based on their own value.\n */\nfunction computeCheckState(option: Option, selected: string[]): \"none\" | \"some\" | \"all\" {\n if (!option.children || option.children.length === 0) {\n return selected.includes(option.value) ? \"all\" : \"none\";\n }\n const states = option.children.map((child) => computeCheckState(child, selected));\n if (states.every((s) => s === \"all\")) return \"all\";\n if (states.every((s) => s === \"none\")) return \"none\";\n return \"some\";\n}\n\nexport default class ForgeSelect {\n /** The original element ForgeSelect was mounted on. */\n readonly el: HTMLElement;\n\n private opts: ResolvedOptions;\n private strings: Strings;\n private data: DataItem[];\n private selected: string[] = [];\n private selectedOptions = new Map<string, Option>();\n private emitter = new Emitter();\n private plugins: ForgeSelectPlugin[];\n\n private uid = `forge-select-${++uidCounter}`;\n private root!: HTMLDivElement;\n private control!: HTMLDivElement;\n private valueEl!: HTMLDivElement;\n private clearBtn!: HTMLButtonElement;\n private dropdown!: HTMLDivElement;\n private searchInput: HTMLInputElement | null = null;\n private list!: HTMLUListElement;\n\n private isOpen = false;\n private isDisabled = false;\n private destroyed = false;\n private query = \"\";\n private rows: Row[] = [];\n private navItems: NavItem[] = [];\n private highlightedIndex = -1;\n private rowContentCache = new Map<string, Node>();\n private expandedValues = new Set<string>();\n\n private loading = false;\n private loadingMore = false;\n private page = 0;\n private hasMore = true;\n private ajaxTimer: ReturnType<typeof setTimeout> | null = null;\n private ajaxRequestId = 0;\n private remoteLoaded = false;\n\n private onDocumentMouseDown = (event: MouseEvent): void => {\n if (!this.root.contains(event.target as Node)) this.close();\n };\n\n constructor(target: string | HTMLElement, options: ForgeSelectOptions = {}) {\n const el = typeof target === \"string\" ? document.querySelector<HTMLElement>(target) : target;\n if (!el) {\n throw new Error(`ForgeSelect: target element not found: ${String(target)}`);\n }\n this.el = el;\n\n const nativeSelect = el instanceof HTMLSelectElement ? el : null;\n this.opts = {\n placeholder: options.placeholder ?? \"\",\n searchable: options.searchable ?? true,\n multiple: options.multiple ?? nativeSelect?.multiple ?? false,\n clearable: options.clearable ?? false,\n allowCreate: options.allowCreate ?? false,\n theme: options.theme ?? \"default\",\n disabled: options.disabled ?? false,\n data: options.data,\n ajax: options.ajax,\n templateResult: options.templateResult,\n templateSelection: options.templateSelection,\n virtualScroll: options.virtualScroll,\n itemHeight: options.itemHeight ?? DEFAULT_ITEM_HEIGHT,\n language: options.language ?? \"en\",\n plugins: options.plugins ?? [],\n };\n this.strings = getStrings(this.opts.language);\n this.plugins = this.opts.plugins;\n\n this.data = this.opts.data ?? (nativeSelect ? parseNativeOptions(nativeSelect) : []);\n if (nativeSelect && !this.opts.data) {\n for (const option of Array.from(nativeSelect.querySelectorAll(\"option\"))) {\n if (option.hasAttribute(\"selected\")) this.selectValue(option.value, false);\n }\n }\n\n this.buildDom();\n this.renderValue();\n if (this.opts.disabled) this.disable();\n\n for (const plugin of this.plugins) plugin.onInit?.(this);\n }\n\n // ---------------------------------------------------------------- public API\n\n open(): void {\n if (this.isOpen || this.isDisabled || this.destroyed) return;\n this.isOpen = true;\n this.dropdown.hidden = false;\n this.root.classList.add(\"forge-select--open\");\n this.control.setAttribute(\"aria-expanded\", \"true\");\n document.addEventListener(\"mousedown\", this.onDocumentMouseDown);\n\n if (this.opts.ajax && !this.remoteLoaded) {\n this.scheduleRemoteLoad(this.query, 0);\n }\n this.renderList();\n if (this.searchInput) this.searchInput.focus();\n\n this.emitter.emit(\"open\");\n for (const plugin of this.plugins) plugin.onOpen?.(this);\n }\n\n close(): void {\n if (!this.isOpen) return;\n this.isOpen = false;\n this.dropdown.hidden = true;\n this.root.classList.remove(\"forge-select--open\");\n this.control.setAttribute(\"aria-expanded\", \"false\");\n document.removeEventListener(\"mousedown\", this.onDocumentMouseDown);\n this.highlightedIndex = -1;\n if (this.searchInput) {\n this.searchInput.value = \"\";\n this.query = \"\";\n }\n\n this.emitter.emit(\"close\");\n for (const plugin of this.plugins) plugin.onClose?.(this);\n }\n\n destroy(): void {\n if (this.destroyed) return;\n this.close();\n for (const plugin of this.plugins) plugin.onDestroy?.(this);\n this.destroyed = true;\n if (this.ajaxTimer) clearTimeout(this.ajaxTimer);\n this.rowContentCache.clear();\n this.root.remove();\n this.el.style.display = \"\";\n this.emitter.clear();\n }\n\n getValue(): ForgeSelectValue {\n if (this.opts.multiple) return [...this.selected];\n return this.selected[0] ?? null;\n }\n\n setValue(value: ForgeSelectValue): void {\n const values = value == null ? [] : Array.isArray(value) ? value : [value];\n const next = this.opts.multiple ? values : values.slice(0, 1);\n if (arraysEqual(next, this.selected)) return;\n this.selected = [];\n for (const v of next) this.selectValue(v, false);\n this.afterSelectionChange();\n }\n\n enable(): void {\n this.isDisabled = false;\n this.root.classList.remove(\"forge-select--disabled\");\n this.control.tabIndex = 0;\n this.control.setAttribute(\"aria-disabled\", \"false\");\n }\n\n disable(): void {\n this.close();\n this.isDisabled = true;\n this.root.classList.add(\"forge-select--disabled\");\n this.control.tabIndex = -1;\n this.control.setAttribute(\"aria-disabled\", \"true\");\n }\n\n on(event: ForgeSelectEvent, handler: Handler): void {\n this.emitter.on(event, handler);\n }\n\n off(event: ForgeSelectEvent, handler: Handler): void {\n this.emitter.off(event, handler);\n }\n\n // ---------------------------------------------------------------- DOM setup\n\n private buildDom(): void {\n this.root = document.createElement(\"div\");\n this.root.className = \"forge-select\";\n this.root.dataset.theme = this.opts.theme;\n this.root.style.setProperty(\"--fs-item-height\", `${this.opts.itemHeight}px`);\n\n this.control = document.createElement(\"div\");\n this.control.className = \"forge-select__control\";\n this.control.setAttribute(\"role\", \"combobox\");\n this.control.setAttribute(\"aria-haspopup\", \"listbox\");\n this.control.setAttribute(\"aria-expanded\", \"false\");\n this.control.setAttribute(\"aria-controls\", `${this.uid}-list`);\n this.control.tabIndex = 0;\n\n this.valueEl = document.createElement(\"div\");\n this.valueEl.className = \"forge-select__value\";\n\n this.clearBtn = document.createElement(\"button\");\n this.clearBtn.type = \"button\";\n this.clearBtn.className = \"forge-select__clear\";\n this.clearBtn.setAttribute(\"aria-label\", this.strings.clearSelection);\n this.clearBtn.textContent = \"×\";\n this.clearBtn.hidden = true;\n\n const arrow = document.createElement(\"span\");\n arrow.className = \"forge-select__arrow\";\n arrow.setAttribute(\"aria-hidden\", \"true\");\n\n this.control.append(this.valueEl, this.clearBtn, arrow);\n\n this.dropdown = document.createElement(\"div\");\n this.dropdown.className = \"forge-select__dropdown\";\n this.dropdown.hidden = true;\n\n if (this.opts.searchable) {\n this.searchInput = document.createElement(\"input\");\n this.searchInput.type = \"search\";\n this.searchInput.className = \"forge-select__search\";\n this.searchInput.setAttribute(\"aria-label\", this.strings.search);\n this.searchInput.setAttribute(\"aria-autocomplete\", \"list\");\n this.searchInput.setAttribute(\"aria-controls\", `${this.uid}-list`);\n this.dropdown.append(this.searchInput);\n }\n\n this.list = document.createElement(\"ul\");\n this.list.className = \"forge-select__list\";\n this.list.id = `${this.uid}-list`;\n this.list.setAttribute(\"role\", \"listbox\");\n if (this.opts.multiple) this.list.setAttribute(\"aria-multiselectable\", \"true\");\n this.dropdown.append(this.list);\n\n this.root.append(this.control, this.dropdown);\n this.el.style.display = \"none\";\n this.el.insertAdjacentElement(\"afterend\", this.root);\n\n this.bindEvents();\n }\n\n private bindEvents(): void {\n this.control.addEventListener(\"click\", (event) => {\n if (event.target === this.clearBtn) return;\n if (this.isDisabled) return;\n this.isOpen ? this.close() : this.open();\n });\n\n this.control.addEventListener(\"keydown\", (event) => this.handleKeydown(event));\n\n this.clearBtn.addEventListener(\"click\", (event) => {\n event.stopPropagation();\n this.clearSelection();\n });\n\n if (this.searchInput) {\n this.searchInput.addEventListener(\"input\", () => {\n this.query = this.searchInput!.value;\n this.highlightedIndex = -1;\n this.list.scrollTop = 0;\n this.emitter.emit(\"search\", this.query);\n if (this.opts.ajax) {\n this.scheduleRemoteLoad(this.query, this.opts.ajax.debounce ?? 250);\n } else {\n this.renderList();\n }\n });\n this.searchInput.addEventListener(\"keydown\", (event) => this.handleKeydown(event));\n }\n\n this.list.addEventListener(\"click\", (event) => {\n const target = event.target as HTMLElement;\n const twisty = target.closest<HTMLElement>(\"[data-twisty]\");\n if (twisty) {\n const value = twisty.dataset.twisty!;\n if (this.expandedValues.has(value)) this.expandedValues.delete(value);\n else this.expandedValues.add(value);\n this.renderList();\n return;\n }\n const li = target.closest<HTMLLIElement>(\"li[data-nav-index]\");\n if (!li) return;\n const navIndex = Number(li.dataset.navIndex);\n this.activateNavItem(navIndex);\n });\n\n this.list.addEventListener(\"scroll\", () => {\n if (this.usesVirtualScroll()) this.renderRows();\n this.maybeLoadNextPage();\n });\n }\n\n private handleKeydown(event: KeyboardEvent): void {\n if (this.isDisabled) return;\n switch (event.key) {\n case \"Enter\":\n event.preventDefault();\n if (!this.isOpen) this.open();\n else if (this.highlightedIndex >= 0) this.activateNavItem(this.highlightedIndex);\n break;\n case \" \":\n if (event.target === this.control) {\n event.preventDefault();\n if (!this.isOpen) this.open();\n }\n break;\n case \"ArrowDown\":\n event.preventDefault();\n if (!this.isOpen) this.open();\n else this.moveHighlight(1);\n break;\n case \"ArrowUp\":\n event.preventDefault();\n if (this.isOpen) this.moveHighlight(-1);\n break;\n case \"Escape\":\n if (this.isOpen) {\n event.preventDefault();\n this.close();\n this.control.focus();\n }\n break;\n case \"Tab\":\n this.close();\n break;\n }\n }\n\n // ---------------------------------------------------------------- selection\n\n private selectValue(value: string, notify: boolean): void {\n if (this.selected.includes(value)) return;\n const option = this.findOption(value) ?? this.selectedOptions.get(value) ?? { value, label: value };\n this.selectedOptions.set(value, option);\n if (this.opts.multiple) {\n this.selected.push(value);\n // Selecting a tree node cascades to its descendants too; for a plain\n // option (no children) this is a no-op.\n for (const v of collectDescendantValues(option)) {\n if (!this.selected.includes(v)) this.selected.push(v);\n }\n this.syncTreeAncestors();\n } else {\n this.selected = [value];\n }\n if (notify) this.afterSelectionChange();\n }\n\n private deselectValue(value: string, notify: boolean): void {\n const index = this.selected.indexOf(value);\n if (index === -1) return;\n this.selected.splice(index, 1);\n if (this.opts.multiple) {\n const option = this.findOption(value) ?? this.selectedOptions.get(value);\n if (option) {\n for (const v of collectDescendantValues(option)) {\n const i = this.selected.indexOf(v);\n if (i !== -1) this.selected.splice(i, 1);\n }\n }\n this.syncTreeAncestors();\n }\n if (notify) this.afterSelectionChange();\n }\n\n /**\n * Keeps every tree parent's own membership in `selected` consistent with\n * its descendants (post-order, so parents see already-corrected children):\n * a parent counts as selected only when `computeCheckState` says \"all\".\n * No-op for data with no `children` anywhere.\n */\n private syncTreeAncestors(): void {\n const sync = (option: Option): void => {\n if (!option.children || option.children.length === 0) return;\n for (const child of option.children) sync(child);\n const state = computeCheckState(option, this.selected);\n const index = this.selected.indexOf(option.value);\n if (state === \"all\" && index === -1) this.selected.push(option.value);\n else if (state !== \"all\" && index !== -1) this.selected.splice(index, 1);\n };\n for (const item of this.data) {\n const options = isGroup(item) ? item.options : [item];\n options.forEach(sync);\n }\n }\n\n private clearSelection(): void {\n if (this.selected.length === 0) return;\n this.selected = [];\n this.emitter.emit(\"clear\");\n this.afterSelectionChange();\n }\n\n private afterSelectionChange(): void {\n this.renderValue();\n this.syncNativeSelect();\n if (this.isOpen) this.renderList();\n this.emitter.emit(\"change\", this.getValue());\n }\n\n private syncNativeSelect(): void {\n if (!(this.el instanceof HTMLSelectElement)) return;\n const existing = new Set<string>();\n for (const option of Array.from(this.el.options)) {\n existing.add(option.value);\n option.selected = this.selected.includes(option.value);\n }\n for (const value of this.selected) {\n if (existing.has(value)) continue;\n const option = document.createElement(\"option\");\n option.value = value;\n option.textContent = this.selectedOptions.get(value)?.label ?? value;\n option.selected = true;\n this.el.append(option);\n }\n this.el.dispatchEvent(new Event(\"change\", { bubbles: true }));\n }\n\n private findOption(value: string): Option | undefined {\n const search = (options: Option[]): Option | undefined => {\n for (const option of options) {\n if (option.value === value) return option;\n if (option.children) {\n const found = search(option.children);\n if (found) return found;\n }\n }\n return undefined;\n };\n for (const item of this.data) {\n const found = search(isGroup(item) ? item.options : [item]);\n if (found) return found;\n }\n return undefined;\n }\n\n private createFromQuery(): void {\n const label = this.query.trim();\n if (!label) return;\n const option: Option = { value: label, label };\n this.data.push(option);\n if (this.searchInput) {\n this.searchInput.value = \"\";\n this.query = \"\";\n }\n this.selectValue(option.value, true);\n if (!this.opts.multiple) this.close();\n }\n\n private activateNavItem(navIndex: number): void {\n const item = this.navItems[navIndex];\n if (!item) return;\n if (item.kind === \"create\") {\n this.createFromQuery();\n return;\n }\n const { value } = item.option;\n if (this.opts.multiple) {\n this.selected.includes(value) ? this.deselectValue(value, true) : this.selectValue(value, true);\n } else {\n this.selectValue(value, true);\n this.close();\n this.control.focus();\n }\n }\n\n // ---------------------------------------------------------------- rendering\n\n private renderValue(): void {\n this.valueEl.textContent = \"\";\n const hasValue = this.selected.length > 0;\n this.clearBtn.hidden = !(this.opts.clearable && hasValue);\n\n if (!hasValue) {\n const placeholder = document.createElement(\"span\");\n placeholder.className = \"forge-select__placeholder\";\n placeholder.textContent = this.opts.placeholder;\n this.valueEl.append(placeholder);\n return;\n }\n\n if (this.opts.multiple) {\n for (const value of this.selected) {\n const option = this.selectedOptions.get(value) ?? { value, label: value };\n const tag = document.createElement(\"span\");\n tag.className = \"forge-select__tag\";\n const label = document.createElement(\"span\");\n label.className = \"forge-select__tag-label\";\n this.renderTemplate(label, option, this.opts.templateSelection, \"inline\");\n const remove = document.createElement(\"button\");\n remove.type = \"button\";\n remove.className = \"forge-select__tag-remove\";\n remove.setAttribute(\"aria-label\", format(this.strings.removeItem, { label: option.label }));\n remove.textContent = \"×\";\n remove.addEventListener(\"click\", (event) => {\n event.stopPropagation();\n if (!this.isDisabled) this.deselectValue(value, true);\n });\n tag.append(label, remove);\n this.valueEl.append(tag);\n }\n } else {\n const option = this.selectedOptions.get(this.selected[0]) ?? {\n value: this.selected[0],\n label: this.selected[0],\n };\n const span = document.createElement(\"span\");\n span.className = \"forge-select__single-value\";\n this.renderTemplate(span, option, this.opts.templateSelection, \"inline\");\n this.valueEl.append(span);\n }\n }\n\n private renderTemplate(\n container: HTMLElement,\n option: Option,\n template?: TemplateFn,\n variant: \"row\" | \"inline\" = \"row\",\n ): void {\n if (template) {\n const result = template(option);\n if (typeof result === \"string\") container.innerHTML = result;\n else container.append(result);\n return;\n }\n if (!option.avatar && !option.description) {\n container.textContent = option.label;\n return;\n }\n // Built-in rich renderer: DOM built via textContent, so all fields are XSS-safe.\n if (option.avatar) {\n const avatar = document.createElement(\"img\");\n avatar.className =\n variant === \"row\" ? \"forge-select__option-avatar\" : \"forge-select__inline-avatar\";\n avatar.src = option.avatar;\n avatar.alt = \"\";\n avatar.setAttribute(\"loading\", \"lazy\");\n avatar.setAttribute(\"decoding\", \"async\");\n container.append(avatar);\n }\n if (variant === \"row\" && option.description) {\n const body = document.createElement(\"span\");\n body.className = \"forge-select__option-body\";\n const label = document.createElement(\"span\");\n label.className = \"forge-select__option-label\";\n label.textContent = option.label;\n const desc = document.createElement(\"span\");\n desc.className = \"forge-select__option-desc\";\n desc.textContent = option.description;\n body.append(label, desc);\n container.append(body);\n } else {\n const label = document.createElement(\"span\");\n label.className = \"forge-select__option-label\";\n label.textContent = option.label;\n container.append(label);\n }\n }\n\n private buildRows(): void {\n this.rows = [];\n this.navItems = [];\n const query = this.query.trim().toLowerCase();\n const matches = (option: Option): boolean =>\n query === \"\" ||\n option.label.toLowerCase().includes(query) ||\n (option.description?.toLowerCase().includes(query) ?? false);\n\n // A tree node is visible while searching if it matches, or any descendant\n // does (leaf options with no children just reduce to `matches()`).\n const subtreeMatches = (option: Option): boolean =>\n query === \"\" || matches(option) || (option.children ?? []).some(subtreeMatches);\n\n const pushOption = (option: Option, depth: number): void => {\n let navIndex = -1;\n if (!option.disabled) {\n navIndex = this.navItems.length;\n this.navItems.push({ kind: \"option\", option });\n }\n const hasChildren = !!option.children && option.children.length > 0;\n this.rows.push({ kind: \"option\", option, navIndex, depth, hasChildren });\n if (hasChildren) {\n // Expanding is ephemeral while searching (never written to\n // expandedValues), so clearing the query restores manual state.\n const expanded = query !== \"\" || this.expandedValues.has(option.value);\n if (expanded) {\n for (const child of option.children!) {\n if (subtreeMatches(child)) pushOption(child, depth + 1);\n }\n }\n }\n };\n\n if (this.loading) {\n this.rows.push({ kind: \"loading\" });\n return;\n }\n\n for (const item of this.data) {\n if (isGroup(item)) {\n const visible = item.options.filter(subtreeMatches);\n if (visible.length === 0) continue;\n this.rows.push({ kind: \"group\", label: item.label });\n visible.forEach((o) => pushOption(o, 0));\n } else if (subtreeMatches(item)) {\n pushOption(item, 0);\n }\n }\n\n if (this.opts.allowCreate && query !== \"\" && !this.hasExactMatch(query)) {\n const navIndex = this.navItems.length;\n this.navItems.push({ kind: \"create\" });\n this.rows.push({ kind: \"create\", navIndex });\n }\n\n if (this.rows.length === 0) this.rows.push({ kind: \"empty\" });\n else if (this.loadingMore) this.rows.push({ kind: \"loading-more\" });\n }\n\n private hasExactMatch(lowerQuery: string): boolean {\n const matchesExactly = (option: Option): boolean =>\n option.label.toLowerCase() === lowerQuery || (option.children ?? []).some(matchesExactly);\n for (const item of this.data) {\n const options = isGroup(item) ? item.options : [item];\n if (options.some(matchesExactly)) return true;\n }\n return false;\n }\n\n private usesVirtualScroll(): boolean {\n return this.opts.virtualScroll !== false && this.rows.length > VIRTUAL_THRESHOLD;\n }\n\n private renderList(): void {\n this.buildRows();\n this.renderRows();\n }\n\n private renderRows(): void {\n // Capture the scroll offset and viewport height BEFORE clearing the list:\n // removing the children collapses both scrollHeight (clamping scrollTop to 0)\n // and clientHeight (the list has no explicit height, only max-height, so an\n // empty list reports just its padding instead of the real box height).\n const scrollTop = this.list.scrollTop;\n const clientHeight = this.list.clientHeight;\n const virtual = this.usesVirtualScroll();\n this.list.textContent = \"\";\n\n const rowHeight = this.opts.itemHeight;\n let start = 0;\n let end = this.rows.length;\n if (virtual) {\n const viewport = clientHeight || rowHeight * 8;\n start = Math.max(0, Math.floor(scrollTop / rowHeight) - VIRTUAL_BUFFER);\n end = Math.min(this.rows.length, start + Math.ceil(viewport / rowHeight) + VIRTUAL_BUFFER * 2);\n\n const topSpacer = document.createElement(\"li\");\n topSpacer.className = \"forge-select__spacer\";\n topSpacer.setAttribute(\"aria-hidden\", \"true\");\n topSpacer.style.height = `${start * rowHeight}px`;\n this.list.append(topSpacer);\n }\n\n for (let i = start; i < end; i++) {\n this.list.append(this.renderRow(this.rows[i]));\n }\n\n if (virtual) {\n const bottomSpacer = document.createElement(\"li\");\n bottomSpacer.className = \"forge-select__spacer\";\n bottomSpacer.setAttribute(\"aria-hidden\", \"true\");\n bottomSpacer.style.height = `${(this.rows.length - end) * rowHeight}px`;\n this.list.append(bottomSpacer);\n\n // Restore the offset that clearing clamped away. The net scroll change\n // within this handler is zero, so this does not re-fire the scroll\n // listener; even if it did, the re-render is idempotent and settles.\n if (this.list.scrollTop !== scrollTop) {\n this.list.scrollTop = scrollTop;\n }\n }\n\n this.updateActiveDescendant();\n }\n\n private renderRow(row: Row): HTMLLIElement {\n const li = document.createElement(\"li\");\n switch (row.kind) {\n case \"group\":\n li.className = \"forge-select__group-label\";\n li.setAttribute(\"role\", \"presentation\");\n li.textContent = row.label;\n break;\n case \"empty\":\n li.className = \"forge-select__empty\";\n li.textContent = this.strings.noResults;\n break;\n case \"loading\":\n li.className = \"forge-select__loading\";\n li.textContent = this.strings.loading;\n break;\n case \"loading-more\":\n li.className = \"forge-select__loading-more\";\n li.setAttribute(\"aria-hidden\", \"true\");\n li.textContent = this.strings.loadingMore;\n break;\n case \"create\":\n li.className = \"forge-select__option forge-select__option--create\";\n li.setAttribute(\"role\", \"option\");\n li.id = `${this.uid}-nav-${row.navIndex}`;\n li.dataset.navIndex = String(row.navIndex);\n li.textContent = format(this.strings.createOption, { query: this.query.trim() });\n if (row.navIndex === this.highlightedIndex) li.classList.add(\"forge-select__option--highlighted\");\n break;\n case \"option\": {\n li.className = \"forge-select__option\";\n li.setAttribute(\"role\", \"option\");\n const isSelected = this.selected.includes(row.option.value);\n li.setAttribute(\"aria-selected\", String(isSelected));\n if (isSelected) li.classList.add(\"forge-select__option--selected\");\n if (this.opts.multiple && row.hasChildren && computeCheckState(row.option, this.selected) === \"some\") {\n li.classList.add(\"forge-select__option--indeterminate\");\n }\n if (row.depth > 0) {\n li.style.paddingLeft = `calc(12px + ${row.depth} * var(--fs-tree-indent, 18px))`;\n }\n if (row.option.disabled) {\n li.classList.add(\"forge-select__option--disabled\");\n li.setAttribute(\"aria-disabled\", \"true\");\n } else {\n li.id = `${this.uid}-nav-${row.navIndex}`;\n li.dataset.navIndex = String(row.navIndex);\n if (row.navIndex === this.highlightedIndex) li.classList.add(\"forge-select__option--highlighted\");\n }\n if (row.hasChildren) {\n const twisty = document.createElement(\"span\");\n twisty.className = \"forge-select__twisty\";\n twisty.dataset.twisty = row.option.value;\n twisty.setAttribute(\"aria-hidden\", \"true\");\n twisty.textContent = this.expandedValues.has(row.option.value) ? \"▼\" : \"▶\";\n li.append(twisty);\n }\n li.append(this.optionContent(row.option));\n break;\n }\n }\n return li;\n }\n\n /**\n * Rendered row content is cached per option value and cloned on each render,\n * so templates run once per option instead of once per scroll frame.\n * State classes (selected/highlighted/disabled) live on the <li>, keeping the\n * cached content state-free.\n */\n private optionContent(option: Option): Node {\n let cached = this.rowContentCache.get(option.value);\n if (!cached) {\n const holder = document.createElement(\"span\");\n holder.className = \"forge-select__option-content\";\n this.renderTemplate(holder, option, this.opts.templateResult);\n if (this.rowContentCache.size >= ROW_CACHE_LIMIT) {\n // FIFO eviction keeps memory bounded on very large lists.\n const oldest = this.rowContentCache.keys().next().value as string;\n this.rowContentCache.delete(oldest);\n }\n this.rowContentCache.set(option.value, holder);\n cached = holder;\n }\n return cached.cloneNode(true);\n }\n\n private moveHighlight(delta: number): void {\n if (this.navItems.length === 0) return;\n const next =\n this.highlightedIndex === -1 && delta > 0\n ? 0\n : (this.highlightedIndex + delta + this.navItems.length) % this.navItems.length;\n this.highlightedIndex = next;\n\n if (this.usesVirtualScroll()) {\n const rowIndex = this.rows.findIndex(\n (row) => (row.kind === \"option\" || row.kind === \"create\") && row.navIndex === next,\n );\n if (rowIndex >= 0) {\n const rowHeight = this.opts.itemHeight;\n const top = rowIndex * rowHeight;\n const viewport = this.list.clientHeight || rowHeight * 8;\n let target = this.list.scrollTop;\n if (top < target) target = top;\n else if (top + rowHeight > target + viewport) target = top + rowHeight - viewport;\n if (target !== this.list.scrollTop) this.list.scrollTop = target;\n }\n this.renderRows();\n } else {\n this.renderRows();\n const highlighted = this.list.querySelector<HTMLElement>(\".forge-select__option--highlighted\");\n highlighted?.scrollIntoView?.({ block: \"nearest\" });\n }\n }\n\n private updateActiveDescendant(): void {\n const target = this.searchInput ?? this.control;\n if (this.highlightedIndex >= 0) {\n target.setAttribute(\"aria-activedescendant\", `${this.uid}-nav-${this.highlightedIndex}`);\n } else {\n target.removeAttribute(\"aria-activedescendant\");\n }\n }\n\n // ---------------------------------------------------------------- remote data\n\n private scheduleRemoteLoad(query: string, delay: number): void {\n if (this.ajaxTimer) clearTimeout(this.ajaxTimer);\n this.page = 0;\n this.hasMore = true;\n this.loading = true;\n this.renderList();\n this.ajaxTimer = setTimeout(() => {\n void this.loadRemote(query);\n }, delay);\n }\n\n /**\n * Fires on every list scroll. Only acts when pagination is opted into via\n * `ajax.pagination`; reads real scroll geometry rather than row counts so\n * it works whether or not virtual scrolling is active for this list.\n */\n private maybeLoadNextPage(): void {\n const ajax = this.opts.ajax;\n if (!ajax?.pagination || !this.hasMore || this.loading || this.loadingMore) return;\n const { scrollHeight, scrollTop, clientHeight } = this.list;\n const threshold = this.opts.itemHeight * 2;\n if (scrollHeight - scrollTop - clientHeight >= threshold) return;\n this.loadingMore = true;\n this.renderList();\n void this.loadRemote(this.query, { append: true });\n }\n\n private async loadRemote(query: string, { append = false }: { append?: boolean } = {}): Promise<void> {\n const ajax = this.opts.ajax!;\n const requestId = ++this.ajaxRequestId;\n const page = append ? this.page + 1 : 0;\n try {\n const url = buildUrl(ajax, query, page);\n const response = await fetch(url);\n const json: unknown = await response.json();\n if (requestId !== this.ajaxRequestId || this.destroyed) return;\n const result = ajax.transform ? ajax.transform(json) : (json as Option[]);\n const options = Array.isArray(result) ? result : result.options;\n const hasMore = ajax.pagination ? (Array.isArray(result) ? false : result.hasMore) : false;\n\n if (append) {\n const existing = collectValues(this.data);\n this.data = [...this.data, ...options.filter((o) => !existing.has(o.value))];\n } else {\n this.data = options;\n this.rowContentCache.clear();\n }\n this.page = page;\n this.hasMore = hasMore;\n this.remoteLoaded = true;\n } catch {\n if (requestId !== this.ajaxRequestId || this.destroyed) return;\n if (!append) {\n this.data = [];\n this.rowContentCache.clear();\n }\n this.hasMore = false;\n } finally {\n if (requestId === this.ajaxRequestId && !this.destroyed) {\n this.loading = false;\n this.loadingMore = false;\n if (this.isOpen) this.renderList();\n }\n }\n }\n}\n\nfunction parseNativeOptions(select: HTMLSelectElement): DataItem[] {\n const data: DataItem[] = [];\n for (const child of Array.from(select.children)) {\n if (child instanceof HTMLOptGroupElement) {\n data.push({\n label: child.label,\n options: Array.from(child.querySelectorAll(\"option\")).map(parseOption),\n });\n } else if (child instanceof HTMLOptionElement) {\n data.push(parseOption(child));\n }\n }\n return data;\n}\n\nfunction parseOption(option: HTMLOptionElement): Option {\n return {\n value: option.value,\n label: option.textContent?.trim() ?? option.value,\n disabled: option.disabled || undefined,\n };\n}\n\nfunction buildUrl(ajax: AjaxConfig, query: string, page: number): string {\n if (typeof ajax.url === \"function\") return ajax.url(query);\n if (!ajax.params) return ajax.url;\n const params = new URLSearchParams();\n for (const [key, value] of Object.entries(ajax.params(query, page))) {\n params.set(key, String(value));\n }\n const separator = ajax.url.includes(\"?\") ? \"&\" : \"?\";\n return `${ajax.url}${separator}${params.toString()}`;\n}\n\nfunction collectValues(items: DataItem[]): Set<string> {\n const values = new Set<string>();\n for (const item of items) {\n if (isGroup(item)) {\n for (const option of item.options) values.add(option.value);\n } else {\n values.add(item.value);\n }\n }\n return values;\n}\n\nfunction arraysEqual(a: string[], b: string[]): boolean {\n return a.length === b.length && a.every((value, index) => value === b[index]);\n}\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "forge-select",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "A modern, lightweight, highly customizable replacement for Select2.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"select",
|
|
@@ -27,6 +27,9 @@
|
|
|
27
27
|
"publishConfig": {
|
|
28
28
|
"access": "public"
|
|
29
29
|
},
|
|
30
|
+
"workspaces": [
|
|
31
|
+
"packages/*"
|
|
32
|
+
],
|
|
30
33
|
"type": "module",
|
|
31
34
|
"main": "./dist/index.cjs",
|
|
32
35
|
"module": "./dist/index.js",
|