forge-select 0.7.1 → 0.7.2
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 +43 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +13 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.global.js +1 -1
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +43 -4
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -61,8 +61,6 @@ Browse the documentation website at **<https://forgeselect.konexforge.com/docs/>
|
|
|
61
61
|
- Internationalization (en/vi built in, custom string tables)
|
|
62
62
|
- TypeScript Support (written in strict TypeScript, ships `.d.ts`)
|
|
63
63
|
|
|
64
|
-
> Planned/in-progress capabilities — Angular/Svelte wrappers — are tracked in the [Roadmap](#roadmap) below and intentionally not listed above as shipped features.
|
|
65
|
-
|
|
66
64
|
## Installation
|
|
67
65
|
|
|
68
66
|
```bash
|
|
@@ -189,8 +187,8 @@ Forge Select is vanilla TypeScript/JavaScript, so it can be mounted inside any f
|
|
|
189
187
|
- Vanilla JavaScript
|
|
190
188
|
- React — via [`forge-select-react`](./packages/react/README.md) (`ForgeSelectReact` component, controlled `value`/`onChange`)
|
|
191
189
|
- Vue — via [`forge-select-vue`](./packages/vue/README.md) (`ForgeSelectVue` component, `v-model` support)
|
|
192
|
-
- Angular — mount manually
|
|
193
|
-
- Svelte — mount manually
|
|
190
|
+
- Angular — mount manually (see [`docs/examples.md`](./docs/examples.md))
|
|
191
|
+
- Svelte — mount manually (see [`docs/examples.md`](./docs/examples.md))
|
|
194
192
|
- Next.js
|
|
195
193
|
- Nuxt
|
|
196
194
|
- Astro
|
|
@@ -221,8 +219,6 @@ Run `npm run bench` for a reproducible JSON baseline covering bundle size, initi
|
|
|
221
219
|
- [x] CSS Variables
|
|
222
220
|
- [x] React Component
|
|
223
221
|
- [x] Vue Component
|
|
224
|
-
- [ ] Angular Component
|
|
225
|
-
- [ ] Svelte Component
|
|
226
222
|
|
|
227
223
|
## Plugin Development Guide
|
|
228
224
|
|
package/dist/index.cjs
CHANGED
|
@@ -351,6 +351,12 @@ var ForgeSelect = class {
|
|
|
351
351
|
this.highlightedIndex = -1;
|
|
352
352
|
this.typeaheadBuffer = "";
|
|
353
353
|
this.typeaheadTimer = null;
|
|
354
|
+
// Keyed by the option object, not its value: duplicateValuePolicy only warns
|
|
355
|
+
// by default, so two options can share a value while carrying different
|
|
356
|
+
// labels, and a value-keyed cache would render the first one's content for
|
|
357
|
+
// both. Still explicitly bounded — a WeakMap would not collect anything while
|
|
358
|
+
// `data` holds every key, so scrolling a long list would retain a detached
|
|
359
|
+
// node per option visited.
|
|
354
360
|
this.rowContentCache = /* @__PURE__ */ new Map();
|
|
355
361
|
this.rowElementCache = /* @__PURE__ */ new Map();
|
|
356
362
|
this.rowHeightCache = /* @__PURE__ */ new Map();
|
|
@@ -538,8 +544,21 @@ var ForgeSelect = class {
|
|
|
538
544
|
this.typeaheadBuffer = "";
|
|
539
545
|
this.highlightedIndex = -1;
|
|
540
546
|
if (this.searchInput) {
|
|
547
|
+
const hadQuery = this.query !== "";
|
|
541
548
|
this.searchInput.value = "";
|
|
542
549
|
this.query = "";
|
|
550
|
+
if (hadQuery && this.opts.ajax) {
|
|
551
|
+
this.ajaxRequestId += 1;
|
|
552
|
+
if (this.ajaxTimer) {
|
|
553
|
+
clearTimeout(this.ajaxTimer);
|
|
554
|
+
this.ajaxTimer = null;
|
|
555
|
+
}
|
|
556
|
+
this.ajaxController?.abort();
|
|
557
|
+
this.ajaxController = null;
|
|
558
|
+
this.setLoading(false);
|
|
559
|
+
this.loadingMore = false;
|
|
560
|
+
this.remoteLoaded = false;
|
|
561
|
+
}
|
|
543
562
|
}
|
|
544
563
|
this.emitter.emit("close");
|
|
545
564
|
for (const plugin of this.plugins) plugin.onClose?.(this);
|
|
@@ -1589,11 +1608,28 @@ var ForgeSelect = class {
|
|
|
1589
1608
|
this.rowHeightCache.clear();
|
|
1590
1609
|
this.rowOffsetsCache = null;
|
|
1591
1610
|
}
|
|
1611
|
+
/** Identifies a row *position*, which is what measured heights are tied to. */
|
|
1592
1612
|
rowKey(row, index) {
|
|
1593
1613
|
if (row.kind === "option") return `option:${row.option.value}:${index}`;
|
|
1594
1614
|
if (row.kind === "group") return `group:${row.label}:${index}`;
|
|
1595
1615
|
return `${row.kind}:${index}`;
|
|
1596
1616
|
}
|
|
1617
|
+
/**
|
|
1618
|
+
* Identifies a row's *content* for `<li>` recycling, deliberately without the
|
|
1619
|
+
* row index: filtering shifts every index below the first change, so an
|
|
1620
|
+
* index-keyed element cache misses on every keystroke — exactly when the list
|
|
1621
|
+
* re-renders most. renderRow() rewrites the element completely, so reusing it
|
|
1622
|
+
* at a new position is safe.
|
|
1623
|
+
*
|
|
1624
|
+
* Keys are not guaranteed unique within a render — duplicateValuePolicy
|
|
1625
|
+
* defaults to warning rather than rejecting duplicate values — so callers
|
|
1626
|
+
* must not hand the same element to two rows of one render.
|
|
1627
|
+
*/
|
|
1628
|
+
rowElementKey(row) {
|
|
1629
|
+
if (row.kind === "option") return `option:${row.option.value}`;
|
|
1630
|
+
if (row.kind === "group") return `group:${row.label}`;
|
|
1631
|
+
return row.kind;
|
|
1632
|
+
}
|
|
1597
1633
|
measuredRowHeight(index) {
|
|
1598
1634
|
return this.opts.variableItemHeight ? this.rowHeightCache.get(this.rowKey(this.rows[index], index)) ?? this.opts.itemHeight : this.opts.itemHeight;
|
|
1599
1635
|
}
|
|
@@ -1655,9 +1691,12 @@ var ForgeSelect = class {
|
|
|
1655
1691
|
this.list.append(topSpacer);
|
|
1656
1692
|
}
|
|
1657
1693
|
const appended = [];
|
|
1694
|
+
const claimed = /* @__PURE__ */ new Set();
|
|
1658
1695
|
for (let i = start; i < end; i++) {
|
|
1659
|
-
const key = this.
|
|
1660
|
-
const
|
|
1696
|
+
const key = this.rowElementKey(this.rows[i]);
|
|
1697
|
+
const cached = this.rowElementCache.get(key);
|
|
1698
|
+
const element = this.renderRow(this.rows[i], cached && !claimed.has(cached) ? cached : void 0);
|
|
1699
|
+
claimed.add(element);
|
|
1661
1700
|
this.rowElementCache.set(key, element);
|
|
1662
1701
|
if (this.rowElementCache.size > ROW_CACHE_LIMIT) {
|
|
1663
1702
|
const oldest = this.rowElementCache.keys().next().value;
|
|
@@ -1827,7 +1866,7 @@ var ForgeSelect = class {
|
|
|
1827
1866
|
}
|
|
1828
1867
|
return holder;
|
|
1829
1868
|
}
|
|
1830
|
-
let cached = this.rowContentCache.get(option
|
|
1869
|
+
let cached = this.rowContentCache.get(option);
|
|
1831
1870
|
if (!cached) {
|
|
1832
1871
|
const holder = document.createElement("span");
|
|
1833
1872
|
holder.className = "forge-select__option-content";
|
|
@@ -1836,7 +1875,7 @@ var ForgeSelect = class {
|
|
|
1836
1875
|
const oldest = this.rowContentCache.keys().next().value;
|
|
1837
1876
|
this.rowContentCache.delete(oldest);
|
|
1838
1877
|
}
|
|
1839
|
-
this.rowContentCache.set(option
|
|
1878
|
+
this.rowContentCache.set(option, holder);
|
|
1840
1879
|
cached = holder;
|
|
1841
1880
|
}
|
|
1842
1881
|
return cached.cloneNode(true);
|