Changelog
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
Unreleased
0.6.0 - 2026-07-21
Added
- Fuller dropdown keyboard navigation:
Home/Endjump to the first/last option,PageUp/PageDownjump by a page, and typing a letter jumps to the next option starting with it (native<select>-style typeahead, with accent-insensitive matching and repeated-letter cycling).
Changed
- Rendering and caching perf: search no longer clears the rendered-row content cache on every keystroke, a plain (non-capped) selection only re-renders visible rows instead of re-scanning the whole dataset, virtual-scroll row measurement no longer forces a synchronous reflow per row,
rowOffsets()is memoized, and scroll-driven re-renders (including portal dropdown repositioning) are coalesced to one per animation frame.RemoteCachenow bounds its size with FIFO eviction instead of growing unboundedly.
Fixed
- The virtual-scroll measured row-height cache is now cleared alongside the render cache on
setData()/updateOptions()/remote reloads, so it can no longer serve a stale height for a changed option. - Typeahead now runs only from the combobox control (not while typing into search), avoiding a conflict with the search box's own filtering; closing the dropdown also cancels any pending typeahead/scroll work.
- Creating a tag while a multi-select stays open now rebuilds the filtered rows after clearing the query, avoiding a stale create row.
0.5.0 - 2026-07-21
Added
- Runtime
.updateOptions(), controlled search/open state APIs, and reactive wrapper synchronization. - Accent-insensitive token/metadata search, custom scoring, and optional safe match highlighting.
- Remote query/page caching, retry backoff, prefetch,
loadOnOpen,.reload(),.clearRemoteCache(), andloadingevents. - Public validation methods and the
invalidevent. - Variable-height virtual rows via
itemHeight: "auto"and an explicit mixed-selection tree indicator.
Changed
- Search and remote-cache logic now live in focused, directly tested internal modules.
0.4.0 - 2026-07-21
Added
- The dropdown now automatically flips above the control when there isn't room below but there is above (recomputed on open and on window resize) — no configuration needed.
.setData(data)instance method to replace the option list after construction and re-render an already-open dropdown, without a full destroy/remount. A selection whose value isn't in the new data stays selected.closeOnSelectandmaxSelectionsoptions for multi-select: close the dropdown after each pick, and/or cap the number of selected values (including viaallowCreate).filterOptioncustom match predicate andminSearchLengthto gate filtering/remote loads until a minimum query length is reached.requiredoption with native form validation integration: on a native<select>mount it blocks form submission and shows inline invalid styling until a value is picked, plusaria-required.isOptionDisabledcallback for dynamically disabling an option per render, and aclassNamefield onOptionfor custom row styling..selectAll()/.clearAll()instance methods for multi-select.- Pasting a comma/newline-separated string into the search box in tags mode (
allowCreate+multiple) now creates one tag per value instead of a single tag containing the whole string. openOnFocusoption to open the dropdown when the control receives keyboard focus.minResultsForSearchto hide the search input for small local data sets, anddropdownParentto portal the dropdown outside clipping containers.- A custom
ajax.requesttransport for GraphQL, authenticated clients, and other non-URL remote sources. - Typed
select,unselect,create,reorder, andmaximumdetail events.
Fixed
maxSelectionsnow evaluates the complete tree cascade before interactive selection, so descendants/ancestors cannot push the result over the configured limit.setData()now cancels and invalidates pending AJAX work and pagination state, preventing a stale response from overwriting manually supplied data.closeOnSelectnow closes afterallowCreate, and stays open when a pick is rejected bymaxSelections.- Reaching
maxSelectionsnow disables remaining options and announces the limit through the live region.
Changed
- The supported Node.js runtime is now 20.19 or newer, matching the development toolchain requirements.
- React and Vue
datainputs now update existing instances without remounting. - Dropdown placement calculation is isolated in a directly tested internal module.
- CI now verifies compatibility on Node.js 20.19 and 24 in addition to the primary Node.js 22 job.
0.3.0 - 2026-07-21
Added
- Reproducible
npm run benchbaseline for bundle size, initialization, 10,000-option search, virtual-scroll frame timing, and rendered-row count. - Automated axe accessibility checks in the real-browser Playwright suite.
- Repository-wide LF policy and CI coverage for direct pushes to the
devintegration branch. - Quality gates: ESLint, Prettier, 80% V8 coverage thresholds, generated-site/package validation, and Playwright coverage across Chromium, Firefox, and WebKit.
- Additive
setValue(value, { emitChange: false })API and anerrorevent/localized row for remote-load failures. - Keyboard-operable tree navigation with
ArrowRight/ArrowLeftandaria-expandedstate. - Cloudflare deploy: the site now deploys to Cloudflare (Workers with static assets, project
forge-select) on every push tomain, via a Workers Builds project connected directly to this repo through the Cloudflare Dashboard's Git integration (not a GitHub Actions workflow), configured by the newwrangler.jsoncat the repo root; see "Deploying the site" inCONTRIBUTING.md. Adds awranglerdev dependency and adeploy:cloudflarescript for manual/local deploys. - Drag & Drop Ordering:
sortable: true(multi-select only) lets users reorder selected tags by dragging with mouse/touch/pen (Pointer Events), or viaAlt+Left/Alt+Rightwhen a tag has keyboard focus. Fully opt-in — multi-select behavior, markup, and events are unchanged whensortableis left at its defaultfalse. When mounted on a native<select multiple>, the underlying<option>elements are also reordered to match, so a plain<form>submission serializes values in the dragged order. - Live demo: new "Rich items — 1,000 users (multiple)" card showing the built-in
avatar/descriptionrich-item rendering combined withmultiple+ tags on a 1,000-item virtualized list. - The React and Vue wrappers now forward
onOpen/onClose/onSearch/onClear/onErrorprops (React) andopen/close/search/clear/erroremits (Vue), matching the fullForgeSelectEventunion instead of onlychange. - A visually-hidden
aria-live="polite"status region now announces loading/error/no-results state changes to screen readers. - GitHub issue templates (bug report, feature request) and a pull request template under
.github/.
Changed
- Built-in and custom option rendering now lives in a focused internal module with direct XSS-safety and template tests; the public API is unchanged.
- React and Vue package tarballs now include the project MIT license.
- The IIFE bundle (
dist/index.global.js, the CDN/<script>build) is now minified — the ESM/CJS builds are unaffected. - CI: added
concurrencygroups toci.yml/release.ymlso superseded runs don't queue redundantly, added an explicitpermissions: contents: readblock toci.yml, restructured steps sodist//workspace builds run once per job instead of up to 3×, and cached the Playwright browser binaries (keyed on OS + installed@playwright/testversion) so most runs skip the ~1-2 minute browser download. - Dev dependency major bumps, verified individually against the full test/lint/build suite:
@types/react/@types/react-dom18→19 (packages/react),eslint9→10 +typescript-eslintpatch bump,jsdom26→29 (root + both wrapper packages),vitest/@vitest/coverage-v83→4 (root + both wrapper packages).typescript5.9→7.0 was evaluated and not bumped —typescript-eslinthard-errors on TS 7.0 (not yet supported, see typescript-eslint#10940); revisit once that lands. - React/Vue controlled values now synchronize silently; callbacks and model events are reserved for user changes.
- Internal selection, native-select parsing, and remote normalization helpers are split into focused, directly tested modules.
- Site version badges are sourced from
package.json, and generated local links are checked in CI. - Site FAQ (landing page + JSON-LD) and docs no longer say React/Vue/Angular/Svelte wrappers are "on the roadmap but not yet available" —
forge-select-reactandforge-select-vueare published and documented indocs/examples.mdanddocs/api-reference.md; the live demo intro links to both. - README's mini API table expanded from 4 to 14 options;
FUNDING.ymltrimmed to only the activegithubentry;CODE_OF_CONDUCT.mdnow routes reports to the maintainer's GitHub profile instead of the security-vulnerability channel. - Cloudflare is now the canonical live site, at a custom domain:
https://forgeselect.konexforge.com/.homepageinpackage.json(root + both wrapper packages) and every hardcoded canonical/OG/JSON-LD URL acrosssite/,demo/,README.md, anddocs/now point there instead of the old GitHub Pages URL. - Display name is now "Forge Select" (with a space) everywhere it's used as a marketing/brand name — page titles, headings, meta tags, JSON-LD
namefields, and prose across the site,README.md, anddocs/. Code identifiers (ForgeSelectthe TypeScript class,ForgeSelectOptions/ForgeSelectPlugintypes,forge-select/forge-select-react/forge-select-vuenpm package names,.forge-select__*CSS classes) and thecmm-cmm/ForgeSelectGitHub repository name are unaffected — those are technical identifiers, not the display name. - SEO/GEO pass on every site page (informed by another KonexForge site's setup): added
keywords,robots,author,og:locale,og:image:alt, and an explicit<link rel="sitemap">tag; homepage JSON-LD reorganized into a linkedWebSite+SoftwareApplicationgraph (via matching@id/isPartOf) with afeatureList,applicationSubCategory,softwareHelp, andscreenshot;llms.txtgeneration now includes the version number. - Real PNG social-preview image:
og-banner.svgis now rasterized to a 1200×630 PNG at build time (newsharpdev dependency, not committed — generated fresh into_site/assets/on every build) and used forog:image/twitter:image/apple-touch-iconin place of the SVG, withtwitter:cardupgraded fromsummarytosummary_large_image. Resolves the SVG-preview-inconsistency limitation noted in the 0.2.0 entry below (Twitter/X and LinkedIn don't render SVGog:imagereliably). - Version badge: the site header now shows the current version next to the brand name (e.g. "Forge Select v0.2.0") on every page, plus a version eyebrow line on the homepage hero — sourced from
package.jsonat build time everywhere, including the hand-authoredsite/index.html/demo/index.html/site/playground/index.html/site/theme-builder/index.htmlpages, via a{{FORGE_SELECT_VERSION}}placeholder thatscripts/build-site.mjsreplaces at build time, so no manual bump is needed at release time (seeCONTRIBUTING.md).
Removed
- GitHub Pages deployment (
.github/workflows/pages.yml) — retired now that Cloudflare is canonical. If agh-pagesbranch and the GitHub Pages site are still active in this repo's Settings, they should be cleaned up manually (not something a workflow change can do).
Fixed
- Abort and ignore stale AJAX responses immediately when a new debounced query is scheduled; reject unsuccessful HTTP responses.
- Preserve native selected/disabled/display state, inherited optgroup disabling, external changes, and form resets.
- Repair generated React/Vue changelog links and update the supported security version.
ajax.urlas a function now receives the current page number as a second argument, sopagination: trueworks with function-based URLs (it previously always fetched page 0).- Pressing
ArrowUpas the very first navigation keypress now highlights the last item, not the second-to-last. - Selecting/deselecting a tree parent in multi-select mode no longer cascades onto disabled descendants — they were unreachable through the UI (excluded from keyboard/click navigation) and could get stuck permanently selected.
ajax.transformreturning a value that isn't an array or{ options, hasMore }now throws a clear error instead of silently producing a broken options list.- The custom combobox control now forwards an accessible name from the original element's
aria-label/aria-labelledby, or from an existing<label for>pointing at it — previously that association was lost once the original<select>/element becamedisplay:none. - The loading/error/no-results rows inside the
role="listbox"dropdown now exposerole="option"/aria-disabled, fixing anaria-required-childrenviolation (an axe scan of the empty-state dropdown found the listbox had no accessible children). - Default theme's muted text color (
--fs-muted, used by the loading/error/empty rows and helper text) darkened from#9ca3afto#6b7280to meet the WCAG AA 4.5:1 contrast minimum against the dropdown's white background.
0.2.0 - 2026-07-14
Changed
- Website rebranded with a green accent palette and a cursor logo/favicon across all pages.
- README Features list no longer lists unshipped items ("Tree Select", "Infinite Scroll") as if they were available; a pointer to the Roadmap replaces them.
Added
CHANGELOG.md,LICENSE,SECURITY.md,CONTRIBUTING.md, andCODE_OF_CONDUCT.md(Contributor Covenant v2.1)..github/workflows/release.yml: publishes to npm with provenance onvX.Y.Ztags (or manual dispatch), gated on typecheck/test/build and a tag/version match check.homepage,bugs,engines, andpublishConfigfields inpackage.json;prepublishOnlyruns the full verification suite before any publish.- SEO: canonical links, Open Graph, and Twitter Card meta tags on every page. The preview image (
site/assets/og-banner.svg) is a temporary SVG banner — some platforms (Twitter/X, LinkedIn) render SVGog:image/twitter:imageinconsistently; swap for a PNG once raster-image tooling is available. - GEO: JSON-LD structured data (
SoftwareApplication,FAQPage,BreadcrumbList,TechArticleon docs pages) plus generatedsitemap.xml,robots.txt, andllms.txt, all sourced from a single page-metadata list inscripts/build-site.mjs. - FAQ section on the homepage; introductory copy on the demo and playground pages so a visitor landing directly on either still gets context on what Forge Select is.
- The Changelog is now rendered on the site (
docs/changelog.html), sourced from this file. - Theme Builder: a new site page (
/theme-builder/) for live-editing every--fs-*CSS variable against real Forge Select instances, starting from Light/Dark presets, with a one-click "Copy CSS" output. Site-only — no library code changes. - Async pagination:
ajax.paginationopts in to loading further pages as the user scrolls near the bottom of the dropdown, instead of only reloading on search (paramsnow also receives the currentpage;transformmay return{ options, hasMore }to control it). Fully backward compatible — existingajaxconfigs withoutpaginationbehave exactly as before, and cached row content is preserved across appended pages instead of being cleared. - Tree select: add
childrento anyOptionto get an expandable/collapsible tree node, with cascading select/deselect and an indeterminate state for partially-selected parents inmultiplemode. Purely additive — lists with nochildrenanywhere render and behave exactly as before. - Framework wrapper packages: new npm workspaces under
packages/—forge-select-reactandforge-select-vue, each a thin component wrapping the core library with controlled-value support (onChange/v-model). CI now also runs typecheck/test/build across workspaces after the core library's own steps. Published independently via their own tags (react-v*,vue-v*); unscoped names (rather than@forge-select/*) so publishing doesn't depend on first creating an npm organization.
Fixed
- Playground stylesheet order so site accent overrides apply to Forge Select widgets.
- Virtual scroll rendering a mostly-blank window after scrolling: the viewport height was read from
list.clientHeightafter the list's children were cleared, at which point the list (no explicitheight, onlymax-height) collapses to its padding (~8px) instead of the real box height. This under-provisioned the rendered row window on every scroll-triggered re-render, leaving a visible blank gap at the bottom of the dropdown for any scroll position other than the very top.
0.1.0 - 2026-07-12
Added
- Core library:
ForgeSelectclass with options (placeholder,searchable,multiple,clearable,allowCreate,theme,disabled,data,ajax,templateResult/templateSelection,virtualScroll,itemHeight,language,plugins), instance methods (open,close,destroy,getValue,setValue,enable,disable,on,off), and events (change,open,close,search,clear). - Rich items: built-in
avatar,description, andmetafields onOptionwith an XSS-safe layout; search also matches descriptions. - Performance: automatic virtualization for lists over ~100 rows, per-option render caching (FIFO-bounded), lazy-loaded avatars, and a
scrollTopcapture/restore fix for real-browser scrolling. - Plugin architecture:
onInit,onOpen,onClose,onDestroylifecycle hooks. - Theming: CSS-variable-driven default and dark themes.
- i18n: built-in
en/vilocales and custom string tables. - Tooling: strict TypeScript, tsup build (ESM + CJS + IIFE +
.d.ts), vitest + jsdom test suite, GitHub Actions CI, GitHub Pages deployment. - Website: landing page, rendered documentation, interactive playground, and feature demo at https://cmm-cmm.github.io/ForgeSelect/.
- Documentation: API reference, examples, playground guide, Select2 migration guide, benchmarks methodology, and plugin development guide under
docs/.