Drag'n'Drop Div вернуться в новую конечную позицию - PullRequest
0 голосов
/ 19 июня 2020

Я пытаюсь создать перетаскивание, где я могу переместить элемент в другую позицию в списке, и он будет обновлять список в реальном времени, поэтому, если в приведенном ниже примере я перемещаю div с содержимым 1 в div с содержимым 3 div 2 и 3 переместятся вверх, а div с содержимым 1 будет на месте третьего. Пока без проблем, могу закинуть туда и все правильно. Проблема возникает, когда я не роняю и не перемещаю div в область без возможности выпадения и позволяю ему go, в этом случае он вернется в исходное место, откуда я сначала вытащил его в первой позиции, однако, Я хочу, чтобы он вернулся в свою новую позицию, где раньше было 3.

Пример кода: https://codesandbox.io/s/jovial-night-phej3?fontsize=14&hidenavigation=1&theme=dark

Начать перетаскивание 1: Start drag 1

Наведите указатель мыши на 3, что заставит 1 занять его место Hover over 3, what makes 1 take its place

Без остановки перетаскивать выдвигаться и опускаться Without stop drag move out and drop

Я хочу, чтобы div с 1 вернулся на свое новое место после строки gree, вместо этого он возвращается на исходное место после красной строки.

Мой код реакции только для справки

import React, { useState } from "react";
import "./App.css";

const initList = [1, 2, 3, 4, 5, 6, 7, 8, 9];
function App() {
  const [list, setList] = useState(initList);
  const [draggedItem, setDraggedItem] = useState(null);

  function onDragStartHandle(e, index) {
    setDraggedItem(list[index]);
    e.dataTransfer.effectAllowed = "move";
    e.dataTransfer.setData("text/html", e.target.parentNode);
    e.dataTransfer.setDragImage(e.target.parentNode, 20, 22);
  }

  function onDragOverHandle(e, index) {
    e.preventDefault();

    const draggedOverItem = list[index];

    if (draggedOverItem === draggedItem) {
      return;
    }

    const items = list.filter((item) => item !== draggedItem);
    items.splice(index, 0, draggedItem);
    setList(items);
  }

  return (
    <div className="App">
      <header className="App-header">
        <h3>Drag'n'Drop</h3>
        <ul>
          {list.map((item, index) => (
            <li
              key={index}
              onDragOver={(e) => onDragOverHandle(e, index)}
              className="item-style"
            >
              <div draggable onDragStart={(e) => onDragStartHandle(e, index)}>
                {item}
              </div>
            </li>
          ))}
        </ul>
      </header>
    </div>
  );
}

export default App;

1 Ответ

2 голосов
/ 20 июня 2020

Хорошо, потребовалось время, чтобы по-настоящему разобраться в этом, и лучшее, что я мог придумать, - это когда вы перетаскиваете элемент, который вы продвигали, и обновляете состояние, но если пользователь отказывается от перетаскивания, элемент "возвращается" обратно в откуда он возник, и не последним, где он завис. Я потратил некоторое время на изучение Drag and Drop API , и ничего особо не выскочило, как возможность изменить это поведение «в полете».

То, что я считаю технически более правильное поведение для брошенных отбрасываний, т. Е. Пользователь не отбрасывает элемент в списке, состоит в том, чтобы список сохранял состояние, в котором он находился ранее с до перетаскивания.

Вот решение, которое добавляет дополнительную часть состояния для изменения во время события перетаскивания, и если перетаскивание прошло успешно, то временное состояние фиксируется в «реальном» состоянии списка, а в случае неудачи возвращается обратно в существующее состояние списка (оно просто остается неизменным).

import React, { useState } from "react";
import "./App.css";

const initList = [1, 2, 3, 4, 5, 6, 7, 8, 9];

function App() {
  const [list, setList] = useState(initList);
  const [dragList, setDragList] = useState(null);
  const [draggedItem, setDraggedItem] = useState(null);

  /**
   * On drag start
   * Initialize the temporary item being dragged and drag list.
   */
  const onDragStartHandle = index => e => {
    setDraggedItem(list[index]);
    setDragList(list);
    e.dataTransfer.effectAllowed = "move";
    e.dataTransfer.setData("text/html", e.target.parentNode);
    e.dataTransfer.setDragImage(e.target.parentNode, 20, 22);
  };

  /**
   * On drag end
   * Save updated drag list to list and nullify temporary values.
   */
  const onDragEndHandle = e => {
    e.preventDefault();
    setList(dragList);
    setDragList(null);
    setDraggedItem(null);
  };

  /**
   * On drag leave
   * Item wasn't dropped so reset temporary drag list
   */
  const onDragLeaveHandle = e => {
    e.preventDefault();
    setDragList(list);
  };

  /**
   * On drag over
   * If dragging over another item update the drag list with new position
   */
  const onDragOverHandle = index => e => {
    e.preventDefault();

    const draggedOverItem = dragList[index];

    if (draggedOverItem === draggedItem) {
      return;
    }

    const items = dragList.filter(item => item !== draggedItem);
    items.splice(index, 0, draggedItem);
    setDragList(items);
  };

  return (
    <div className="App">
      <header className="App-header">
        <h3>Drag'n'Drop</h3>
        <ul>
          {(draggedItem ? dragList : list).map((item, index) => (
            <li key={index} className="item-style">
              <div
                draggable
                onDragOver={onDragOverHandle(index)} // Moved *
                onDragLeave={onDragLeaveHandle}
                onDragEnd={onDragEndHandle}
                onDragStart={onDragStartHandle(index)}
              >
                {item}
              </div>
            </li>
          ))}
        </ul>
      </header>
    </div>
  );
}

export default App;

* Перемещено из элемента li в div, чтобы все события происходили из одного элемента. Наличие прослушивателя событий onDragOver в элементе списка вызывало странную проблему, из-за которой пользовательский интерфейс иногда «застревал» в состоянии полу-зависания.

Редактировать список перетаскивания

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...