Imported from markom01/Selection-Widget (
src/components/SelectionSection/AGENTS.md). Install upstream withnpx skills add markom01/Selection-Widget --skill SelectionSection. Copyright stays with the author.
SelectionSection
OVERVIEW
Expandable selection UI — the "edit" view of the widget. Renders a virtualized list of up to 10001 elements with search, numeric filter, and draft-buffer save/cancel. All inside an MUI Collapse (not a modal).
FILES
SelectionSection/
├── ElementsList.tsx # react-window v2 virtualized <List>, 400px fixed height
├── ElementRow.tsx # Memoized row: Checkbox + label, highlight, disabled logic
├── SearchAndFilterBar.tsx # Text search + filter dropdown (>100, >2500, >10000)
├── SelectedPreviewSection.tsx # Temp selected chips + Save (primary) / Cancel (outlined)
├── SelectionSection.tsx # Orchestrator: filter → list → preview, Collapse wrapper
├── index.ts # Barrel exports all 5 components
├── *.test.tsx # Co-located Vitest tests (5 files, ~400+ lines)
KEY PATTERNS
- react-window v2:
RowisReact.memowrapped, receivesstylefrom parent<List>.rowComponent={Row},rowProps={itemData},rowCount,rowHeight={48},overscanCount={5}. - Parent height constraint:
<Box sx={{ height: 400, overflow: 'hidden' }}>wraps<List>. Without this, react-window v2 renders all items. - Memo chain:
ElementRow(memoized) ←Row(memoized) ←ElementsList(derived data viauseMemo). - Disabled logic:
isDisabled = selectedSet.size >= 3 && !isSelected. Selected items always stay enabled so you can deselect. - Empty state:
ElementsListshows "No elements match your search" centered whenfilteredElements.length === 0.
WHERE TO LOOK
| Need | File | Details |
|---|---|---|
| Virtualization performance | ElementsList.tsx |
Row memo + useMemo for itemData |
| Disable/unselected behavior | ElementRow.tsx |
Opacity 0.5 on .Mui-disabled, tabIndex={-1} on checkbox |
| Save/Cancel enabled state | SelectedPreviewSection.tsx |
Save disabled when tempSelectedItems.length === 0 |
| Section collapse/expand | SelectionSection.tsx |
<Collapse in={isSectionOpen}> |
| Filter option values | config/constants.ts |
FILTER_OPTIONS: 'all', 100, 2500, 10000 |
INTERFACES
ElementRowProps—item,isSelected,isDisabled,onToggleElementsListProps—filteredElements,tempSelectedIds,onToggleItemSearchAndFilterBarProps—searchQuery,filterValue,onSearchChange,onFilterChangeSelectedPreviewSectionProps—tempSelectedItems,onRemoveTempItem,onSave,onCancelRowData— internal:items,selectedSet,onToggleItem,maxSelectionSelectionSection— reads store directly (no props), selectors split into individualuseSelectionStore()calls
CONVENTIONS
- All props interfaces co-located in the same
.tsxfile (not extracted to types/) React.memo+displayNameonElementRowand internalRow- All visible text from
CONSTANTS.LABELS sxprop only, no styled-components