如何集成 React-Beautiful-DND 和 Ant Design 表?

发布于 2025-01-11 20:57:38 字数 7864 浏览 0 评论 0原文

我正在尝试集成 React-Beautiful-DND 和 Ant Design 表,但在拖动行时遇到行问题。他们的风格在变化,整个桌子都在跳动。以前有人尝试过这个吗?如何保持被拖动行的样式与未拖动时的样式相同? 任何想法将不胜感激。

这是 链接到完整的沙箱。

可以看到,如果拖动任意行,表格就会跳转。

这是链接中的完整代码:

import React, { useState } from "react";
import { Table, Row, Col, Card, Empty } from "antd";
import { DragDropContext, Draggable, Droppable } from "react-beautiful-dnd";

import { mutliDragAwareReorder } from "./utils";
import "./style.css";

const entitiesMock = {
  tasks: [
    { id: "0", title: "Very long task title" },
    { id: "1", title: "Task 1" },
    { id: "2", title: "Task 2" },
    { id: "3", title: "Task 3" },
    { id: "4", title: "Task 4" },
    { id: "5", title: "Task 5" },
    { id: "6", title: "Task 6" },
    { id: "7", title: "Task 7" },
    { id: "8", title: "Task 8" },
    { id: "9", title: "Task 9" },
    { id: "10", title: "Task 10" },
    { id: "11", title: "Task 11" },
    { id: "12", title: "Task 12" },
    { id: "13", title: "Task 13" },
    { id: "14", title: "Task 14" },
    { id: "15", title: "Task 15" },
    { id: "16", title: "Task 16" },
    { id: "17", title: "Task 17" },
    { id: "18", title: "Task 18" },
    { id: "19", title: "Task 19" }
  ],
  columnIds: ["todo"],
  columns: {
    todo: {
      id: "todo",
      title: "To do",
      taskIds: [
        "0",
        "1",
        "2",
        "3",
        "4",
        "5",
        "6",
        "7",
        "8",
        "9",
        "10",
        "11",
        "12",
        "13",
        "14",
        "15",
        "16",
        "17",
        "18",
        "19"
      ]
    },
    done: {
      id: "done",
      title: "Done",
      taskIds: []
    }
  }
};

const COLUMN_ID_DONE = "done";

export const MultiTableDrag = () => {
  const [entities, setEntities] = useState(entitiesMock);
  const [selectedTaskIds, setSelectedTaskIds] = useState([]);
  const [draggingTaskId, setDraggingTaskId] = useState(null);

  const tableColumns = [
    {
      title: "ID",
      dataIndex: "id"
    },
    {
      title: "Title",
      dataIndex: "title"
    }
  ];

  /**
   * Droppable table body
   */
  const DroppableTableBody = ({ columnId, tasks, ...props }) => {
    return (
      <Droppable droppableId={columnId}>
        {(provided, snapshot) => (
          <tbody
            ref={provided.innerRef}
            {...props}
            {...provided.droppableProps}
            className={`${props.className} ${
              snapshot.isDraggingOver && columnId === COLUMN_ID_DONE
                ? "is-dragging-over"
                : ""
            }`}
          >
            {props.children}
            {provided.placeholder}
          </tbody>
        )}
      </Droppable>
    );
  };

  /**
   * Draggable table row
   */
  const DraggableTableRow = ({ index, record, columnId, tasks, ...props }) => {
    if (!tasks.length) {
      return (
        <tr className="ant-table-placeholder row-item" {...props}>
          <td colSpan={tableColumns.length} className="ant-table-cell">
            <div className="ant-empty ant-empty-normal">
              <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} />
            </div>
          </td>
        </tr>
      );
    }

    const isSelected = selectedTaskIds.some(
      (selectedTaskId) => selectedTaskId === record.id
    );
    const isGhosting =
      isSelected && Boolean(draggingTaskId) && draggingTaskId !== record.id;

    return (
      <Draggable
        key={props["data-row-key"]}
        draggableId={props["data-row-key"].toString()}
        index={index}
      >
        {(provided, snapshot) => {
          return (
            <tr
              ref={provided.innerRef}
              {...props}
              {...provided.draggableProps}
              {...provided.dragHandleProps}
              className={`row-item ${isSelected ? "row-selected" : ""} ${
                isGhosting ? "row-ghosting" : ""
              } ${snapshot.isDragging ? "row-dragging" : ""}`}
            >
              {props.children}
            </tr>
          );
        }}
      </Draggable>
    );
  };

  /**
   * Get tasks
   */
  const getTasks = (entities, id) => {
    return entities.columns[id].taskIds.map((taskId) =>
      entities.tasks.find((item) => item.id === taskId)
    );
  };

  /**
   * On before capture
   */
  const onBeforeCapture = (start) => {
    const draggableId = start.draggableId;
    const selected = selectedTaskIds.find((taskId) => taskId === draggableId);

    // if dragging an item that is not selected - unselect all items
    if (!selected) {
      setSelectedTaskIds([]);
    }

    setDraggingTaskId(draggableId);
  };

  /**
   * On drag end
   */
  const onDragEnd = (result) => {
    const destination = result.destination;
    const source = result.source;

    // nothing to do
    if (!destination || result.reason === "CANCEL") {
      setDraggingTaskId(null);
      return;
    }

    const processed = mutliDragAwareReorder({
      entities,
      selectedTaskIds,
      source,
      destination
    });

    setEntities(processed.entities);
    setDraggingTaskId(null);
  };

  return (
    <>
      <Card
        className={`c-multi-drag-table ${draggingTaskId ? "is-dragging" : ""}`}
      >
        <div>
          selectedTaskIds: {JSON.stringify(selectedTaskIds)}
          <br />
          draggingTaskId: {JSON.stringify(draggingTaskId)}
        </div>
        <br />

        <DragDropContext
          onBeforeCapture={onBeforeCapture}
          onDragEnd={onDragEnd}
        >
          <Row gutter={40}>
            {entities.columnIds.map((id) => (
              <Col key={id} xs={12}>
                <div className="inner-col">
                  <Row justify="space-between" align="middle">
                    <h2>{id}</h2>
                    <span>
                      {draggingTaskId && selectedTaskIds.length > 0
                        ? selectedTaskIds.length +
                          " record(s) are being dragged"
                        : draggingTaskId && selectedTaskIds.length <= 0
                        ? "1 record(s) are being dragged"
                        : ""}
                    </span>
                  </Row>

                  <Table
                    dataSource={getTasks(entities, id)}
                    columns={tableColumns}
                    rowKey="id"
                    components={{
                      body: {
                        // Custom tbody
                        wrapper: (val) =>
                          DroppableTableBody({
                            columnId: entities.columns[id].id,
                            tasks: getTasks(entities, id),
                            ...val
                          }),
                        // Custom td
                        row: (val) =>
                          DraggableTableRow({
                            tasks: getTasks(entities, id),
                            ...val
                          })
                      }
                    }}
                    // Set props on per row (td)
                    onRow={(record, index) => ({
                      index,
                      record
                    })}
                  />
                </div>
              </Col>
            ))}
          </Row>
          <br />
        </DragDropContext>
      </Card>
    </>
  );
};

I am trying to integrate React-Beautiful-DND and Ant Design table, but I am facing an issue with rows as I drag them. Their style is changing and the whole table jumps. Anyone tried this before? How do I keep the style of dragged row as it was when it was not dragged?
Any ideas will be much appreciated.

Here is a link to full sandbox.

You can see that if you drag any row, the table will jump.

An here is a full code from the link:

import React, { useState } from "react";
import { Table, Row, Col, Card, Empty } from "antd";
import { DragDropContext, Draggable, Droppable } from "react-beautiful-dnd";

import { mutliDragAwareReorder } from "./utils";
import "./style.css";

const entitiesMock = {
  tasks: [
    { id: "0", title: "Very long task title" },
    { id: "1", title: "Task 1" },
    { id: "2", title: "Task 2" },
    { id: "3", title: "Task 3" },
    { id: "4", title: "Task 4" },
    { id: "5", title: "Task 5" },
    { id: "6", title: "Task 6" },
    { id: "7", title: "Task 7" },
    { id: "8", title: "Task 8" },
    { id: "9", title: "Task 9" },
    { id: "10", title: "Task 10" },
    { id: "11", title: "Task 11" },
    { id: "12", title: "Task 12" },
    { id: "13", title: "Task 13" },
    { id: "14", title: "Task 14" },
    { id: "15", title: "Task 15" },
    { id: "16", title: "Task 16" },
    { id: "17", title: "Task 17" },
    { id: "18", title: "Task 18" },
    { id: "19", title: "Task 19" }
  ],
  columnIds: ["todo"],
  columns: {
    todo: {
      id: "todo",
      title: "To do",
      taskIds: [
        "0",
        "1",
        "2",
        "3",
        "4",
        "5",
        "6",
        "7",
        "8",
        "9",
        "10",
        "11",
        "12",
        "13",
        "14",
        "15",
        "16",
        "17",
        "18",
        "19"
      ]
    },
    done: {
      id: "done",
      title: "Done",
      taskIds: []
    }
  }
};

const COLUMN_ID_DONE = "done";

export const MultiTableDrag = () => {
  const [entities, setEntities] = useState(entitiesMock);
  const [selectedTaskIds, setSelectedTaskIds] = useState([]);
  const [draggingTaskId, setDraggingTaskId] = useState(null);

  const tableColumns = [
    {
      title: "ID",
      dataIndex: "id"
    },
    {
      title: "Title",
      dataIndex: "title"
    }
  ];

  /**
   * Droppable table body
   */
  const DroppableTableBody = ({ columnId, tasks, ...props }) => {
    return (
      <Droppable droppableId={columnId}>
        {(provided, snapshot) => (
          <tbody
            ref={provided.innerRef}
            {...props}
            {...provided.droppableProps}
            className={`${props.className} ${
              snapshot.isDraggingOver && columnId === COLUMN_ID_DONE
                ? "is-dragging-over"
                : ""
            }`}
          >
            {props.children}
            {provided.placeholder}
          </tbody>
        )}
      </Droppable>
    );
  };

  /**
   * Draggable table row
   */
  const DraggableTableRow = ({ index, record, columnId, tasks, ...props }) => {
    if (!tasks.length) {
      return (
        <tr className="ant-table-placeholder row-item" {...props}>
          <td colSpan={tableColumns.length} className="ant-table-cell">
            <div className="ant-empty ant-empty-normal">
              <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} />
            </div>
          </td>
        </tr>
      );
    }

    const isSelected = selectedTaskIds.some(
      (selectedTaskId) => selectedTaskId === record.id
    );
    const isGhosting =
      isSelected && Boolean(draggingTaskId) && draggingTaskId !== record.id;

    return (
      <Draggable
        key={props["data-row-key"]}
        draggableId={props["data-row-key"].toString()}
        index={index}
      >
        {(provided, snapshot) => {
          return (
            <tr
              ref={provided.innerRef}
              {...props}
              {...provided.draggableProps}
              {...provided.dragHandleProps}
              className={`row-item ${isSelected ? "row-selected" : ""} ${
                isGhosting ? "row-ghosting" : ""
              } ${snapshot.isDragging ? "row-dragging" : ""}`}
            >
              {props.children}
            </tr>
          );
        }}
      </Draggable>
    );
  };

  /**
   * Get tasks
   */
  const getTasks = (entities, id) => {
    return entities.columns[id].taskIds.map((taskId) =>
      entities.tasks.find((item) => item.id === taskId)
    );
  };

  /**
   * On before capture
   */
  const onBeforeCapture = (start) => {
    const draggableId = start.draggableId;
    const selected = selectedTaskIds.find((taskId) => taskId === draggableId);

    // if dragging an item that is not selected - unselect all items
    if (!selected) {
      setSelectedTaskIds([]);
    }

    setDraggingTaskId(draggableId);
  };

  /**
   * On drag end
   */
  const onDragEnd = (result) => {
    const destination = result.destination;
    const source = result.source;

    // nothing to do
    if (!destination || result.reason === "CANCEL") {
      setDraggingTaskId(null);
      return;
    }

    const processed = mutliDragAwareReorder({
      entities,
      selectedTaskIds,
      source,
      destination
    });

    setEntities(processed.entities);
    setDraggingTaskId(null);
  };

  return (
    <>
      <Card
        className={`c-multi-drag-table ${draggingTaskId ? "is-dragging" : ""}`}
      >
        <div>
          selectedTaskIds: {JSON.stringify(selectedTaskIds)}
          <br />
          draggingTaskId: {JSON.stringify(draggingTaskId)}
        </div>
        <br />

        <DragDropContext
          onBeforeCapture={onBeforeCapture}
          onDragEnd={onDragEnd}
        >
          <Row gutter={40}>
            {entities.columnIds.map((id) => (
              <Col key={id} xs={12}>
                <div className="inner-col">
                  <Row justify="space-between" align="middle">
                    <h2>{id}</h2>
                    <span>
                      {draggingTaskId && selectedTaskIds.length > 0
                        ? selectedTaskIds.length +
                          " record(s) are being dragged"
                        : draggingTaskId && selectedTaskIds.length <= 0
                        ? "1 record(s) are being dragged"
                        : ""}
                    </span>
                  </Row>

                  <Table
                    dataSource={getTasks(entities, id)}
                    columns={tableColumns}
                    rowKey="id"
                    components={{
                      body: {
                        // Custom tbody
                        wrapper: (val) =>
                          DroppableTableBody({
                            columnId: entities.columns[id].id,
                            tasks: getTasks(entities, id),
                            ...val
                          }),
                        // Custom td
                        row: (val) =>
                          DraggableTableRow({
                            tasks: getTasks(entities, id),
                            ...val
                          })
                      }
                    }}
                    // Set props on per row (td)
                    onRow={(record, index) => ({
                      index,
                      record
                    })}
                  />
                </div>
              </Col>
            ))}
          </Row>
          <br />
        </DragDropContext>
      </Card>
    </>
  );
};

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文