diff --git a/.babelrc.js b/.babelrc.js index f32e0bda..1c1b8889 100755 --- a/.babelrc.js +++ b/.babelrc.js @@ -10,9 +10,7 @@ module.exports = { ], env: { test: { - plugins: [ - '@babel/plugin-transform-modules-commonjs', - ], - } - } + plugins: ['@babel/plugin-transform-modules-commonjs'], + }, + }, }; diff --git a/.eslintrc.js b/.eslintrc.js index 4f37152a..a643b25a 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,19 +1,19 @@ module.exports = { - extends: [ - 'eslint-config-airbnb', - 'prettier', - 'prettier/react' - ], + extends: ['eslint-config-airbnb', 'prettier', 'prettier/react'], parser: 'babel-eslint', env: { browser: true, - jest: true + jest: true, }, rules: { 'react/destructuring-assignment': 0, 'react/jsx-filename-extension': 0, 'react/prefer-stateless-function': 0, 'react/no-did-mount-set-state': 0, - 'react/sort-comp': 0 + 'react/sort-comp': 0, + 'react/jsx-props-no-spreading': 0, + 'react/prop-types': 0, + 'no-shadow': 0, + 'jsx-a11y/label-has-associated-control': 0, }, -} +}; diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 00000000..de3f9e53 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,2 @@ +github: wuweiweiwu +open_collective: react-sortable-tree diff --git a/.github/ISSUE_TEMPLATE/bug-report.md b/.github/ISSUE_TEMPLATE/bug-report.md new file mode 100644 index 00000000..73258a62 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug-report.md @@ -0,0 +1,8 @@ +--- +name: Bug Report +about: Bugs, missing documentation, or unexpected behavior 🤔. +--- + +# Reporting a Bug? + +Please include either a failing unit test or a simple reproduction. You can start by forking the [CodeSandbox example](https://codesandbox.io/s/wkxvy3z15w) diff --git a/.github/ISSUE_TEMPLATE/feature-request.md b/.github/ISSUE_TEMPLATE/feature-request.md new file mode 100644 index 00000000..f34434cd --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature-request.md @@ -0,0 +1,12 @@ +--- +name: Feature request +about: Ideas and suggestions +--- + +# Requesting a Feature? + +Provide as much information as possible about your requested feature. Here are a few questions you may consider answering: + +- What's your use case? (Tell me about your application and what problem you're trying to solve.) +- What interface do you have in mind? (What new properties or methods do you think might be helpful?) +- Can you point to similar functionality with any existing libraries or components? (Working demos can be helpful.) diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 00000000..83b69470 --- /dev/null +++ b/.prettierignore @@ -0,0 +1 @@ +CHANGELOG.md \ No newline at end of file diff --git a/.storybook/addons.js b/.storybook/addons.js deleted file mode 100644 index 09462308..00000000 --- a/.storybook/addons.js +++ /dev/null @@ -1,2 +0,0 @@ -/* eslint-disable import/no-extraneous-dependencies */ -import '@storybook/addon-options/register'; diff --git a/.storybook/config.js b/.storybook/config.js deleted file mode 100644 index 48c0ee0b..00000000 --- a/.storybook/config.js +++ /dev/null @@ -1,16 +0,0 @@ -/* eslint-disable import/no-extraneous-dependencies */ -import { configure } from '@storybook/react'; -import { setOptions } from '@storybook/addon-options'; - -setOptions({ - name: 'React Sortable Tree', - url: 'https://github.com/frontend-collective/react-sortable-tree', - showAddonPanel: false, -}); - -function loadStories() { - // eslint-disable-next-line global-require - require('../stories'); -} - -configure(loadStories, module); diff --git a/.storybook/main.js b/.storybook/main.js new file mode 100644 index 00000000..c96320fc --- /dev/null +++ b/.storybook/main.js @@ -0,0 +1,3 @@ +module.exports = { + stories: ['../stories/index.js'], +}; diff --git a/.storybook/manager.js b/.storybook/manager.js new file mode 100644 index 00000000..dde5c38c --- /dev/null +++ b/.storybook/manager.js @@ -0,0 +1,11 @@ +import { addons } from '@storybook/addons'; +import { create } from '@storybook/theming/create'; + +addons.setConfig({ + theme: create({ + base: 'light', + brandTitle: 'React Sortable Tree', + brandUrl: 'https://github.com/frontend-collective/react-sortable-tree', + gridCellSize: 12, + }), +}); diff --git a/CHANGELOG.md b/CHANGELOG.md index 7baaf985..4ab17d11 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,43 @@ -# Change Log +# Changelog All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. +## [2.8.0](https://github.com/frontend-collective/react-sortable-tree/compare/v2.7.1...v2.8.0) (2020-08-10) + + +### Features + +* adding FUNDING.yml ([8e87804](https://github.com/frontend-collective/react-sortable-tree/commit/8e87804195fcc6cfc98ac0c8ae3a6f8511c05898)) +* remove current codesandbox website ([30749c7](https://github.com/frontend-collective/react-sortable-tree/commit/30749c74deba9b254c674bc0ded4fe2e6eb4cdce)) + + +### Bug Fixes + +* accidentally deleted own styling ([c664ade](https://github.com/frontend-collective/react-sortable-tree/commit/c664adee1cc045a76a9f89c38b644aa996f38365)) +* don't prettify changelog ([8615412](https://github.com/frontend-collective/react-sortable-tree/commit/86154120b0814a72ad45b23b4a24f45f2bbac225)) +* open collective link ([d55561e](https://github.com/frontend-collective/react-sortable-tree/commit/d55561e91b6abc7268be261c55c95a1fac5627e9)) +* remove outdated links from readme ([7a07263](https://github.com/frontend-collective/react-sortable-tree/commit/7a07263719044709ea177cd7d59ed0c0d56e86d0)) +* scroll to search focused tree item ([#756](https://github.com/frontend-collective/react-sortable-tree/issues/756)) ([e528a4c](https://github.com/frontend-collective/react-sortable-tree/commit/e528a4c6167cf64a6c0ff43caf22be45cccb21e3)) +* set themes using new api ([c2c1075](https://github.com/frontend-collective/react-sortable-tree/commit/c2c1075dfab844412f375174697ab30692b6055b)) +* site ([95cb249](https://github.com/frontend-collective/react-sortable-tree/commit/95cb249e24fb8cab2134567f71447bd728228c1e)) +* website imports ([8e7f83d](https://github.com/frontend-collective/react-sortable-tree/commit/8e7f83dc483c4697edd5ae29080316cf68de248a)) +* website pt 2 ([6914959](https://github.com/frontend-collective/react-sortable-tree/commit/69149596c884cb28c83c17f238c7d7d186271c44)) + + +## [2.7.1](https://github.com/frontend-collective/react-sortable-tree/compare/v2.7.0...v2.7.1) (2019-11-12) + + + + +# [2.7.0](https://github.com/frontend-collective/react-sortable-tree/compare/v2.6.2...v2.7.0) (2019-10-14) + + +### Features + +* update react-dnd ([#531](https://github.com/frontend-collective/react-sortable-tree/issues/531)) ([c449524](https://github.com/frontend-collective/react-sortable-tree/commit/c449524)) + + + ## [2.6.2](https://github.com/frontend-collective/react-sortable-tree/compare/v2.6.1...v2.6.2) (2019-03-21) diff --git a/ISSUE_TEMPLATE.md b/ISSUE_TEMPLATE.md deleted file mode 100644 index 8cd8f6d7..00000000 --- a/ISSUE_TEMPLATE.md +++ /dev/null @@ -1,10 +0,0 @@ -# Reporting a Bug? - -Please include either a failing unit test or a simple reproduction. You can start by forking the [CodeSandbox example](https://codesandbox.io/s/wkxvy3z15w) - -# Requesting a Feature? - -Provide as much information as possible about your requested feature. Here are a few questions you may consider answering: -* What's your use case? (Tell me about your application and what problem you're trying to solve.) -* What interface do you have in mind? (What new properties or methods do you think might be helpful?) -* Can you point to similar functionality with any existing libraries or components? (Working demos can be helpful.) diff --git a/README.md b/README.md index 12ed876a..2a9b3583 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,7 @@ +# Note on maintenance + +This library is not actively maintained. [Please find and discuss alternatives here](https://github.com/frontend-collective/react-sortable-tree/discussions/942). +
@@ -12,7 +16,7 @@ [![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) [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](http://makeapullrequest.com) -> A React component for Drag-and-drop sortable representation of hierarchical data. Checkout the [demo](https://frontend-collective.github.io/react-sortable-tree/) for a demonstration of some basic features. Checkout the [storybook](https://frontend-collective.github.io/react-sortable-tree/storybook) for advanced usage. +> 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.
@@ -74,7 +78,10 @@ export default class Tree extends Component { super(props); this.state = { - treeData: [{ title: 'Chicken', children: [{ title: 'Egg' }] }], + treeData: [ + { title: 'Chicken', children: [{ title: 'Egg' }] }, + { title: 'Fish', children: [{ title: 'fingerline' }] }, + ], }; } @@ -113,7 +120,7 @@ export default class Tree extends Component { | searchFocusOffset | number | Outline the <`searchFocusOffset`>th node and scroll to it. | | onlyExpandSearchedNodes | boolean | Only expand the nodes that match searches. Collapses all other nodes. Defaults to `false`. | | searchFinishCallback | func | Get the nodes that match the search criteria. Used for counting total matches, etc.
`(matches: { node: object, path: number[] or string[], treeIndex: number }[]): void`
| -| dndType | string | String value used by [react-dnd](http://react-dnd.github.io/react-dnd/docs-overview.html) (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 | +| 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 | | 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`.
`({ node: object, prevPath: number[] or string[], prevTreeIndex: number, }): bool`
| | 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) | | style | object | Style applied to the container wrapping the tree (style defaults to `{height: '100%'}`) | @@ -132,23 +139,23 @@ Need a hand turning your flat data into nested tree data? Want to perform add/remove operations on the tree data without creating your own recursive function? Check 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). -- [**`getTreeFromFlatData`**](https://github.com/frontend-collective/react-sortable-tree/blob/master/src/utils/tree-data-utils.js#L972): Convert flat data (like that from a database) into nested tree data. -- [**`getFlatDataFromTree`**](https://github.com/frontend-collective/react-sortable-tree/blob/master/src/utils/tree-data-utils.js#L939): Convert tree data back to flat data. -- [**`addNodeUnderParent`**](https://github.com/frontend-collective/react-sortable-tree/blob/master/src/utils/tree-data-utils.js#L612): Add a node under the parent node at the given path. -- [**`removeNode`**](https://github.com/frontend-collective/react-sortable-tree/blob/master/src/utils/tree-data-utils.js#L533): For a given path, get the node at that path, treeIndex, and the treeData with that node removed. -- [**`removeNodeAtPath`**](https://github.com/frontend-collective/react-sortable-tree/blob/master/src/utils/tree-data-utils.js#L505): For a given path, remove the node and return the treeData. -- [**`changeNodeAtPath`**](https://github.com/frontend-collective/react-sortable-tree/blob/master/src/utils/tree-data-utils.js#L409): Modify the node object at the given path. -- [**`map`**](https://github.com/frontend-collective/react-sortable-tree/blob/master/src/utils/tree-data-utils.js#L359): Perform a change on every node in the tree. -- [**`walk`**](https://github.com/frontend-collective/react-sortable-tree/blob/master/src/utils/tree-data-utils.js#L326): Visit every node in the tree in order. -- [**`getDescendantCount`**](https://github.com/frontend-collective/react-sortable-tree/blob/master/src/utils/tree-data-utils.js#L60): Count how many descendants this node has. -- [**`getVisibleNodeCount`**](https://github.com/frontend-collective/react-sortable-tree/blob/master/src/utils/tree-data-utils.js#L248): Count how many visible descendants this node has. -- [**`getVisibleNodeInfoAtIndex`**](https://github.com/frontend-collective/react-sortable-tree/blob/master/src/utils/tree-data-utils.js#L286): Get the th visible node in the tree data. -- [**`toggleExpandedForAll`**](https://github.com/frontend-collective/react-sortable-tree/blob/master/src/utils/tree-data-utils.js#L389): Expand or close every node in the tree. -- [**`getNodeAtPath`**](https://github.com/frontend-collective/react-sortable-tree/blob/master/src/utils/tree-data-utils.js#L572): Get the node at the input path. -- [**`insertNode`**](https://github.com/frontend-collective/react-sortable-tree/blob/master/src/utils/tree-data-utils.js#L878): Insert the input node at the specified depth and minimumTreeIndex. -- [**`find`**](https://github.com/frontend-collective/react-sortable-tree/blob/master/src/utils/tree-data-utils.js#L1070): Find nodes matching a search query in the tree. -- [**`isDescendant`**](https://github.com/frontend-collective/react-sortable-tree/blob/master/src/utils/tree-data-utils.js#L1020): Check if a node is a descendant of another node. -- [**`getDepth`**](https://github.com/frontend-collective/react-sortable-tree/blob/master/src/utils/tree-data-utils.js#L1038): Get the longest path in the tree. +- **`getTreeFromFlatData`**: Convert flat data (like that from a database) into nested tree data. +- **`getFlatDataFromTree`**: Convert tree data back to flat data. +- **`addNodeUnderParent`**: Add a node under the parent node at the given path. +- **`removeNode`**: For a given path, get the node at that path, treeIndex, and the treeData with that node removed. +- **`removeNodeAtPath`**: For a given path, remove the node and return the treeData. +- **`changeNodeAtPath`**: Modify the node object at the given path. +- **`map`**: Perform a change on every node in the tree. +- **`walk`**: Visit every node in the tree in order. +- **`getDescendantCount`**: Count how many descendants this node has. +- **`getVisibleNodeCount`**: Count how many visible descendants this node has. +- **`getVisibleNodeInfoAtIndex`**: Get the th visible node in the tree data. +- **`toggleExpandedForAll`**: Expand or close every node in the tree. +- **`getNodeAtPath`**: Get the node at the input path. +- **`insertNode`**: Insert the input node at the specified depth and minimumTreeIndex. +- **`find`**: Find nodes matching a search query in the tree. +- **`isDescendant`**: Check if a node is a descendant of another node. +- **`getDepth`**: Get the longest path in the tree. ## Themes @@ -197,27 +204,27 @@ import { SortableTreeWithoutDndContext as SortableTree } from 'react-sortable-tr Please read the [Code of Conduct](CODE_OF_CONDUCT.md). I actively welcome pull requests :) -After cloning the repository and running `npm install` inside, you can use the following commands to develop and build the project. +After cloning the repository and running `yarn install` inside, you can use the following commands to develop and build the project. ```sh # Starts a webpack dev server that hosts a demo page with the component. # It uses react-hot-loader so changes are reflected on save. -npm start +yarn start # Start the storybook, which has several different examples to play with. # Also hot-reloaded. -npm run storybook +yarn run storybook # Runs the library tests -npm test +yarn test # Lints the code with eslint -npm run lint +yarn run lint # Lints and builds the code, placing the result in the dist directory. # This build is necessary to reflect changes if you're # `npm link`-ed to this repository from another local project. -npm run build +yarn run build ``` Pull requests are welcome! diff --git a/__mocks__/react-dnd-html5-backend.js b/__mocks__/react-dnd-html5-backend.js index e8087b35..e23ee579 100644 --- a/__mocks__/react-dnd-html5-backend.js +++ b/__mocks__/react-dnd-html5-backend.js @@ -1,4 +1,4 @@ /* eslint-disable import/no-extraneous-dependencies */ -import TestBackend from 'react-dnd-test-backend'; +import { TestBackend } from 'react-dnd-test-backend'; -module.exports = TestBackend; +module.exports = { HTML5Backend: TestBackend }; diff --git a/package.json b/package.json index fc55a56d..c32212fa 100644 --- a/package.json +++ b/package.json @@ -1,19 +1,16 @@ { "name": "react-sortable-tree", - "version": "2.6.2", + "version": "2.8.0", "description": "Drag-and-drop sortable component for nested data and hierarchies", "scripts": { - "start": "parcel website/index.html", - "prebuild": "npm run lint && npm run clean", + "prebuild": "yarn run lint && yarn run clean", "build": "rollup -c", - "build:storybook": "npm run clean:storybook && build-storybook -o build/storybook", - "build:website": "npm run clean:website && parcel build website/index.html -d build --public-url /react-sortable-tree/", + "build:storybook": "build-storybook -o build", "clean": "rimraf dist", - "clean:storybook": "rimraf build/storybook", - "clean:website": "rimraf build", + "clean:storybook": "rimraf build", "lint": "eslint src", "prettier": "prettier --write \"{src,example/src,stories}/**/*.{js,css,md}\"", - "prepublishOnly": "npm run lint && npm run test && npm run build", + "prepublishOnly": "yarn run test && yarn run build", "release": "standard-version", "test": "jest", "test:watch": "jest --watchAll", @@ -37,7 +34,9 @@ ], "license": "MIT", "jest": { - "setupTestFrameworkScriptFile": "./node_modules/jest-enzyme/lib/index.js", + "setupFilesAfterEnv": [ + "./node_modules/jest-enzyme/lib/index.js" + ], "setupFiles": [ "./test-config/shim.js", "./test-config/test-setup.js" @@ -51,7 +50,13 @@ "node_modules" ], "moduleNameMapper": { - "\\.(css|jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "/__mocks__/fileMock.js" + "\\.(css|jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "/__mocks__/fileMock.js", + "^dnd-core$": "dnd-core/dist/cjs", + "^react-dnd$": "react-dnd/dist/cjs", + "^react-dnd-html5-backend$": "react-dnd-html5-backend/dist/cjs", + "^react-dnd-touch-backend$": "react-dnd-touch-backend/dist/cjs", + "^react-dnd-test-backend$": "react-dnd-test-backend/dist/cjs", + "^react-dnd-test-utils$": "react-dnd-test-utils/dist/cjs" } }, "browserslist": [ @@ -63,11 +68,10 @@ "frontend-collective-react-dnd-scrollzone": "^1.0.2", "lodash.isequal": "^4.5.0", "prop-types": "^15.6.1", - "react-dnd": "^7.3.0", - "react-dnd-html5-backend": "^7.0.1", + "react-dnd": "^11.1.3", + "react-dnd-html5-backend": "^11.1.3", "react-lifecycles-compat": "^3.0.4", - "react-sortable-tree": "^2.6.0", - "react-virtualized": "^9.19.1" + "react-virtualized": "^9.21.2" }, "peerDependencies": { "react": "^16.3.0", @@ -75,50 +79,51 @@ "react-dom": "^16.3.0" }, "devDependencies": { - "@babel/cli": "^7.1.5", - "@babel/core": "^7.1.6", + "@babel/cli": "^7.7.0", + "@babel/core": "^7.7.2", "@babel/plugin-transform-modules-commonjs": "^7.1.0", - "@babel/preset-env": "^7.1.6", - "@babel/preset-react": "^7.0.0", - "@storybook/addon-options": "^4.0.0-alpha.4", - "@storybook/addon-storyshots": "^4.0.0-alpha.4", - "@storybook/react": "^4.0.0-alpha.4", - "autoprefixer": "^9.3.1", + "@babel/preset-env": "^7.7.1", + "@babel/preset-react": "^7.7.0", + "@storybook/addon-storyshots": "^5.2.6", + "@storybook/addons": "^5.3.17", + "@storybook/react": "^5.2.6", + "@storybook/theming": "^5.3.17", + "autoprefixer": "^9.7.1", "babel-core": "^7.0.0-bridge.0", - "babel-eslint": "^10.0.1", - "babel-jest": "^23.6.0", + "babel-eslint": "^10.0.3", + "babel-jest": "^24.9.0", "babel-loader": "^8.0.4", - "codesandbox": "^1.2.10", + "codesandbox": "~2.1.10", "coveralls": "^3.0.1", - "cross-env": "^5.1.6", - "enzyme": "^3.3.0", - "enzyme-adapter-react-16": "^1.1.1", - "eslint": "^5.9.0", - "eslint-config-airbnb": "^17.1.0", - "eslint-config-prettier": "^3.3.0", - "eslint-plugin-import": "^2.12.0", - "eslint-plugin-jsx-a11y": "^6.0.3", - "eslint-plugin-react": "^7.8.2", - "gh-pages": "^2.0.1", - "jest": "^23.1.0", - "jest-enzyme": "^7.0.1", - "parcel-bundler": "^1.11.0", - "prettier": "^1.13.3", - "react": "^16.3.0", + "cross-env": "^6.0.3", + "enzyme": "^3.10.0", + "enzyme-adapter-react-16": "^1.14.0", + "eslint": "^6.6.0", + "eslint-config-airbnb": "^18.0.1", + "eslint-config-prettier": "^6.5.0", + "eslint-plugin-import": "^2.18.2", + "eslint-plugin-jsx-a11y": "^6.2.3", + "eslint-plugin-react": "^7.16.0", + "gh-pages": "^2.1.1", + "jest": "^24.9.0", + "jest-enzyme": "^7.1.2", + "parcel-bundler": "^1.12.4", + "prettier": "^1.19.1", + "react": "^16.11.0", "react-addons-shallow-compare": "^15.6.2", - "react-dnd-test-backend": "^7.0.1", - "react-dnd-touch-backend": "^0.6.0", - "react-dom": "^16.3.0", - "react-hot-loader": "^4.3.0", - "react-sortable-tree-theme-file-explorer": "^1.1.2", - "react-test-renderer": "^16.4.0", - "rimraf": "^2.6.2", - "rollup": "^0.67.1", + "react-dnd-test-backend": "^11.1.3", + "react-dnd-touch-backend": "^9.4.0", + "react-dom": "^16.11.0", + "react-hot-loader": "^4.12.17", + "react-sortable-tree-theme-file-explorer": "^2.0.0", + "react-test-renderer": "^16.11.0", + "rimraf": "^3.0.0", + "rollup": "^1.27.0", "rollup-plugin-babel": "^4.0.3", - "rollup-plugin-commonjs": "^9.1.3", - "rollup-plugin-node-resolve": "^3.3.0", - "rollup-plugin-postcss": "^1.6.2", - "standard-version": "^4.4.0" + "rollup-plugin-commonjs": "^10.1.0", + "rollup-plugin-node-resolve": "^5.2.0", + "rollup-plugin-postcss": "^2.0.3", + "standard-version": "^8.0.1" }, "keywords": [ "react", diff --git a/src/react-sortable-tree.js b/src/react-sortable-tree.js index 1c4fb743..16eca0e2 100644 --- a/src/react-sortable-tree.js +++ b/src/react-sortable-tree.js @@ -1,40 +1,41 @@ -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; -import { AutoSizer, List } from 'react-virtualized'; -import isEqual from 'lodash.isequal'; import withScrolling, { + createHorizontalStrength, createScrollingComponent, createVerticalStrength, - createHorizontalStrength, } from 'frontend-collective-react-dnd-scrollzone'; -import { DragDropContextConsumer } from 'react-dnd'; +import isEqual from 'lodash.isequal'; +import PropTypes from 'prop-types'; +import React, { Component } from 'react'; +import { DndContext, DndProvider } from 'react-dnd'; +import { HTML5Backend } from 'react-dnd-html5-backend'; import { polyfill } from 'react-lifecycles-compat'; +import { AutoSizer, List } from 'react-virtualized'; import 'react-virtualized/styles.css'; -import TreeNode from './tree-node'; import NodeRendererDefault from './node-renderer-default'; -import TreePlaceholder from './tree-placeholder'; import PlaceholderRendererDefault from './placeholder-renderer-default'; -import { - walk, - changeNodeAtPath, - removeNode, - insertNode, - find, - toggleExpandedForAll, -} from './utils/tree-data-utils'; -import { - memoizedInsertNode, - memoizedGetFlatDataFromTree, - memoizedGetDescendantCount, -} from './utils/memoized-tree-data-utils'; -import { slideRows } from './utils/generic-utils'; +import './react-sortable-tree.css'; +import TreeNode from './tree-node'; +import TreePlaceholder from './tree-placeholder'; +import classnames from './utils/classnames'; import { defaultGetNodeKey, defaultSearchMethod, } from './utils/default-handlers'; import DndManager from './utils/dnd-manager'; -import classnames from './utils/classnames'; -import './react-sortable-tree.css'; +import { slideRows } from './utils/generic-utils'; +import { + memoizedGetDescendantCount, + memoizedGetFlatDataFromTree, + memoizedInsertNode, +} from './utils/memoized-tree-data-utils'; +import { + changeNodeAtPath, + find, + insertNode, + removeNode, + toggleExpandedForAll, + walk, +} from './utils/tree-data-utils'; let treeIdCounter = 1; @@ -193,8 +194,8 @@ class ReactSortableTree extends Component { instanceProps.searchQuery = nextProps.searchQuery; instanceProps.searchFocusOffset = nextProps.searchFocusOffset; - newState.instanceProps = instanceProps; - + newState.instanceProps = {...instanceProps, ...newState.instanceProps }; + return newState; } @@ -229,7 +230,7 @@ class ReactSortableTree extends Component { // it means that the drag was canceled or the dragSource dropped // elsewhere, and we should reset the state of this tree if (!monitor.isDragging() && this.state.draggingTreeData) { - this.endDrag(); + setTimeout(() => {this.endDrag()}); } } @@ -312,7 +313,7 @@ class ReactSortableTree extends Component { return { searchMatches: [] }; } - const newState = {}; + const newState = { instanceProps: {} }; // if onlyExpandSearchedNodes collapse the tree and search const { treeData: expandedTreeData, matches: searchMatches } = find({ @@ -332,7 +333,7 @@ class ReactSortableTree extends Component { // Update the tree with data leaving all paths leading to matching nodes open if (expand) { - newState.ignoreOneTreeUpdate = true; // Prevents infinite loop + newState.instanceProps.ignoreOneTreeUpdate = true; // Prevents infinite loop onChange(expandedTreeData); } @@ -770,7 +771,9 @@ class ReactSortableTree extends Component { } ReactSortableTree.propTypes = { - dragDropManager: PropTypes.shape({}).isRequired, + dragDropManager: PropTypes.shape({ + getMonitor: PropTypes.func, + }).isRequired, // Tree data in the following format: // [{title: 'main', subtitle: 'sub'}, { title: 'value2', expanded: true, children: [{ title: 'value3') }] }] @@ -936,13 +939,19 @@ ReactSortableTree.defaultProps = { polyfill(ReactSortableTree); const SortableTreeWithoutDndContext = props => ( - + {({ dragDropManager }) => dragDropManager === undefined ? null : ( ) } - + +); + +const SortableTree = props => ( + + + ); // Export the tree component without the react-dnd DragDropContext, @@ -950,4 +959,4 @@ const SortableTreeWithoutDndContext = props => ( // see: https://github.com/gaearon/react-dnd/issues/186 export { SortableTreeWithoutDndContext }; -export default DndManager.wrapRoot(SortableTreeWithoutDndContext); +export default SortableTree; diff --git a/src/react-sortable-tree.test.js b/src/react-sortable-tree.test.js index 13214a8a..90f05eb2 100644 --- a/src/react-sortable-tree.test.js +++ b/src/react-sortable-tree.test.js @@ -1,9 +1,11 @@ +/* eslint-disable react/no-multi-comp */ import React, { Component } from 'react'; import PropTypes from 'prop-types'; import renderer from 'react-test-renderer'; import { mount } from 'enzyme'; import { List } from 'react-virtualized'; -import { DragDropContext } from 'react-dnd'; +import { DndProvider, DndContext } from 'react-dnd'; +import TestBackend from 'react-dnd-test-backend'; import HTML5Backend from 'react-dnd-html5-backend'; import TouchBackend from 'react-dnd-touch-backend'; import SortableTree, { @@ -401,41 +403,50 @@ describe('', () => { }); it('loads using SortableTreeWithoutDndContext', () => { - const HTML5Wrapped = DragDropContext(HTML5Backend)( - SortableTreeWithoutDndContext - ); - const TouchWrapped = DragDropContext(TouchBackend)( - SortableTreeWithoutDndContext - ); - expect( - mount( {}} />) + mount( + + {}} + /> + + ) ).toBeDefined(); expect( - mount( {}} />) + mount( + + {}} + /> + + ) ).toBeDefined(); }); it('loads using SortableTreeWithoutDndContext', () => { - const TestWrapped = DragDropContext(HTML5Backend)( - SortableTreeWithoutDndContext - ); - const onDragStateChanged = jest.fn(); const treeData = [{ title: 'a' }, { title: 'b' }]; + let manager = null; + const wrapper = mount( - {}} - /> + + + {({ dragDropManager }) => { + manager = dragDropManager; + }} + + {}} + /> + ); // Obtain a reference to the backend - const backend = wrapper - .instance() - .getManager() - .getBackend(); + const backend = manager.getBackend(); // Retrieve our DnD-wrapped node component type const wrappedNodeType = wrapper.find('ReactSortableTree').instance() diff --git a/src/utils/default-handlers.js b/src/utils/default-handlers.js index e23d7ecb..faf15fa9 100644 --- a/src/utils/default-handlers.js +++ b/src/utils/default-handlers.js @@ -9,6 +9,7 @@ function getReactElementText(parent) { } if ( + parent === null || typeof parent !== 'object' || !parent.props || !parent.props.children || diff --git a/src/utils/dnd-manager.js b/src/utils/dnd-manager.js index 64d74337..6adb8dca 100644 --- a/src/utils/dnd-manager.js +++ b/src/utils/dnd-manager.js @@ -1,9 +1,4 @@ -import { - DragDropContext as dragDropContext, - DragSource as dragSource, - DropTarget as dropTarget, -} from 'react-dnd'; -import HTML5Backend from 'react-dnd-html5-backend'; +import { DragSource as dragSource, DropTarget as dropTarget } from 'react-dnd'; import { findDOMNode } from 'react-dom'; import { getDepth } from './tree-data-utils'; import { memoizedInsertNode } from './memoized-tree-data-utils'; @@ -13,10 +8,6 @@ export default class DndManager { this.treeRef = treeRef; } - static wrapRoot(el) { - return dragDropContext(HTML5Backend)(el); - } - get startDrag() { return this.treeRef.startDrag; } @@ -241,11 +232,15 @@ export default class DndManager { return; } - this.dragHover({ - node: draggedNode, - path: monitor.getItem().path, - minimumTreeIndex: dropTargetProps.listIndex, - depth: targetDepth, + // throttle `dragHover` work to available animation frames + cancelAnimationFrame(this.rafId); + this.rafId = requestAnimationFrame(() => { + this.dragHover({ + node: draggedNode, + path: monitor.getItem().path, + minimumTreeIndex: dropTargetProps.listIndex, + depth: targetDepth, + }); }); }, diff --git a/src/utils/tree-data-utils.js b/src/utils/tree-data-utils.js index 836c902b..09cb4dd5 100644 --- a/src/utils/tree-data-utils.js +++ b/src/utils/tree-data-utils.js @@ -621,10 +621,15 @@ export function addNodeUnderParent({ addAsFirstChild = false, }) { if (parentKey === null) { - return { - treeData: [...(treeData || []), newNode], - treeIndex: (treeData || []).length, - }; + return addAsFirstChild + ? { + treeData: [newNode, ...(treeData || [])], + treeIndex: 0, + } + : { + treeData: [...(treeData || []), newNode], + treeIndex: (treeData || []).length, + }; } let insertedTreeIndex = null; diff --git a/src/utils/tree-data-utils.test.js b/src/utils/tree-data-utils.test.js index 448331cf..68128a3c 100644 --- a/src/utils/tree-data-utils.test.js +++ b/src/utils/tree-data-utils.test.js @@ -1145,6 +1145,20 @@ describe('addNodeUnderParent', () => { expect(expectedNewNode).toEqual(nestedParams.newNode); expect(previousChildren).toEqual(nestedParams.treeData[0].children); }); + + it('should add new node as first child under root if addAsFirstChild is true', () => { + const result = addNodeUnderParent({ + ...nestedParams, + parentKey: null, + getNodeKey: keyFromKey, + addAsFirstChild: true, + }); + + const [expectedNewNode, ...previousTreeData] = result.treeData; + + expect(expectedNewNode).toEqual(nestedParams.newNode); + expect(previousTreeData).toEqual(nestedParams.treeData); + }); }); describe('insertNode', () => { diff --git a/stories/__snapshots__/storyshots.test.js.snap b/stories/__snapshots__/storyshots.test.js.snap index bb29966a..e9d855e0 100644 --- a/stories/__snapshots__/storyshots.test.js.snap +++ b/stories/__snapshots__/storyshots.test.js.snap @@ -2,11 +2,234 @@ exports[`Storyshots Advanced Drag from external source 1`] = `
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + Mama Rabbit + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + Papa Rabbit + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Baby Rabbit +
+ ← drag this +
+`; + +exports[`Storyshots Advanced Drag out to remove 1`] = ` +
+
@@ -45,8 +268,8 @@ exports[`Storyshots Advanced Drag from external source 1`] = ` role="rowgroup" style={ Object { - "height": 124, - "maxHeight": 124, + "height": 310, + "maxHeight": 310, "maxWidth": 200, "overflow": "hidden", "pointerEvents": "", @@ -113,7 +336,7 @@ exports[`Storyshots Advanced Drag from external source 1`] = ` - Mama Rabbit + 1
- Papa Rabbit + 2
-
-
-
-
-
-
- Baby Rabbit -
- ← drag this -
-
-
- -
- - - VIEW SOURCE → - -
-`; - -exports[`Storyshots Advanced Drag out to remove 1`] = ` -
-
-
-
-
-
-
-
+
+ } + >
+
-
-
- - 1 - -
-
+ 3 +
+
+
+
+
-
-
+
-
-
-
-
-
-
-
-
-
-
- - 5 - -
-
+ 5 +
+
@@ -693,318 +663,225 @@ exports[`Storyshots Advanced Drag out to remove 1`] = `
-
-
- -
- - - VIEW SOURCE → -
`; -exports[`Storyshots Advanced Playing with generateNodeProps 1`] = ` -
-
-
+
+ } + > +
-
+
+
-
+
-
-
-
-
-
- - Green Captain - -
-
-
+ Egg +
+
@@ -1015,1089 +892,845 @@ exports[`Storyshots Advanced Playing with generateNodeProps 1`] = `
-
-
- -
- - - VIEW SOURCE → -
`; -exports[`Storyshots Advanced Prevent some nodes from having children 1`] = ` +exports[`Storyshots Advanced Playing with generateNodeProps 1`] = `
-
+
-
+
-
+ tabIndex={0} + >
-
+
+ } + >
-
-
+
-
-
-
-
-
-
-
-
-
- - Rob - -
-
-
-
-
-
-
-
+ />
-
-
+
-
-
- - Joe - -
-
+ Black Captain +
+
+
+
-
+
+ } + >
-
+
+
-
-
+
+
+
+
+`; + +exports[`Storyshots Advanced Prevent some nodes from having children 1`] = ` +
+
+
+
+
+
+
+
-
-
+
+ } + >
-
+ - - VIEW SOURCE → - -
-`; - -exports[`Storyshots Advanced Touch support (Experimental) 1`] = ` -
-
- - This is - not - a touch-supporting browser - -
-
-
-
-
+
+ } + /> +
-
-
+
-
-
+
+ } + >
-
+
-
-
-
-
- -
- - - VIEW SOURCE → - -
-`; - -exports[`Storyshots Advanced Tree-to-tree dragging 1`] = ` -
-
-
-
-
-
-
+
+ } + /> +
-
-
+
-
-
- - node1 - -
-
+ Bertha +
+
+
+
+
+
-
+
-
-
- - node2 - -
-
+ Billy +
+
@@ -2108,196 +1741,232 @@ exports[`Storyshots Advanced Tree-to-tree dragging 1`] = `
+
+
+`; + +exports[`Storyshots Advanced Touch support (Experimental) 1`] = ` +
+ + This is + not + a touch-supporting browser + +
-
+
-
+ tabIndex={0} + >
-
+
+ } + >
-
+ - - VIEW SOURCE → -
`; -exports[`Storyshots Advanced onlyExpandSearchedNodes 1`] = ` +exports[`Storyshots Advanced Tree-to-tree dragging 1`] = `
-
-

- Find the needle! -

-
- - - - -   - 0 -  /  - 0 - -
+ } + >
-
+
-
+ tabIndex={0} + >
-
+
+ } + >
-
+
+
-
-
+
-
-
- - Haystack - -
-
+ node1 +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + node2 + +
+
@@ -2555,263 +2196,195 @@ exports[`Storyshots Advanced onlyExpandSearchedNodes 1`] = `
-
-
- -
- - - VIEW SOURCE → - -
-`; - -exports[`Storyshots Basics Add and remove nodes programmatically 1`] = ` -
-
-
+
-
+ tabIndex={0} + >
-
+
+ } + >
+
-
-
- - Peter Olofsson - -
-
-
- -
-
- -
-
+ node3 +
+
+
+
-
+
+ } + >
+
-
-
- - Karl Johansson - -
-
-
- -
-
- -
-
+ node4 +
+
@@ -2822,343 +2395,214 @@ exports[`Storyshots Basics Add and remove nodes programmatically 1`] = `
- -
+
+
+
-
+
+`; + +exports[`Storyshots Advanced onlyExpandSearchedNodes 1`] = ` +
+

+ Find the needle! +

+ + + +   + 0 +  /  + 0 +
- - - VIEW SOURCE → - -
-`; - -exports[`Storyshots Basics Callbacks 1`] = ` -
-
- Open your console to see callback parameter info
-
+
-
+ tabIndex={0} + >
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- - B - -
-
-
-
-
-
-
-
+ "height": 62, + "left": 0, + "position": "absolute", + "top": 0, + "width": "100%", + } + } + >
-
+
+ } + >
-
+
+
+
-
-
- - C - -
-
+ Haystack +
+
@@ -3170,36 +2614,10 @@ exports[`Storyshots Basics Callbacks 1`] = `
-
-
- -
- - - VIEW SOURCE → -
`; -exports[`Storyshots Basics Minimal implementation 1`] = ` +exports[`Storyshots Basics Add and remove nodes programmatically 1`] = `
-
- +
+
+ +
+
@@ -3356,14 +2772,6 @@ exports[`Storyshots Basics Minimal implementation 1`] = ` } } > -
@@ -3410,12 +2818,31 @@ exports[`Storyshots Basics Minimal implementation 1`] = ` - Egg + Karl Johansson
+ > +
+ +
+
+ +
+
@@ -3427,348 +2854,314 @@ exports[`Storyshots Basics Minimal implementation 1`] = `
-
-
- -
- + + Add new nodes at start + +
`; -exports[`Storyshots Basics Modify nodes 1`] = ` +exports[`Storyshots Basics Callbacks 1`] = `
-
+ Open your console to see callback parameter info +
-
+
-
+ tabIndex={0} + >
+
-
-
+
+
-
-
+
+ } + >
-
+
+
-
-
- - - -
-
+ C +
+
@@ -3780,185 +3173,292 @@ exports[`Storyshots Basics Modify nodes 1`] = `
-
-
- -
- - - VIEW SOURCE → -
`; -exports[`Storyshots Basics Prevent drop 1`] = ` -
+exports[`Storyshots Basics Minimal implementation 1`] = ` +
-
+
-
+ tabIndex={0} + >
+
+ > +
+
+
+
+
+
-
-
+
+
+
+
+
+`; + +exports[`Storyshots Basics Modify nodes 1`] = ` +
+
+
+
+
+
-
@@ -4025,7 +3517,15 @@ exports[`Storyshots Basics Prevent drop 1`] = ` - Trapped +
- Jeannie - - - Doesn't allow grandchildren +
- Jimmy +
+
+
+
+
+
+
+`; + +exports[`Storyshots Basics Prevent drop 1`] = ` +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + Trapped + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
+ } + /> +
+
-
-
- - Twin #1 - - - Doesn't play with other twin - -
-
+ Jimmy +
+
+
+
-
+
+ } + >
+
-
-
- - Twin #2 - - - Doesn't play with other twin - -
-
+ Twin #1 + + + Doesn't play with other twin +
+
-
-
-
-
-
-
- -
- - - VIEW SOURCE → - -
-`; - -exports[`Storyshots Basics Row direction support 1`] = ` -
-
-
-
-
-
+
+ } + >
-
-
+
+
+
+
+
+`; + +exports[`Storyshots Basics Row direction support 1`] = ` +
+
+
+
+
+
-
-
+
+ } + >
-
+ - - VIEW SOURCE → - -
-`; - -exports[`Storyshots Basics Search 1`] = ` -
-
-

- Find the needle! -

-
- - - - -   - 0 -  /  - 0 - -
-
-
-
+
+
+
+
+
+
+
-
-
-
+
-
-
-
- - Haystack - -
-
-
+ Egg +
+
@@ -5231,37 +4905,55 @@ exports[`Storyshots Basics Search 1`] = `
-
+
+`; + +exports[`Storyshots Basics Search 1`] = ` +
+

+ Find the needle! +

+ + + +   + 0 +  /  + 0 +
- - - VIEW SOURCE → - -
-`; - -exports[`Storyshots Basics Themes 1`] = ` -
+
-
+
+
+
+ + Haystack + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+`; + +exports[`Storyshots Basics Themes 1`] = ` +
+
+
+
+
+
+
+ - - VIEW SOURCE → -
`; exports[`Storyshots Basics treeData import/export 1`] = `
-
- ↓treeData for this tree was generated from flat data similar to DB rows↓ + ↓treeData for this tree was generated from flat data similar to DB rows↓ +
-
+
-
+ tabIndex={0} + >
-
+
+ } + >
+
-
-
- - N1 - -
-
+ N1 +
+
+
+
-
+
+ } + >
-
+
+
-
-
+
-
-
- - N2 - -
-
+ N2 +
+
@@ -5967,68 +5764,42 @@ exports[`Storyshots Basics treeData import/export 1`] = `
-
- ↓This flat data is generated from the modified tree data↓ -
    -
  • - id: - 1 - , name: - N1 - , parent: - null -
  • -
  • - id: - 2 - , name: - N2 - , parent: - null -
  • -
  • - id: - 3 - , name: - N3 - , parent: - 2 -
  • -
  • - id: - 4 - , name: - N4 - , parent: - 3 -
  • -
-
-
- -
- - - VIEW SOURCE → - +
+ ↓This flat data is generated from the modified tree data↓ +
    +
  • + id: + 1 + , name: + N1 + , parent: + null +
  • +
  • + id: + 2 + , name: + N2 + , parent: + null +
  • +
  • + id: + 3 + , name: + N3 + , parent: + 2 +
  • +
  • + id: + 4 + , name: + N4 + , parent: + 3 +
  • +
`; diff --git a/stories/barebones-no-context.js b/stories/barebones-no-context.js new file mode 100644 index 00000000..daeaf8f8 --- /dev/null +++ b/stories/barebones-no-context.js @@ -0,0 +1,31 @@ +import React, { Component } from 'react'; +import { DndProvider } from 'react-dnd'; +import { HTML5Backend } from 'react-dnd-html5-backend'; +import { SortableTreeWithoutDndContext as SortableTree } from '../src'; +// In your own app, you would need to use import styles once in the app +// import 'react-sortable-tree/styles.css'; + +export default class App extends Component { + constructor(props) { + super(props); + + this.state = { + treeData: [ + { title: 'Chicken', expanded: true, children: [{ title: 'Egg' }] }, + ], + }; + } + + render() { + return ( +
+ + this.setState({ treeData })} + /> + +
+ ); + } +} diff --git a/stories/drag-out-to-remove.js b/stories/drag-out-to-remove.js index 8d2d34f1..da6cc849 100644 --- a/stories/drag-out-to-remove.js +++ b/stories/drag-out-to-remove.js @@ -1,8 +1,8 @@ /* eslint-disable react/no-multi-comp */ -import React, { Component } from 'react'; import PropTypes from 'prop-types'; -import { DragDropContext, DropTarget } from 'react-dnd'; -import HTML5Backend from 'react-dnd-html5-backend'; +import React, { Component } from 'react'; +import { DndProvider, DropTarget } from 'react-dnd'; +import { HTML5Backend } from 'react-dnd-html5-backend'; import { SortableTreeWithoutDndContext as SortableTree } from '../src'; // In your own app, you would need to use import styles once in the app // import 'react-sortable-tree/styles.css'; @@ -54,7 +54,7 @@ const TrashAreaComponent = DropTarget( trashAreaCollect )(trashAreaBaseComponent); -class UnwrappedApp extends Component { +class App extends Component { constructor(props) { super(props); @@ -70,20 +70,21 @@ class UnwrappedApp extends Component { render() { return ( -
- -
- this.setState({ treeData })} - dndType={trashAreaType} - /> -
-
-
+ +
+ +
+ this.setState({ treeData })} + dndType={trashAreaType} + /> +
+
+
+
); } } -const App = DragDropContext(HTML5Backend)(UnwrappedApp); export default App; diff --git a/stories/external-node.js b/stories/external-node.js index a9627836..be2d0adf 100644 --- a/stories/external-node.js +++ b/stories/external-node.js @@ -1,8 +1,8 @@ /* eslint-disable react/no-multi-comp */ -import React, { Component } from 'react'; import PropTypes from 'prop-types'; -import { DragDropContext, DragSource } from 'react-dnd'; -import HTML5Backend from 'react-dnd-html5-backend'; +import React, { Component } from 'react'; +import { DndProvider, DragSource } from 'react-dnd'; +import { HTML5Backend } from 'react-dnd-html5-backend'; import { SortableTreeWithoutDndContext as SortableTree } from '../src'; // In your own app, you would need to use import styles once in the app // import 'react-sortable-tree/styles.css'; @@ -55,7 +55,7 @@ const YourExternalNodeComponent = DragSource( externalNodeCollect )(externalNodeBaseComponent); -class UnwrappedApp extends Component { +class App extends Component { constructor(props) { super(props); @@ -66,19 +66,21 @@ class UnwrappedApp extends Component { render() { return ( -
-
- this.setState({ treeData })} - dndType={externalNodeType} - /> + +
+
+ this.setState({ treeData })} + dndType={externalNodeType} + /> +
+ ← drag + this
- ← drag this -
+ ); } } -const App = DragDropContext(HTML5Backend)(UnwrappedApp); export default App; diff --git a/stories/index.js b/stories/index.js index 04c171d7..c9e9d636 100644 --- a/stories/index.js +++ b/stories/index.js @@ -1,92 +1,45 @@ /* eslint-disable import/no-extraneous-dependencies */ -import React from 'react'; import { storiesOf } from '@storybook/react'; +import React from 'react'; import AddRemoveExample from './add-remove'; import BarebonesExample from './barebones'; +import BarebonesExampleNoContext from './barebones-no-context'; import CallbacksExample from './callbacks'; import CanDropExample from './can-drop'; import ChildlessNodes from './childless-nodes'; import DragOutToRemoveExample from './drag-out-to-remove'; import ExternalNodeExample from './external-node'; import GenerateNodePropsExample from './generate-node-props'; +import './generic.css'; import ModifyNodesExample from './modify-nodes'; -import SearchExample from './search'; import OnlyExpandSearchedNodesExample from './only-expand-searched-node'; +import RowDirectionExample from './rtl-support'; +import SearchExample from './search'; import ThemesExample from './themes'; import TouchSupportExample from './touch-support'; import TreeDataIOExample from './tree-data-io'; import TreeToTreeExample from './tree-to-tree'; -import RowDirectionExample from './rtl-support'; -import './generic.css'; - -import { handleClick, SANDBOX_URL } from './sandbox-utils'; - -const wrapWithSource = (node, src) => ( -
- {node} - -
-
- -
- - - VIEW SOURCE → - -
-); storiesOf('Basics', module) - .add('Minimal implementation', () => - wrapWithSource(, 'barebones.js') - ) - .add('treeData import/export', () => - wrapWithSource(, 'tree-data-io.js') - ) - .add('Add and remove nodes programmatically', () => - wrapWithSource(, 'add-remove.js') - ) - .add('Modify nodes', () => - wrapWithSource(, 'modify-nodes.js') - ) - .add('Prevent drop', () => wrapWithSource(, 'can-drop.js')) - .add('Search', () => wrapWithSource(, 'search.js')) - .add('Themes', () => wrapWithSource(, 'themes.js')) - .add('Callbacks', () => wrapWithSource(, 'callbacks.js')) - .add('Row direction support', () => - wrapWithSource(, 'rtl-support.js') - ); + .add('Minimal implementation', () => ) + .add('treeData import/export', () => ) + .add('Add and remove nodes programmatically', () => ) + .add('Modify nodes', () => ) + .add('Prevent drop', () => ) + .add('Search', () => ) + .add('Themes', () => ) + .add('Callbacks', () => ) + .add('Row direction support', () => ); storiesOf('Advanced', module) - .add('Drag from external source', () => - wrapWithSource(, 'external-node.js') - ) - .add('Touch support (Experimental)', () => - wrapWithSource(, 'touch-support.js') - ) - .add('Tree-to-tree dragging', () => - wrapWithSource(, 'tree-to-tree.js') - ) - .add('Playing with generateNodeProps', () => - wrapWithSource(, 'generate-node-props.js') - ) - .add('Drag out to remove', () => - wrapWithSource(, 'drag-out-to-remove.js') - ) - .add('onlyExpandSearchedNodes', () => - wrapWithSource( - , - 'only-expand-searched-node.js' - ) - ) - .add('Prevent some nodes from having children', () => - wrapWithSource(, 'childless-nodes.js') - ); + .add('Drag from external source', () => ) + .add('Touch support (Experimental)', () => ) + .add('Tree-to-tree dragging', () => , 'tree-to-tree.js') + .add('Playing with generateNodeProps', () => ) + .add('Drag out to remove', () => ) + .add('onlyExpandSearchedNodes', () => ) + .add('Prevent some nodes from having children', () => ) + .add('Minimal implementation without Dnd Context', () => ( + + )); diff --git a/stories/sandbox-utils.js b/stories/sandbox-utils.js deleted file mode 100644 index 9e0156d5..00000000 --- a/stories/sandbox-utils.js +++ /dev/null @@ -1,90 +0,0 @@ -/* eslint-disable import/no-extraneous-dependencies */ -import { getParameters } from 'codesandbox/lib/api/define'; - -const GIT_URL = - 'https://api.github.com/repos/frontend-collective/react-sortable-tree/contents'; - -export const SANDBOX_URL = 'https://codesandbox.io/api/v1/sandboxes/define'; - -// full url for github api call -const getURL = filename => `${GIT_URL}/stories/${filename}`; - -// strip ../src from the src -const strip = code => - code.replace('../src', 'react-sortable-tree').replace( - `// In your own app, you would need to use import styles once in the app -// import 'react-sortable-tree/styles.css';`, - '' - ); - -// modify code so we can just have one file in the sandbox. index.js -const modify = code => { - const addToTop = `import { render } from 'react-dom';\nimport 'react-sortable-tree/style.css';\n`; - const addToBottom = `\nrender(, document.getElementById('root'));`; - return addToTop + code + addToBottom; -}; - -// parse. Possible the atob throws an exception -const parse = base64 => { - let parsed; - try { - parsed = atob(base64); - } catch (error) { - console.error('Failed to parse base64 from GitHub', error); // eslint-disable-line no-console - } - return parsed; -}; - -const html = `
`; - -// using codesandbox util -// returns the payload to send to the define endpoint -const getPayload = code => - getParameters({ - files: { - 'package.json': { - content: { - dependencies: { - react: 'latest', - 'react-dom': 'latest', - 'prop-types': 'latest', - 'react-dnd': 'latest', - 'react-dnd-html5-backend': 'latest', - 'react-sortable-tree-theme-file-explorer': 'latest', - 'react-dnd-touch-backend': 'latest', - 'react-sortable-tree': 'latest', - }, - }, - }, - 'index.js': { - content: code, - }, - 'index.html': { - content: html, - }, - }, - }); - -// set the form values and submit the form -const sendSandboxRequest = parameters => { - document.getElementById('codesandbox-parameters').value = parameters; - document.getElementById('codesandbox-form').submit(); -}; - -// what is called when the view sandbox element is clicked -// get blob from github and the process it and send the POST request -export const handleClick = file => event => { - event.preventDefault(); - const url = getURL(file); - fetch(url) - .then(response => response.json()) - .catch(error => console.error('Error getting blob from GitHub:', error)) // eslint-disable-line no-console - .then(response => { - const parsed = parse(response.content); - if (!parsed) { - return; - } - const payload = getPayload(modify(strip(parsed))); - sendSandboxRequest(payload); - }); -}; diff --git a/stories/touch-support.js b/stories/touch-support.js index 7c56f8bd..4f0cb148 100644 --- a/stories/touch-support.js +++ b/stories/touch-support.js @@ -1,7 +1,7 @@ /* eslint-disable import/no-extraneous-dependencies */ import React, { Component } from 'react'; -import { DragDropContext } from 'react-dnd'; -import HTML5Backend from 'react-dnd-html5-backend'; +import { DndProvider } from 'react-dnd'; +import { HTML5Backend } from 'react-dnd-html5-backend'; import TouchBackend from 'react-dnd-touch-backend'; import { SortableTreeWithoutDndContext as SortableTree } from '../src'; // In your own app, you would need to use import styles once in the app @@ -9,8 +9,9 @@ import { SortableTreeWithoutDndContext as SortableTree } from '../src'; // https://stackoverflow.com/a/4819886/1601953 const isTouchDevice = !!('ontouchstart' in window || navigator.maxTouchPoints); +const dndBackend = isTouchDevice ? TouchBackend : HTML5Backend; -class UnwrappedApp extends Component { +class App extends Component { constructor(props) { super(props); @@ -23,23 +24,22 @@ class UnwrappedApp extends Component { render() { return ( -
- - This is {!isTouchDevice && 'not '}a touch-supporting browser - + +
+ + This is {!isTouchDevice && 'not '}a touch-supporting browser + -
- this.setState({ treeData })} - /> +
+ this.setState({ treeData })} + /> +
-
+
); } } -const App = DragDropContext(isTouchDevice ? TouchBackend : HTML5Backend)( - UnwrappedApp -); export default App; diff --git a/website/favicon.ico b/website/favicon.ico deleted file mode 100644 index 945ae6fb..00000000 Binary files a/website/favicon.ico and /dev/null differ diff --git a/website/index.css b/website/index.css deleted file mode 100644 index 5ea542c5..00000000 --- a/website/index.css +++ /dev/null @@ -1,56 +0,0 @@ -@import url('https://fonts.googleapis.com/css?family=Roboto'); - -iframe { - height: calc(100% - 90px); - width: 100%; - border: 0; - border-radius: 4px; - overflow: hidden; - margin-top: 10px; -} - -.wrapper { - height: 100%; - width: 100%; -} - -body { - margin: 0; - padding: 0; - font-family: Roboto, sans-serif; -} - -.navigation-wrapper { - display: flex; - flex-direction: row; - align-items: center; - padding: 10px; - padding-bottom: 0; -} - -.title { - display: block; - font-size: 2em; - font-weight: bold; - color: black; - text-decoration: none !important; - margin-right: 10px; -} - -.link { - margin-left: 15px; - font-size: 1em; - text-decoration: none !important; - color: rgb(140, 140, 140); -} - -.link:hover { - color: rgb(203, 101, 156); -} - -.description { - color: rgb(198, 198, 198); - font-size: 18px; - padding: 5px; - padding-left: 10px; -} diff --git a/website/index.html b/website/index.html deleted file mode 100644 index cda7cecb..00000000 --- a/website/index.html +++ /dev/null @@ -1,12 +0,0 @@ - - - React Sortable Tree - - - - -
- - - - diff --git a/website/index.js b/website/index.js deleted file mode 100644 index 849cf388..00000000 --- a/website/index.js +++ /dev/null @@ -1,46 +0,0 @@ -import React from 'react'; -import ReactDOM from 'react-dom'; - -import './index.css'; -import '../style.css'; - -class App extends React.Component { - render() { - return ( -
- -
- Drag-and-drop sortable component for nested data and hierarchies -
-