Range Select Guard

Reads back how many rows a shift-click just selected, with Undo, so a runaway range selection doesn't act on rows nobody saw.

StopsShift-click selected thousand rows

Installation

pnpm dlx shadcn@latest add https://bollard-ui.vercel.app/r/range-select-guard.json

The files land in your project. They're yours to edit.

Usage

range-select-guard-usage.tsx
import { RangeSelectGuard, type RangeRow } from "@/components/ui/range-select-guard"

<RangeSelectGuard label="Invoices" rows={rows} pageSize={6} value={selected} onValueChange={setSelected} />

What it catches

  • Shift-click ranges much larger than intended
  • Rows hidden by the filter: skipped and reported
  • Selected rows on pages never opened
  • Selected rows that a filter now hides, with deselect

Props

RangeSelectGuard props
PropTypeDefault
rows* (required)

Every row in display order, including ones the current filter hides.

RangeRow[]—
pageSize

Rows per page.

number10
value

Controlled selected ids.

string[]—
defaultValue

Initial selected ids when uncontrolled.

string[][]
onValueChange

Called with the selected ids.

(ids: string[]) => void—
label

Accessible name of the list.

string"Rows"
rangeText

Templates. `{how}`, `{count}`, `{pages}` are filled in.

string"{how} selected {count} rows"
skippedText

Template for rows skipped because the filter hides them.

string"skipped {count} hidden by your filter"
offPageText

Template for selected rows on pages not opened.

string"{count} are on {pages}, out of view"
onlyPageLabel

Button text to keep only this page's rows.

string"Only the ones I saw ({count})"
undoLabel

Undo button text.

string"Undo"
hiddenSelectedText

Template for selected rows the filter hides.

string"{count} selected rows are hidden by your filter."
deselectHiddenLabel

Button text to deselect hidden rows.

string"Deselect them"

* required. Also accepts the native props of <div> (className, aria-*, data-*…), passed through to it.

Accessibility

  • Pagination buttons have aria-labels; the page indicator is a polite live region
  • Selection readback is a polite live region
  • Focus stays on the clicked row; highlight motion is skipped under reduced motion

Only what the source does. Test with your own screen reader and content before shipping.

Limitations

  • Works on the rows you pass; server-side "select all" isn't covered
  • Bulk actions are yours to gate

What it stops

Shift-click selected thousand rows

Also known as

shift click · range select · multi select · data table · checkbox rows · tanstack table · bulk select

Field type

Dependencies

Pairs well with