Skip to content

Commit c29c361

Browse files
authored
Merge pull request #25 from shunjizhan/v5
upgrade to V5
2 parents a5e3068 + 545f680 commit c29c361

File tree

12 files changed

+453
-1395
lines changed

12 files changed

+453
-1395
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
## 5.0.0 (2021.6.3)
2+
- restruct internal state management using `use-tree-state` hook
3+
- separate css file
4+
- interface change: `onNameClick(defaultOnClick, nodeData)` => `onNameClick({ defaultOnClick, nodeData })`

README.md

Lines changed: 56 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
# React Folder Tree
2-
A versatile and customizable react treeview library. It supports:
3-
- ✅ customize icons
4-
- ✅ customize event handlers
5-
- ✅ inline add, modify, and delete tree nodes
6-
- ✅ checkbox with half check (indeterminate check)
2+
A versatile and customizable react treeview library. Features:
3+
✅ custom icons
4+
✅ custom event handlers
5+
✅ inline add, modify, and delete tree nodes
6+
✅ checkbox with half check (indeterminate check)
7+
✅ read-only mode
8+
9+
It uses [use-tree-state](https://www.npmjs.com/package/use-tree-state) hook internally for convenient state management.
710
### Quick Preview
811
![folder-tree-demo](/assets/folder-tree-demo.gif)
912

@@ -20,9 +23,10 @@ $ npm install react-folder-tree --save
2023
### 🌀 basic tree
2124
```tsx
2225
import FolderTree, { testData } from 'react-folder-tree';
26+
import 'react-folder-tree/dist/style.css';
2327

2428
const BasicTree = () => {
25-
const onTreeStateChange = state => console.log('tree state: ', state);
29+
const onTreeStateChange = (state, event) => console.log(state, event);
2630

2731
return (
2832
<FolderTree
@@ -34,20 +38,26 @@ const BasicTree = () => {
3438
```
3539

3640
### 🌀 custom initial state
37-
tree state is an object that looks like:
41+
Initial tree state is an object that describes a nested tree node structure, which looks like:
3842
```jsx
3943
{
40-
// reserved keys
41-
name: 'Goku',
44+
// reserved keys, can customize initial value
45+
name: 'root node',
4246
checked (optional): 0 (unchecked, default) | 0.5 (half checked) | 1(checked),
43-
isOpen (optional): false (default) | true,
47+
isOpen (optional): true (default) | false,
4448
children (optional): [array of treenode],
4549

46-
// not reserved
47-
key1 (optional): 'what ever data you need',
48-
url (optional): 'url of this node for example',
50+
// internal keys, don't customize plz
51+
path: [], // path is an array of indexes to this node from root node
52+
_id: 0,
53+
54+
// not reserved, can carry any extra info about this node
55+
nickname (optional): 'pikachu',
56+
url (optional): 'url of this node',
4957
}
5058
```
59+
`checked` and `isOpen` status could be auto initialized by props `initCheckedStatus` and `initOpenStatus`. We can also provide data with custom `checked` and `isOpen` status, and set `initCheckedStatus` and `initOpenStatus` to `'custom'`.
60+
5161
This example shows how to render a tree with custom initial state
5262
```tsx
5363
const treeState = {
@@ -93,6 +103,15 @@ const CustomInitState = () => (
93103
/>
94104
```
95105

106+
### 🌀 read only?
107+
we can use it as a classical view-only tree
108+
```jsx
109+
<FolderTree
110+
data={ treeState }
111+
showCheckbox={ false } // hiding checkbox is not required but recommended for better UX
112+
readOnly // <== here!!
113+
/>
114+
```
96115
---
97116
## Advanced Usage
98117
### 🔥 sync tree state
@@ -102,7 +121,7 @@ This example shows how to download all selected files.
102121
```jsx
103122
const SuperApp = () => {
104123
const [treeState, setTreeState] = useState(initState);
105-
const onTreeStateChange = state => setTreeState(state);
124+
const onTreeStateChange = (state, event) => setTreeState(state);
106125

107126
const onDownload = () => downloadAllSelected(treeState);
108127

@@ -167,10 +186,25 @@ const BitcoinApp = () => {
167186
};
168187
```
169188

170-
### 🔥 disable icons
171-
This usage is a subset of custom icons. For example, to hide `FileIcon` we can simply pass in a dummy custom icon, so nothing will be rendered.
189+
### 🔥 hide icons / disable interaction
190+
This usage is a subset of custom icons.
191+
192+
For example, if we want to disable editing, we can hide `EditIcon` by passing in a dummy custom icon, so nothing will be rendered.
172193
```tsx
173-
const FileIcon = (...args) => null;
194+
const EditIcon = (...args) => null;
195+
```
196+
197+
A little more complex but more flexible way is to have extra node data, say `editable`, then build a custom icon that utilize this data
198+
```ts
199+
const EditIcon = ({ onClick: defaultOnClick, nodeData }) => {
200+
const { editable } = nodeData;
201+
202+
// if this node is editable, render an EditIcon, otherwise render air
203+
return editable ? (<FaEdit onClick={ defaultOnClick } />) : null;
204+
205+
// or render a 'not editable' icon
206+
return editable ? (<FaEdit onClick={ defaultOnClick } />) : (<DontEdit />));
207+
};
174208
```
175209

176210
### 🔥 custom `onClick` for node names
@@ -182,7 +216,7 @@ const dataWithUrl = {
182216
// ...
183217
};
184218

185-
const onNameClick = (defaultOnClick, nodeData) => {
219+
const onNameClick = ({ defaultOnClick, nodeData }) => {
186220
defaultOnClick();
187221

188222
const {
@@ -209,13 +243,14 @@ const Downloader = () => (
209243
| prop | description | type | options |
210244
|-------------------|-----------------------------------------|----------|------------------------------------------------|
211245
| data | initial tree state data (required) | object | N/A |
212-
| onChange | callback when tree state changes | function | console.log (default) |
213246
| initCheckedStatus | initial check status of all nodes | string | 'unchecked' (default) \| 'checked' \| 'custom' |
214-
| initOpenStatus | initial open status of all treenodes | string | 'open' (default) \| 'close' \| 'custom' |
247+
| initOpenStatus | initial open status of all treenodes | string | 'open' (default) \| 'closed' \| 'custom' |
215248
| iconComponents | custom icon components | object | ant design icons (default) |
249+
| onChange | callback when tree state changes | function | console.log (default) |
250+
| onNameClick | callback when click treenode name | function | open treenode inline toolbox (default) |
216251
| indentPixels | ident pixels of 1x level treenode | number | 30 (default) |
217252
| showCheckbox | show check box? | bool | true (default) | false |
218-
| onNameClick | callback when click treenode name | function | open treenode inline toolbox (default) |
253+
| readOnly | readOnly mode? can't click/check node | bool | false (default) | true |
219254

220255
---
221256
## Bugs? Questions? Contributions?

config/webpack.config.js

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -31,14 +31,6 @@ module.exports = {
3131
loader: 'babel-loader',
3232
},
3333
},
34-
{
35-
test: /\.html$/,
36-
use: [
37-
{
38-
loader: 'html-loader',
39-
},
40-
],
41-
},
4234
{
4335
test: /\.scss$/,
4436
use: [
@@ -51,6 +43,7 @@ module.exports = {
5143
},
5244
plugins: [
5345
// generates an HTML file by injecting automatically all our generated bundles.
46+
// any css result will be automagically included as a <link>
5447
new HtmlWebPackPlugin({
5548
template: path.resolve(__dirname, '../public/index.html'),
5649
favicon: path.resolve(__dirname, '../public/pokeball.ico'),

config/webpack.prod.config.js

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
const path = require('path');
22
const CleanTerminalPlugin = require('clean-terminal-webpack-plugin');
3+
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
34
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');
45

56
module.exports = {
@@ -32,16 +33,17 @@ module.exports = {
3233
{
3334
test: /\.scss$/,
3435
use: [
35-
'style-loader', // creates style nodes from JS strings
36-
'css-loader', // translates CSS into CommonJS
37-
'sass-loader', // compiles Sass to CSS, using Node Sass by default
36+
MiniCssExtractPlugin.loader, // generate a separate style.css
37+
'css-loader', // translates CSS into CommonJS
38+
'sass-loader', // compiles Sass to CSS, using Node Sass by default
3839
],
3940
},
4041
],
4142
},
4243
plugins: [
4344
// clear terminal in each build
4445
new CleanTerminalPlugin(),
46+
new MiniCssExtractPlugin({ filename: 'style.css' }),
4547
// new BundleAnalyzerPlugin(),
4648
],
4749
externals: {

package.json

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44
"description": "customizable react folder tree library",
55
"main": "dist/react-folder-tree.bundle.js",
66
"files": [
7-
"dist/react-folder-tree.bundle.js"
7+
"dist/react-folder-tree.bundle.js",
8+
"dist/style.css"
89
],
910
"author": "Shunji Zhan <[email protected]>",
1011
"keywords": [
@@ -63,6 +64,7 @@
6364
"@babel/plugin-proposal-optional-chaining": "^7.12.7",
6465
"@babel/preset-env": "^7.11.5",
6566
"@babel/preset-react": "^7.10.4",
67+
"@wojtekmaj/enzyme-adapter-react-17": "^0.6.1",
6668
"babel-eslint": "^10.1.0",
6769
"babel-loader": "^8.1.0",
6870
"clean-terminal-webpack-plugin": "^3.0.0",
@@ -81,7 +83,10 @@
8183
"identity-obj-proxy": "^3.0.0",
8284
"jest": "^26.6.3",
8385
"lint-staged": "^10.5.1",
86+
"mini-css-extract-plugin": "^1.6.0",
8487
"node-sass": "^5.0.0",
88+
"react": "^16.8.0 || ^17",
89+
"react-dom": "^16.8.0 || ^17",
8590
"sass-loader": "^10.1.0",
8691
"style-loader": "^2.0.0",
8792
"webpack": "^5.1.0",
@@ -90,11 +95,12 @@
9095
"webpack-dev-server": "^3.11.0"
9196
},
9297
"peerDependencies": {
93-
"react": "^16.13.1",
94-
"react-dom": "^16.13.1"
98+
"react": "^16.8.0 || ^17",
99+
"react-dom": "^16.8.0 || ^17"
95100
},
96101
"dependencies": {
97102
"prop-types": "^15.7.2",
98-
"react-icons": "^4.1.0"
103+
"react-icons": "^4.1.0",
104+
"use-tree-state": "^1.0.0"
99105
}
100106
}

0 commit comments

Comments
 (0)