forge-select 0.7.1 → 0.7.3
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 +2 -6
- package/dist/index.cjs +54 -9
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +21 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.global.js +1 -1
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +54 -9
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -324,6 +324,12 @@ var ForgeSelect = class {
|
|
|
324
324
|
this.highlightedIndex = -1;
|
|
325
325
|
this.typeaheadBuffer = "";
|
|
326
326
|
this.typeaheadTimer = null;
|
|
327
|
+
// Keyed by the option object, not its value: duplicateValuePolicy only warns
|
|
328
|
+
// by default, so two options can share a value while carrying different
|
|
329
|
+
// labels, and a value-keyed cache would render the first one's content for
|
|
330
|
+
// both. Still explicitly bounded — a WeakMap would not collect anything while
|
|
331
|
+
// `data` holds every key, so scrolling a long list would retain a detached
|
|
332
|
+
// node per option visited.
|
|
327
333
|
this.rowContentCache = /* @__PURE__ */ new Map();
|
|
328
334
|
this.rowElementCache = /* @__PURE__ */ new Map();
|
|
329
335
|
this.rowHeightCache = /* @__PURE__ */ new Map();
|
|
@@ -511,8 +517,21 @@ var ForgeSelect = class {
|
|
|
511
517
|
this.typeaheadBuffer = "";
|
|
512
518
|
this.highlightedIndex = -1;
|
|
513
519
|
if (this.searchInput) {
|
|
520
|
+
const hadQuery = this.query !== "";
|
|
514
521
|
this.searchInput.value = "";
|
|
515
522
|
this.query = "";
|
|
523
|
+
if (hadQuery && this.opts.ajax) {
|
|
524
|
+
this.ajaxRequestId += 1;
|
|
525
|
+
if (this.ajaxTimer) {
|
|
526
|
+
clearTimeout(this.ajaxTimer);
|
|
527
|
+
this.ajaxTimer = null;
|
|
528
|
+
}
|
|
529
|
+
this.ajaxController?.abort();
|
|
530
|
+
this.ajaxController = null;
|
|
531
|
+
this.setLoading(false);
|
|
532
|
+
this.loadingMore = false;
|
|
533
|
+
this.remoteLoaded = false;
|
|
534
|
+
}
|
|
516
535
|
}
|
|
517
536
|
this.emitter.emit("close");
|
|
518
537
|
for (const plugin of this.plugins) plugin.onClose?.(this);
|
|
@@ -1562,11 +1581,28 @@ var ForgeSelect = class {
|
|
|
1562
1581
|
this.rowHeightCache.clear();
|
|
1563
1582
|
this.rowOffsetsCache = null;
|
|
1564
1583
|
}
|
|
1584
|
+
/** Identifies a row *position*, which is what measured heights are tied to. */
|
|
1565
1585
|
rowKey(row, index) {
|
|
1566
1586
|
if (row.kind === "option") return `option:${row.option.value}:${index}`;
|
|
1567
1587
|
if (row.kind === "group") return `group:${row.label}:${index}`;
|
|
1568
1588
|
return `${row.kind}:${index}`;
|
|
1569
1589
|
}
|
|
1590
|
+
/**
|
|
1591
|
+
* Identifies a row's *content* for `<li>` recycling, deliberately without the
|
|
1592
|
+
* row index: filtering shifts every index below the first change, so an
|
|
1593
|
+
* index-keyed element cache misses on every keystroke — exactly when the list
|
|
1594
|
+
* re-renders most. renderRow() rewrites the element completely, so reusing it
|
|
1595
|
+
* at a new position is safe.
|
|
1596
|
+
*
|
|
1597
|
+
* Keys are not guaranteed unique within a render — duplicateValuePolicy
|
|
1598
|
+
* defaults to warning rather than rejecting duplicate values — so callers
|
|
1599
|
+
* must not hand the same element to two rows of one render.
|
|
1600
|
+
*/
|
|
1601
|
+
rowElementKey(row) {
|
|
1602
|
+
if (row.kind === "option") return `option:${row.option.value}`;
|
|
1603
|
+
if (row.kind === "group") return `group:${row.label}`;
|
|
1604
|
+
return row.kind;
|
|
1605
|
+
}
|
|
1570
1606
|
measuredRowHeight(index) {
|
|
1571
1607
|
return this.opts.variableItemHeight ? this.rowHeightCache.get(this.rowKey(this.rows[index], index)) ?? this.opts.itemHeight : this.opts.itemHeight;
|
|
1572
1608
|
}
|
|
@@ -1628,9 +1664,12 @@ var ForgeSelect = class {
|
|
|
1628
1664
|
this.list.append(topSpacer);
|
|
1629
1665
|
}
|
|
1630
1666
|
const appended = [];
|
|
1667
|
+
const claimed = /* @__PURE__ */ new Set();
|
|
1631
1668
|
for (let i = start; i < end; i++) {
|
|
1632
|
-
const key = this.
|
|
1633
|
-
const
|
|
1669
|
+
const key = this.rowElementKey(this.rows[i]);
|
|
1670
|
+
const cached = this.rowElementCache.get(key);
|
|
1671
|
+
const element = this.renderRow(this.rows[i], cached && !claimed.has(cached) ? cached : void 0);
|
|
1672
|
+
claimed.add(element);
|
|
1634
1673
|
this.rowElementCache.set(key, element);
|
|
1635
1674
|
if (this.rowElementCache.size > ROW_CACHE_LIMIT) {
|
|
1636
1675
|
const oldest = this.rowElementCache.keys().next().value;
|
|
@@ -1800,7 +1839,7 @@ var ForgeSelect = class {
|
|
|
1800
1839
|
}
|
|
1801
1840
|
return holder;
|
|
1802
1841
|
}
|
|
1803
|
-
let cached = this.rowContentCache.get(option
|
|
1842
|
+
let cached = this.rowContentCache.get(option);
|
|
1804
1843
|
if (!cached) {
|
|
1805
1844
|
const holder = document.createElement("span");
|
|
1806
1845
|
holder.className = "forge-select__option-content";
|
|
@@ -1809,7 +1848,7 @@ var ForgeSelect = class {
|
|
|
1809
1848
|
const oldest = this.rowContentCache.keys().next().value;
|
|
1810
1849
|
this.rowContentCache.delete(oldest);
|
|
1811
1850
|
}
|
|
1812
|
-
this.rowContentCache.set(option
|
|
1851
|
+
this.rowContentCache.set(option, holder);
|
|
1813
1852
|
cached = holder;
|
|
1814
1853
|
}
|
|
1815
1854
|
return cached.cloneNode(true);
|
|
@@ -1877,13 +1916,19 @@ var ForgeSelect = class {
|
|
|
1877
1916
|
}
|
|
1878
1917
|
return false;
|
|
1879
1918
|
}
|
|
1919
|
+
/**
|
|
1920
|
+
* Reads the id off the row the render pass just marked, rather than rebuilding
|
|
1921
|
+
* it from the highlight index. Virtual scrolling drops off-window rows, so an
|
|
1922
|
+
* index-derived id can name an element that no longer exists, and
|
|
1923
|
+
* aria-activedescendant must resolve to a real one — a dangling reference
|
|
1924
|
+
* reads to assistive tech as no active option at all. Keyboard navigation
|
|
1925
|
+
* scrolls the highlight back into view, which restores the reference.
|
|
1926
|
+
*/
|
|
1880
1927
|
updateActiveDescendant() {
|
|
1881
1928
|
const target = this.searchInput ?? this.control;
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
target.removeAttribute("aria-activedescendant");
|
|
1886
|
-
}
|
|
1929
|
+
const highlighted = this.list.querySelector(".forge-select__option--highlighted");
|
|
1930
|
+
if (highlighted) target.setAttribute("aria-activedescendant", highlighted.id);
|
|
1931
|
+
else target.removeAttribute("aria-activedescendant");
|
|
1887
1932
|
}
|
|
1888
1933
|
// ---------------------------------------------------------------- remote data
|
|
1889
1934
|
scheduleRemoteLoad(query, delay) {
|