@revell29/dnd-kit-sortable-tree
v0.0.2
Published
[![npm version](https://badge.fury.io/js/dnd-kit-sortable-tree.svg)](https://www.npmjs.org/package/dnd-kit-sortable-tree) [![npm](https://img.shields.io/npm/dt/dnd-kit-sortable-tree.svg)](https://www.npmjs.org/package/dnd-kit-sortable-tree) [![MIT](https:
Downloads
204
Readme
dnd-kit-sortable-tree
This is a Tree component extracted from dndkit examples and abstracted a bit. Here's how it could look like (visuals are completely customizable via css though)
Play around in examples to check the API and see what it can do.
Install
npm install dnd-kit-sortable-tree @dnd-kit/core @dnd-kit/sortable @dnd-kit/utilities
How-to use
Check out the Storybook for code samples and play around. You could also play with it on playcode
Shortly, you need to render:
<SortableTree
items={/* array of your tree items */}
onItemsChanged={/* callback when items are reordered */}
TreeItemComponent={/* component that renders a single tree item */}
/>
And TreeItemComponent
is usually your data wrapped in SimpleTreeItemWrapper
or FolderTreeItemWrapper
:
React.forwardRef((props, ref) => (
<SimpleTreeItemWrapper {...props} ref={ref}>
<div>{props.item.value}</div>
</SimpleTreeItemWrapper>
));
Note that wrapping in forwardRef
and passing ref
to SimpleTreeItemWrapper
is very important!
Examples
Here's the very minimal code to add a Sortable Tree. You shouldn't use it as is in your project, but it could be easier to grasp what's going on.
export const Minimal = () => { const [items, setItems] = useState(initialMinimalData); return ( <SortableTree items={items} onItemsChanged={setItems} { /* * You need to pass the component rendering a single item via TreeItemComponent props. * This component will receive the data via `props.item`. * In this example we inline the component, but in reality you should extract it into a const. */ ...{} } TreeItemComponent={React.forwardRef((props, ref) => ( <SimpleTreeItemWrapper {...props} ref={ref}> {/* HERE GOES THE ACTUAL CONTENT OF YOUR COMPONENT */} <div>{props.item.id}</div> </SimpleTreeItemWrapper> ))} /> ); }; /* * Configure the tree data. */ const initialMinimalData = [ { id: '1', children: [{ id: '4' }, { id: '5' }] }, { id: '2' }, { id: '3' }, ];
Here's the minimal viable example that you could potentially copy&paste to your project to start from.
export const MinimalViable = () => { const [items, setItems] = useState(initialViableMinimalData); return ( <SortableTree items={items} onItemsChanged={setItems} TreeItemComponent={MinimalTreeItemComponent} /> ); }; type MinimalTreeItemData = { value: string; }; /* * Here's the component that will render a single row of your tree */ const MinimalTreeItemComponent = React.forwardRef< HTMLDivElement, TreeItemComponentProps<MinimalTreeItemData> >((props, ref) => ( /* you could also use FolderTreeItemWrapper if you want to show vertical lines. */ <SimpleTreeItemWrapper {...props} ref={ref}> <div>{props.item.value}</div> </SimpleTreeItemWrapper> )); /* * Configure the tree data. */ const initialViableMinimalData: TreeItems<MinimalTreeItemData> = [ { id: '1', value: 'Jane', children: [ { id: '4', value: 'John' }, { id: '5', value: 'Sally' }, ], }, { id: '2', value: 'Fred', children: [{ id: '6', value: 'Eugene' }] }, { id: '3', value: 'Helen', canHaveChildren: false }, ];
API
Data configuration (each TreeItem element could define them):
canHaveChildren
- Default:true
.If set to
false
, prevents any node from being dragged into the current one.Also accepts a function:
(dragItem) => bool
which could conditionally determine if a certain item could be a children of a nodedisableSorting
- Default:false
. If set totrue
, prevents node from being dragged (i.e. it can't be sorted or moved to another node)
Tree configuration (props of <SortableTree>
)
items
- mandatory, items shown in a treeonItemsChanged
- mandatory, callback that is called when dragging of certain item is finished. You should preserve new state and adjust the value ofitems
prop as needed.TreeItemComponent
- mandatory, component that renders a single tree row.indentationWidth
- optional, padding used for childrenpointerSensorOptions
- optional, configures the condition when item dragging starts. Defaults to:{ "activationConstraint": { "distance": 3 } }
disableSorting
- optional, you could set this totrue
to completely disable the sortingkeepGhostInPlace
- optional, you could set this totrue
to keep the Node that you are dragging in it's original place in a Tree. Check VSCode sample to see it in action.dndContextProps
- optional, override any prop of underlying DndContext.sortableProps
- optional, override any prop that is passed to underlying useSortable hook.
TreeItemWrapper configuration (props of <SimpleTreeItemWrapper>
and <FolderTreeItemWrapper>
)
manualDrag
- Default:false
. Set totrue
if you want tree item to be draggable ONLY from dragHandle.showDragHandle
- optional, set tofalse
if you want to hide default dragHandle and show your own instead. Use<div {...props.handleProps}>DRAG_ME</div>
for your own drag handle.
FAQ
- If you want to disable animation completely, you need to do the following:
- Pass
null
asdropAnimation
prop (this disables the actual 'drop' animation for the Node that was dragged). - Pass
{ animateLayoutChanges: () => false }
tosortableProps
(this disables the animation of all other nodes that were not dragged)
- Pass
Troubleshooting
- If your dragged item is shown at the end of a list, make sure you that:
- You wrapped your
TreeItem
component inReact.forwardRef
and passing theref
toSimpleTreeItemWrapper
- You pass the
styles
prop fromTreeItem
toSimpleTreeItemWrapper
- You wrapped your