如何在 React 中拖放嵌套列表?

问题描述 投票:0回答:0

我有一个可以拖放的项目列表,它工作正常。但是,我在跨索引拖放分组元素时遇到问题。该数组与我提供的代码中显示的一样,我无法更改其结构。

我想要实现的是能够拖放任务和组,当我将一个组拖放到一个任务或另一个组中时,该组应该成为该任务或组的子项。

如果您需要更多信息,请告诉我。阵列如图所示,别无他法。

export default function App() {
  const dragRef = useRef<number>(-1);

  const [items, setItems] = useState([
    { schema: "task", title: "Tarea A", level: 0, position: "1" },
    { schema: "task", title: "Tarea B", level: 0, position: "2" },
    { schema: "group", title: "Grupo 0", level: 0, position: "3" },
    { schema: "group", title: "Grupo 1", level: 0, position: "4" },
    { schema: "task", title: "Tarea 1", level: 1, position: "4.1" },
    { schema: "task", title: "Tarea 2", level: 1, position: "4.2" },
    { schema: "group", title: "Sub group 1", level: 1, position: "4.3" },
    { schema: "task", title: "Tarea sub 1", level: 2, position: "4.3.1" },
    { schema: "task", title: "Tarea sub 2", level: 2, position: "4.3.2" },
    { schema: "task", title: "Tarea C", level: 0, position: "5" }
  ]);

这是逻辑:

const handleDragStart = (e: React.DragEvent, i: number) => {
  dragRef.current = i;
};

const getTargetIndex = (sourceLevel: number, indexTarget: number) => {
  let index = indexTarget;
  let target = items[index];

  while (target.level > sourceLevel) {
    index -= 1;
    target = items[index];
  }

  return index;
};

const getLastChildIndex = (targetIndex: number) => {
  let childIndex = targetIndex + 1;
  return childIndex;
};

const handleReSort = (indexTarget: number, indexSource: number) => {
  const source = items[indexSource];

  let targetIndex = getTargetIndex(source.level, indexTarget);
  const target = items[targetIndex];

  const isSameLevel = target.level === source.level;
  const isAnyAGroup = target.schema === "group" || source.schema === "group";

  const _items = [...items];

  if (isSameLevel && !isAnyAGroup) {
    _items.splice(indexSource, 1);
    _items.splice(targetIndex, 0, source);
    setItems(_items);
    return;
  }

  if (isSameLevel && isAnyAGroup) {
    const targetIsGroup = target.schema === "group" || source.schema === "group";
    const targetIsLast = targetIndex + 1 === items.length;
    const targetIsEmpty =
      !targetIsGroup || targetIsLast ? true : items[targetIndex + 1].level === target.level;

    const sourceIsGroup = source.schema === "group";
    const sourceIsLast = indexSource + 1 === items.length;
    const sourceIsEmpty =
      !sourceIsGroup || sourceIsLast ? true : items[indexSource + 1].level === source.level;

    // Remove the source element from _items list
    _items.splice(indexSource, 1);

    // Insert the source element into its new position
    if (targetIsEmpty && sourceIsEmpty) {
      // If both target and source elements are empty, insert the source element at the target index
      _items.splice(indexTarget, 0, source);
      setItems(_items);
    } else {
      if (!sourceIsEmpty && !targetIsEmpty) {
        // If both target and source elements have children
      } else if (!targetIsEmpty) {
        targetIndex = getLastChildIndex(targetIndex);
        _items.splice(targetIndex, 0, source);
        setItems(_items);
      } else {
        // If only the source element has children
      }
    }
  }
};

const handleDrop = (e: React.DragEvent, indexTarget: number) => {
  e.preventDefault();

  if (!dragRef || dragRef.current === -1) return;

  const indexSource = dragRef.current;
  handleReSort(indexTarget, indexSource);
};

return (
  <div className="App">
    <ul>
      {items.map((item: any, i: number) => (
        <li
          draggable
          className={`item-level-${item.level} schema-${item.schema}`}
          onDragStart={(e: React.DragEvent) => handleDragStart(e, i)}
          onDrop={(e: React.DragEvent) => handleDrop(e, i)}
          onDragOver={(e) => e.preventDefault()}
        >
          {item.title}
        </li>
      ))}
    </ul>
  </div>
);
reactjs typescript drag-and-drop nested-lists splice
© www.soinside.com 2019 - 2024. All rights reserved.