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.global.js
CHANGED
|
@@ -56,6 +56,7 @@ var ForgeSelectBundle = (() => {
|
|
|
56
56
|
en: {
|
|
57
57
|
noResults: "No results found",
|
|
58
58
|
loading: "Loading\u2026",
|
|
59
|
+
loadingMore: "Loading more\u2026",
|
|
59
60
|
createOption: 'Create "{query}"',
|
|
60
61
|
clearSelection: "Clear selection",
|
|
61
62
|
removeItem: "Remove {label}",
|
|
@@ -64,6 +65,7 @@ var ForgeSelectBundle = (() => {
|
|
|
64
65
|
vi: {
|
|
65
66
|
noResults: "Kh\xF4ng t\xECm th\u1EA5y k\u1EBFt qu\u1EA3",
|
|
66
67
|
loading: "\u0110ang t\u1EA3i\u2026",
|
|
68
|
+
loadingMore: "\u0110ang t\u1EA3i th\xEAm\u2026",
|
|
67
69
|
createOption: 'T\u1EA1o "{query}"',
|
|
68
70
|
clearSelection: "X\xF3a l\u1EF1a ch\u1ECDn",
|
|
69
71
|
removeItem: "X\xF3a {label}",
|
|
@@ -89,6 +91,23 @@ var ForgeSelectBundle = (() => {
|
|
|
89
91
|
function isGroup(item) {
|
|
90
92
|
return item.options !== void 0;
|
|
91
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
|
+
}
|
|
92
111
|
var ForgeSelect = class {
|
|
93
112
|
constructor(target, options = {}) {
|
|
94
113
|
this.selected = [];
|
|
@@ -104,7 +123,11 @@ var ForgeSelectBundle = (() => {
|
|
|
104
123
|
this.navItems = [];
|
|
105
124
|
this.highlightedIndex = -1;
|
|
106
125
|
this.rowContentCache = /* @__PURE__ */ new Map();
|
|
126
|
+
this.expandedValues = /* @__PURE__ */ new Set();
|
|
107
127
|
this.loading = false;
|
|
128
|
+
this.loadingMore = false;
|
|
129
|
+
this.page = 0;
|
|
130
|
+
this.hasMore = true;
|
|
108
131
|
this.ajaxTimer = null;
|
|
109
132
|
this.ajaxRequestId = 0;
|
|
110
133
|
this.remoteLoaded = false;
|
|
@@ -294,13 +317,23 @@ var ForgeSelectBundle = (() => {
|
|
|
294
317
|
this.searchInput.addEventListener("keydown", (event) => this.handleKeydown(event));
|
|
295
318
|
}
|
|
296
319
|
this.list.addEventListener("click", (event) => {
|
|
297
|
-
const
|
|
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]");
|
|
298
330
|
if (!li) return;
|
|
299
331
|
const navIndex = Number(li.dataset.navIndex);
|
|
300
332
|
this.activateNavItem(navIndex);
|
|
301
333
|
});
|
|
302
334
|
this.list.addEventListener("scroll", () => {
|
|
303
335
|
if (this.usesVirtualScroll()) this.renderRows();
|
|
336
|
+
this.maybeLoadNextPage();
|
|
304
337
|
});
|
|
305
338
|
}
|
|
306
339
|
handleKeydown(event) {
|
|
@@ -343,16 +376,53 @@ var ForgeSelectBundle = (() => {
|
|
|
343
376
|
if (this.selected.includes(value)) return;
|
|
344
377
|
const option = this.findOption(value) ?? this.selectedOptions.get(value) ?? { value, label: value };
|
|
345
378
|
this.selectedOptions.set(value, option);
|
|
346
|
-
if (this.opts.multiple)
|
|
347
|
-
|
|
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
|
+
}
|
|
348
388
|
if (notify) this.afterSelectionChange();
|
|
349
389
|
}
|
|
350
390
|
deselectValue(value, notify) {
|
|
351
391
|
const index = this.selected.indexOf(value);
|
|
352
392
|
if (index === -1) return;
|
|
353
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
|
+
}
|
|
354
404
|
if (notify) this.afterSelectionChange();
|
|
355
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
|
+
}
|
|
356
426
|
clearSelection() {
|
|
357
427
|
if (this.selected.length === 0) return;
|
|
358
428
|
this.selected = [];
|
|
@@ -383,13 +453,19 @@ var ForgeSelectBundle = (() => {
|
|
|
383
453
|
this.el.dispatchEvent(new Event("change", { bubbles: true }));
|
|
384
454
|
}
|
|
385
455
|
findOption(value) {
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
if (
|
|
390
|
-
|
|
391
|
-
|
|
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
|
+
}
|
|
392
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;
|
|
393
469
|
}
|
|
394
470
|
return void 0;
|
|
395
471
|
}
|
|
@@ -507,13 +583,23 @@ var ForgeSelectBundle = (() => {
|
|
|
507
583
|
this.navItems = [];
|
|
508
584
|
const query = this.query.trim().toLowerCase();
|
|
509
585
|
const matches = (option) => query === "" || option.label.toLowerCase().includes(query) || (option.description?.toLowerCase().includes(query) ?? false);
|
|
510
|
-
const
|
|
586
|
+
const subtreeMatches = (option) => query === "" || matches(option) || (option.children ?? []).some(subtreeMatches);
|
|
587
|
+
const pushOption = (option, depth) => {
|
|
511
588
|
let navIndex = -1;
|
|
512
589
|
if (!option.disabled) {
|
|
513
590
|
navIndex = this.navItems.length;
|
|
514
591
|
this.navItems.push({ kind: "option", option });
|
|
515
592
|
}
|
|
516
|
-
|
|
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
|
+
}
|
|
517
603
|
};
|
|
518
604
|
if (this.loading) {
|
|
519
605
|
this.rows.push({ kind: "loading" });
|
|
@@ -521,12 +607,12 @@ var ForgeSelectBundle = (() => {
|
|
|
521
607
|
}
|
|
522
608
|
for (const item of this.data) {
|
|
523
609
|
if (isGroup(item)) {
|
|
524
|
-
const visible = item.options.filter(
|
|
610
|
+
const visible = item.options.filter(subtreeMatches);
|
|
525
611
|
if (visible.length === 0) continue;
|
|
526
612
|
this.rows.push({ kind: "group", label: item.label });
|
|
527
|
-
visible.forEach(pushOption);
|
|
528
|
-
} else if (
|
|
529
|
-
pushOption(item);
|
|
613
|
+
visible.forEach((o) => pushOption(o, 0));
|
|
614
|
+
} else if (subtreeMatches(item)) {
|
|
615
|
+
pushOption(item, 0);
|
|
530
616
|
}
|
|
531
617
|
}
|
|
532
618
|
if (this.opts.allowCreate && query !== "" && !this.hasExactMatch(query)) {
|
|
@@ -535,11 +621,13 @@ var ForgeSelectBundle = (() => {
|
|
|
535
621
|
this.rows.push({ kind: "create", navIndex });
|
|
536
622
|
}
|
|
537
623
|
if (this.rows.length === 0) this.rows.push({ kind: "empty" });
|
|
624
|
+
else if (this.loadingMore) this.rows.push({ kind: "loading-more" });
|
|
538
625
|
}
|
|
539
626
|
hasExactMatch(lowerQuery) {
|
|
627
|
+
const matchesExactly = (option) => option.label.toLowerCase() === lowerQuery || (option.children ?? []).some(matchesExactly);
|
|
540
628
|
for (const item of this.data) {
|
|
541
629
|
const options = isGroup(item) ? item.options : [item];
|
|
542
|
-
if (options.some(
|
|
630
|
+
if (options.some(matchesExactly)) return true;
|
|
543
631
|
}
|
|
544
632
|
return false;
|
|
545
633
|
}
|
|
@@ -599,6 +687,11 @@ var ForgeSelectBundle = (() => {
|
|
|
599
687
|
li.className = "forge-select__loading";
|
|
600
688
|
li.textContent = this.strings.loading;
|
|
601
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;
|
|
602
695
|
case "create":
|
|
603
696
|
li.className = "forge-select__option forge-select__option--create";
|
|
604
697
|
li.setAttribute("role", "option");
|
|
@@ -613,6 +706,12 @@ var ForgeSelectBundle = (() => {
|
|
|
613
706
|
const isSelected = this.selected.includes(row.option.value);
|
|
614
707
|
li.setAttribute("aria-selected", String(isSelected));
|
|
615
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
|
+
}
|
|
616
715
|
if (row.option.disabled) {
|
|
617
716
|
li.classList.add("forge-select__option--disabled");
|
|
618
717
|
li.setAttribute("aria-disabled", "true");
|
|
@@ -621,6 +720,14 @@ var ForgeSelectBundle = (() => {
|
|
|
621
720
|
li.dataset.navIndex = String(row.navIndex);
|
|
622
721
|
if (row.navIndex === this.highlightedIndex) li.classList.add("forge-select__option--highlighted");
|
|
623
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
|
+
}
|
|
624
731
|
li.append(this.optionContent(row.option));
|
|
625
732
|
break;
|
|
626
733
|
}
|
|
@@ -683,30 +790,62 @@ var ForgeSelectBundle = (() => {
|
|
|
683
790
|
// ---------------------------------------------------------------- remote data
|
|
684
791
|
scheduleRemoteLoad(query, delay) {
|
|
685
792
|
if (this.ajaxTimer) clearTimeout(this.ajaxTimer);
|
|
793
|
+
this.page = 0;
|
|
794
|
+
this.hasMore = true;
|
|
686
795
|
this.loading = true;
|
|
687
796
|
this.renderList();
|
|
688
797
|
this.ajaxTimer = setTimeout(() => {
|
|
689
798
|
void this.loadRemote(query);
|
|
690
799
|
}, delay);
|
|
691
800
|
}
|
|
692
|
-
|
|
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 } = {}) {
|
|
693
817
|
const ajax = this.opts.ajax;
|
|
694
818
|
const requestId = ++this.ajaxRequestId;
|
|
819
|
+
const page = append ? this.page + 1 : 0;
|
|
695
820
|
try {
|
|
696
|
-
const url = buildUrl(ajax, query);
|
|
821
|
+
const url = buildUrl(ajax, query, page);
|
|
697
822
|
const response = await fetch(url);
|
|
698
823
|
const json = await response.json();
|
|
699
824
|
if (requestId !== this.ajaxRequestId || this.destroyed) return;
|
|
700
|
-
|
|
701
|
-
|
|
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;
|
|
702
837
|
this.remoteLoaded = true;
|
|
703
838
|
} catch {
|
|
704
839
|
if (requestId !== this.ajaxRequestId || this.destroyed) return;
|
|
705
|
-
|
|
706
|
-
|
|
840
|
+
if (!append) {
|
|
841
|
+
this.data = [];
|
|
842
|
+
this.rowContentCache.clear();
|
|
843
|
+
}
|
|
844
|
+
this.hasMore = false;
|
|
707
845
|
} finally {
|
|
708
846
|
if (requestId === this.ajaxRequestId && !this.destroyed) {
|
|
709
847
|
this.loading = false;
|
|
848
|
+
this.loadingMore = false;
|
|
710
849
|
if (this.isOpen) this.renderList();
|
|
711
850
|
}
|
|
712
851
|
}
|
|
@@ -733,16 +872,27 @@ var ForgeSelectBundle = (() => {
|
|
|
733
872
|
disabled: option.disabled || void 0
|
|
734
873
|
};
|
|
735
874
|
}
|
|
736
|
-
function buildUrl(ajax, query) {
|
|
875
|
+
function buildUrl(ajax, query, page) {
|
|
737
876
|
if (typeof ajax.url === "function") return ajax.url(query);
|
|
738
877
|
if (!ajax.params) return ajax.url;
|
|
739
878
|
const params = new URLSearchParams();
|
|
740
|
-
for (const [key, value] of Object.entries(ajax.params(query))) {
|
|
879
|
+
for (const [key, value] of Object.entries(ajax.params(query, page))) {
|
|
741
880
|
params.set(key, String(value));
|
|
742
881
|
}
|
|
743
882
|
const separator = ajax.url.includes("?") ? "&" : "?";
|
|
744
883
|
return `${ajax.url}${separator}${params.toString()}`;
|
|
745
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
|
+
}
|
|
746
896
|
function arraysEqual(a, b) {
|
|
747
897
|
return a.length === b.length && a.every((value, index) => value === b[index]);
|
|
748
898
|
}
|
package/dist/index.global.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/emitter.ts","../src/i18n.ts","../src/ForgeSelect.ts"],"sourcesContent":["export { default } from \"./ForgeSelect\";\nexport { default as ForgeSelect } from \"./ForgeSelect\";\nexport type {\n AjaxConfig,\n DataItem,\n ForgeSelectEvent,\n ForgeSelectOptions,\n ForgeSelectPlugin,\n ForgeSelectValue,\n Option,\n OptionGroup,\n TemplateFn,\n} from \"./types\";\n","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/index.ts","../src/emitter.ts","../src/i18n.ts","../src/ForgeSelect.ts"],"sourcesContent":["export { default } from \"./ForgeSelect\";\nexport { default as ForgeSelect } from \"./ForgeSelect\";\nexport type {\n AjaxConfig,\n DataItem,\n ForgeSelectEvent,\n ForgeSelectOptions,\n ForgeSelectPlugin,\n ForgeSelectValue,\n Option,\n OptionGroup,\n TemplateFn,\n} from \"./types\";\n","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":[]}
|