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.js
CHANGED
|
@@ -138,7 +138,7 @@ function renderOptionContent(container, option, template, variant = "row", sanit
|
|
|
138
138
|
// src/remote.ts
|
|
139
139
|
function buildUrl(ajax, query, page, cursor) {
|
|
140
140
|
if (!ajax.url) throw new Error("ForgeSelect: ajax requires either url or request.");
|
|
141
|
-
if (typeof ajax.url === "function") return ajax.url(query, page);
|
|
141
|
+
if (typeof ajax.url === "function") return ajax.url(query, page, cursor);
|
|
142
142
|
if (!ajax.params) return ajax.url;
|
|
143
143
|
const params = new URLSearchParams();
|
|
144
144
|
for (const [key, value] of Object.entries(ajax.params(query, page, cursor))) params.set(key, String(value));
|
|
@@ -554,9 +554,7 @@ var ForgeSelect = class {
|
|
|
554
554
|
this.nativeSelect?.removeEventListener("change", this.onNativeChange);
|
|
555
555
|
this.nativeSelect?.removeEventListener("invalid", this.onNativeInvalid);
|
|
556
556
|
this.nativeForm?.removeEventListener("reset", this.onFormReset);
|
|
557
|
-
this.
|
|
558
|
-
this.rowElementCache.clear();
|
|
559
|
-
this.rowHeightCache.clear();
|
|
557
|
+
this.clearRowCaches();
|
|
560
558
|
this.searchIndex.clear();
|
|
561
559
|
this.portalHost?.remove();
|
|
562
560
|
this.root.remove();
|
|
@@ -648,9 +646,7 @@ var ForgeSelect = class {
|
|
|
648
646
|
}
|
|
649
647
|
this.root.classList.toggle("forge-select--sortable", this.opts.sortable && this.opts.multiple);
|
|
650
648
|
this.updateSearchVisibility();
|
|
651
|
-
this.
|
|
652
|
-
this.rowElementCache.clear();
|
|
653
|
-
this.rowHeightCache.clear();
|
|
649
|
+
this.clearRowCaches();
|
|
654
650
|
this.searchIndex.clear();
|
|
655
651
|
this.renderValue();
|
|
656
652
|
if (this.isOpen) this.renderList();
|
|
@@ -734,9 +730,7 @@ var ForgeSelect = class {
|
|
|
734
730
|
this.afterSelectionChange();
|
|
735
731
|
}
|
|
736
732
|
this.updateSearchVisibility();
|
|
737
|
-
this.
|
|
738
|
-
this.rowElementCache.clear();
|
|
739
|
-
this.rowHeightCache.clear();
|
|
733
|
+
this.clearRowCaches();
|
|
740
734
|
this.searchIndex.clear();
|
|
741
735
|
this.highlightedIndex = -1;
|
|
742
736
|
if (this.isOpen) this.renderList();
|
|
@@ -877,6 +871,7 @@ var ForgeSelect = class {
|
|
|
877
871
|
if (portalParent) {
|
|
878
872
|
this.portalHost = document.createElement("div");
|
|
879
873
|
this.portalHost.className = "forge-select forge-select--portal-host";
|
|
874
|
+
this.portalHost.style.direction = getComputedStyle(this.root).direction;
|
|
880
875
|
this.portalHost.dataset.theme = this.opts.theme;
|
|
881
876
|
this.portalHost.style.setProperty("--fs-item-height", `${this.opts.itemHeight}px`);
|
|
882
877
|
this.portalHost.append(this.dropdown);
|
|
@@ -1255,6 +1250,7 @@ var ForgeSelect = class {
|
|
|
1255
1250
|
this.announceMaximum(existing);
|
|
1256
1251
|
return void 0;
|
|
1257
1252
|
}
|
|
1253
|
+
if (this.opts.beforeSelect?.(existing) === false) return void 0;
|
|
1258
1254
|
this.selectValue(existing.value, false);
|
|
1259
1255
|
return { option: existing, created: false };
|
|
1260
1256
|
}
|
|
@@ -1273,6 +1269,7 @@ var ForgeSelect = class {
|
|
|
1273
1269
|
const duplicate = this.findOption(option.value);
|
|
1274
1270
|
if (duplicate) {
|
|
1275
1271
|
if (this.selected.includes(duplicate.value)) return void 0;
|
|
1272
|
+
if (this.opts.beforeSelect?.(duplicate) === false) return void 0;
|
|
1276
1273
|
this.selectValue(duplicate.value, false);
|
|
1277
1274
|
return { option: duplicate, created: false };
|
|
1278
1275
|
}
|
|
@@ -1553,6 +1550,18 @@ var ForgeSelect = class {
|
|
|
1553
1550
|
usesVirtualScroll() {
|
|
1554
1551
|
return this.opts.virtualScroll !== false && this.rows.length > VIRTUAL_THRESHOLD;
|
|
1555
1552
|
}
|
|
1553
|
+
/**
|
|
1554
|
+
* Drops every per-row cache at once. Rendered content, recycled <li>
|
|
1555
|
+
* elements, and measured heights are all keyed off the current `data`, so
|
|
1556
|
+
* they must be invalidated together whenever `data` is replaced — clearing
|
|
1557
|
+
* only some of them leaves recycled rows carrying state from the old list.
|
|
1558
|
+
*/
|
|
1559
|
+
clearRowCaches() {
|
|
1560
|
+
this.rowContentCache.clear();
|
|
1561
|
+
this.rowElementCache.clear();
|
|
1562
|
+
this.rowHeightCache.clear();
|
|
1563
|
+
this.rowOffsetsCache = null;
|
|
1564
|
+
}
|
|
1556
1565
|
rowKey(row, index) {
|
|
1557
1566
|
if (row.kind === "option") return `option:${row.option.value}:${index}`;
|
|
1558
1567
|
if (row.kind === "group") return `group:${row.label}:${index}`;
|
|
@@ -1666,7 +1675,12 @@ var ForgeSelect = class {
|
|
|
1666
1675
|
"aria-expanded",
|
|
1667
1676
|
"aria-level",
|
|
1668
1677
|
"data-nav-index",
|
|
1669
|
-
"data-option-value"
|
|
1678
|
+
"data-option-value",
|
|
1679
|
+
"data-selection-state",
|
|
1680
|
+
// Tree rows set an inline padding-left indent; clearing the whole
|
|
1681
|
+
// attribute keeps recycling self-contained, so a row reused at a
|
|
1682
|
+
// shallower depth can't inherit the previous row's indent.
|
|
1683
|
+
"style"
|
|
1670
1684
|
])
|
|
1671
1685
|
li.removeAttribute(attribute);
|
|
1672
1686
|
switch (row.kind) {
|
|
@@ -1716,45 +1730,47 @@ var ForgeSelect = class {
|
|
|
1716
1730
|
li.textContent = format(this.strings.createOption, { query: this.query.trim() });
|
|
1717
1731
|
if (row.navIndex === this.highlightedIndex) li.classList.add("forge-select__option--highlighted");
|
|
1718
1732
|
break;
|
|
1719
|
-
case "option":
|
|
1720
|
-
li
|
|
1721
|
-
li.dataset.optionValue = row.option.value;
|
|
1722
|
-
if (row.option.className) li.classList.add(...row.option.className.trim().split(/\s+/).filter(Boolean));
|
|
1723
|
-
li.setAttribute("role", "option");
|
|
1724
|
-
const isSelected = this.selected.includes(row.option.value);
|
|
1725
|
-
li.setAttribute("aria-selected", String(isSelected));
|
|
1726
|
-
if (isSelected) li.classList.add("forge-select__option--selected");
|
|
1727
|
-
if (this.opts.multiple && row.hasChildren && computeCheckState(row.option, this.selected, this.isOptionDisabled) === "some") {
|
|
1728
|
-
li.classList.add("forge-select__option--indeterminate");
|
|
1729
|
-
li.dataset.selectionState = "mixed";
|
|
1730
|
-
}
|
|
1731
|
-
if (row.depth > 0) {
|
|
1732
|
-
li.style.paddingLeft = `calc(12px + ${row.depth} * var(--fs-tree-indent, 18px))`;
|
|
1733
|
-
}
|
|
1734
|
-
if (this.isOptionDisabled(row.option) || this.hasReachedMaximum() && !this.selected.includes(row.option.value)) {
|
|
1735
|
-
li.classList.add("forge-select__option--disabled");
|
|
1736
|
-
li.setAttribute("aria-disabled", "true");
|
|
1737
|
-
} else {
|
|
1738
|
-
li.id = `${this.uid}-nav-${row.navIndex}`;
|
|
1739
|
-
li.dataset.navIndex = String(row.navIndex);
|
|
1740
|
-
if (row.navIndex === this.highlightedIndex) li.classList.add("forge-select__option--highlighted");
|
|
1741
|
-
}
|
|
1742
|
-
if (row.hasChildren) {
|
|
1743
|
-
const expanded = this.query !== "" || this.expandedValues.has(row.option.value);
|
|
1744
|
-
li.setAttribute("aria-expanded", String(expanded));
|
|
1745
|
-
const twisty = document.createElement("span");
|
|
1746
|
-
twisty.className = "forge-select__twisty";
|
|
1747
|
-
twisty.dataset.twisty = row.option.value;
|
|
1748
|
-
twisty.setAttribute("aria-hidden", "true");
|
|
1749
|
-
twisty.textContent = expanded ? "\u25BC" : "\u25B6";
|
|
1750
|
-
li.append(twisty);
|
|
1751
|
-
}
|
|
1752
|
-
li.append(this.optionContent(row.option));
|
|
1733
|
+
case "option":
|
|
1734
|
+
this.renderOptionRow(li, row);
|
|
1753
1735
|
break;
|
|
1754
|
-
}
|
|
1755
1736
|
}
|
|
1756
1737
|
return li;
|
|
1757
1738
|
}
|
|
1739
|
+
renderOptionRow(li, row) {
|
|
1740
|
+
li.className = "forge-select__option";
|
|
1741
|
+
li.dataset.optionValue = row.option.value;
|
|
1742
|
+
if (row.option.className) li.classList.add(...row.option.className.trim().split(/\s+/).filter(Boolean));
|
|
1743
|
+
li.setAttribute("role", "option");
|
|
1744
|
+
const isSelected = this.selected.includes(row.option.value);
|
|
1745
|
+
li.setAttribute("aria-selected", String(isSelected));
|
|
1746
|
+
if (isSelected) li.classList.add("forge-select__option--selected");
|
|
1747
|
+
if (this.opts.multiple && row.hasChildren && computeCheckState(row.option, this.selected, this.isOptionDisabled) === "some") {
|
|
1748
|
+
li.classList.add("forge-select__option--indeterminate");
|
|
1749
|
+
li.dataset.selectionState = "mixed";
|
|
1750
|
+
}
|
|
1751
|
+
if (row.depth > 0) {
|
|
1752
|
+
li.style.paddingLeft = `calc(12px + ${row.depth} * var(--fs-tree-indent, 18px))`;
|
|
1753
|
+
}
|
|
1754
|
+
if (this.isOptionDisabled(row.option) || this.hasReachedMaximum() && !this.selected.includes(row.option.value)) {
|
|
1755
|
+
li.classList.add("forge-select__option--disabled");
|
|
1756
|
+
li.setAttribute("aria-disabled", "true");
|
|
1757
|
+
} else {
|
|
1758
|
+
li.id = `${this.uid}-nav-${row.navIndex}`;
|
|
1759
|
+
li.dataset.navIndex = String(row.navIndex);
|
|
1760
|
+
if (row.navIndex === this.highlightedIndex) li.classList.add("forge-select__option--highlighted");
|
|
1761
|
+
}
|
|
1762
|
+
if (row.hasChildren) {
|
|
1763
|
+
const expanded = this.query !== "" || this.expandedValues.has(row.option.value);
|
|
1764
|
+
li.setAttribute("aria-expanded", String(expanded));
|
|
1765
|
+
const twisty = document.createElement("span");
|
|
1766
|
+
twisty.className = "forge-select__twisty";
|
|
1767
|
+
twisty.dataset.twisty = row.option.value;
|
|
1768
|
+
twisty.setAttribute("aria-hidden", "true");
|
|
1769
|
+
twisty.textContent = expanded ? "\u25BC" : "\u25B6";
|
|
1770
|
+
li.append(twisty);
|
|
1771
|
+
}
|
|
1772
|
+
li.append(this.optionContent(row.option));
|
|
1773
|
+
}
|
|
1758
1774
|
/**
|
|
1759
1775
|
* Rendered row content is cached per option value and cloned on each render,
|
|
1760
1776
|
* so templates run once per option instead of once per scroll frame.
|
|
@@ -1987,8 +2003,7 @@ var ForgeSelect = class {
|
|
|
1987
2003
|
this.data = [...this.data, ...options.filter((o) => !existing.has(o.value))];
|
|
1988
2004
|
} else {
|
|
1989
2005
|
this.data = options;
|
|
1990
|
-
this.
|
|
1991
|
-
this.rowHeightCache.clear();
|
|
2006
|
+
this.clearRowCaches();
|
|
1992
2007
|
}
|
|
1993
2008
|
this.page = page;
|
|
1994
2009
|
this.hasMore = hasMore;
|
|
@@ -2001,8 +2016,7 @@ var ForgeSelect = class {
|
|
|
2001
2016
|
const error = cause instanceof Error ? cause : new Error(String(cause));
|
|
2002
2017
|
if (!append) {
|
|
2003
2018
|
this.data = [];
|
|
2004
|
-
this.
|
|
2005
|
-
this.rowHeightCache.clear();
|
|
2019
|
+
this.clearRowCaches();
|
|
2006
2020
|
}
|
|
2007
2021
|
this.hasMore = false;
|
|
2008
2022
|
this.loadError = error;
|