Implement shift + click selection

This commit is contained in:
IRBorisov 2024-03-25 23:10:29 +03:00
parent 22ce5094d4
commit c1fc52821b
6 changed files with 58 additions and 12 deletions

View File

@ -100,6 +100,7 @@ function DataTable<TData extends RowData>({
...restProps
}: DataTableProps<TData>) {
const [sorting, setSorting] = useState<SortingState>(initialSorting ? [initialSorting] : []);
const [lastSelected, setLastSelected] = useState<string | undefined>(undefined);
const [pagination, setPagination] = useState<PaginationState>({
pageIndex: 0,
@ -147,6 +148,7 @@ function DataTable<TData extends RowData>({
enableRowSelection={enableRowSelection}
enableSorting={enableSorting}
headPosition={headPosition}
setLastSelected={setLastSelected}
/>
) : null}
@ -155,6 +157,8 @@ function DataTable<TData extends RowData>({
dense={dense}
conditionalRowStyles={conditionalRowStyles}
enableRowSelection={enableRowSelection}
lastSelected={lastSelected}
setLastSelected={setLastSelected}
onRowClicked={onRowClicked}
onRowDoubleClicked={onRowDoubleClicked}
/>

View File

@ -4,9 +4,15 @@ import CheckboxTristate from '@/components/ui/CheckboxTristate';
interface SelectAllProps<TData> {
table: Table<TData>;
setLastSelected: React.Dispatch<React.SetStateAction<string | undefined>>;
}
function SelectAll<TData>({ table, setLastSelected }: SelectAllProps<TData>) {
function handleChange(value: boolean | null) {
setLastSelected(undefined);
table.toggleAllPageRowsSelected(value !== false);
}
function SelectAll<TData>({ table }: SelectAllProps<TData>) {
return (
<CheckboxTristate
tabIndex={-1}
@ -14,7 +20,7 @@ function SelectAll<TData>({ table }: SelectAllProps<TData>) {
value={
!table.getIsAllPageRowsSelected() && table.getIsSomePageRowsSelected() ? null : table.getIsAllPageRowsSelected()
}
setValue={value => table.toggleAllPageRowsSelected(value !== false)}
setValue={handleChange}
/>
);
}

View File

@ -4,10 +4,16 @@ import Checkbox from '@/components/ui/Checkbox';
interface SelectRowProps<TData> {
row: Row<TData>;
setLastSelected: React.Dispatch<React.SetStateAction<string | undefined>>;
}
function SelectRow<TData>({ row }: SelectRowProps<TData>) {
return <Checkbox tabIndex={-1} value={row.getIsSelected()} setValue={row.getToggleSelectedHandler()} />;
function SelectRow<TData>({ row, setLastSelected }: SelectRowProps<TData>) {
function handleChange(value: boolean) {
setLastSelected(row.id);
row.toggleSelected(value);
}
return <Checkbox tabIndex={-1} value={row.getIsSelected()} setValue={handleChange} />;
}
export default SelectRow;

View File

@ -8,6 +8,10 @@ interface TableBodyProps<TData> {
dense?: boolean;
enableRowSelection?: boolean;
conditionalRowStyles?: IConditionalStyle<TData>[];
lastSelected: string | undefined;
setLastSelected: React.Dispatch<React.SetStateAction<string | undefined>>;
onRowClicked?: (rowData: TData, event: React.MouseEvent<Element, MouseEvent>) => void;
onRowDoubleClicked?: (rowData: TData, event: React.MouseEvent<Element, MouseEvent>) => void;
}
@ -17,15 +21,34 @@ function TableBody<TData>({
dense,
enableRowSelection,
conditionalRowStyles,
lastSelected,
setLastSelected,
onRowClicked,
onRowDoubleClicked
}: TableBodyProps<TData>) {
function handleRowClicked(row: Row<TData>, event: React.MouseEvent<Element, MouseEvent>) {
function handleRowClicked(target: Row<TData>, event: React.MouseEvent<Element, MouseEvent>) {
if (onRowClicked) {
onRowClicked(row.original, event);
onRowClicked(target.original, event);
}
if (enableRowSelection && target.getCanSelect()) {
if (event.shiftKey && !!lastSelected && lastSelected !== target.id) {
const { rows, rowsById } = table.getRowModel();
const lastIndex = rowsById[lastSelected].index;
const currentIndex = target.index;
const toggleRows = rows.slice(
lastIndex > currentIndex ? currentIndex : lastIndex + 1,
lastIndex > currentIndex ? lastIndex : currentIndex + 1
);
const newSelection: { [key: string]: boolean } = {};
toggleRows.forEach(row => {
newSelection[row.id] = !target.getIsSelected();
});
table.setRowSelection(prev => ({ ...prev, ...newSelection }));
setLastSelected(undefined);
} else {
setLastSelected(target.id);
target.toggleSelected(!target.getIsSelected());
}
if (enableRowSelection && row.getCanSelect()) {
row.getToggleSelectedHandler()(!row.getIsSelected());
}
}
@ -53,7 +76,7 @@ function TableBody<TData>({
>
{enableRowSelection ? (
<td key={`select-${row.id}`} className='pl-3 pr-1 align-middle border-y'>
<SelectRow row={row} />
<SelectRow row={row} setLastSelected={setLastSelected} />
</td>
) : null}
{row.getVisibleCells().map((cell: Cell<TData, unknown>) => (

View File

@ -8,9 +8,16 @@ interface TableHeaderProps<TData> {
headPosition?: string;
enableRowSelection?: boolean;
enableSorting?: boolean;
setLastSelected: React.Dispatch<React.SetStateAction<string | undefined>>;
}
function TableHeader<TData>({ table, headPosition, enableRowSelection, enableSorting }: TableHeaderProps<TData>) {
function TableHeader<TData>({
table,
headPosition,
enableRowSelection,
enableSorting,
setLastSelected
}: TableHeaderProps<TData>) {
return (
<thead
className='clr-app shadow-border'
@ -23,7 +30,7 @@ function TableHeader<TData>({ table, headPosition, enableRowSelection, enableSor
<tr key={headerGroup.id}>
{enableRowSelection ? (
<th className='pl-3 pr-1 align-middle'>
<SelectAll table={table} />
<SelectAll table={table} setLastSelected={setLastSelected} />
</th>
) : null}
{headerGroup.headers.map((header: Header<TData, unknown>) => (

View File

@ -3,8 +3,8 @@
import clsx from 'clsx';
import { useLayoutEffect, useMemo, useState } from 'react';
import { type RowSelectionState } from '@/components/ui/DataTable';
import SelectedCounter from '@/components/info/SelectedCounter';
import { type RowSelectionState } from '@/components/ui/DataTable';
import { useConceptTheme } from '@/context/ThemeContext';
import { ConstituentaID, CstType } from '@/models/rsform';