Skip to content

Fix highlight clamping and add VirtualList to CommandPalette#6

Merged
zion-off merged 1 commit intomainfrom
fix-select-component-bugs
Feb 20, 2026
Merged

Fix highlight clamping and add VirtualList to CommandPalette#6
zion-off merged 1 commit intomainfrom
fix-select-component-bugs

Conversation

@zion-off
Copy link
Owner

Motivation

The Select, MultiSelect, Autocomplete, and CommandPalette components were calling setHighlightIndex during render to clamp the highlight when the options list shrank. This violates React's rules and can cause render loops or warnings.

Changes

  • Move highlight-index clamping from render-time into a useEffect in Select, MultiSelect, and Autocomplete
  • Wrap the Autocomplete filtered-options computation in useMemo to avoid recalculating on every render
  • Replace the plain .map() list in CommandPalette with VirtualList, adding maxVisible and paginatorStyle props for scrollable command lists

…andPalette

Avoid state updates during render in Select, MultiSelect, and
Autocomplete by moving highlightIndex clamping into useEffect.
Memoize Autocomplete's filtered list. Retrofit CommandPalette with
VirtualList and expose maxVisible/paginatorStyle props.
@zion-off zion-off merged commit 227e241 into main Feb 20, 2026
1 check passed
zion-off added a commit that referenced this pull request Feb 23, 2026
FocusStore gains store.dispatch(input, key) — the full dispatch algorithm:
passive-scope skipping (the key new capability), capture mode, trap boundary,
and mounted-binding fallback. normalizeKey is called inside the store.

FocusContext is updated to dual-register every node in the store and to call
store.focusNode whenever it moves focus. This keeps the store's active branch
path and focusedId in sync with FocusContext's tree so that store.dispatch
walks the correct nodes while old UI components still register through
FocusContext. The bridge is removed in chunk #6.

InputRouter is reduced to a single line:
  useInput((input, key) => store.dispatch(input, key))
The inline dispatch loop and its useFocusContext/useStore calls are gone.
zion-off added a commit that referenced this pull request Feb 24, 2026
FocusStore gains store.dispatch(input, key) — the full dispatch algorithm:
passive-scope skipping (the key new capability), capture mode, trap boundary,
and mounted-binding fallback. normalizeKey is called inside the store.

FocusContext is updated to dual-register every node in the store and to call
store.focusNode whenever it moves focus. This keeps the store's active branch
path and focusedId in sync with FocusContext's tree so that store.dispatch
walks the correct nodes while old UI components still register through
FocusContext. The bridge is removed in chunk #6.

InputRouter is reduced to a single line:
  useInput((input, key) => store.dispatch(input, key))
The inline dispatch loop and its useFocusContext/useStore calls are gone.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant