haltu / muuri

Infinite responsive, sortable, filterable and draggable layouts
https://muuri.dev
MIT License
10.8k stars 643 forks source link

Deep nested list #493

Open elvonkh opened 3 years ago

elvonkh commented 3 years ago

I am using muuri-react and I want to make deep nested sortable list like in the image below image I am using following code to generate nested list

import { MuuriComponent } from "muuri-react";
import React, { useCallback, useEffect, useState } from "react";
import CollectionItem from "./CollectionItem";
import { useGrid } from "muuri-react";

const sortedColl = [
  {
    id: "1db3cf0f-1705-4d24-a279-368d9ceb3a89",
    title: "New",
    children: [
      {
        id: "f653da27-7cba-4a94-9f3e-d08988d87348",
        title: "Dresses",
      },
      {
        id: "cce17318-21e6-42fa-9dc2-f60affd22e0a",
        title: "Dresses",
        children: [
          {
            id: "3f445f24-1000-415e-93f5-f59db4985dc2",
            title: "Dresses",
            children: [
              {
                id: "7510d247-5774-4bbc-8094-67ab5c530a16",
                title: "Clothing",
              },
            ],
          },
        ],
      },
    ],
  },
  {
    id: "91f27837-e17d-4064-96f6-51ff1aa8facb",
    title: "New",
  },

  {
    id: "3f445f24-1000-415e-93f5-f59db4985dc2",
    title: "Dresses",
  },
];
const coll =  [
  {
    id: "1db3cf0f-1705-4d24-a279-368d9ceb3a89",
    title: "New",

  },
  {
    id: "7510d247-5774-4bbc-8094-67ab5c530a16",
    title: "Clothing",
  },
  {
    id: "91f27837-e17d-4064-96f6-51ff1aa8facb",
    title: "New",
  },
  {
    id: "3f445f24-1000-415e-93f5-f59db4985dc2",
    title: "Dresses",
  },
];

function useSend(setItems, collections) {
  return useCallback(
    ({ key, fromId, toId, fromIndex, toIndex }) => {
      const blockPathTo = getBlockPath(toId);
      const blockPathFrom = getBlockPath(fromId);
      console.log(blockPathFrom);
      console.log(blockPathTo);
      if (fromId === "available") {
        console.log(collections, key);
        const item = collections.find(
          (collectionItem) => collectionItem.id === key
        );
        item.id = uuidv4();
        setItems((currentList) => {
          const tempList = [...currentList];
          console.log('tempList', tempList)
          if (blockPathTo?.length === 0) {
            console.log('Block path is empty')
            tempList.splice(toIndex, 0, item);
          } else {

          }
          // const newList = addItemRecursively(currentList, blockPathTo, toIndex, item)
          return tempList;
        });
        // const item = collections.find(
        //   (collectionItem) => collectionItem.id === key
        // );
        // item.id = uuid();
        return;
      }
      setItems((currentList) => {
        const item = findItemRecursively(currentList, key);
        removeRecursivelyFromChildren(
          currentList,
          blockPathFrom,
          fromIndex,
          item
        );
      });
    },
    [collections]
  );
}

export default function DndCollections() {
  const [collections, setCollections] = useState([]);
  const [sortedCollections, setSortedCollections] = useState(sortedColl);

  const sortedListOptions = {
    onSend: useSend(setSortedCollections, collections),
  };
  const MuuriWrapper = React.memo(({ item, blockIndex }) => {
    let id = "sorted";
    let blockIndexStringified = "-";
    if (Array.isArray(blockIndex)) {
      blockIndexStringified += blockIndex.join("-");
    }
    id += blockIndexStringified;
    const handleClass = `handle${blockIndexStringified}`;
    return (
      <MuuriComponent
        {...sortedListOptions}
        groupIds={["sortedList"]}
        dragSort={{ groupId: "sortedList" }}
        id={id}
        dragEnabled
        dragStartPredicate={{
          handle: "." + handleClass,
        }}
      >
        {item?.children.map((childItem, childIdx) => (
          <Item
            blockIndex={[...blockIndex, childIdx]}
            handleClass={handleClass}
            item={childItem}
            key={childItem.id}
          />
        ))}
      </MuuriComponent>
    );
  });

  const Item = React.memo(({ item, blockIndex, handleClass = "handle" }) => {

    return (
      <div
        style={{
          background: "white",
          boxSizing: "border-box",
          maxWidth: "100%",
          overflow: "hidden",
        }}
      >
        <CollectionItem
          item={item}
          handleClass={handleClass}
        />
        {item?.children?.length && (
          <div style={{ marginLeft: "2rem", height: itemHeight + "px" }}>
            <MuuriWrapper item={item} blockIndex={blockIndex} />
          </div>
        )}
      </div>
    );
  });

  const children = sortedCollections.map((item, index) => (
    <Item blockIndex={[index]} key={item.id} item={item} />
  ));

  return (<Card title="All collections" sectioned>
        {collections?.length && (
          <MuuriComponent
            id="available"
            dragEnabled
            groupIds={["LIST"]}
            dragSort={{ groupId: "LIST" }}
            dragPlaceholder={{
              enabled: true,
              createElement(item) {
                const placeholder = document.createElement("div");
                placeholder.innerHTML = "<div></div>";
                return placeholder;
              },
            }}
            onSend={onAvailableSend}
          >
            {collections.map((item, index) => (
              <div
                key={item.id}
                style={{
                  background: "white",
                  boxSizing: "border-box",
                  maxWidth: "100%",
                  overflow: "hidden",
                }}
              >
                <CollectionItem item={item} handleClass="handle" />
              </div>
            ))}
          </MuuriComponent>
        )}
      </Card>
      <Card title="Sorted collections" sectioned>
        <MuuriComponent
          groupIds={["LIST", "sortedList"]}
          dragSort={{ groupId: "sortedList" }}
          id="sorted"
          dragEnabled
          dragStartPredicate={{ handle: ".handle" }}
          {...sortedListOptions}
        >
          {children}
        </MuuriComponent>
      </Card>
  );
}
EminQasimov commented 3 years ago

@elvonkh , hi, does muuri-react work with latest react versions(17)? https://github.com/Paol-imi/muuri-react/issues/50 https://github.com/Paol-imi/muuri-react/issues/59

or do you use fork version of it? https://github.com/adamerose/muuri-react btwn, your example looks similar with dndkit examples - https://5fc05e08a4a65d0021ae0bf2-gngonedmxi.chromatic.com/?path=/story/examples-tree-sortable--all-features

alexandrsek commented 3 years ago

Hello! Sorry for being a little off topic, but I had a similar task to make a nested drag and drop navigation menu and tried many DnD packages like DnDKit, react-beautiful-dnd and etc...

I found a solution using a package called "atlaskit/tree". It required some tweaks to data structure, but in the end it solved this problem for me perfectly. The documentation is not the best so I recommend reading source code on the bitbucket

Docs and examples: https://atlaskit.atlassian.com/packages/confluence/tree BitBucket: https://bitbucket.org/atlassian/atlaskit-mk-2/src/master/packages/core/tree/src/