https://codesandbox.io/p/sandbox /drag-and-drop-reorder-multiple-rows-at-once-forked-56pl7c?workspaceId=ws_F2QxELJDBKDDSAxdPEPbQt
В некоторых случаях у моего пользователя могут быть сотни файлов, и он хочет изменить порядок группы из 20 строк. Текущий процесс изменения порядка каждой строки вручную очень утомителен, я хочу добавить пользователю возможность перетаскивания и удалить сразу несколько строк, переупорядочив их все.
Я нашел хороший пост о UX с изображениями о том, как это можно сделать, и хочу попробовать реализовать его в мой React Table.js компонент:
https://ux.stackexchange.com/questions/ ... ble-and-be -able-to-reorder-the-selec
Итак, если пользователь выбирает несколько строк:

Они могут перетаскиваться, чтобы изменить порядок всех текущих выбранных строк с помощью специального всплывающего окна пользовательского интерфейса слева. таблицы:

В настоящее время находится в мой codeandbox.io пользователь может выбрать несколько файлов и всегда изменять порядок только одной строки за раз:

Я попробовал реализовать это здесь:
https://codesandbox.io/p/sandbox/drag-a ... SAxdPEPbQt
Но есть в пользовательском интерфейсе нет индикации призрачных строк о том, сколько строк я переупорядочиваю, что делает процесс неинтуитивным и неясным:
//Table.js
// Table.js
import React, { useState } from "react";
import {
ColumnDef,
getCoreRowModel,
useReactTable,
flexRender,
getSortedRowModel,
getFilteredRowModel,
getPaginationRowModel,
} from "@tanstack/react-table";
import { DndContext, closestCenter } from "@dnd-kit/core";
import {
useSortable,
SortableContext,
verticalListSortingStrategy,
arrayMove,
} from "@dnd-kit/sortable";
import { CSS } from "@dnd-kit/utilities";
import styles from "./Table.module.css";
// Indeterminate Checkbox Component
function IndeterminateCheckbox({ indeterminate, className = "", ...rest }) {
const ref = React.useRef(null);
React.useEffect(() => {
if (typeof indeterminate === "boolean") {
ref.current.indeterminate = indeterminate;
}
}, [indeterminate]);
return (
);
}
// Drag handle for rows
function DragHandle({ row }) {
const { attributes, listeners } = useSortable({ id: row.original.id });
return (
);
}
// Row Component
function Row({ row }) {
const { setNodeRef, transform, transition } = useSortable({
id: row.original.id,
});
const isSelected = row.getIsSelected();
const style = {
transform: CSS.Transform.toString(transform),
transition,
backgroundColor: isSelected ? "#e0f7fa" : "inherit", // Highlight selected rows
};
return (
{row.getVisibleCells().map((cell, index) => (
{index === 1 ? : null}
{flexRender(cell.column.columnDef.cell, cell.getContext())}
))}
);
}
// Table Component
function Table({ data, setData, columns, rowSelection, setRowSelection }) {
const [globalFilter, setGlobalFilter] = useState("");
const [sorting, setSorting] = useState([]);
const tableColumns = React.useMemo(() => [
{
id: "select",
header: ({ table }) => (
),
cell: ({ row }) => (
),
},
{ accessorKey: "draggable", header: "Drag" },
{ accessorKey: "fileName", header: "File Name" },
{ accessorKey: "duration", header: "Duration" },
]);
const table = useReactTable({
data,
columns: tableColumns,
getRowId: (row) => row.id,
state: { rowSelection, globalFilter, sorting },
onRowSelectionChange: setRowSelection,
onSortingChange: setSorting,
getCoreRowModel: getCoreRowModel(),
getSortedRowModel: getSortedRowModel(),
getFilteredRowModel: getFilteredRowModel(),
getPaginationRowModel: getPaginationRowModel(),
});
const handleDragEnd = (event) => {
const { active, over } = event;
if (active && over && active.id !== over.id) {
// Get the IDs of all selected rows
const selectedRowIds = Object.keys(rowSelection).filter(
(id) => rowSelection[id]
);
if (selectedRowIds.length > 0) {
// Find the indices of the selected rows
const selectedRows = data.filter((item) =>
selectedRowIds.includes(item.id)
);
const otherRows = data.filter(
(item) => !selectedRowIds.includes(item.id)
);
// Determine the target index in `otherRows` where the first selected row is dropped
const targetIndex = otherRows.findIndex((item) => item.id === over.id);
// Rebuild the data array with the selected rows inserted at the target index
const newData = [
...otherRows.slice(0, targetIndex),
...selectedRows,
...otherRows.slice(targetIndex),
];
setData(newData);
} else {
// Single row drag and drop
const oldIndex = data.findIndex((item) => item.id === active.id);
const newIndex = data.findIndex((item) => item.id === over.id);
if (oldIndex !== -1 && newIndex !== -1) {
const newData = arrayMove([...data], oldIndex, newIndex);
setData(newData);
}
}
}
};
return (
setGlobalFilter(e.target.value)}
placeholder="Search..."
className={styles.search}
/>
row.id)}
strategy={verticalListSortingStrategy}
>
{table.getHeaderGroups().map((headerGroup) => (
{headerGroup.headers.map((header) => (
header.column.toggleSorting()
: undefined
}
>
{flexRender(
header.column.columnDef.header,
header.getContext()
)}
{header.column.getIsSorted() === "asc" ? "
{header.column.getIsSorted() === "desc" ? "
))}
))}
{table.getRowModel().rows.map((row) => (
))}
table.previousPage()}
disabled={!table.getCanPreviousPage()}
>
Previous
Page {table.getState().pagination.pageIndex + 1} of{" "}
{table.getPageCount()}
table.nextPage()}
disabled={!table.getCanNextPage()}
>
Next
{Object.keys(rowSelection).length} of {data.length} rows selected
);
}
export default Table;
Подробнее здесь: https://stackoverflow.com/questions/793 ... k-table-co