Skip to content

Commit b7eb1dc

Browse files
committed
Fix multi-row selection state issue when (de)selecting all rows for deletion.
1 parent dcef302 commit b7eb1dc

File tree

2 files changed

+63
-44
lines changed

2 files changed

+63
-44
lines changed

trailbase-assets/js/admin/src/components/Table.tsx

Lines changed: 37 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import {
22
For,
33
Show,
4+
createEffect,
45
createMemo,
56
createSignal,
67
splitProps,
@@ -14,9 +15,11 @@ import {
1415
} from "@tanstack/solid-table";
1516
import type {
1617
ColumnDef,
17-
Table as TableType,
18-
PaginationState,
1918
OnChangeFn,
19+
PaginationState,
20+
Row,
21+
RowSelectionState,
22+
Table as TableType,
2023
} from "@tanstack/solid-table";
2124

2225
import { Button } from "@/components/ui/button";
@@ -60,13 +63,18 @@ type Props<TData, TValue> = {
6063
pagination?: PaginationState;
6164
onPaginationChange?: OnChangeFn<PaginationState>;
6265

63-
onRowSelection?: (idx: number, row: TData, value: boolean) => void;
66+
onRowSelection?: (rows: Row<TData>[], value: boolean) => void;
6467
onRowClick?: (idx: number, row: TData) => void;
6568
};
6669

6770
export function DataTable<TData, TValue>(props: Props<TData, TValue>) {
6871
const [local] = splitProps(props, ["columns", "data"]);
69-
const [rowSelection, setRowSelection] = createSignal({});
72+
const [rowSelection, setRowSelection] = createSignal<RowSelectionState>({});
73+
createEffect(() => {
74+
// NOTE: because we use our own state for row selection, reset it when data changes.
75+
local.data();
76+
setRowSelection({});
77+
});
7078

7179
const paginationEnabled = () => props.onPaginationChange !== undefined;
7280
const [paginationState, setPaginationState] =
@@ -82,7 +90,7 @@ export function DataTable<TData, TValue>(props: Props<TData, TValue>) {
8290
};
8391
});
8492

85-
const columns = createMemo(() => {
93+
const columns = () => {
8694
const onRowSelection = props.onRowSelection;
8795
if (!onRowSelection) {
8896
return local.columns();
@@ -93,18 +101,28 @@ export function DataTable<TData, TValue>(props: Props<TData, TValue>) {
93101
id: "select",
94102
header: (ctx) => (
95103
<Checkbox
96-
indeterminate={ctx.table.getIsSomePageRowsSelected()}
97104
checked={ctx.table.getIsAllPageRowsSelected()}
98-
onChange={(value) => ctx.table.toggleAllPageRowsSelected(!!value)}
105+
onChange={(value: boolean) => {
106+
console.debug(
107+
"Select all",
108+
value,
109+
ctx.table.getIsSomeRowsSelected(),
110+
);
111+
ctx.table.toggleAllPageRowsSelected(value);
112+
113+
const allRows = ctx.table.getRowModel();
114+
onRowSelection(allRows.rows, value);
115+
}}
99116
aria-label="Select all"
100117
/>
101118
),
102119
cell: (ctx) => (
103120
<Checkbox
104121
checked={ctx.row.getIsSelected()}
105-
onChange={(value) => {
106-
onRowSelection(ctx.row.index, ctx.row.original, value);
122+
onChange={(value: boolean) => {
107123
ctx.row.toggleSelected(value);
124+
125+
onRowSelection([ctx.row], value);
108126
}}
109127
aria-label="Select row"
110128
onClick={(event: Event) => {
@@ -118,13 +136,13 @@ export function DataTable<TData, TValue>(props: Props<TData, TValue>) {
118136
} as ColumnDef<TData, TValue>,
119137
...local.columns(),
120138
];
121-
});
139+
};
122140

123-
const table = createMemo(() =>
124-
createSolidTable({
125-
get data() {
126-
return local.data() || [];
127-
},
141+
const table = createMemo(() => {
142+
console.debug("table data rebuild");
143+
144+
return createSolidTable({
145+
data: local.data() || [],
128146
state: {
129147
pagination: paginationState(),
130148
rowSelection: rowSelection(),
@@ -136,7 +154,7 @@ export function DataTable<TData, TValue>(props: Props<TData, TValue>) {
136154
// enableColumnResizing: true,
137155
// columnResizeMode: 'onChange',
138156

139-
// pagination {
157+
// pagination:
140158
manualPagination: paginationEnabled(),
141159
onPaginationChange: (state) => {
142160
setPaginationState(state);
@@ -146,7 +164,6 @@ export function DataTable<TData, TValue>(props: Props<TData, TValue>) {
146164
}
147165
},
148166
rowCount: props.rowCount,
149-
// } pagination
150167

151168
// Just means, the input data is already filtered.
152169
manualFiltering: true,
@@ -157,10 +174,11 @@ export function DataTable<TData, TValue>(props: Props<TData, TValue>) {
157174
// NOTE: In our current setup this causes infinite reload cycles when paginating.
158175
autoResetPageIndex: false,
159176

177+
enableRowSelection: true,
160178
enableMultiRowSelection: props.onRowSelection ? true : false,
161179
onRowSelectionChange: setRowSelection,
162-
}),
163-
);
180+
});
181+
});
164182

165183
return (
166184
<>

trailbase-assets/js/admin/src/components/tables/TablePane.tsx

Lines changed: 26 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import type {
88
} from "@tanstack/solid-table";
99
import { createWritableMemo } from "@solid-primitives/memo";
1010
import { createColumnHelper } from "@tanstack/solid-table";
11+
import type { Row } from "@tanstack/solid-table";
1112
import type { DialogTriggerProps } from "@kobalte/core/dialog";
1213
import { asyncBase64Encode } from "trailbase";
1314

@@ -593,15 +594,19 @@ function RowDataTable(props: {
593594
}
594595
onRowSelection={
595596
mutable()
596-
? (_idx: number, row: RowData, value: boolean) => {
597-
const rows = new Set(selectedRows());
598-
const rowId = row[pkColumnIndex()] as string;
599-
if (value) {
600-
rows.add(rowId);
601-
} else {
602-
rows.delete(rowId);
597+
? (rows: Row<RowData>[], value: boolean) => {
598+
const newSelection = new Set(selectedRows());
599+
for (const row of rows) {
600+
const rowId = row.original[
601+
pkColumnIndex()
602+
] as string;
603+
if (value) {
604+
newSelection.add(rowId);
605+
} else {
606+
newSelection.delete(rowId);
607+
}
603608
}
604-
setSelectedRows(rows);
609+
setSelectedRows(newSelection);
605610
}
606611
: undefined
607612
}
@@ -644,20 +649,17 @@ function RowDataTable(props: {
644649
variant="destructive"
645650
disabled={selectedRows().size === 0}
646651
onClick={() => {
647-
const ids = Array.from(selectedRows());
652+
const ids = [...selectedRows()];
648653
if (ids.length === 0) {
649654
return;
650655
}
651656

657+
setSelectedRows(new Set<string>());
652658
deleteRows(table().name.name, {
653659
primary_key_column: columns()[pkColumnIndex()].name,
654660
values: ids,
655661
})
656-
// eslint-disable-next-line solid/reactivity
657-
.then(() => {
658-
setSelectedRows(new Set<string>());
659-
rowsRefetch();
660-
})
662+
.finally(rowsRefetch)
661663
.catch(console.error);
662664
}}
663665
>
@@ -868,18 +870,17 @@ export function TablePane(props: {
868870
onRowSelection={
869871
hidden()
870872
? undefined
871-
: (
872-
_idx: number,
873-
index: TableIndex,
874-
value: boolean,
875-
) => {
876-
const rows = new Set(selectedIndexes());
877-
if (value) {
878-
rows.add(index.name.name);
879-
} else {
880-
rows.delete(index.name.name);
873+
: (rows: Row<TableIndex>[], value: boolean) => {
874+
const newSelection = new Set(selectedIndexes());
875+
for (const row of rows) {
876+
const name = row.original.name.name;
877+
if (value) {
878+
newSelection.add(name);
879+
} else {
880+
newSelection.delete(name);
881+
}
881882
}
882-
setSelectedIndexes(rows);
883+
setSelectedIndexes(newSelection);
883884
}
884885
}
885886
/>

0 commit comments

Comments
 (0)