Skip to content

Commit a01f3df

Browse files
Copilothuangyiirene
andcommitted
fix: Improve row selection with unique IDs and add onSelectionChange callback
Co-authored-by: huangyiirene <7665279+huangyiirene@users.noreply.github.com>
1 parent 901653f commit a01f3df

1 file changed

Lines changed: 95 additions & 51 deletions

File tree

packages/components/src/renderers/complex/data-table.tsx

Lines changed: 95 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ const DataTableRenderer = ({ schema }: { schema: DataTableSchema }) => {
8383
const [searchQuery, setSearchQuery] = useState('');
8484
const [sortColumn, setSortColumn] = useState<string | null>(null);
8585
const [sortDirection, setSortDirection] = useState<SortDirection>(null);
86-
const [selectedRows, setSelectedRows] = useState<Set<number>>(new Set());
86+
const [selectedRowIds, setSelectedRowIds] = useState<Set<any>>(new Set());
8787
const [currentPage, setCurrentPage] = useState(1);
8888
const [pageSize, setPageSize] = useState(initialPageSize);
8989

@@ -120,6 +120,12 @@ const DataTableRenderer = ({ schema }: { schema: DataTableSchema }) => {
120120
? sortedData.slice((currentPage - 1) * pageSize, currentPage * pageSize)
121121
: sortedData;
122122

123+
// Generate unique row ID
124+
const getRowId = (row: any, index: number) => {
125+
// Try to use 'id' field, fall back to index
126+
return row.id !== undefined ? row.id : `row-${index}`;
127+
};
128+
123129
// Handlers
124130
const handleSort = (columnKey: string) => {
125131
if (!sortable) return;
@@ -138,21 +144,43 @@ const DataTableRenderer = ({ schema }: { schema: DataTableSchema }) => {
138144
};
139145

140146
const handleSelectAll = (checked: boolean) => {
147+
const newSelected = new Set<any>();
141148
if (checked) {
142-
setSelectedRows(new Set(paginatedData.map((_, idx) => idx)));
143-
} else {
144-
setSelectedRows(new Set());
149+
paginatedData.forEach((row, idx) => {
150+
const globalIndex = (currentPage - 1) * pageSize + idx;
151+
const rowId = getRowId(row, globalIndex);
152+
newSelected.add(rowId);
153+
});
154+
}
155+
setSelectedRowIds(newSelected);
156+
157+
// Call callback if provided
158+
if (schema.onSelectionChange) {
159+
const selectedData = sortedData.filter((row, idx) => {
160+
const rowId = getRowId(row, idx);
161+
return newSelected.has(rowId);
162+
});
163+
schema.onSelectionChange(selectedData);
145164
}
146165
};
147166

148-
const handleSelectRow = (index: number, checked: boolean) => {
149-
const newSelected = new Set(selectedRows);
167+
const handleSelectRow = (rowId: any, checked: boolean) => {
168+
const newSelected = new Set(selectedRowIds);
150169
if (checked) {
151-
newSelected.add(index);
170+
newSelected.add(rowId);
152171
} else {
153-
newSelected.delete(index);
172+
newSelected.delete(rowId);
173+
}
174+
setSelectedRowIds(newSelected);
175+
176+
// Call callback if provided
177+
if (schema.onSelectionChange) {
178+
const selectedData = sortedData.filter((row, idx) => {
179+
const id = getRowId(row, idx);
180+
return newSelected.has(id);
181+
});
182+
schema.onSelectionChange(selectedData);
154183
}
155-
setSelectedRows(newSelected);
156184
};
157185

158186
const handleExport = () => {
@@ -182,8 +210,18 @@ const DataTableRenderer = ({ schema }: { schema: DataTableSchema }) => {
182210
return <ChevronDown className="h-4 w-4 ml-1" />;
183211
};
184212

185-
const allSelected = paginatedData.length > 0 && selectedRows.size === paginatedData.length;
186-
const someSelected = selectedRows.size > 0 && selectedRows.size < paginatedData.length;
213+
// Check if all rows on current page are selected
214+
const allPageRowsSelected = paginatedData.length > 0 && paginatedData.every((row, idx) => {
215+
const globalIndex = (currentPage - 1) * pageSize + idx;
216+
const rowId = getRowId(row, globalIndex);
217+
return selectedRowIds.has(rowId);
218+
});
219+
220+
const somePageRowsSelected = paginatedData.some((row, idx) => {
221+
const globalIndex = (currentPage - 1) * pageSize + idx;
222+
const rowId = getRowId(row, globalIndex);
223+
return selectedRowIds.has(rowId);
224+
}) && !allPageRowsSelected;
187225

188226
return (
189227
<div className={`space-y-4 ${className || ''}`}>
@@ -219,9 +257,9 @@ const DataTableRenderer = ({ schema }: { schema: DataTableSchema }) => {
219257
</Button>
220258
)}
221259

222-
{selectable && selectedRows.size > 0 && (
260+
{selectable && selectedRowIds.size > 0 && (
223261
<div className="text-sm text-muted-foreground">
224-
{selectedRows.size} selected
262+
{selectedRowIds.size} selected
225263
</div>
226264
)}
227265
</div>
@@ -236,7 +274,7 @@ const DataTableRenderer = ({ schema }: { schema: DataTableSchema }) => {
236274
{selectable && (
237275
<TableHead className="w-12">
238276
<Checkbox
239-
checked={allSelected ? true : someSelected ? 'indeterminate' : false}
277+
checked={allPageRowsSelected ? true : somePageRowsSelected ? 'indeterminate' : false}
240278
onCheckedChange={handleSelectAll}
241279
/>
242280
</TableHead>
@@ -270,43 +308,49 @@ const DataTableRenderer = ({ schema }: { schema: DataTableSchema }) => {
270308
</TableCell>
271309
</TableRow>
272310
) : (
273-
paginatedData.map((row, rowIndex) => (
274-
<TableRow key={rowIndex} data-state={selectedRows.has(rowIndex) ? 'selected' : undefined}>
275-
{selectable && (
276-
<TableCell>
277-
<Checkbox
278-
checked={selectedRows.has(rowIndex)}
279-
onCheckedChange={(checked) => handleSelectRow(rowIndex, checked as boolean)}
280-
/>
281-
</TableCell>
282-
)}
283-
{columns.map((col, colIndex) => (
284-
<TableCell key={colIndex} className={col.cellClassName}>
285-
{row[col.accessorKey]}
286-
</TableCell>
287-
))}
288-
{rowActions && (
289-
<TableCell className="text-right">
290-
<div className="flex items-center justify-end gap-1">
291-
<Button
292-
variant="ghost"
293-
size="icon-sm"
294-
onClick={() => schema.onRowEdit?.(row)}
295-
>
296-
<Edit className="h-4 w-4" />
297-
</Button>
298-
<Button
299-
variant="ghost"
300-
size="icon-sm"
301-
onClick={() => schema.onRowDelete?.(row)}
302-
>
303-
<Trash2 className="h-4 w-4 text-destructive" />
304-
</Button>
305-
</div>
306-
</TableCell>
307-
)}
308-
</TableRow>
309-
))
311+
paginatedData.map((row, rowIndex) => {
312+
const globalIndex = (currentPage - 1) * pageSize + rowIndex;
313+
const rowId = getRowId(row, globalIndex);
314+
const isSelected = selectedRowIds.has(rowId);
315+
316+
return (
317+
<TableRow key={rowId} data-state={isSelected ? 'selected' : undefined}>
318+
{selectable && (
319+
<TableCell>
320+
<Checkbox
321+
checked={isSelected}
322+
onCheckedChange={(checked) => handleSelectRow(rowId, checked as boolean)}
323+
/>
324+
</TableCell>
325+
)}
326+
{columns.map((col, colIndex) => (
327+
<TableCell key={colIndex} className={col.cellClassName}>
328+
{row[col.accessorKey]}
329+
</TableCell>
330+
))}
331+
{rowActions && (
332+
<TableCell className="text-right">
333+
<div className="flex items-center justify-end gap-1">
334+
<Button
335+
variant="ghost"
336+
size="icon-sm"
337+
onClick={() => schema.onRowEdit?.(row)}
338+
>
339+
<Edit className="h-4 w-4" />
340+
</Button>
341+
<Button
342+
variant="ghost"
343+
size="icon-sm"
344+
onClick={() => schema.onRowDelete?.(row)}
345+
>
346+
<Trash2 className="h-4 w-4 text-destructive" />
347+
</Button>
348+
</div>
349+
</TableCell>
350+
)}
351+
</TableRow>
352+
);
353+
})
310354
)}
311355
</TableBody>
312356
</Table>

0 commit comments

Comments
 (0)