{"id":17109125,"url":"https://github.com/shaddix/dnd-kit-sortable-tree","last_synced_at":"2025-04-06T02:08:03.220Z","repository":{"id":43379740,"uuid":"405711131","full_name":"Shaddix/dnd-kit-sortable-tree","owner":"Shaddix","description":"Tree component for React with ability to drag-and-drop items throughout the tree. Based on an example from dnd-kit (https://github.com/clauderic/dnd-kit).","archived":false,"fork":false,"pushed_at":"2024-01-25T16:30:02.000Z","size":1746,"stargazers_count":114,"open_issues_count":18,"forks_count":26,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-03-30T01:11:18.256Z","etag":null,"topics":["drag-and-drop","react","tree-structure"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Shaddix.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-09-12T17:41:06.000Z","updated_at":"2025-03-28T20:02:17.000Z","dependencies_parsed_at":"2024-06-19T00:13:40.235Z","dependency_job_id":"07e21171-c310-480e-8962-39f09a6a790d","html_url":"https://github.com/Shaddix/dnd-kit-sortable-tree","commit_stats":{"total_commits":87,"total_committers":3,"mean_commits":29.0,"dds":0.3563218390804598,"last_synced_commit":"0db15faed467115640dbb53cf68720adddc0b0a4"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Shaddix%2Fdnd-kit-sortable-tree","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Shaddix%2Fdnd-kit-sortable-tree/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Shaddix%2Fdnd-kit-sortable-tree/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Shaddix%2Fdnd-kit-sortable-tree/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Shaddix","download_url":"https://codeload.github.com/Shaddix/dnd-kit-sortable-tree/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247423513,"owners_count":20936626,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["drag-and-drop","react","tree-structure"],"created_at":"2024-10-14T16:22:11.116Z","updated_at":"2025-04-06T02:08:03.193Z","avatar_url":"https://github.com/Shaddix.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# dnd-kit-sortable-tree\r\n\r\n[![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://img.shields.io/dub/l/vibe-d.svg)](https://opensource.org/licenses/MIT)\r\n\r\nThis is a Tree component extracted from [dndkit](https://github.com/clauderic/dnd-kit) examples and abstracted a bit.\r\nHere's how it could look like (visuals are completely customizable via css though)\r\n[![img.png](img.png)](https://shaddix.github.io/dnd-kit-sortable-tree)\r\n\r\nPlay around in [examples](https://shaddix.github.io/dnd-kit-sortable-tree) to check the API and see what it can do.\r\n\r\n## Install\r\n\r\n    npm install dnd-kit-sortable-tree @dnd-kit/core @dnd-kit/sortable @dnd-kit/utilities\r\n\r\n## How-to use\r\n\r\nCheck out the Storybook for code samples and play around. You could also play with it on [playcode](https://playcode.io/1032571)\r\n\r\nShortly, you need to render:\r\n\r\n```tsx\r\n\u003cSortableTree\r\n  items={/* array of your tree items */}\r\n  onItemsChanged={/* callback when items are reordered */}\r\n  TreeItemComponent={/* component that renders a single tree item */}\r\n/\u003e\r\n```\r\n\r\nAnd `TreeItemComponent` is usually your data wrapped in `SimpleTreeItemWrapper` or `FolderTreeItemWrapper`:\r\n\r\n```tsx\r\nReact.forwardRef((props, ref) =\u003e (\r\n  \u003cSimpleTreeItemWrapper {...props} ref={ref}\u003e\r\n    \u003cdiv\u003e{props.item.value}\u003c/div\u003e\r\n  \u003c/SimpleTreeItemWrapper\u003e\r\n));\r\n```\r\n\r\nNote that wrapping in `forwardRef` and passing `ref` to `SimpleTreeItemWrapper` is very important!\r\n\r\n## Examples\r\n\r\n1. 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.\r\n   ```jsx\r\n   export const Minimal = () =\u003e {\r\n     const [items, setItems] = useState(initialMinimalData);\r\n     return (\r\n       \u003cSortableTree\r\n         items={items}\r\n         onItemsChanged={setItems}\r\n         {\r\n           /*\r\n            * You need to pass the component rendering a single item via TreeItemComponent props.\r\n            * This component will receive the data via `props.item`.\r\n            * In this example we inline the component, but in reality you should extract it into a const.\r\n            */ ...{}\r\n         }\r\n         TreeItemComponent={React.forwardRef((props, ref) =\u003e (\r\n           \u003cSimpleTreeItemWrapper {...props} ref={ref}\u003e\r\n             {/* HERE GOES THE ACTUAL CONTENT OF YOUR COMPONENT */}\r\n             \u003cdiv\u003e{props.item.id}\u003c/div\u003e\r\n           \u003c/SimpleTreeItemWrapper\u003e\r\n         ))}\r\n       /\u003e\r\n     );\r\n   };\r\n   /*\r\n    * Configure the tree data.\r\n    */\r\n   const initialMinimalData = [\r\n     { id: '1', children: [{ id: '4' }, { id: '5' }] },\r\n     { id: '2' },\r\n     { id: '3' },\r\n   ];\r\n   ```\r\n2. Here's the minimal viable example that you could potentially copy\u0026paste to your project to start from.\r\n\r\n   ```tsx\r\n   export const MinimalViable = () =\u003e {\r\n     const [items, setItems] = useState(initialViableMinimalData);\r\n     return (\r\n       \u003cSortableTree\r\n         items={items}\r\n         onItemsChanged={setItems}\r\n         TreeItemComponent={MinimalTreeItemComponent}\r\n       /\u003e\r\n     );\r\n   };\r\n   type MinimalTreeItemData = {\r\n     value: string;\r\n   };\r\n   /*\r\n    * Here's the component that will render a single row of your tree\r\n    */\r\n   const MinimalTreeItemComponent = React.forwardRef\u003c\r\n     HTMLDivElement,\r\n     TreeItemComponentProps\u003cMinimalTreeItemData\u003e\r\n   \u003e((props, ref) =\u003e (\r\n     /* you could also use FolderTreeItemWrapper if you want to show vertical lines.  */\r\n     \u003cSimpleTreeItemWrapper {...props} ref={ref}\u003e\r\n       \u003cdiv\u003e{props.item.value}\u003c/div\u003e\r\n     \u003c/SimpleTreeItemWrapper\u003e\r\n   ));\r\n\r\n   /*\r\n    * Configure the tree data.\r\n    */\r\n   const initialViableMinimalData: TreeItems\u003cMinimalTreeItemData\u003e = [\r\n     {\r\n       id: '1',\r\n       value: 'Jane',\r\n       children: [\r\n         { id: '4', value: 'John' },\r\n         { id: '5', value: 'Sally' },\r\n       ],\r\n     },\r\n     { id: '2', value: 'Fred', children: [{ id: '6', value: 'Eugene' }] },\r\n     { id: '3', value: 'Helen', canHaveChildren: false },\r\n   ];\r\n   ```\r\n\r\n## API\r\n\r\n### Data configuration (each TreeItem element could define them):\r\n\r\n- `canHaveChildren` - Default: `true`.\r\n\r\n  If set to `false`, prevents any node from being dragged into the current one.\r\n\r\n  Also accepts a function: `(dragItem) =\u003e bool` which could conditionally determine if a certain item could be a children of a node\r\n\r\n- `disableSorting` - Default: `false`. If set to `true`, prevents node from being dragged (i.e. it can't be sorted or moved to another node)\r\n\r\n### Tree configuration (props of `\u003cSortableTree\u003e`)\r\n\r\n- `items` - _mandatory_, items shown in a tree\r\n- `onItemsChanged` - _mandatory_, callback that is called when dragging of certain item is finished. You should preserve new state and adjust the value of `items` prop as needed.\r\n- `TreeItemComponent` - _mandatory_, component that renders a single tree row.\r\n- `indentationWidth` - _optional_, padding used for children\r\n- `pointerSensorOptions` - _optional_, configures the condition when item dragging starts. Defaults to:\r\n\r\n  ```json\r\n  {\r\n    \"activationConstraint\": {\r\n      \"distance\": 3\r\n    }\r\n  }\r\n  ```\r\n\r\n- `disableSorting` - _optional_, you could set this to `true` to completely disable the sorting\r\n- `keepGhostInPlace` - _optional_, you could set this to `true` to keep the Node that you are dragging in it's original place in a Tree. Check [VSCode](https://shaddix.github.io/dnd-kit-sortable-tree/?path=/story/simple--vs-code-like) sample to see it in action.\r\n- `dndContextProps` - _optional_, override any prop of underlying [DndContext](https://docs.dndkit.com/api-documentation/context-provider#props).\r\n- `sortableProps` - _optional_, override any prop that is passed to underlying [useSortable](https://docs.dndkit.com/presets/sortable/usesortable) hook.\r\n\r\n### TreeItemWrapper configuration (props of `\u003cSimpleTreeItemWrapper\u003e` and `\u003cFolderTreeItemWrapper\u003e`)\r\n\r\n- `manualDrag` - Default: `false`. Set to `true` if you want tree item to be draggable ONLY from dragHandle.\r\n- `showDragHandle` - _optional_, set to `false` if you want to hide default dragHandle and show your own instead. Use `\u003cdiv {...props.handleProps}\u003eDRAG_ME\u003c/div\u003e` for your own drag handle.\r\n\r\n# FAQ\r\n\r\n1. If you want to [disable animation completely](https://shaddix.github.io/dnd-kit-sortable-tree/?path=/story/simple--no-drop-animation), you need to do the following:\r\n   1. Pass `null` as `dropAnimation` prop (this disables the actual 'drop' animation for the Node that was dragged).\r\n   1. Pass `{ animateLayoutChanges: () =\u003e false }` to `sortableProps` (this disables the animation of all other nodes that were not dragged)\r\n\r\n# Troubleshooting\r\n\r\n1. If your dragged item is shown at the end of a list, make sure you that:\r\n   1. You wrapped your `TreeItem` component in `React.forwardRef` and passing the `ref` to `SimpleTreeItemWrapper`\r\n   1. You pass the `styles` prop from `TreeItem` to `SimpleTreeItemWrapper`\r\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fshaddix%2Fdnd-kit-sortable-tree","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fshaddix%2Fdnd-kit-sortable-tree","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fshaddix%2Fdnd-kit-sortable-tree/lists"}