forge-select 0.7.0 → 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +64 -50
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +9 -1
- package/dist/index.d.ts +9 -1
- package/dist/index.global.js +1 -1
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +64 -50
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -165,7 +165,7 @@ function renderOptionContent(container, option, template, variant = "row", sanit
|
|
|
165
165
|
// src/remote.ts
|
|
166
166
|
function buildUrl(ajax, query, page, cursor) {
|
|
167
167
|
if (!ajax.url) throw new Error("ForgeSelect: ajax requires either url or request.");
|
|
168
|
-
if (typeof ajax.url === "function") return ajax.url(query, page);
|
|
168
|
+
if (typeof ajax.url === "function") return ajax.url(query, page, cursor);
|
|
169
169
|
if (!ajax.params) return ajax.url;
|
|
170
170
|
const params = new URLSearchParams();
|
|
171
171
|
for (const [key, value] of Object.entries(ajax.params(query, page, cursor))) params.set(key, String(value));
|
|
@@ -581,9 +581,7 @@ var ForgeSelect = class {
|
|
|
581
581
|
this.nativeSelect?.removeEventListener("change", this.onNativeChange);
|
|
582
582
|
this.nativeSelect?.removeEventListener("invalid", this.onNativeInvalid);
|
|
583
583
|
this.nativeForm?.removeEventListener("reset", this.onFormReset);
|
|
584
|
-
this.
|
|
585
|
-
this.rowElementCache.clear();
|
|
586
|
-
this.rowHeightCache.clear();
|
|
584
|
+
this.clearRowCaches();
|
|
587
585
|
this.searchIndex.clear();
|
|
588
586
|
this.portalHost?.remove();
|
|
589
587
|
this.root.remove();
|
|
@@ -675,9 +673,7 @@ var ForgeSelect = class {
|
|
|
675
673
|
}
|
|
676
674
|
this.root.classList.toggle("forge-select--sortable", this.opts.sortable && this.opts.multiple);
|
|
677
675
|
this.updateSearchVisibility();
|
|
678
|
-
this.
|
|
679
|
-
this.rowElementCache.clear();
|
|
680
|
-
this.rowHeightCache.clear();
|
|
676
|
+
this.clearRowCaches();
|
|
681
677
|
this.searchIndex.clear();
|
|
682
678
|
this.renderValue();
|
|
683
679
|
if (this.isOpen) this.renderList();
|
|
@@ -761,9 +757,7 @@ var ForgeSelect = class {
|
|
|
761
757
|
this.afterSelectionChange();
|
|
762
758
|
}
|
|
763
759
|
this.updateSearchVisibility();
|
|
764
|
-
this.
|
|
765
|
-
this.rowElementCache.clear();
|
|
766
|
-
this.rowHeightCache.clear();
|
|
760
|
+
this.clearRowCaches();
|
|
767
761
|
this.searchIndex.clear();
|
|
768
762
|
this.highlightedIndex = -1;
|
|
769
763
|
if (this.isOpen) this.renderList();
|
|
@@ -904,6 +898,7 @@ var ForgeSelect = class {
|
|
|
904
898
|
if (portalParent) {
|
|
905
899
|
this.portalHost = document.createElement("div");
|
|
906
900
|
this.portalHost.className = "forge-select forge-select--portal-host";
|
|
901
|
+
this.portalHost.style.direction = getComputedStyle(this.root).direction;
|
|
907
902
|
this.portalHost.dataset.theme = this.opts.theme;
|
|
908
903
|
this.portalHost.style.setProperty("--fs-item-height", `${this.opts.itemHeight}px`);
|
|
909
904
|
this.portalHost.append(this.dropdown);
|
|
@@ -1282,6 +1277,7 @@ var ForgeSelect = class {
|
|
|
1282
1277
|
this.announceMaximum(existing);
|
|
1283
1278
|
return void 0;
|
|
1284
1279
|
}
|
|
1280
|
+
if (this.opts.beforeSelect?.(existing) === false) return void 0;
|
|
1285
1281
|
this.selectValue(existing.value, false);
|
|
1286
1282
|
return { option: existing, created: false };
|
|
1287
1283
|
}
|
|
@@ -1300,6 +1296,7 @@ var ForgeSelect = class {
|
|
|
1300
1296
|
const duplicate = this.findOption(option.value);
|
|
1301
1297
|
if (duplicate) {
|
|
1302
1298
|
if (this.selected.includes(duplicate.value)) return void 0;
|
|
1299
|
+
if (this.opts.beforeSelect?.(duplicate) === false) return void 0;
|
|
1303
1300
|
this.selectValue(duplicate.value, false);
|
|
1304
1301
|
return { option: duplicate, created: false };
|
|
1305
1302
|
}
|
|
@@ -1580,6 +1577,18 @@ var ForgeSelect = class {
|
|
|
1580
1577
|
usesVirtualScroll() {
|
|
1581
1578
|
return this.opts.virtualScroll !== false && this.rows.length > VIRTUAL_THRESHOLD;
|
|
1582
1579
|
}
|
|
1580
|
+
/**
|
|
1581
|
+
* Drops every per-row cache at once. Rendered content, recycled <li>
|
|
1582
|
+
* elements, and measured heights are all keyed off the current `data`, so
|
|
1583
|
+
* they must be invalidated together whenever `data` is replaced — clearing
|
|
1584
|
+
* only some of them leaves recycled rows carrying state from the old list.
|
|
1585
|
+
*/
|
|
1586
|
+
clearRowCaches() {
|
|
1587
|
+
this.rowContentCache.clear();
|
|
1588
|
+
this.rowElementCache.clear();
|
|
1589
|
+
this.rowHeightCache.clear();
|
|
1590
|
+
this.rowOffsetsCache = null;
|
|
1591
|
+
}
|
|
1583
1592
|
rowKey(row, index) {
|
|
1584
1593
|
if (row.kind === "option") return `option:${row.option.value}:${index}`;
|
|
1585
1594
|
if (row.kind === "group") return `group:${row.label}:${index}`;
|
|
@@ -1693,7 +1702,12 @@ var ForgeSelect = class {
|
|
|
1693
1702
|
"aria-expanded",
|
|
1694
1703
|
"aria-level",
|
|
1695
1704
|
"data-nav-index",
|
|
1696
|
-
"data-option-value"
|
|
1705
|
+
"data-option-value",
|
|
1706
|
+
"data-selection-state",
|
|
1707
|
+
// Tree rows set an inline padding-left indent; clearing the whole
|
|
1708
|
+
// attribute keeps recycling self-contained, so a row reused at a
|
|
1709
|
+
// shallower depth can't inherit the previous row's indent.
|
|
1710
|
+
"style"
|
|
1697
1711
|
])
|
|
1698
1712
|
li.removeAttribute(attribute);
|
|
1699
1713
|
switch (row.kind) {
|
|
@@ -1743,45 +1757,47 @@ var ForgeSelect = class {
|
|
|
1743
1757
|
li.textContent = format(this.strings.createOption, { query: this.query.trim() });
|
|
1744
1758
|
if (row.navIndex === this.highlightedIndex) li.classList.add("forge-select__option--highlighted");
|
|
1745
1759
|
break;
|
|
1746
|
-
case "option":
|
|
1747
|
-
li
|
|
1748
|
-
li.dataset.optionValue = row.option.value;
|
|
1749
|
-
if (row.option.className) li.classList.add(...row.option.className.trim().split(/\s+/).filter(Boolean));
|
|
1750
|
-
li.setAttribute("role", "option");
|
|
1751
|
-
const isSelected = this.selected.includes(row.option.value);
|
|
1752
|
-
li.setAttribute("aria-selected", String(isSelected));
|
|
1753
|
-
if (isSelected) li.classList.add("forge-select__option--selected");
|
|
1754
|
-
if (this.opts.multiple && row.hasChildren && computeCheckState(row.option, this.selected, this.isOptionDisabled) === "some") {
|
|
1755
|
-
li.classList.add("forge-select__option--indeterminate");
|
|
1756
|
-
li.dataset.selectionState = "mixed";
|
|
1757
|
-
}
|
|
1758
|
-
if (row.depth > 0) {
|
|
1759
|
-
li.style.paddingLeft = `calc(12px + ${row.depth} * var(--fs-tree-indent, 18px))`;
|
|
1760
|
-
}
|
|
1761
|
-
if (this.isOptionDisabled(row.option) || this.hasReachedMaximum() && !this.selected.includes(row.option.value)) {
|
|
1762
|
-
li.classList.add("forge-select__option--disabled");
|
|
1763
|
-
li.setAttribute("aria-disabled", "true");
|
|
1764
|
-
} else {
|
|
1765
|
-
li.id = `${this.uid}-nav-${row.navIndex}`;
|
|
1766
|
-
li.dataset.navIndex = String(row.navIndex);
|
|
1767
|
-
if (row.navIndex === this.highlightedIndex) li.classList.add("forge-select__option--highlighted");
|
|
1768
|
-
}
|
|
1769
|
-
if (row.hasChildren) {
|
|
1770
|
-
const expanded = this.query !== "" || this.expandedValues.has(row.option.value);
|
|
1771
|
-
li.setAttribute("aria-expanded", String(expanded));
|
|
1772
|
-
const twisty = document.createElement("span");
|
|
1773
|
-
twisty.className = "forge-select__twisty";
|
|
1774
|
-
twisty.dataset.twisty = row.option.value;
|
|
1775
|
-
twisty.setAttribute("aria-hidden", "true");
|
|
1776
|
-
twisty.textContent = expanded ? "\u25BC" : "\u25B6";
|
|
1777
|
-
li.append(twisty);
|
|
1778
|
-
}
|
|
1779
|
-
li.append(this.optionContent(row.option));
|
|
1760
|
+
case "option":
|
|
1761
|
+
this.renderOptionRow(li, row);
|
|
1780
1762
|
break;
|
|
1781
|
-
}
|
|
1782
1763
|
}
|
|
1783
1764
|
return li;
|
|
1784
1765
|
}
|
|
1766
|
+
renderOptionRow(li, row) {
|
|
1767
|
+
li.className = "forge-select__option";
|
|
1768
|
+
li.dataset.optionValue = row.option.value;
|
|
1769
|
+
if (row.option.className) li.classList.add(...row.option.className.trim().split(/\s+/).filter(Boolean));
|
|
1770
|
+
li.setAttribute("role", "option");
|
|
1771
|
+
const isSelected = this.selected.includes(row.option.value);
|
|
1772
|
+
li.setAttribute("aria-selected", String(isSelected));
|
|
1773
|
+
if (isSelected) li.classList.add("forge-select__option--selected");
|
|
1774
|
+
if (this.opts.multiple && row.hasChildren && computeCheckState(row.option, this.selected, this.isOptionDisabled) === "some") {
|
|
1775
|
+
li.classList.add("forge-select__option--indeterminate");
|
|
1776
|
+
li.dataset.selectionState = "mixed";
|
|
1777
|
+
}
|
|
1778
|
+
if (row.depth > 0) {
|
|
1779
|
+
li.style.paddingLeft = `calc(12px + ${row.depth} * var(--fs-tree-indent, 18px))`;
|
|
1780
|
+
}
|
|
1781
|
+
if (this.isOptionDisabled(row.option) || this.hasReachedMaximum() && !this.selected.includes(row.option.value)) {
|
|
1782
|
+
li.classList.add("forge-select__option--disabled");
|
|
1783
|
+
li.setAttribute("aria-disabled", "true");
|
|
1784
|
+
} else {
|
|
1785
|
+
li.id = `${this.uid}-nav-${row.navIndex}`;
|
|
1786
|
+
li.dataset.navIndex = String(row.navIndex);
|
|
1787
|
+
if (row.navIndex === this.highlightedIndex) li.classList.add("forge-select__option--highlighted");
|
|
1788
|
+
}
|
|
1789
|
+
if (row.hasChildren) {
|
|
1790
|
+
const expanded = this.query !== "" || this.expandedValues.has(row.option.value);
|
|
1791
|
+
li.setAttribute("aria-expanded", String(expanded));
|
|
1792
|
+
const twisty = document.createElement("span");
|
|
1793
|
+
twisty.className = "forge-select__twisty";
|
|
1794
|
+
twisty.dataset.twisty = row.option.value;
|
|
1795
|
+
twisty.setAttribute("aria-hidden", "true");
|
|
1796
|
+
twisty.textContent = expanded ? "\u25BC" : "\u25B6";
|
|
1797
|
+
li.append(twisty);
|
|
1798
|
+
}
|
|
1799
|
+
li.append(this.optionContent(row.option));
|
|
1800
|
+
}
|
|
1785
1801
|
/**
|
|
1786
1802
|
* Rendered row content is cached per option value and cloned on each render,
|
|
1787
1803
|
* so templates run once per option instead of once per scroll frame.
|
|
@@ -2014,8 +2030,7 @@ var ForgeSelect = class {
|
|
|
2014
2030
|
this.data = [...this.data, ...options.filter((o) => !existing.has(o.value))];
|
|
2015
2031
|
} else {
|
|
2016
2032
|
this.data = options;
|
|
2017
|
-
this.
|
|
2018
|
-
this.rowHeightCache.clear();
|
|
2033
|
+
this.clearRowCaches();
|
|
2019
2034
|
}
|
|
2020
2035
|
this.page = page;
|
|
2021
2036
|
this.hasMore = hasMore;
|
|
@@ -2028,8 +2043,7 @@ var ForgeSelect = class {
|
|
|
2028
2043
|
const error = cause instanceof Error ? cause : new Error(String(cause));
|
|
2029
2044
|
if (!append) {
|
|
2030
2045
|
this.data = [];
|
|
2031
|
-
this.
|
|
2032
|
-
this.rowHeightCache.clear();
|
|
2046
|
+
this.clearRowCaches();
|
|
2033
2047
|
}
|
|
2034
2048
|
this.hasMore = false;
|
|
2035
2049
|
this.loadError = error;
|