samceracas / react-reorderable-list

A simple UI framework friendly reorderable list component for React.
https://samceracas.github.io/react-reorderable-list/
6 stars 2 forks source link

react-reorderable-list 📄

A simple UI framework friendly reorderable list component for React.

NPM JavaScript Style Guide Build Status Bundlephobia

Install

npm

npm install react-reorderable-list

yarn

yarn add react-reorderable-list

Features

Examples

You can head over to our demo page to view usage with UI frameworks.

Supported Use Cases

There are 2 types of lists available for use. You can use an ungrouped list if you don't want to transfer items between lists or you can use a grouped list which gives you the ability to transfer data from one list to another by dragging and dropping the list item.

Below are supported use case scenarios for grouped and ungrouped lists.

Wrapped reorderable list group (Basic Usage)

A group of lists wrapped within a ReOrderableListGroup. Lists within the group can transfer data to each other. Provides less setup and a cleaner configuration for generic grouped lists.

Example

import React, { useState } from 'react'
import {
  ReOrderableItem,
  ReOrderableList,
  ReOrderableListGroup
} from 'react-reorderable-list'
import 'react-reorderable-list/dist/index.css'

export default function () {
  //sample data
  const [group, setGroup] = useState([
    [
      { id: 1, name: 'Test' },
      { id: 2, name: 'Hello' },
      { id: 3, name: 'World!' }
    ],
    [
      { id: 1, name: 'Item' },
      { id: 2, name: 'Name' }
    ]
  ])

  return (
    <div style={{ display: 'flex', gap: '20px' }}>
      <ReOrderableListGroup
        //The name for this group must be unique as this serves as the identifier
        //when validating if the item should be transfered to the list or not.
        //Items can only be transferred to the list with the same group name.
        name='uniqueGroupName'
        //group data
        group={group}
        //update list group
        onListGroupUpdate={(newList) => setGroup(newList)}
      >
        {group.map((list, index) => (
          <ReOrderableList key={`list-${index}`} style={{ width: '300px' }}>
            {list.map((data, index) => (
              <ReOrderableItem key={`item-${index}`}>
                <div style={{ border: '1px solid black' }}>{data.name}</div>
              </ReOrderableItem>
            ))}
          </ReOrderableList>
        ))}
      </ReOrderableListGroup>
    </div>
  )
}

Using the path property to access your list array.

import React, { useState } from 'react'
import {
  ReOrderableItem,
  ReOrderableList,
  ReOrderableListGroup
} from 'react-reorderable-list'
import 'react-reorderable-list/dist/index.css'

export default function () {
  //sample data
  const [groups, setGroup] = useState([
    {
      id: 1,
      name: 'test',
      tasks: [
        { id: 1, name: 'Test' },
        { id: 2, name: 'Hello' },
        { id: 3, name: 'World!' }
      ]
    },
    {
      id: 2,
      name: 'test2',
      tasks: [
        { id: 1, name: 'Item' },
        { id: 2, name: 'Name' }
      ]
    }
  ])

  return (
    <div style={{ display: 'flex', gap: '20px' }}>
      <ReOrderableListGroup
        //The name for this group must be unique as this serves as the identifier
        //when validating if the item should be transfered to the list or not.
        //Items can only be transferred to the list with the same group name.
        name='uniqueGroupName'
        //group data
        group={groups}
        //update list group
        onListGroupUpdate={(newList) => setGroup(newList)}
      >
        {groups.map((list, index) => (
          //here we use the path property to access the list array from the object
          <ReOrderableList
            key={`list-${index}`}
            path={`${index}.tasks`}
            style={{ width: '300px' }}
          >
            {list.tasks.map((data, index) => (
              <ReOrderableItem key={`item-${index}`}>
                <div style={{ border: '1px solid black' }}>{data.name}</div>
              </ReOrderableItem>
            ))}
          </ReOrderableList>
        ))}
      </ReOrderableListGroup>
    </div>
  )
}

Unwrapped reorderable list group

A group of lists not bound within a ReOrderableListGroup component. Useful for use cases where you need to put multiple reorderable lists into different components while still making them interactable with each other. A state management system such as Redux is required for managing app states for such scenarios.

import React, { useState } from 'react'
import { ReOrderableItem, ReOrderableList } from 'react-reorderable-list'
import 'react-reorderable-list/dist/index.css'

export default function () {
  //sample data
  const [group, setGroup] = useState([
    [
      { id: 1, name: 'Ayy ' },
      { id: 2, name: 'This ' },
      { id: 3, name: 'One' },
      { id: 4, name: 'Is' }
    ],
    [
      { id: 1, name: 'An example of' },
      { id: 2, name: 'A non wrapped' },
      { id: 3, name: 'list group' }
    ]
  ])

  return (
    <div style={{ display: 'flex', gap: '20px' }}>
      <ReOrderableList
        //The name for this group must be unique as this serves as the identifier
        //when validating if the item should be transfered to the list or not.
        //Items can only be transferred to the list with the same group name.
        name='list3'
        //group data
        group={group}
        //The object path that tells our component where to access the actual array for our list.
        //For lists not embedded within an object simply use the index for our list.
        //The path below retrieves the 1st list from our group.
        path={0}
        //our list update callback
        onListUpdate={(newList) => setGroup(newList)}
        style={{
          width: '300px'
        }}
      >
        {group[0].map((data, index) => (
          <ReOrderableItem key={`item-${index}`}>
            <div
              style={{
                border: '1px solid black'
              }}
            >
              {data.name}
            </div>
          </ReOrderableItem>
        ))}
      </ReOrderableList>

      <ReOrderableList
        //The name for this group must be unique as this serves as the identifier
        //when validating if the item should be transfered to the list or not.
        //Items can only be transferred to the list with the same group name.
        name='list3'
        group={group}
        //The object path that tells our component where to access the actual array for our list.
        //For lists not embedded within an object simply use the index for our list.
        //The path below retrieves the 2nd list from our group.
        path={1}
        //our list update callback
        onListUpdate={(newList) => setGroup(newList)}
        style={{
          width: '300px'
        }}
      >
        {group[1].map((data, index) => (
          <ReOrderableItem key={`item-${index}`}>
            <div
              style={{
                border: '1px solid black'
              }}
            >
              {data.name}
            </div>
          </ReOrderableItem>
        ))}
      </ReOrderableList>
    </div>
  )
}

Ungrouped list

Ungrouped lists cannot transfer and recieve items from other lists. You can only reorder items from within the list.

Example

import React, { useState } from 'react'
import { ReOrderableItem, ReOrderableList } from 'react-reorderable-list'
import 'react-reorderable-list/dist/index.css'

export default function () {
  //sample data
  const [list, setList] = useState([
    { id: 1, name: 'This' },
    { id: 2, name: 'list' },
    { id: 2, name: 'is restricted' },
    { id: 2, name: 'to itself' }
  ])

  return (
    <div style={{ display: 'flex', gap: '20px' }}>
      <ReOrderableList
        //The unique identifier for this list. Should be unique from other lists and list groups.
        name='list2'
        //your list data
        list={list}
        //the list update callback
        onListUpdate={(newList) => setList(newList)}
        style={{
          width: '300px'
        }}
      >
        {list.map((data, index) => (
          <ReOrderableItem key={`item-${index}`}>
            <div
              style={{
                border: '1px solid black'
              }}
            >
              {data.name}
            </div>
          </ReOrderableItem>
        ))}
      </ReOrderableList>
    </div>
  )
}

Components

Below are the primary components used when making lists along with the available properties for the respective component.

ReOrderableListGroup

Property Type Description Default
name (Required) | String | The identifier when validating transferred items. This must be unique from other list groups. | undefined
group (Required) | Array | The array of lists for this group. | null
onListGroupUpdate (Required) | Function | The callback function which passes the new value for group as parameter. | undefined

ReOrderableList

Property Type Description Default
name (Required) | String | The identifier when validating transferred items. This must be unique from other list groups. | undefined
group (Required when the list is not wrapped within a ReOrderableListGroup component, otherwise Optional) | Array | The array of lists for this group. | null
list (Required when this list is ungrouped, otherwise Optional) | Array | The data for this list. This is automatically set when wrapped within a ReOrderableListGroup. | []
onListUpdate (Required) | Function | The callback function which passes the new value for group. | undefined
path (Optional) | String | Number | The index or object path needed to access the list array from the group. This property is automatically set when wrapped within a ReOrderableListGroup using the index as the accessor the to the list array.| 0
component (Optional) | String | Component | Define a custom component for this list. | div
componentProps (Optional) | Object | The props for the component. | null
orientation (Optional) | String | The orientation for this list. Use vertical for vertically oriented lists, otherwise use horizontal. | vertical
placeholder (Optional) | Function | A function that returns a Component or HTMLElement to be used as placeholder when an item is being hovered on the list. Passes the currently dragged ReOrderableItem as parameter. | (item) => <div style={{ width: `${item.rect.width}px`, height: `${item.rect.height}px`, backgroundColor: 'rgba(0, 0, 0, 0.1)' }} />

ReOrderableItem

Property Type Description Default
drag (Optional) | String | A function that returns a Component or HTMLElement to be used as graphic when dragging an item. Manually setting the width and height styles is required before returning the element or component. Passes the currently dragged ReOrderableItem as parameter. A clone of the item element.
component (Optional) | String | Component | The component to be used for this item. | div
componentProps (Optional) | Object | The props for the component. | null
itemIndex (Optional) | Number | The index for this item on the list. This property is automatically set by the ReOrderableList component but can be manually overridden.. | 0
itemData (Optional) | Object | The data for this item. This property is automatically set by the ReOrderableList component but can be manually overridden. | null
onItemDragStart (Optional) | Function | Event triggered on item drag start. Passes an object with the following properties
{
item, //the currently dragged item component.
dragX, //the calculated drag coordinate for the X axis.
dragY, //the calculated drag coordinate for the Y axis.
pageX, //the page coordinate for the X axis.
pageY, //the page coordinate for the Y axis.
clientX, //the client viewport coordinate for the X axis.
clientY //the client viewport coordinate for the Y axis.
}
| null
onItemDrag (Optional) | Function | Event triggered on item drag. Passes an object with the following properties
{
item, //the currently dragged item component.
dragX, //the calculated drag coordinate for the X axis.
dragY, //the calculated drag coordinate for the Y axis.
pageX, //the page coordinate for the X axis.
pageY, //the page coordinate for the Y axis.
clientX, //the client viewport coordinate for the X axis.
clientY //the client viewport coordinate for the Y axis.
}
| null
onItemDragEnd (Optional) | Function | Event triggered on item drag end. Passes an object with the following properties
{
item, //the currently dragged item component.
pageX, //the page coordinate for the X axis.
pageY, //the page coordinate for the Y axis.
clientX, //the client viewport coordinate for the X axis.
clientY //the client viewport coordinate for the Y axis.
}
| null

API

ReOrderableItem

Property Type Description
instanceID String The instance ID for this reorderable item.
model Object An model with the following properties.
{
data, //the data for this item.
index, //the index for this item on the list.
instanceID, //the instance id for this item.
}
draggedElement HTMLElement The element being dragged. This is basically a copy of the item element but is used as graphic when dragging the item.
listComponent | ReOrderableList | The ReOrderableList component this item belongs to.
overlappingListElement HTMLElement The currently overlapping list container element.
clonedItemElement HTMLElement A clone of the item element.
rect ClientRect The bounding client rect of the item element.

Issues and Suggestions

Hi! Your feedback for using this library is very valuable and if you have any issues or suggestions feel free to file them on the Issues page.

License

MIT © samceracas