diff --git a/.babelrc b/.babelrc
new file mode 100644
index 00000000..486e876c
--- /dev/null
+++ b/.babelrc
@@ -0,0 +1,9 @@
+{
+ "presets": [
+ "@babel/preset-env",
+ "@babel/preset-react"
+ ],
+ "plugins": [
+ "@babel/plugin-proposal-optional-chaining"
+ ]
+}
diff --git a/.eslintignore b/.eslintignore
new file mode 100644
index 00000000..1fd61fc5
--- /dev/null
+++ b/.eslintignore
@@ -0,0 +1,2 @@
+src/components/SandBox.jsx
+src/components/SandBoxComponents/
\ No newline at end of file
diff --git a/.eslintrc.js b/.eslintrc.js
new file mode 100644
index 00000000..a3b80303
--- /dev/null
+++ b/.eslintrc.js
@@ -0,0 +1,69 @@
+module.exports = {
+ parser: 'babel-eslint', // use babel to parse
+ env: {
+ browser: true,
+ es6: true,
+ node: true, // for node globals, such as 'module'
+ jest: true, // for jest globals, such as 'test' and 'expect'
+ },
+ // 'plugins' section only 'activate' these plugins
+ // still need manually edit 'rules' to actually use them
+ // such as { react/xxx: 'error' }
+ plugins: [
+ 'react',
+ ],
+ // 'extends' section will extend these configs
+ // and they will take effect directly
+ extends: [
+ 'eslint:recommended',
+ 'plugin:react/recommended',
+ 'airbnb',
+ 'airbnb/hooks',
+ 'plugin:react-hooks/recommended',
+ ],
+ rules: {
+ indent: [2, 2, { SwitchCase: 1 }],
+ quotes: [2, 'single'],
+ semi: [2, 'always'],
+ 'jsx-quotes': [2, 'prefer-single'],
+ 'linebreak-style': [2, 'unix'],
+ 'arrow-parens': [2, 'as-needed'],
+ 'react/jsx-curly-spacing': [2, {
+ when: 'always',
+ spacing: { objectLiterals: 'never' },
+ }],
+ 'no-restricted-syntax': [
+ 'error',
+ 'ForInStatement',
+ // 'ForOfStatement',
+ 'LabeledStatement',
+ 'WithStatement',
+ ],
+
+ /* ---------- turned off ---------- */
+ 'max-len': 0,
+ 'react/jsx-filename-extension': 0,
+ 'react/forbid-prop-types': 0,
+ 'react/require-default-props': 0,
+ 'no-underscore-dangle': 0,
+ 'no-multi-spaces': 0,
+ 'jsx-a11y/click-events-have-key-events': 0, // allow click handler on
+ 'jsx-a11y/no-static-element-interactions': 0, // allow click handler on
+ 'no-unused-expressions': [2, { allowShortCircuit: true }], // allow x && y()
+ 'import/no-extraneous-dependencies': [2, { devDependencies: true }], // so can import enzyme, which is dev dependencies
+ },
+
+ // don't know what these are... generated by eslint --init
+ // TODO: do more research about these
+ globals: {
+ Atomics: 'readonly',
+ SharedArrayBuffer: 'readonly',
+ },
+ parserOptions: {
+ ecmaFeatures: {
+ jsx: true,
+ },
+ ecmaVersion: 2018,
+ sourceType: 'module',
+ },
+};
diff --git a/.gitignore b/.gitignore
index 8706a704..588dc17a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,6 @@
+dist/
+notes.md
+
yarn-error.log
npm-debug.log
build
diff --git a/LICENSE b/LICENSE
deleted file mode 100644
index cf62dcd9..00000000
--- a/LICENSE
+++ /dev/null
@@ -1,21 +0,0 @@
-MIT License
-
-Copyright (c) 2017 Shunji Zhan
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
diff --git a/README.md b/README.md
index a30811c1..4857f0a8 100644
--- a/README.md
+++ b/README.md
@@ -1,188 +1,50 @@
-# Introduction
+# React Folder Tree
+A powerful and customizable react treeview library.
-This is a folder tree written in ReactJS.
-We can do:
+## Core Features
+- **Half Check** (indeterminate checkboxes): when some of the children nodes are checked, parent node automatically becomes half check.
+- **Inline CRUD** operations: rename nodes, create new nodes, and delete nodes.
+- **Customizable**: all icons are customizable, so you can build your favorite styles, as well as custom functionalities for user interactions.
-- click each carat to expand/collapse the folder
-- click the checkbox to (un)check each folder and file. (un)check each folder will automatically (un)check all sub-folders, including all the files in these folders. If part of the sub-folders or files in a folder are checked, this folder will display a half check.
-- click the folder/file name to select it, and it will be hightlighted in blue
-- click the pencil beside the folder/file to rename it
-- click the delete button to delete the selected folder/file
-- click the Add button to add new file in the selected folder/file. Adding a file-2 to a file-1 will change file-1 to a folder; if all sub folder/files of a folder are deleted, this folder will become a file. The new file's check status is same as its parent
+## Quick Preview
+
-# Props
-- data: initial data to construct the tree. Sample data can be found [below](#sample-data)
-- onChange(data): It will call this function after any of these four actions: (un)check, add, delete, or rename. Where data is the object representing the tree of all selected files/folders (filtered out all unchecked files/folders).
-- FileComponent & FolderComponent: you can inject your own components here. Default file/folder component is already provided.
-# Sample Tree:
+## Demos & Code Examples
+[===== HERE =====](https://shunjizhan.github.io/react-folder-tree-demos/)
+[===== HERE =====](https://shunjizhan.github.io/react-folder-tree-demos/)
+[===== HERE =====](https://shunjizhan.github.io/react-folder-tree-demos/)
-
+## Basic Usage & Props
+```tsx
+import FolderTree, { testData } from 'react-folder-tree';
-# To Install:
- npm install --save react-folder-tree
+const BasicTree = () => {
+ const onTreeStateChange = state => console.log('tree state: ', state);
-# To Run:
+ return (
+
+ );
+};
-
+```
-Remember to include the above link in your html page. Otherwise the icons won't show up.
+| prop name | description | type | options |
+|-------------------|-----------------------------------------|----------|------------------------------------------------|
+| data | initial tree state data (required) | object | N/A |
+| onChange | callback when tree state changes | function | console.log (default) |
+| initCheckedStatus | initial check status of all nodes | string | 'unchecked' (default) \| 'checked' \| 'custom' |
+| initOpenStatus | initial open status of all folder nodes | string | 'close' (default) \| 'open' \| 'custom' |
+| iconComponents | custom icon components | object | N/A |
+| indentPixels | ident pixels | number | 30 (default) |
- import React from 'react';
- import ReactDOM from 'react-dom';
- import FolderTree from 'react-folder-tree';
+## Note
+After upgrading to `v4.0`, old versions are no compatible anymore, please try out new version or specify old version when installing!
- const testData = YOUR DATA;
-
- ReactDOM.render(
- ,
- document.getElementById('root')
- )
-
-# Data Format:
-
- {
- id: number,
- filename: string,
- children: array of *this* [optional]
- }
-
-# Sample Data:
-
- const testData = {
- "id": 1,
- "filename": "All Categories",
- "children": [
- {
- "id": 2,
- "filename": "For Sale",
- "children": [
- {
- "id": 3,
- "filename": "Audio & Stereo",
- "children": [
- {
- "id": 4,
- "filename": "For Sale",
- "children": [
- {
- "id": 5,
- "filename": "Audio & Stereo",
- },
- {
- "id": 6,
- "filename": "Baby & Kids Stuff",
- },
- {
- "id": 7,
- "filename": "Music, Films, Books & Games",
- }
- ]
- },
- {
- "id": 8,
- "filename": "Motors",
- "children": [
- {
- "id": 9,
- "filename": "Car Parts & Accessories",
- },
- {
- "id": 10,
- "filename": "Cars",
- },
- {
- "id": 11,
- "filename": "Motorbike Parts & Accessories",
- }
- ]
- },
- {
- "id": 12,
- "filename": "Jobs",
- "children": [
- {
- "id": 13,
- "filename": "Accountancy",
- },
- {
- "id": 14,
- "filename": "Financial Services & Insurance",
- },
- {
- "id": 15,
- "filename": "Bar Staff & Management",
- }
- ]
- }
- ]
- },
- {
- "id": 16,
- "filename": "Baby & Kids Stuff",
- },
- {
- "id": 17,
- "filename": "Music, Films, Books & Games",
- }
- ]
- },
- {
- "id": 18,
- "filename": "Motors",
- "children": [
- {
- "id": 19,
- "filename": "Car Parts & Accessories",
- },
- {
- "id": 20,
- "filename": "Cars",
- },
- {
- "id": 21,
- "filename": "Motorbike Parts & Accessories",
- }
- ]
- },
- {
- "id": 22,
- "filename": "Jobs",
- "children": [
- {
- "id": 23,
- "filename": "Accountancy",
- },
- {
- "id": 24,
- "filename": "Financial Services & Insurance",
- },
- {
- "id": 25,
- "filename": "Bar Staff & Management",
- }
- ]
- }
- ]
- }
-
-# Resources
-
-Testing data is from [here](http://codepen.io/anon/pen/Ftkln?editors=0010)
-
-Icons are from [here](https://www.npmjs.com/package/react-fontawesome)
-
-# Contribution
-Any contributions are welcomed! Please upload issues or pull requests on the github page, thank you!
-
-# TODO
-- css modules of font awesome
-- in editing mode the inputbox doesn't hover automatically
-- press enter to confirm (now must click the confirm icon)
-- fully test all funtionalities
-- change structure: since now each Treenode has path, there should exist more concise way to handle check
-- remove excessive dependencies
\ No newline at end of file
+## Contributions
+Welcome!
diff --git a/assets/folder-tree-demo.gif b/assets/folder-tree-demo.gif
new file mode 100644
index 00000000..46819d3b
Binary files /dev/null and b/assets/folder-tree-demo.gif differ
diff --git a/config/env.js b/config/env.js
deleted file mode 100644
index ce99b6b4..00000000
--- a/config/env.js
+++ /dev/null
@@ -1,36 +0,0 @@
-// Grab NODE_ENV and REACT_APP_* environment variables and prepare them to be
-// injected into the application via DefinePlugin in Webpack configuration.
-
-var REACT_APP = /^REACT_APP_/i;
-
-function getClientEnvironment(publicUrl) {
- var raw = Object
- .keys(process.env)
- .filter(key => REACT_APP.test(key))
- .reduce((env, key) => {
- env[key] = process.env[key];
- return env;
- }, {
- // Useful for determining whether we’re running in production mode.
- // Most importantly, it switches React into the correct mode.
- 'NODE_ENV': process.env.NODE_ENV || 'development',
- // Useful for resolving the correct path to static assets in `public`.
- // For example, .
- // This should only be used as an escape hatch. Normally you would put
- // images into the `src` and `import` them in code to get their paths.
- 'PUBLIC_URL': publicUrl
- });
- // Stringify all values so we can feed into Webpack DefinePlugin
- var stringified = {
- 'process.env': Object
- .keys(raw)
- .reduce((env, key) => {
- env[key] = JSON.stringify(raw[key]);
- return env;
- }, {})
- };
-
- return { raw, stringified };
-}
-
-module.exports = getClientEnvironment;
diff --git a/config/jest/cssTransform.js b/config/jest/cssTransform.js
deleted file mode 100644
index aa17d127..00000000
--- a/config/jest/cssTransform.js
+++ /dev/null
@@ -1,12 +0,0 @@
-// This is a custom Jest transformer turning style imports into empty objects.
-// http://facebook.github.io/jest/docs/tutorial-webpack.html
-
-module.exports = {
- process() {
- return 'module.exports = {};';
- },
- getCacheKey(fileData, filename) {
- // The output is always the same.
- return 'cssTransform';
- },
-};
diff --git a/config/jest/fileTransform.js b/config/jest/fileTransform.js
deleted file mode 100644
index 927eb305..00000000
--- a/config/jest/fileTransform.js
+++ /dev/null
@@ -1,10 +0,0 @@
-const path = require('path');
-
-// This is a custom Jest transformer turning file imports into filenames.
-// http://facebook.github.io/jest/docs/tutorial-webpack.html
-
-module.exports = {
- process(src, filename) {
- return 'module.exports = ' + JSON.stringify(path.basename(filename)) + ';';
- },
-};
diff --git a/config/paths.js b/config/paths.js
deleted file mode 100644
index 97417b9c..00000000
--- a/config/paths.js
+++ /dev/null
@@ -1,79 +0,0 @@
-var path = require('path');
-var fs = require('fs');
-var url = require('url');
-
-// Make sure any symlinks in the project folder are resolved:
-// https://github.com/facebookincubator/create-react-app/issues/637
-var appDirectory = fs.realpathSync(process.cwd());
-function resolveApp(relativePath) {
- return path.resolve(appDirectory, relativePath);
-}
-
-// We support resolving modules according to `NODE_PATH`.
-// This lets you use absolute paths in imports inside large monorepos:
-// https://github.com/facebookincubator/create-react-app/issues/253.
-
-// It works similar to `NODE_PATH` in Node itself:
-// https://nodejs.org/api/modules.html#modules_loading_from_the_global_folders
-
-// We will export `nodePaths` as an array of absolute paths.
-// It will then be used by Webpack configs.
-// Jest doesn’t need this because it already handles `NODE_PATH` out of the box.
-
-// Note that unlike in Node, only *relative* paths from `NODE_PATH` are honored.
-// Otherwise, we risk importing Node.js core modules into an app instead of Webpack shims.
-// https://github.com/facebookincubator/create-react-app/issues/1023#issuecomment-265344421
-
-var nodePaths = (process.env.NODE_PATH || '')
- .split(process.platform === 'win32' ? ';' : ':')
- .filter(Boolean)
- .filter(folder => !path.isAbsolute(folder))
- .map(resolveApp);
-
-var envPublicUrl = process.env.PUBLIC_URL;
-
-function ensureSlash(path, needsSlash) {
- var hasSlash = path.endsWith('/');
- if (hasSlash && !needsSlash) {
- return path.substr(path, path.length - 1);
- } else if (!hasSlash && needsSlash) {
- return path + '/';
- } else {
- return path;
- }
-}
-
-function getPublicUrl(appPackageJson) {
- return envPublicUrl || require(appPackageJson).homepage;
-}
-
-// We use `PUBLIC_URL` environment variable or "homepage" field to infer
-// "public path" at which the app is served.
-// Webpack needs to know it to put the right