import React, { Fragment, useState, useContext } from "react"; import classNames from "classnames"; import { TableAddon } from "../../TableProps"; import { TableContext } from "../../TableContext"; import { TableDraggableContext } from "../../TableDraggableContext"; import { Icon } from "../../../icon"; import { noop } from "../../../_util/noop"; import { mergeRefs } from "../../../_util/merge-refs"; import { rowsDraggable, RowsDraggableOptions } from "./rowsDraggable"; import { injectPropsIfTargetNotExisted } from "../../util/inject-props-if-target-not-existed"; /** * `draggable` 插件用于支持拖拽排序操作。 */ export interface DraggableOptions { /** * 当前支持 `react-beautiful-dnd` */ module?: any; /** * 拖拽完成回调 */ onDragEnd?: (records: Record[], context: any) => void; /** * 拖拽开始时回调 */ onDragStart?: (context: any) => void; /** * 拖拽手柄列宽度 */ width?: string | number; /** * 拖拽手柄图标 * * *参考 Icon 组件[图标类型](/component/icon)* * * @default "drop" */ icon?: string; /** * 隐藏拖拽手柄图标 * * *默认值:* * * - *内置拖拽 `"unhover"`* * - *第三方库拖拽 `false`* * * @since 2.4.2 */ hideHandler?: boolean | "unhover"; /** * 是否整行可拖拽 * * **内置拖拽当前不支持改变该行为** * * @since 2.4.2 */ full?: boolean; /** * 提供一个列的 `key`,将手柄组件插入到一个目标列 * * *默认在最前新建一列插入* * * @since 2.6.7 */ targetColumnKey?: string; } const fallbackColumnKey = "__draggable_addon__"; const cellPaddingWidth = 20; const reorder = (list, startIndex, endIndex) => { const result = Array.from(list); if (typeof endIndex === "undefined") { return result; } const [removed] = result.splice(startIndex, 1); result.splice(endIndex, 0, removed); return result; }; export function draggable({ module, width = 26, full, icon = "drop", hideHandler, targetColumnKey, onDragEnd = noop, onDragStart = noop, draggableRowKeys = "all", childrenColName = "childrenList", dragType = "row", relations, onExpandedKeysChange, expandedKeys, indent = 20, }: DraggableOptions & RowsDraggableOptions): TableAddon { // 支持新的行拖拽,轻量版本 if (!module) { return rowsDraggable({ onDragEnd, onDragStart, draggableRowKeys, childrenColName, dragType, relations, onExpandedKeysChange, expandedKeys, indent, icon, hideHandler, width, targetColumnKey, }); } const { Draggable } = module; let total = null; return { getInfo: () => ({ name: "draggable" }), onInjectProps: props => { const { records } = props; let columns = [...props.columns]; if (hideHandler !== true) { columns = injectPropsIfTargetNotExisted( props.columns, targetColumnKey, { key: fallbackColumnKey, width, header: null, render: () => null, } ); } total = records.length; return { ...props, columns, draggable: true }; }, onInjectColumn: render => ( record, rowKey, recordIndex, column, columnIndex ) => { const result = render(record, rowKey, recordIndex, column, columnIndex); // 非表头 if (columnIndex === -1) { return result; } if ( recordIndex !== -1 && (column.key === fallbackColumnKey || column.key === targetColumnKey) ) { result.children = ( ); } return { props: result.props, children: ( {result.children} ), }; }, onInjectRow: render => (record, rowKey, recordIndex, ...args) => { const result = render(record, rowKey, recordIndex, ...args); const row = ( {(provided, snapshot) => ( {React.cloneElement(result.row, { ...provided.draggableProps, ...(full ? provided.dragHandleProps : {}), style: { ...(result.row.props.style || {}), ...provided.draggableProps.style, ...(full ? provided.dragHandleProps.style : {}), }, ref: provided.innerRef, className: classNames(result.row.props.className, { "is-dragging": snapshot.isDragging, }), })} )} ); return { ...result, row: recordIndex === total - 1 ? ( {row} ) : ( row ), }; }, onInjectBody: render => (...args) => { return ( { const records = args[0]; onDragEnd( reorder( records, context.source?.index, context.destination?.index ), context ); }} > {render(...args)} ); }, }; } export interface DraggableTableRowContextValue { provided?: any; } export const DraggableTableRowContext = React.createContext< DraggableTableRowContextValue >({}); interface DraggableTableBodyProps extends React.HTMLAttributes { module: DraggableOptions["module"]; onDragEnd: (result: any) => void; onDragStart?: (context: any) => void; children: JSX.Element; } const DraggableTableBody = React.forwardRef( ( { module, onDragEnd, onDragStart, children, ...props }: DraggableTableBodyProps, ref ) => { const { DragDropContext, Droppable } = module; const [isDragging, setIsDragging] = useState(false); return ( setIsDragging(true)} onDragStart={context => onDragStart(context)} onDragEnd={result => { setIsDragging(false); onDragEnd(result); }} > {provided => { return ( {React.cloneElement(children, { ...props, ref: mergeRefs(provided.innerRef, ref), className: classNames( children.props.className, props.className, { "is-dragging": isDragging, } ), })} ); }} ); } ); function DraggableTableRowIcon({ icon, index, hideHandler, }: { icon: string; index: number; hideHandler: DraggableOptions["hideHandler"]; }) { const { columnsWidths } = useContext(TableContext); const { isDragging } = useContext(TableDraggableContext); const { provided } = useContext(DraggableTableRowContext); if (!provided) { return null; } return (
); } function DraggableTablePlaceHolder() { const { placeholder } = useContext(TableDraggableContext); return <>{placeholder}; } function DraggableTableCell({ index, children }) { const { columnsWidths } = useContext(TableContext); const { isDragging } = useContext(TableDraggableContext); return (
{children}
); }