web-dev-qa-db-fra.com

React / AntDesign Comment rendre les lignes glissables? (Tri par glisser-déposer)

J'ai codé ce simple tableau glisser-déposer sur une colonne, mais les lignes ne bougent pas lorsque je les fais glisser. Où puis-je corriger ou vérifier cela?

J'utilise React, AntDesign et JavaScript (avec TypeScript)

import * as React from 'react';

import ReactDOM from "react-dom";

import { Table } from "antd";

import { DndProvider, DragSource, DropTarget } from "react-dnd";

import HTML5Backend from "react-dnd-html5-backend";

import update from "immutability-helper";

let dragingIndex = -1;

interface propsDD {
    isOver: any,
    connectDragSource: any,
    connectDropTarget: any,
    moveRow: any,
    restProps: {
        readonly [x: string]: any;
        children?: React.ReactNode;
    }
    className: any,
    index: any,
}

class BodyRow extends React.Component<propsDD>{
    render() {
        const { isOver, connectDragSource, connectDropTarget, moveRow, ...restProps } = this.props;
        const style = { ...restProps, cursor: 'move' };
        let { className } = restProps;
        if (isOver) {
            if (restProps.index > dragingIndex) {
                className += " drop-over-downward";
            }
            if (restProps.index < dragingIndex) {
                className += " drop-over-upward";
            }
        }
        return connectDragSource(
            connectDropTarget(
                <tr {...restProps} className={className} style={style} />
            )
        );
    }
}

const rowSource = {
    beginDrag(props: any) {
        dragingIndex = props.index;
        return {
            index: props.index,
        };
    },
};

const rowTarget = {
    drop(props: any, monitor: any) {
        const dragIndex = monitor.getItem().index;
        const hoverIndex = props.index;
        if (dragIndex === hoverIndex) {
            return;
        }
        props.moveRow(dragIndex, hoverIndex);
        monitor.getItem().index = hoverIndex;
    },
};

const DragableBodyRow = DropTarget("row", rowTarget, (connect, monitor) => ({
    connectDropTarget: connect.dropTarget(),
    isOver: monitor.isOver()
}))(
    DragSource("row", rowSource, connect => ({
        connectDragSource: connect.dragSource()
    }))(BodyRow)
);

const columns = [
    {
        title: 'Orden de Ejecución',
        dataIndex: 'attributes.name',
        key: 'name',
    },
];

type propsFromList = {
    receivedTasks: Task[],
    onReceivedTasks: (tasks: Task[]) => void,
}

export default class DDTasks extends React.Component<propsFromList, State>{
    public state: State = {
        data: [],
    };

    components = {
        body: {
            row: DragableBodyRow,
        },
    };

    onReceivedTasks(tasks: Task[]): void {
        this.setState({
            data: this.props.receivedTasks,
        } as State)
    }

    moveRow = (dragIndex: any, hoverIndex: any) => {
        const { data } = this.state;
        const dragRow = data[dragIndex];
        this.setState(
            update(this.state, {
                data: {
                    $splice: [[dragIndex, 1], [hoverIndex, 0, dragRow]]
                }
            })
        );
    };

    render() {
        return (
            < DndProvider backend={HTML5Backend} >
                <Table
                    rowKey="id"
                    bordered={true}
                    pagination={false}
                    columns={columns}
                    dataSource={this.props.receivedTasks}
                    components={this.components}
                    onRow={(index) => ({
                        index,
                        moveRow: this.moveRow,
                    })}
                />
            </DndProvider >
        );
    }
}

Je m'attends à faire glisser des lignes. Le contenu des lignes est effectivement affiché.

4
Adrián Méndez

Avant de coller mon long blob de code, je veux mentionner quelques choses:

  • Il est fortement basé sur exemple de tri par glisser-déposer d'origine .
  • Heureusement React DnD a un support TypeScript parfait comme il est écrit en TypeScript.
  • Le code ci-dessous est une version de remplacement des modèles Ant <table>.
  • C'est générique.
  • Il utilise des composants fonctionnels et des crochets.
  • Il existe un onDragSort, qui est rappelé à chaque fois qu'un tri par glissement se produit. Le paramètre inclut les données triées.

Installez d'abord les packages react-dnd et react-dnd-html5-backend. Ajoutez ceci à votre DragSortingTable.tsx:

import * as React from 'react';
import Table, { TableProps, TableComponents } from 'antd/lib/table';
import { DndProvider, DropTarget, DragSource, DragElementWrapper, DropTargetSpec } from 'react-dnd';
import HTML5Backend from 'react-dnd-html5-backend';

let dragingIndex = -1;

interface BodyRowProps {
  isOver: boolean;
  connectDragSource: DragElementWrapper<{}>;
  connectDropTarget: DragElementWrapper<{}>;
  style?: React.CSSProperties;
  index: number;
  className?: string;
}

const BodyRow: React.FC<BodyRowProps> = props => {
  const style = { ...props.style, cursor: 'move' };

  let { className } = props;
  if (props.isOver) {
    if (props.index > dragingIndex) {
      className += ' drop-over-downward';
    }
    if (props.index < dragingIndex) {
      className += ' drop-over-upward';
    }
  }

  return props.connectDragSource(
    props.connectDropTarget(
      <tr className={className} style={style}>
        {props.children}
      </tr>
    )
  );
};

const rowSource = {
  beginDrag(props) {
    dragingIndex = props.index;
    return {
      index: props.index
    };
  }
};

interface DropProps {
  index: number;
  moveRow: (dragIndex: number, hoverIndex: number) => void;
}

const rowTarget: DropTargetSpec<DropProps> = {
  drop(props, monitor) {
    const dragIndex = monitor.getItem().index;
    const hoverIndex = props.index;

    // Don't replace items with themselves
    if (dragIndex === hoverIndex) {
      return;
    }

    // Time to actually perform the action
    props.moveRow(dragIndex, hoverIndex);

    // Note: we're mutating the monitor item here!
    // Generally it's better to avoid mutations,
    // but it's good here for the sake of performance
    // to avoid expensive index searches.
    monitor.getItem().index = hoverIndex;
  }
};

const DragableBodyRow = DropTarget<DropProps>('row', rowTarget, (connect, monitor) => ({
  connectDropTarget: connect.dropTarget(),
  isOver: monitor.isOver()
}))(
  DragSource('row', rowSource, connect => ({
    connectDragSource: connect.dragSource()
  }))(BodyRow)
);

interface DragSortingTableProps<T> extends TableProps<T> {
  onDragSort?: (sortedData: T[]) => void;
}

type extractTableType<T> = T extends DragSortingTableProps<infer T> ? T : never;

export const DragSortingTable: <T>(
  props: DragSortingTableProps<T>
) => React.ReactElement<DragSortingTableProps<T>> = props => {
  const [dataSource, setDataSource] = React.useState(props.dataSource);

  React.useEffect(() => {
    setDataSource(props.dataSource);
  }, [props.dataSource]);

  const components: TableComponents = {
    body: {
      row: DragableBodyRow
    }
  };

  const moveRow: DropProps['moveRow'] = (dragIndex, hoverIndex) => {
    const dragRow = dataSource[dragIndex];
    const remaining = dataSource.filter(i => i !== dragRow);
    const sorted = [...remaining.slice(0, hoverIndex), dragRow, ...remaining.slice(hoverIndex)];

    setDataSource(sorted);

    if (props.onDragSort) {
      props.onDragSort(sorted);
    }
  };

  const tableProps: TableProps<extractTableType<typeof props>> = {
    ...props,
    className: props.className ? props.className + ' drag-sorting-table' : 'drag-sorting-table',
    components,
    dataSource,
    onRow: (_record, index) => ({ index, moveRow })
  };

  return (
    <DndProvider backend={HTML5Backend}>
      <Table {...tableProps}>{props.children}</Table>
    </DndProvider>
  );
};

Ajoutez les styles suivants à votre antd.less écrase:

.drag-sorting-table tr.drop-over-downward td {
  border-bottom: 2px dashed @primary-color;
}

.drag-sorting-table tr.drop-over-upward td {
  border-top: 2px dashed @primary-color;
}

Utilisez votre nouvelle table de tri par glisser-déposer comme suit:

<DragSortingTable<Users>
      dataSource={props.users}
      rowKey='id'
    >
      <Column
        title='Title'
        dataIndex='id'
        key='id'
      />
</DragSortingTable>
0
Robin