|
|
@@ -1,8 +1,30 @@
|
|
|
import classNames from 'classnames';
|
|
|
-import {forwardRef} from 'react';
|
|
|
+import {
|
|
|
+ Dispatch,
|
|
|
+ MutableRefObject,
|
|
|
+ SetStateAction,
|
|
|
+ forwardRef,
|
|
|
+ useEffect,
|
|
|
+ useState,
|
|
|
+} from 'react';
|
|
|
import BodyTr from './BodyTr';
|
|
|
import {Empty} from 'antd';
|
|
|
-import {HeaderGroup, RowModel} from '@tanstack/react-table';
|
|
|
+import {Cell, HeaderGroup, RowModel} from '@tanstack/react-table';
|
|
|
+import {
|
|
|
+ DndContext,
|
|
|
+ DragEndEvent,
|
|
|
+ DragOverlay,
|
|
|
+ DragStartEvent,
|
|
|
+ PointerSensor,
|
|
|
+ closestCenter,
|
|
|
+ useSensor,
|
|
|
+} from '@dnd-kit/core';
|
|
|
+import {
|
|
|
+ SortableContext,
|
|
|
+ arrayMove,
|
|
|
+ verticalListSortingStrategy,
|
|
|
+} from '@dnd-kit/sortable';
|
|
|
+import {debounce} from 'lodash-es';
|
|
|
|
|
|
type Props = {
|
|
|
scrollProgess: 'start' | 'end' | 'process',
|
|
|
@@ -11,9 +33,19 @@ type Props = {
|
|
|
highlightValue?: unknown,
|
|
|
hightlightKey?: any,
|
|
|
getHeaderGroups: () => HeaderGroup<Record<string, any>>[],
|
|
|
+ enableDnd?: boolean,
|
|
|
+ rawKey?: any,
|
|
|
+ data: any[],
|
|
|
+ setData: Dispatch<SetStateAction<any[]>>,
|
|
|
+ isSecondLevel?: boolean,
|
|
|
};
|
|
|
|
|
|
-export default forwardRef<HTMLTableElement, Props>(function TableBody(
|
|
|
+type ActiveState = {
|
|
|
+ getVisibleCells: () => Cell<Record<string, any>, unknown>[],
|
|
|
+ id: string,
|
|
|
+} | null;
|
|
|
+
|
|
|
+export default forwardRef<HTMLDivElement, Props>(function TableBody(
|
|
|
{
|
|
|
scrollProgess,
|
|
|
getCenterTotalSize,
|
|
|
@@ -21,56 +53,141 @@ export default forwardRef<HTMLTableElement, Props>(function TableBody(
|
|
|
highlightValue,
|
|
|
hightlightKey,
|
|
|
getHeaderGroups,
|
|
|
+ data,
|
|
|
+ enableDnd,
|
|
|
+ rawKey,
|
|
|
+ setData,
|
|
|
+ isSecondLevel,
|
|
|
},
|
|
|
ref,
|
|
|
) {
|
|
|
+ const sensor = useSensor(PointerSensor);
|
|
|
+
|
|
|
+ const [active, setActive] = useState<ActiveState>(null);
|
|
|
+ function onDragStart(event: DragStartEvent) {
|
|
|
+ setActive(event.active.data.current as ActiveState);
|
|
|
+ }
|
|
|
+ function onDragEnd({over, active}: DragEndEvent) {
|
|
|
+ setActive(null);
|
|
|
+ if (!over)
|
|
|
+ return;
|
|
|
+
|
|
|
+ const {id: activeId} = active,
|
|
|
+ {id: overId} = over;
|
|
|
+
|
|
|
+ if (activeId === overId)
|
|
|
+ return;
|
|
|
+
|
|
|
+ setData(function(prev) {
|
|
|
+ const fromIdx = prev.findIndex(val => val[rawKey ?? 'id'] === activeId),
|
|
|
+ toIdx = prev.findIndex(val => val[rawKey ?? 'id'] === overId);
|
|
|
+
|
|
|
+ return arrayMove(prev, fromIdx, toIdx);
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ const [tableWidth, setTableWidth] = useState(0);
|
|
|
+
|
|
|
+ useEffect(function() {
|
|
|
+ if (!enableDnd) return;
|
|
|
+
|
|
|
+ const dom = (ref as MutableRefObject<HTMLDivElement>).current;
|
|
|
+ const obsever = new ResizeObserver(debounce(
|
|
|
+ function([entrie]) {
|
|
|
+ setTableWidth(entrie.contentRect.width);
|
|
|
+ },
|
|
|
+ 300,
|
|
|
+ {leading: false, trailing: true},
|
|
|
+ ));
|
|
|
+
|
|
|
+ obsever.observe(dom);
|
|
|
+
|
|
|
+ return () => obsever.disconnect();
|
|
|
+ }, [enableDnd, ref]);
|
|
|
+
|
|
|
return (
|
|
|
- <div className="ld-table-body-wrapper" ref={ref}>
|
|
|
- <table
|
|
|
- className={classNames('ld-table', {
|
|
|
- 'ld-table-enabled-right-fixed-shadow': scrollProgess !== 'end',
|
|
|
- 'ld-table-enabled-left-fixed-shadow': scrollProgess !== 'start',
|
|
|
- })}
|
|
|
- style={{width: getCenterTotalSize()}}
|
|
|
+ <DndContext
|
|
|
+ sensors={[sensor]}
|
|
|
+ collisionDetection={closestCenter}
|
|
|
+ onDragStart={onDragStart}
|
|
|
+ onDragEnd={onDragEnd}
|
|
|
+ >
|
|
|
+ <SortableContext
|
|
|
+ items={data.map(val => val[rawKey ?? 'id'])}
|
|
|
+ strategy={verticalListSortingStrategy}
|
|
|
>
|
|
|
- <tbody className="ld-table-body">
|
|
|
- {getRowModel().rows.length > 0 ? (
|
|
|
- getRowModel().rows.map(function({
|
|
|
- id,
|
|
|
- getVisibleCells,
|
|
|
- original,
|
|
|
- }) {
|
|
|
- return (
|
|
|
- <BodyTr
|
|
|
- getVisibleCells={getVisibleCells}
|
|
|
- key={id}
|
|
|
- highlight={original[hightlightKey as string ?? 'id'] === highlightValue}
|
|
|
- />
|
|
|
- );
|
|
|
- })
|
|
|
- ) : (
|
|
|
- <tr>
|
|
|
- <td
|
|
|
- colSpan={getHeaderGroups()[0].headers.length}
|
|
|
- style={{padding: 0}}
|
|
|
+ <div className="ld-table-body-wrapper" ref={ref}>
|
|
|
+ <table
|
|
|
+ className={classNames('ld-table', {
|
|
|
+ 'ld-table-enabled-right-fixed-shadow': scrollProgess !== 'end',
|
|
|
+ 'ld-table-enabled-left-fixed-shadow': scrollProgess !== 'start',
|
|
|
+ })}
|
|
|
+ style={{width: getCenterTotalSize()}}
|
|
|
+ >
|
|
|
+ <tbody className="ld-table-body">
|
|
|
+ {getRowModel().rows.length > 0 ? (
|
|
|
+ getRowModel().rows.map(function({
|
|
|
+ id,
|
|
|
+ getVisibleCells,
|
|
|
+ original,
|
|
|
+ }) {
|
|
|
+ return (
|
|
|
+ <BodyTr
|
|
|
+ id={id}
|
|
|
+ getVisibleCells={getVisibleCells}
|
|
|
+ key={id}
|
|
|
+ highlight={original[hightlightKey as string ?? 'id'] === highlightValue}
|
|
|
+ enableDnd={enableDnd}
|
|
|
+ />
|
|
|
+ );
|
|
|
+ })
|
|
|
+ ) : (
|
|
|
+ <tr>
|
|
|
+ <td
|
|
|
+ colSpan={getHeaderGroups()[0].headers.length}
|
|
|
+ style={{padding: 0}}
|
|
|
+ >
|
|
|
+ <Empty
|
|
|
+ image={Empty.PRESENTED_IMAGE_SIMPLE}
|
|
|
+ style={{
|
|
|
+ width: '400px',
|
|
|
+ position: 'sticky',
|
|
|
+ left: '50%',
|
|
|
+ transform: 'translateX(-50%)',
|
|
|
+ overflow: 'hidden',
|
|
|
+ margin: 0,
|
|
|
+ padding: '50px 10px',
|
|
|
+ }}
|
|
|
+ />
|
|
|
+ </td>
|
|
|
+ </tr>
|
|
|
+ )}
|
|
|
+ </tbody>
|
|
|
+ </table>
|
|
|
+ </div>
|
|
|
+ <DragOverlay>
|
|
|
+ {active && (
|
|
|
+ <div
|
|
|
+ className={classNames({
|
|
|
+ 'ld-table-preview-transform': isSecondLevel,
|
|
|
+ })}
|
|
|
+ style={{width: tableWidth, overflow: 'hidden'}}
|
|
|
+ >
|
|
|
+ <table
|
|
|
+ className={classNames('ld-table', {
|
|
|
+ 'ld-table-enabled-right-fixed-shadow': scrollProgess !== 'end',
|
|
|
+ 'ld-table-enabled-left-fixed-shadow': scrollProgess !== 'start',
|
|
|
+ })}
|
|
|
+ style={{width: getCenterTotalSize()}}
|
|
|
>
|
|
|
- <Empty
|
|
|
- image={Empty.PRESENTED_IMAGE_SIMPLE}
|
|
|
- style={{
|
|
|
- width: '400px',
|
|
|
- position: 'sticky',
|
|
|
- left: '50%',
|
|
|
- transform: 'translateX(-50%)',
|
|
|
- overflow: 'hidden',
|
|
|
- margin: 0,
|
|
|
- padding: '50px 10px',
|
|
|
- }}
|
|
|
- />
|
|
|
- </td>
|
|
|
- </tr>
|
|
|
+ <tbody>
|
|
|
+ <BodyTr {...active} preview />
|
|
|
+ </tbody>
|
|
|
+ </table>
|
|
|
+ </div>
|
|
|
)}
|
|
|
- </tbody>
|
|
|
- </table>
|
|
|
- </div>
|
|
|
+ </DragOverlay>
|
|
|
+ </SortableContext>
|
|
|
+ </DndContext>
|
|
|
);
|
|
|
});
|