npm package: @iliab/easy-dnd
Description: This library provides easy drag-and-drop functionality.
Latest version: 1.0.0

# React Drag-and-Drop Library
This library provides easy drag-and-drop functionality.

## Components

### `DragnDropList`

`DragnDropList` is a flexible container that enables drag-and-drop functionality for its child elements. 

#### Usage:
```tsx
import DragnDropList from "./DragnDropList";

const SimpeExample = () => {
  
  const containerId1 = useId(); 
  const containerId2 = useId();
  const containerId3 = useId();
  const items = ["Item 1", "Item 2", "Item 3", "Item 4", "Item 5", "Item 6"];
  return (
    <div style={{display: "flex", flexDirection: "row", overflowX: "auto"}}>
      <DragnDropList width="200px" height="200px" itemsInRow={1} containerId={containerId1} className="morePadding">
        {items.map((item) => (
          <div className="textItem" >{item}</div>
        ))}
      </DragnDropList>
      
      <DragnDropList width="300px" height="200px" itemsInRow={0} containerId={containerId2} className="morePadding" />

      <DragnDropList width="300px" height="200px" itemsInRow={2} containerId={containerId3} className="morePadding" />
    </div>
    );
}
```
- The above example will show 3 containers - Vertical, Horizontal and Grid. Items can be moved between them and will be arranged accordingly.
- As you see all you need to have basic drag and drop is to wrap components you want to drag in a DragnDropList.
- Source and target DragnDropList do not need to have any particular common ancestor. 
- And items inside DragnDropList do not need to have anything special on them.

#### Props:

##### Mandatory:

- `containerId: string` - Unique identifier for the container.  useId() should be part of that string to make sure that if your component contains DragnDropList and there are multiple instances of your component on sceen, there is no conflict. You can also start or end containerId with some string you can later reference in onCreateNew or onMove if you define them.

##### Optional: 

- `itemsInRow?: number` -  0 - horizontal list. 1 - vertical list. other positive n - a grid with n columns
- `width?: string` - Width of the container.
- `height?: string` - Height of the container.
***
- `fixedContent?: boolean` - with this set to true, items inside will stay as they are in the order they are in. By defualt it's false and items can change position or be dragged out of the container.
- `allowMultipleItemCopies?: boolean` - false by default. With it set to true, when new items are dragged into the container from another container, they will appear even if identical ones already exist.
***
- `onCreateNew?: (item: DragItem, newIndex: number | null) => ReactNode|null` - This is the hook that allows full control over what happens when an item is dragged into the container that defines it. More on this hook later. This callback is triggered when the source of the item is another container. If the source is te same as the target use onMove.  
- `onMove?: (item: DragItem, newIndex: number | null) => ReactNode|null` - Like onCreateNew when source is is the same as target.
***
- `className?: string` - Additional CSS classes.
- `style?: React.CSSProperties` - Inline styles.


---

#### `DragItem`

`DragItem` is a component that gets passed into onCreateNew and onMove. It renders as a div that is wrapped around each child of DragnDropList. That div keeps all the drag and drop related event handlers. 

```tsx
export type DragItem = {
  key: string; // for internal use, do not change or rely on
  content: ReactNode; // the child of DragnDropList
  sourceContanerId: string; // when passed into onCreateNew indicates the last parent DragnDropList
  dndData?: any; // if your child (of DragnDropList) had dnd-data attribute, its content can now be found here. FYI for better performance, dnd-data will not be rendered on the child. 
};
```
---

#### onCreateNew
- If you want to have full control over what happens when an item is dropped on your container define this method. 
- If it is not defined, the default behaviour is - the item simply appears in your container and disapears from the source container.
- If your handler returns null, the drop is canceled and the source container keeps the item.
- If it returns TSX representing a new child, that child will be rendered inside the DragnDropList.

#### Usage: 
```tsx

    const createBox = (item: DragItem, targetIndex: number|null) => {
      if (item.dndData as ComplexItem) {
        return <div className="boxItem" dnd-data={item.dndData}>
                <div className="boxHeader">{item.dndData.header}</div>
                <div className="boxBody">Table for (
                  <b>{item.dndData.body ? item.dndData.body : item.dndData.header}</b>)
                </div>
              </div>
      
      }
      return null; //rejects drop
    }; 

     <DragnDropList ... onCreateNew={createBox} ... />
```
Note the use of item.dndData and dnd-data. The idea here is that you can attach whatever info you need to pass with the source item in whatever format you like using dnd-data. Whatever you pass you can then collect in item.dndData. 

#### Othings you can do inside onCreateNew : 
- Filter by source container: use item.sourceContanerId for that.
- Filter by something you store in the dnd-data (let's say you defined itemType property)

```tsx
    const createBox = (item: DragItem, targetIndex: number|null) => {
      if (item.sourceContanerId.startsWith( "GoodSource" ) && item.dndData && item.dndData.itemType==="GoodType") 
        return item.content; 
      else
        return null; //rejects drop
    }; 
```
---

#### onMove
- onMove hanlder follows the same logic as onCreateNew, but is called when an item is dropped in the same container it was dragged from. 
- In other words if user wants to change the order of items in the container they drag it to a new place. 
- You should not need to, but you can intervine in that process as well. 
For example this should prevent items from changing position:
```tsx
    const moveBox = (item: DragItem, targetIndex: number|null) => {
      return null; // this should prevent items from changing position. 
    };

    <DragnDropList ... ononMove={moveBox} ... />
```

---
#### Styling
These styles are used by DragnDropList and DragItem. 
```css
/* DragnDropList has both className and style props as alternatives and they will override what is in .dragnDropList */
.dragnDropList {
  border: 1px solid #ccc;
  padding: 0px;
}

/* .dragItem is applied to the div wrapping each of the children of DragnDropList */
.dragItem {
  margin: 0px;
  padding: 0px;
  cursor: move;
  border: 2px solid transparent;
}

/* These 4 below are used in addition to .dragItem to show light blue line between children of DragnDropList to indicate where new item will be dropped */
.drag-over-left {
  border-left: 2px solid lightblue;
}

.drag-over-top {
  border-top: 2px solid lightblue;
}

.drag-over-right {
  border-right: 2px solid lightblue;
}

.drag-over-bottom {
  border-bottom: 2px solid lightblue;
}
```