mirror of
https://github.com/supabase/supabase.git
synced 2026-10-05 09:25:06 +03:00
Stack 3/4 · previous: #50687 · next: #50689 ## Problem `DataTableInfinite` could only open one row at a time. Multi-select meant clicking a separate checkbox column, with no support for ranges or modifier keys. ## Solution This PR only adds shared primitives. Behavior stays the same until a consumer passes `onSelectRow` (wired up in the next PR). - `selectTableRow` (`rowSelection.utils.ts`): a pure reducer for plain, Cmd/Ctrl-toggle, Shift-range, and additive-range selection over the current display order, with a fixed anchor. - `useTableRowSelection`: holds the selection state and resets it when its `scope` (project/filters) changes. - `DataTableProvider` gets an optional `onSelectRow`. When it's set, `DataTableInfinite` rows use it and reflect `row.getIsSelected()`. Rows also get `aria-selected`, Space activates them like Enter, and shift-click no longer selects text. ## Review instructions 1. Read `rowSelection.utils.test.ts`: it covers the selection rules. 2. Open Unified Logs. Row clicks should behave the same as on master. ## Checklist - [ ] I have read [CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Added richer data-table row selection, including multi-select, range selection, toggling, and keyboard support. - Added visual and accessibility feedback for selected rows, including pointer cursor and `aria-selected`. - Preserved selections across paging and live updates while resetting them when filters or projects change. - Added support for external row-selection callbacks. - **Tests** - Added comprehensive coverage for selection behavior and state persistence. <!-- end of auto-generated comment: release notes by coderabbit.ai --> Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
54 lines
1.6 KiB
TypeScript
54 lines
1.6 KiB
TypeScript
import type { RowSelectionState } from '@tanstack/react-table'
|
|
|
|
export interface RowSelectionModifiers {
|
|
shiftKey?: boolean
|
|
metaKey?: boolean
|
|
ctrlKey?: boolean
|
|
toggle?: boolean
|
|
}
|
|
|
|
export interface TableRowSelection {
|
|
selected: RowSelectionState
|
|
anchorId?: string
|
|
activeId?: string
|
|
}
|
|
|
|
export function selectTableRow(
|
|
state: TableRowSelection,
|
|
orderedIds: string[],
|
|
targetId: string,
|
|
modifiers: RowSelectionModifiers = {}
|
|
): TableRowSelection {
|
|
const targetIndex = orderedIds.indexOf(targetId)
|
|
if (targetIndex === -1) return state
|
|
|
|
const anchorIndex = state.anchorId ? orderedIds.indexOf(state.anchorId) : -1
|
|
const isAdditive = modifiers.metaKey || modifiers.ctrlKey
|
|
let selected: RowSelectionState = {}
|
|
|
|
if (modifiers.shiftKey && anchorIndex !== -1) {
|
|
selected = isAdditive ? { ...state.selected } : {}
|
|
for (const id of orderedIds.slice(
|
|
Math.min(anchorIndex, targetIndex),
|
|
Math.max(anchorIndex, targetIndex) + 1
|
|
)) {
|
|
selected[id] = true
|
|
}
|
|
} else if (isAdditive || modifiers.toggle) {
|
|
selected = { ...state.selected }
|
|
if (selected[targetId]) delete selected[targetId]
|
|
else selected[targetId] = true
|
|
} else {
|
|
// Plain click on the only selected row clears the selection.
|
|
const selectedIds = Object.keys(state.selected).filter((id) => state.selected[id])
|
|
if (selectedIds.length === 1 && selectedIds[0] === targetId) return { selected: {} }
|
|
selected[targetId] = true
|
|
}
|
|
|
|
return {
|
|
selected,
|
|
anchorId: modifiers.shiftKey && anchorIndex !== -1 ? state.anchorId : targetId,
|
|
activeId: targetId,
|
|
}
|
|
}
|