npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

react-nested-dnd

v1.0.1

Published

infinite nested drag and drop implementation

Downloads

9

Readme

React-nested-dnd

infinite nested drag and drop library

example

try it out with this sandbox example

Components

Provider

Is the context Root component. it requires 2 props, the htmlBackend and the onDrop handler

import React from "react";
import { Provider, OnDrop } from "react-nested-dnd";
import { HTML5Backend } from "react-dnd-html5-backend";

const MyApp = () => {
  const [state, setState] = useState();
  const onDrop: OnDrop = ({ source, destination, dropType, sameSource }) => {
    const { index: srcIndex, droppableId: srcContainerId } = source;
    const { index: destIndex, droppableId: destContainerId } = destination;
    // your application logic goes here
    // setState(newState)
  };
  return (
    <Provider onDrop={onDrop} htmlBackend={HTML5Backend}>
      <Children />
    </Provider>
  );
};

Draggable

is the draggable element wrapper. it requires 5 props:

  • type the Draggable item type
  • id its own id
  • droppableId its parent id, the id of the list it belongs to
  • index its index in the items list.
  • children a callback that provides some style props and snapShot
import React from "react";
import { Draggable } from "react-nested-dnd";

const DraggableItem = (props: {
  value: number;
  id: string;
  parentId: string;
}) => {
  const type = props.value % 2 === 0 ? "even" : "odd";

  return (
    <Draggable
      droppableId={props.parentId}
      id={props.id}
      index={index}
      type={type}
    >
      {(providedStyle, snapshot) => {
        return (
          <div
            {...providedStyle}
            className={snapshot.isDragging ? "dragging" : ""}
          >
            {props.value}
          </div>
        );
      }}
    </Draggable>
  );
};

Droppable

the draggable items list container. it requires 3 props

  • id the droppable id
  • accept array of allowed draggable item types.
  • children a call back that provides a droppable style, a place holder for the dragged item and the droppable component snapshot
import React from "react";
import { Droppable } from "react-nested-dnd";

const DroppableList = (props: {
    id: string;
    items: number[];
  ;
}) => {

  const Items = props.items.map((item, index) => <DraggableItem droppableId={props.id} {...restOfItemProps}/>)

  return (
    <Droppable id={props.id} accept={["even", "odd"]}>
      {(providedStyle, snapshot, placeholder) => {
        return (
          <div {...providedStyle}>
            {Items}
            // when an item is dragged over the container, we should add a placehold so the container will have enought space for the item to be dropped
            {placeholder}
          </div>
        );
      }}
    </Droppable>
  );
};

useOnDrop

here is an example of how we can handle onDrop using a custom hook

// utils.ts

const initialValues = {
  workspace: {
    containersOrder: ["container-1", "container-2"] as const,
  },
  containers: {
    "container-1": {
      id: "container-1",
      items: ["1", "2", "3", "4", "5"],
      isNested: false,
    },
    "container-2": {
      id: "container-2",
      items: ["6", "7", "8", "9", "10", "11"],
      isNested: false,
    },
  },
  items: {
    "1": { id: "1", text: "one", type: "odd" },
    "2": { id: "2", text: "two", type: "even" },
    "3": { id: "3", text: "three", type: "odd" },
    "4": { id: "4", text: "four", type: "even" },
    "5": { id: "5", text: "five", type: "odd" },
    "6": { id: "6", text: "six", type: "even" },
    "7": { id: "7", text: "seven", type: "odd" },
    "8": { id: "8", text: "height", type: "even" },
    "9": { id: "9", text: "nine", type: "odd" },
    "10": { id: "10", text: "ten", type: "even" },
    "11": { id: "11", text: "eleven", type: "odd" },
  },
};

export const useOnDrop = () => {
  const [state, setState] = useState(initialValues);

  const onDrop: OnDrop = ({ source, destination, dropType, sameSource }) => {
    const { index: srcIndex, droppableId: srcContainerId } = source;
    const { index: destIndex, droppableId: destContainerId } = destination;

    const newState = { ...state };
    type ContainerKey = keyof typeof newState.containers;

    if (srcContainerId === "workspace") {
      if (dropType !== "replace" || !sameSource || srcIndex === destIndex) {
        return;
      }
      const { workspace } = newState;

      const items = workspace.containersOrder as any as string[];
      const srcItemId = items[srcIndex as any];

      if (destIndex > srcIndex) {
        // we add the source id at the destination index
        items.splice(destIndex + 1, 0, srcItemId);
        // we remove the source id at its previous position
        items.splice(srcIndex, 1);
      } else {
        // we remove the source id at its previous position
        items.splice(srcIndex, 1);
        // we add the source id at the destination index
        items.splice(destIndex, 0, srcItemId);
      }
      setState(newState);
      return;
    }

    if (dropType === "replace") {
      if (sameSource) {
        if (srcIndex === destIndex) return;

        const container = newState.containers[srcContainerId as ContainerKey];
        if (!container) return;
        const items = container.items;
        const srcItemId = items[srcIndex as any];

        if (destIndex > srcIndex) {
          // we add the source id at the destination index
          items.splice(destIndex + 1, 0, srcItemId);
          // we remove the source id at its previous position
          items.splice(srcIndex, 1);
        } else {
          // we remove the source id at its previous position
          items.splice(srcIndex, 1);
          // we add the source id at the destination index
          items.splice(destIndex, 0, srcItemId);
        }
        setState(newState);
      } else {
        // different drop zones
        const srcContainer =
          newState.containers[srcContainerId as ContainerKey];
        const destContainer =
          newState.containers[destContainerId as ContainerKey];
        if (!srcContainer || !destContainer) return;

        const srcItems = srcContainer.items;
        const destItems = destContainer.items;
        // removing srcItem from the source items list
        const [srcItem] = srcItems.splice(source.index, 1);
        // adding the source item to the destination items list
        destItems.splice(destination.index, 0, srcItem);

        if (srcContainer.isNested && !srcContainer.items.length) {
          delete newState.containers[srcContainerId as ContainerKey];
        }
        setState(newState);
      }
    } else {
      const srcContainer = newState.containers[srcContainerId as ContainerKey];
      const srcItemId = srcContainer.items[srcIndex];

      const destContainer =
        newState.containers[destContainerId as ContainerKey];
      const destItemId = destContainer.items[destIndex];

      srcContainer.items.splice(srcIndex, 1);

      const nestedContainerId = `nested-${destItemId}`;
      const nestedContainer: any = {
        id: nestedContainerId,
        items: [srcItemId],
        isNested: true,
      };
      newState.containers[nestedContainerId as ContainerKey] = nestedContainer;
      if (srcContainer.isNested && !srcContainer.items.length) {
        delete newState.containers[srcContainerId as ContainerKey];
      }
      setState(newState);
    }
  };
  return { onDrop, appState: state };
};

MIT © https://github.com/fernandoem88/typed-classnames