SortableJS / Sortable

Reorderable drag-and-drop lists for modern browsers and touch devices. No jQuery or framework required.
https://sortablejs.github.io/Sortable/
MIT License
29.77k stars 3.7k forks source link

[bug] Click events not firing after dragging element with pull: 'clone' #2397

Open Prabhu-Kathiresan opened 3 months ago

Prabhu-Kathiresan commented 3 months ago

Describe the bug I am using sortable.js in my react app, and i have the use-case where I wanted to either drag and drop item from sourceList or if I click on the sourceList item, that item get appended to the targetList.

The click event is triggering fine until I drag the item from sourceList, once I drag the item the click event on the dragged item is getting removed.

I have a sourceList as follow

const SourceList = ({ dataTypes, onClick, onDragStart }) => {
  const listRef = useRef(null);

  useEffect(() => {
    if (listRef.current) {
      Sortable.create(listRef.current, {
        group: {
          name: 'shared',
          put: false,
          pull: 'clone'
        },
        animation: 150,
        sort: false, // Prevent sorting within this list
        onStart: (event) => {
          if (onDragStart) {
            onDragStart(event);
          }
        },
      });
    }

    return () => {
      if (listRef.current && listRef.current.sortable) {
        listRef.current.sortable.destroy();
      }
    };
  }, [onDragStart]);

  const handleClick = (dataType) => {
    if (onClick) {
      onClick(dataType);
    }
  };

  return (
    <ul ref={listRef} style={{ listStyleType: 'none', padding: 0 }}>
      {dataTypes.map((dataType) => (
        <li
          key={dataType.id}
          style={{ padding: '10px', border: '1px solid #ccc', margin: '5px', cursor: 'pointer' }}
          onClick={() => handleClick(dataType)}
        >
          {dataType.content}
        </li>
      ))}
    </ul>
  );
};

And TagretList as follow

const TargetList = ({ items, onDrop }) => {
  const listRef = useRef(null);

  useEffect(() => {
    if (listRef.current) {
      Sortable.create(listRef.current, {
        group: 'shared',
        animation: 150,
        sort: true,
        onEnd: (event) => {
          if (onDrop) {
            onDrop(event);
          }
        },
      });
    }

    return () => {
      if (listRef.current && listRef.current.sortable) {
        listRef.current.sortable.destroy();
      }
    };
  }, [items, onDrop]);

  return (
    <ul ref={listRef} style={{ listStyleType: 'none', padding: 0, minHeight: 200 }}>
      {items.map((item) => (
        <li key={item.id} style={{ padding: '10px', border: '1px solid #ccc', margin: '5px' }}>
          {item.content}
        </li>
      ))}
    </ul>
  );
};

And in my App.jsx

const App = () => {
  const [sourceItems,] = useState([
    { id: 1, content: 'Type A' },
    { id: 2, content: 'Type B' },
  ]);
  const [targetItems, setTargetItems] = useState([]);

  const handleClick = (dataType) => {
    const newItem = {
      id: Date.now(), // Unique id
      content: dataType.content,
    };
    setTargetItems((prevItems) => [...prevItems, newItem]);
  };

  const handleDrop = (event) => {
    const draggedContent = event.item.textContent;
    console.log(draggedContent);
    const newItem = {
      id: Date.now(), // Unique id
      content: draggedContent,
    };
    setTargetItems((prevItems) => [...prevItems, newItem]);
  };

  return (
    <div style={{ display: 'flex', justifyContent: 'space-between' }}>
      <div style={{ width: '45%', border: '1px solid #ededed' }}>
        <h3>Data Types</h3>
        <SourceList dataTypes={sourceItems} onClick={handleClick} />
      </div>
      <div style={{ width: '45%', border: '1px solid #ededed' }}>
        <h3>Target List</h3>
        <TargetList items={targetItems} onDrop={handleDrop} />
      </div>
    </div>
  );
};

Attaching video for reference,

https://github.com/user-attachments/assets/9780bd34-ed56-40d3-8475-2995d012c671

To Reproduce Steps to reproduce the behavior:

  1. Go to '...'
  2. Click on '....'
  3. Scroll down to '....'
  4. See error

Expected behavior

Cloned element on the target list should have the click event, instead the source list element should maintain the click events associated to it.

Information

Versions - Look in your package.json for this information: sortablejs = ^1.15.2 @types/sortablejs = ^1.15.8