{"id":13394401,"url":"https://github.com/frontend-collective/react-sortable-tree","last_synced_at":"2025-05-12T05:33:04.390Z","repository":{"id":37742911,"uuid":"63937742","full_name":"frontend-collective/react-sortable-tree","owner":"frontend-collective","description":"Drag-and-drop sortable component for nested data and hierarchies","archived":false,"fork":false,"pushed_at":"2025-03-28T22:15:01.000Z","size":16005,"stargazers_count":4950,"open_issues_count":343,"forks_count":917,"subscribers_count":60,"default_branch":"master","last_synced_at":"2025-05-11T13:43:30.574Z","etag":null,"topics":["component","drag-and-drop","hierarchical-data","react","sortable","tree","tree-data"],"latest_commit_sha":null,"homepage":"https://frontend-collective.github.io/react-sortable-tree/","language":"JavaScript","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/frontend-collective.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","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,"zenodo":null},"funding":{"github":"wuweiweiwu","open_collective":"react-sortable-tree"}},"created_at":"2016-07-22T08:36:25.000Z","updated_at":"2025-05-09T16:38:40.000Z","dependencies_parsed_at":"2022-07-20T04:18:05.226Z","dependency_job_id":"707cd1ad-0591-4b6e-b368-4b3c3dfa3297","html_url":"https://github.com/frontend-collective/react-sortable-tree","commit_stats":{"total_commits":525,"total_committers":41,"mean_commits":"12.804878048780488","dds":0.3028571428571428,"last_synced_commit":"f482bc7f2853c698cad08fc75bb6fd3e36f2f79a"},"previous_names":["fritz-c/react-sortable-tree"],"tags_count":53,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/frontend-collective%2Freact-sortable-tree","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/frontend-collective%2Freact-sortable-tree/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/frontend-collective%2Freact-sortable-tree/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/frontend-collective%2Freact-sortable-tree/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/frontend-collective","download_url":"https://codeload.github.com/frontend-collective/react-sortable-tree/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253584641,"owners_count":21931549,"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":["component","drag-and-drop","hierarchical-data","react","sortable","tree","tree-data"],"created_at":"2024-07-30T17:01:18.207Z","updated_at":"2025-05-12T05:33:04.324Z","avatar_url":"https://github.com/frontend-collective.png","language":"JavaScript","funding_links":["https://github.com/sponsors/wuweiweiwu","https://opencollective.com/react-sortable-tree"],"categories":["JavaScript","语言资源库","Uncategorized"],"sub_categories":["JavaScript","Uncategorized"],"readme":"# Note on maintenance\n\nThis library is not actively maintained. [Please find and discuss alternatives here](https://github.com/frontend-collective/react-sortable-tree/discussions/942).\n\n\u003cdiv align=\"center\"\u003e\n  \u003cimg src=\"https://cloud.githubusercontent.com/assets/4413963/18860410/26f64de8-84b8-11e6-9284-350308eed30a.png\"/\u003e\n\u003c/div\u003e\n\n# React Sortable Tree\n\n![NPM version](https://img.shields.io/npm/v/react-sortable-tree.svg?style=flat)\n![NPM license](https://img.shields.io/npm/l/react-sortable-tree.svg?style=flat)\n[![NPM total downloads](https://img.shields.io/npm/dt/react-sortable-tree.svg?style=flat)](https://npmcharts.com/compare/react-sortable-tree?minimal=true)\n[![NPM monthly downloads](https://img.shields.io/npm/dm/react-sortable-tree.svg?style=flat)](https://npmcharts.com/compare/react-sortable-tree?minimal=true)\n[![Build Status](https://travis-ci.org/frontend-collective/react-sortable-tree.svg?branch=master)](https://travis-ci.org/frontend-collective/react-sortable-tree)\n[![Coverage Status](https://coveralls.io/repos/github/frontend-collective/react-sortable-tree/badge.svg?branch=master)](https://coveralls.io/github/frontend-collective/react-sortable-tree?branch=master)\n[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](http://makeapullrequest.com)\n\n\u003e A React component for Drag-and-drop sortable representation of hierarchical data. Checkout the [Storybook](https://frontend-collective.github.io/react-sortable-tree/) for a demonstration of some basic and advanced features.\n\n\u003cdiv align=\"center\"\u003e\n  \u003cimg src=\"https://cloud.githubusercontent.com/assets/4413963/19334888/2be8261c-913a-11e6-9508-4b347ae114b4.gif\"/\u003e\n\u003c/div\u003e\n\n## Table of Contents\n\n- [Getting Started](#getting-started)\n- [Usage](#usage)\n- [Props](#props)\n- [Data Helpers](#data-helper-functions)\n- [Themes](#themes)\n- [Browser Compatibility](#browser-compatibility)\n- [Troubleshooting](#troubleshooting)\n- [Contributing](#contributing)\n\n## Getting started\n\nInstall `react-sortable-tree` using npm.\n\n```sh\n# NPM\nnpm install react-sortable-tree --save\n\n# YARN\nyarn add react-sortable-tree\n```\n\nES6 and CommonJS builds are available with each distribution.\nFor example:\n\n```js\n// This only needs to be done once; probably during your application's bootstrapping process.\nimport 'react-sortable-tree/style.css';\n\n// You can import the default tree with dnd context\nimport SortableTree from 'react-sortable-tree';\n\n// Or you can import the tree without the dnd context as a named export. eg\nimport { SortableTreeWithoutDndContext as SortableTree } from 'react-sortable-tree';\n\n// Importing from cjs (default)\nimport SortableTree from 'react-sortable-tree/dist/index.cjs.js';\nimport SortableTree from 'react-sortable-tree';\n\n// Importing from esm\nimport SortableTree from 'react-sortable-tree/dist/index.esm.js';\n```\n\n## Usage\n\n```jsx\nimport React, { Component } from 'react';\nimport SortableTree from 'react-sortable-tree';\nimport 'react-sortable-tree/style.css'; // This only needs to be imported once in your app\n\nexport default class Tree extends Component {\n  constructor(props) {\n    super(props);\n\n    this.state = {\n      treeData: [\n        { title: 'Chicken', children: [{ title: 'Egg' }] },\n        { title: 'Fish', children: [{ title: 'fingerline' }] },\n      ],\n    };\n  }\n\n  render() {\n    return (\n      \u003cdiv style={{ height: 400 }}\u003e\n        \u003cSortableTree\n          treeData={this.state.treeData}\n          onChange={treeData =\u003e this.setState({ treeData })}\n        /\u003e\n      \u003c/div\u003e\n    );\n  }\n}\n```\n\n## Props\n\n| Prop                           |      Type      | \u003cdiv style=\"width: 400px;\"\u003eDescription\u003c/div\u003e                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       |\n| :----------------------------- | :------------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| treeData\u003cbr/\u003e_(required)_      |    object[]    | Tree data with the following keys: \u003cdiv\u003e`title` is the primary label for the node.\u003c/div\u003e\u003cdiv\u003e`subtitle` is a secondary label for the node.\u003c/div\u003e\u003cdiv\u003e`expanded` shows children of the node if true, or hides them if false. Defaults to false.\u003c/div\u003e\u003cdiv\u003e`children` is an array of child nodes belonging to the node.\u003c/div\u003e\u003cdiv\u003e**Example**: `[{title: 'main', subtitle: 'sub'}, { title: 'value2', expanded: true, children: [{ title: 'value3') }] }]`                                                                                                                                                                                           |\n| onChange\u003cbr/\u003e_(required)_      |      func      | Called whenever tree data changed. Just like with React input elements, you have to update your own component's data to see the changes reflected.\u003cdiv\u003e`( treeData: object[] ): void`\u003c/div\u003e                                                                                                                                                                                                                                                                                                                                                                                                                                                        |\n| getNodeKey\u003cbr/\u003e_(recommended)_ |      func      | Specify the unique key used to identify each node and generate the `path` array passed in callbacks. With a setting of `getNodeKey={({ node }) =\u003e node.id}`, for example, in callbacks this will let you easily determine that the node with an `id` of `35` is (or has just become) a child of the node with an `id` of `12`, which is a child of ... and so on. It uses [`defaultGetNodeKey`](https://github.com/frontend-collective/react-sortable-tree/blob/master/src/utils/default-handlers.js) by default, which returns the index in the tree (omitting hidden nodes).\u003cdiv\u003e`({ node: object, treeIndex: number }): string or number`\u003c/div\u003e |\n| generateNodeProps              |      func      | Generate an object with additional props to be passed to the node renderer. Use this for adding buttons via the `buttons` key, or additional `style` / `className` settings.\u003cdiv\u003e`({ node: object, path: number[] or string[], treeIndex: number, lowerSiblingCounts: number[], isSearchMatch: bool, isSearchFocus: bool }): object`\u003c/div\u003e                                                                                                                                                                                                                                                                                                         |\n| onMoveNode                     |      func      | Called after node move operation. \u003cdiv\u003e`({ treeData: object[], node: object, nextParentNode: object, prevPath: number[] or string[], prevTreeIndex: number, nextPath: number[] or string[], nextTreeIndex: number }): void`\u003c/div\u003e                                                                                                                                                                                                                                                                                                                                                                                                                  |\n| onVisibilityToggle             |      func      | Called after children nodes collapsed or expanded. \u003cdiv\u003e`({ treeData: object[], node: object, expanded: bool, path: number[] or string[] }): void`\u003c/div\u003e                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           |\n| onDragStateChanged             |      func      | Called when a drag is initiated or ended. \u003cdiv\u003e`({ isDragging: bool, draggedNode: object }): void`\u003c/div\u003e                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           |\n| maxDepth                       |     number     | Maximum depth nodes can be inserted at. Defaults to infinite.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      |\n| rowDirection                   |     string     | Adds row direction support if set to `'rtl'` Defaults to `'ltr'`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |\n| canDrag                        |  func or bool  | Return false from callback to prevent node from dragging, by hiding the drag handle. Set prop to `false` to disable dragging on all nodes. Defaults to `true`. \u003cdiv\u003e`({ node: object, path: number[] or string[], treeIndex: number, lowerSiblingCounts: number[], isSearchMatch: bool, isSearchFocus: bool }): bool`\u003c/div\u003e                                                                                                                                                                                                                                                                                                                        |\n| canDrop                        |      func      | Return false to prevent node from dropping in the given location. \u003cdiv\u003e`({ node: object, prevPath: number[] or string[], prevParent: object, prevTreeIndex: number, nextPath: number[] or string[], nextParent: object, nextTreeIndex: number }): bool`\u003c/div\u003e                                                                                                                                                                                                                                                                                                                                                                                      |\n| canNodeHaveChildren            |      func      | Function to determine whether a node can have children, useful for preventing hover preview when you have a `canDrop` condition. Default is set to a function that returns `true`. Functions should be of type `(node): bool`.                                                                                                                                                                                                                                                                                                                                                                                                                     |\n| theme                          |     object     | Set an all-in-one packaged appearance for the tree. See the [Themes](#themes) section for more information.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        |\n| searchMethod                   |      func      | The method used to search nodes. Defaults to [`defaultSearchMethod`](https://github.com/frontend-collective/react-sortable-tree/blob/master/src/utils/default-handlers.js), which uses the `searchQuery` string to search for nodes with matching `title` or `subtitle` values. NOTE: Changing `searchMethod` will not update the search, but changing the `searchQuery` will.\u003cdiv\u003e`({ node: object, path: number[] or string[], treeIndex: number, searchQuery: any }): bool`\u003c/div\u003e                                                                                                                                                               |\n| searchQuery                    | string or any  | Used by the `searchMethod` to highlight and scroll to matched nodes. Should be a string for the default `searchMethod`, but can be anything when using a custom search. Defaults to `null`.                                                                                                                                                                                                                                                                                                                                                                                                                                                        |\n| searchFocusOffset              |     number     | Outline the \u003c`searchFocusOffset`\u003eth node and scroll to it.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         |\n| onlyExpandSearchedNodes        |    boolean     | Only expand the nodes that match searches. Collapses all other nodes. Defaults to `false`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         |\n| searchFinishCallback           |      func      | Get the nodes that match the search criteria. Used for counting total matches, etc.\u003cdiv\u003e`(matches: { node: object, path: number[] or string[], treeIndex: number }[]): void`\u003c/div\u003e                                                                                                                                                                                                                                                                                                                                                                                                                                                                 |\n| dndType                        |     string     | String value used by [react-dnd](https://react-dnd.github.io/react-dnd/about) (see overview at the link) for dropTargets and dragSources types. If not set explicitly, a default value is applied by react-sortable-tree for you for its internal use. **NOTE:** Must be explicitly set and the same value used in order for correct functioning of external nodes                                                                                                                                                                                                                                                                                 |\n| shouldCopyOnOutsideDrop        |  func or bool  | Return true, or a callback returning true, and dropping nodes to react-dnd drop targets outside of the tree will not remove them from the tree. Defaults to `false`. \u003cdiv\u003e`({ node: object, prevPath: number[] or string[], prevTreeIndex: number, }): bool`\u003c/div\u003e                                                                                                                                                                                                                                                                                                                                                                                 |\n| reactVirtualizedListProps      |     object     | Custom properties to hand to the internal [react-virtualized List](https://github.com/bvaughn/react-virtualized/blob/master/docs/List.md#prop-types)                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               |\n| style                          |     object     | Style applied to the container wrapping the tree (style defaults to `{height: '100%'}`)                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |\n| innerStyle                     |     object     | Style applied to the inner, scrollable container (for padding, etc.)                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               |\n| className                      |     string     | Class name for the container wrapping the tree                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |\n| rowHeight                      | number or func | Used by react-sortable-tree. Defaults to `62`. Either a fixed row height (number) or a function that returns the height of a row given its index: `({ treeIndex: number, node: object, path: number[] or string[] }): number`                                                                                                                                                                                                                                                                                                                                                                                                                      |\n| slideRegionSize                |     number     | Size in px of the region near the edges that initiates scrolling on dragover. Defaults to `100`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |\n| scaffoldBlockPxWidth           |     number     | The width of the blocks containing the lines representing the structure of the tree. Defaults to `44`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |\n| isVirtualized                  |      bool      | Set to false to disable virtualization. Defaults to `true`. **NOTE**: Auto-scrolling while dragging, and scrolling to the `searchFocusOffset` will be disabled.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |\n| nodeContentRenderer            |      any       | Override the default component ([`NodeRendererDefault`](https://github.com/frontend-collective/react-sortable-tree/blob/master/src/node-renderer-default.js)) for rendering nodes (but keep the scaffolding generator). This is a last resort for customization - most custom styling should be able to be solved with `generateNodeProps`, a `theme` or CSS rules. If you must use it, is best to copy the component in `node-renderer-default.js` to use as a base, and customize as needed.                                                                                                                                                     |\n| placeholderRenderer            |      any       | Override the default placeholder component ([`PlaceholderRendererDefault`](https://github.com/frontend-collective/react-sortable-tree/blob/master/src/placeholder-renderer-default.js)) which is displayed when the tree is empty. This is an advanced option, and in most cases should probably be solved with a `theme` or custom CSS instead.                                                                                                                                                                                                                                                                                                   |\n\n## Data Helper Functions\n\nNeed a hand turning your flat data into nested tree data?\nWant to perform add/remove operations on the tree data without creating your own recursive function?\nCheck out the helper functions exported from [`tree-data-utils.js`](https://github.com/frontend-collective/react-sortable-tree/blob/master/src/utils/tree-data-utils.js).\n\n- **`getTreeFromFlatData`**: Convert flat data (like that from a database) into nested tree data.\n- **`getFlatDataFromTree`**: Convert tree data back to flat data.\n- **`addNodeUnderParent`**: Add a node under the parent node at the given path.\n- **`removeNode`**: For a given path, get the node at that path, treeIndex, and the treeData with that node removed.\n- **`removeNodeAtPath`**: For a given path, remove the node and return the treeData.\n- **`changeNodeAtPath`**: Modify the node object at the given path.\n- **`map`**: Perform a change on every node in the tree.\n- **`walk`**: Visit every node in the tree in order.\n- **`getDescendantCount`**: Count how many descendants this node has.\n- **`getVisibleNodeCount`**: Count how many visible descendants this node has.\n- **`getVisibleNodeInfoAtIndex`**: Get the \u003ctargetIndex\u003eth visible node in the tree data.\n- **`toggleExpandedForAll`**: Expand or close every node in the tree.\n- **`getNodeAtPath`**: Get the node at the input path.\n- **`insertNode`**: Insert the input node at the specified depth and minimumTreeIndex.\n- **`find`**: Find nodes matching a search query in the tree.\n- **`isDescendant`**: Check if a node is a descendant of another node.\n- **`getDepth`**: Get the longest path in the tree.\n\n## Themes\n\nUsing the `theme` prop along with an imported theme module, you can easily override the default appearance with another standard one.\n\n### Featured themes\n\n|                       ![File Explorer Theme](https://user-images.githubusercontent.com/4413963/32144502-1df1ae08-bcfd-11e7-8f63-8b836dace1a4.png)                        |           \u003cimg alt=\"Full Node Drag Theme\" src=\"https://user-images.githubusercontent.com/4413963/33521792-61dc2c50-d81f-11e7-8ab1-359661a11ca4.png\" width=\"300\"\u003e           |  \u003cimg alt=\"MINIMAL THEME\" src=\"https://github.com/lifejuggler/react-sortable-tree-theme-minimal/blob/master/example-resource/main.png\" width=\"300\"\u003e  |\n| :----------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------------------------: |\n|                                                                            **File Explorer**                                                                             |                                                                             **Full Node Drag**                                                                             |                                                   **Minimalistic theme inspired from MATERIAL UI**                                                   |\n|                                                                 react-sortable-tree-theme-file-explorer                                                                  |                                                                  react-sortable-tree-theme-full-node-drag                                                                  |                                                          react-sortable-tree-theme-minimal                                                           |\n| [Github](https://github.com/frontend-collective/react-sortable-tree-theme-file-explorer) \\| [NPM](https://www.npmjs.com/package/react-sortable-tree-theme-file-explorer) | [Github](https://github.com/frontend-collective/react-sortable-tree-theme-full-node-drag) \\| [NPM](https://www.npmjs.com/package/react-sortable-tree-theme-full-node-drag) | [Github](https://github.com/lifejuggler/react-sortable-tree-theme-minimal) \\| [NPM](https://www.npmjs.com/package/react-sortable-tree-theme-minimal) |\n\n**Help Wanted** - As the themes feature has just been enabled, there are very few (only _two_ at the time of this writing) theme modules available. If you've customized the appearance of your tree to be especially cool or easy to use, I would be happy to feature it in this readme with a link to the Github repo and NPM page if you convert it to a theme. You can use my [file explorer theme repo](https://github.com/frontend-collective/react-sortable-tree-theme-file-explorer) as a template to plug in your own stuff.\n\n## Browser Compatibility\n\n| Browser | Works? |\n| :------ | :----- |\n| Chrome  | Yes    |\n| Firefox | Yes    |\n| Safari  | Yes    |\n| IE 11   | Yes    |\n\n## Troubleshooting\n\n### If it throws \"TypeError: fn is not a function\" errors in production\n\nThis issue may be related to an ongoing incompatibility between UglifyJS and Webpack's behavior. See an explanation at [create-react-app#2376](https://github.com/facebookincubator/create-react-app/issues/2376).\n\nThe simplest way to mitigate this issue is by adding `comparisons: false` to your Uglify config as seen here: https://github.com/facebookincubator/create-react-app/pull/2379/files\n\n### If it doesn't work with other components that use react-dnd\n\nreact-dnd only allows for one DragDropContext at a time (see: https://github.com/gaearon/react-dnd/issues/186). To get around this, you can import the context-less tree component via `SortableTreeWithoutDndContext`.\n\n```js\n// before\nimport SortableTree from 'react-sortable-tree';\n\n// after\nimport { SortableTreeWithoutDndContext as SortableTree } from 'react-sortable-tree';\n```\n\n## Contributing\n\nPlease read the [Code of Conduct](CODE_OF_CONDUCT.md). I actively welcome pull requests :)\n\nAfter cloning the repository and running `yarn install` inside, you can use the following commands to develop and build the project.\n\n```sh\n# Starts a webpack dev server that hosts a demo page with the component.\n# It uses react-hot-loader so changes are reflected on save.\nyarn start\n\n# Start the storybook, which has several different examples to play with.\n# Also hot-reloaded.\nyarn run storybook\n\n# Runs the library tests\nyarn test\n\n# Lints the code with eslint\nyarn run lint\n\n# Lints and builds the code, placing the result in the dist directory.\n# This build is necessary to reflect changes if you're\n#  `npm link`-ed to this repository from another local project.\nyarn run build\n```\n\nPull requests are welcome!\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffrontend-collective%2Freact-sortable-tree","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffrontend-collective%2Freact-sortable-tree","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffrontend-collective%2Freact-sortable-tree/lists"}