Skip to content

Commit df700fa

Browse files
authored
Merge pull request #23 from Lodin/feat/add-openness-update
Allow updating node openness programmatically + global refactoring and dependecy updates
2 parents 0da4fa8 + 35cb250 commit df700fa

21 files changed

+7877
-7839
lines changed

.babelrc.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,17 @@ const {BUILD_TYPE} = process.env;
22
const isCjs = BUILD_TYPE === 'cjs';
33
const isLib = BUILD_TYPE === 'lib';
44

5-
module.exports = api => ({
5+
module.exports = (api) => ({
66
plugins: [
77
[require('@babel/plugin-proposal-class-properties'), {loose: true}],
88
[require('@babel/plugin-proposal-optional-chaining'), {loose: true}],
9+
[
10+
require('@babel/plugin-proposal-nullish-coalescing-operator'),
11+
{loose: true},
12+
],
913
...(isLib
1014
? []
1115
: [
12-
[
13-
require('babel-plugin-transform-async-to-promises'),
14-
{hoist: true, inlineHelpers: true},
15-
],
1616
[
1717
require('@babel/plugin-transform-runtime'),
1818
{

.eslintignore

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
11
**/node_modules/**/*
22
**/dist/**/*
33
docs
4-
*.ts
5-
*.tsx

.eslintrc

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,18 @@
11
{
2-
"extends": ["eslint-config-poetez", "plugin:prettier/recommended"],
2+
"extends": [
3+
"eslint-config-poetez/typescript-react",
4+
"eslint-config-prettier/@typescript-eslint",
5+
"prettier/react",
6+
"plugin:prettier/recommended"
7+
],
38
"env": {
4-
"node": true
9+
"browser": true,
10+
"node": true,
11+
"es6": true,
12+
"jest": true
513
},
614
"rules": {
7-
"global-require": "off"
15+
"@typescript-eslint/promise-function-async": "off",
16+
"guard-for-in": "off"
817
}
918
}

.storybook/webpack.config.js

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
/* eslint-disable sort-keys */
2-
31
module.exports = ({config}) => {
42
config.devtool = 'eval';
53

README.md

Lines changed: 28 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,9 @@
55
[![Build Status](https://img.shields.io/travis/Lodin/react-vtree/master.svg)](https://travis-ci.org/Lodin/react-vtree)
66
[![Test Coverage](https://img.shields.io/codecov/c/github/Lodin/react-vtree/master.svg)](https://codecov.io/gh/Lodin/react-vtree)
77

8-
This package provides a lightweight and flexible solution for rendering large
9-
tree structures. It is built on top of the [react-window](https://github.com/bvaughn/react-window)
10-
library.
8+
This package provides a lightweight and flexible solution for rendering large tree structures. It is built on top of the [react-window](https://github.com/bvaughn/react-window) library.
119

12-
**Attention!** This library is entirely rewritten to work with the `react-window`. If
13-
you are looking for the tree view solution for the [react-virtualized](https://github.com/bvaughn/react-virtualized),
14-
take a look at [react-virtualized-tree](https://github.com/diogofcunha/react-virtualized-tree).
10+
**Attention!** This library is entirely rewritten to work with the `react-window`. If you are looking for the tree view solution for the [react-virtualized](https://github.com/bvaughn/react-virtualized), take a look at [react-virtualized-tree](https://github.com/diogofcunha/react-virtualized-tree).
1511

1612
## Installation
1713

@@ -129,73 +125,55 @@ const Example = () => (
129125

130126
#### Props
131127

132-
The component receives all the props of the `FixedSizeList` component except for
133-
the `itemCount`. Additional properties are the following:
128+
The component receives all the props of the `FixedSizeList` component except for the `itemCount`. Additional properties are the following:
134129

135130
##### `children`
136131

137-
The `Node` component that is responsible for rendering each node. It receives
138-
all the properties [`Row`](https://react-window.now.sh/#/api/FixedSizeList)
139-
recieves except for the `index` prop plus the following properties:
132+
The `Node` component that is responsible for rendering each node. It receives all the properties [`Row`](https://react-window.now.sh/#/api/FixedSizeList) recieves except for the `index` prop plus the following properties:
140133

141134
- `data: object` - a data object yielded by the `treeWalker` function.
142135
- `isOpen: boolean` - a current openness status of the node.
143-
- `toggle(): function` - a function to change the openness state of the node. It
144-
receives no arguments and can be provided directly as an `onClick` handler.
145-
- `treeData: any` - any data provided via the `itemData` property of the
146-
`FixedSizeTree` component.
136+
- `toggle(): function` - a function to change the openness state of the node. It receives no arguments and can be provided directly as an `onClick` handler.
137+
- `treeData: any` - any data provided via the `itemData` property of the `FixedSizeTree` component.
147138

148139
##### `rowComponent: component`
149140

150-
This property receives a custom `Row` component for the `FixedSizeList` that
151-
will override the default one. It can be used for adding new functionality to an
152-
existing one by wrapping the default `Row` into a custom component.
141+
This property receives a custom `Row` component for the `FixedSizeList` that will override the default one. It can be used for adding new functionality to an existing one by wrapping the default `Row` into a custom component.
153142

154143
##### `* treeWalker(refresh: boolean)`
155144

156-
An iterator function that walks around the tree and yields each node one by one
157-
flattening them to an array that can be easily displayed by `FixedSizeList`
158-
component.
145+
An iterator function that walks around the tree and yields each node one by one flattening them to an array that can be easily displayed by `FixedSizeList` component.
159146

160-
The function receives `refresh` parameter. If it is `true`, the component
161-
requests the full node update and expects the complete data object yielded. If
162-
it is `false`, the component awaits only the node id to update the order of
163-
displayed nodes.
147+
The function receives `refresh` parameter. If it is `true`, the component requests the full node update and expects the complete data object yielded. If it is `false`, the component awaits only the node id to update the order of displayed nodes.
164148

165149
The data object should contain the following required properties:
166150

167151
- `id` - a unique identifier of the node.
168152
- `isOpenByDefault` - a default openness state of the node.
169153

170-
You can add any other property you need. This object will be sent directly to
171-
the `Node` component.
154+
You can add any other property you need. This object will be sent directly to the `Node` component.
172155

173-
Yielding the object gets the current openness state of the node. Basing on it,
174-
you should decide if the node's children are going to be rendered.
156+
Yielding the object gets the current openness state of the node. Basing on it, you should decide if the node's children are going to be rendered.
175157

176158
#### Methods
177159

178-
The component provides all the methods `FixedSizeList` provides with the
179-
following changes:
160+
The component provides all the methods `FixedSizeList` provides with the following changes:
180161

181162
##### `scrollToItem(id: string | symbol, align?: Align): void`
182163

183164
The `scrollToItem` method receives node `id` instead of `index`.
184165

185166
##### `async recomputeTree(options): void`
186167

187-
This method runs the `treeWalker` function again and, basing on the received
188-
options, updates either nodes or their order.
168+
This method runs the `treeWalker` function again and, basing on the received options, updates either nodes or their order.
189169

190170
It receives options object with the following parameters:
191171

192-
- `refreshNodes: boolean` - if this parameter is `true`, `treeWalker` will
193-
receive `refresh` option, and the component will expect the data object yielded.
194-
If this parameter is either `false` or not provided, the component will expect
195-
string id.
196-
- `useDefaultOpenness: boolean` - if this parameter is `true`, openness state of
197-
all nodes will be reset to `isOpenByDefault`. Nodes updated during the tree
198-
walking will use the new `isOpenByDefault` value.
172+
- `opennessState: Record<string, boolean>` - nodes whose IDs are specified as keys of this object will be opened or closed according to boolean values. If the value is `true`, node will be opened; otherwise, it will be closed. This object can be used for changing nodes' openness programmatically without re-creating the `treeWalker` generator.
173+
174+
**NOTE**: If you specify both `useDefaultOpenness` and `opennessState`, `opennessState` will be overridden by `useDefaultOpenness` results.
175+
- `refreshNodes: boolean` - if this parameter is `true`, `treeWalker` will receive `refresh` option, and the component will expect the data object yielded. If this parameter is either `false` or not provided, the component will expect string id.
176+
- `useDefaultOpenness: boolean` - if this parameter is `true`, openness state of all nodes will be reset to `isOpenByDefault`. Nodes updated during the tree walking will use the new `isOpenByDefault` value.
199177

200178
### `VariableSizeTree`
201179

@@ -292,57 +270,41 @@ const Example = () => (
292270

293271
#### Props
294272

295-
The component receives all the props of the `VariableSizeList` component except
296-
for the `itemCount` and `itemSize`. `itemSize` is still available but not
297-
required, and should be used only if the default behavior is not enough.
298-
Additional properties are the following:
273+
The component receives all the props of the `VariableSizeList` component except for the `itemCount` and `itemSize`. `itemSize` is still available but not required, and should be used only if the default behavior is not enough. Additional properties are the following:
299274

300275
##### `children`
301276

302-
The `Node` component. It is the same as the [`FixedSizeTree`](#fixedsizetree)'s
303-
one but receives two additional properties:
277+
The `Node` component. It is the same as the [`FixedSizeTree`](#fixedsizetree)'s one but receives two additional properties:
304278

305279
- `height: number` - a current height of the node.
306-
- `resize(newHeight: number, shouldForceUpdate?: boolean): function` - a
307-
function to change the height of the node. It receives two parameters:
280+
- `resize(newHeight: number, shouldForceUpdate?: boolean): function` - a function to change the height of the node. It receives two parameters:
308281
- `newHeight: number` - a new height of the node.
309-
- `shouldForceUpdate: boolean` - an optional argument that will be sent to the
310-
[`resetAfterIndex`](https://react-window.now.sh/#/api/VariableSizeList)
311-
method.
282+
- `shouldForceUpdate: boolean` - an optional argument that will be sent to the [`resetAfterIndex`](https://react-window.now.sh/#/api/VariableSizeList) method.
312283

313284
##### `rowComponent: component`
314285

315-
This property receives a custom `Row` component for the `VariableSizeList` that
316-
will override the default one. It can be used for adding new functionality to an
317-
existing one by wrapping the default `Row` into a custom component.
286+
This property receives a custom `Row` component for the `VariableSizeList` that will override the default one. It can be used for adding new functionality to an existing one by wrapping the default `Row` into a custom component.
318287

319288
##### `* treeWalker(refresh: boolean)`
320289

321-
An iterator function that walks over the tree. It behaves the same as
322-
`FixedSizeTree`'s `treeWalker`, but there one additional required property for
323-
the data object:
290+
An iterator function that walks over the tree. It behaves the same as `FixedSizeTree`'s `treeWalker`, but there one additional required property for the data object:
324291

325292
- `defaultHeight: number` - the default height of the node.
326293

327294
#### Methods
328295

329-
The component provides all the methods `VariableSizeList` provides with the
330-
following changes:
296+
The component provides all the methods `VariableSizeList` provides with the following changes:
331297

332298
##### `scrollToItem(id: string | symbol, align?: Align): void`
333299

334300
The `scrollToItem` method receives node `id` instead of `index`.
335301

336302
##### `resetAfterId(id: string | symbol, shouldForceUpdate: boolean = false): void`
337303

338-
This method replaces the `resetAfterIndex` method of `VariableSizeList`, but
339-
works exactly the same. It receives node `id` as a first argument.
304+
This method replaces the `resetAfterIndex` method of `VariableSizeList`, but works exactly the same. It receives node `id` as a first argument.
340305

341306
##### `async recomputeTree(options): void`
342307

343-
This method works exactly the same as the `FixedSizeTree`'s one, but receives
344-
one additional option:
308+
This method works exactly the same as the `FixedSizeTree`'s one, but receives one additional option:
345309

346-
- `useDefaultHeight: boolean` - if this parameter is `true`, the height of
347-
all nodes will be reset to `defaultHeight`. Nodes updated during the tree
348-
walking will use the new `defaultHeight` value.
310+
- `useDefaultHeight: boolean` - if this parameter is `true`, the height of all nodes will be reset to `defaultHeight`. Nodes updated during the tree walking will use the new `defaultHeight` value.

__stories__/FixedSizeTree.story.tsx

Lines changed: 27 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
import {number, withKnobs} from '@storybook/addon-knobs';
22
import {storiesOf} from '@storybook/react';
3-
import * as React from 'react';
3+
import React, {FC} from 'react';
44
import AutoSizer from 'react-virtualized-auto-sizer';
5-
import Tree, {
5+
import {
66
FixedSizeNodeComponentProps,
77
FixedSizeNodeData,
8-
} from '../src/FixedSizeTree';
8+
FixedSizeTree,
9+
} from '../src';
910

1011
document.body.style.margin = '0';
1112
document.body.style.display = 'flex';
@@ -15,22 +16,23 @@ const root = document.getElementById('root')!;
1516
root.style.margin = '10px 0 0 10px';
1617
root.style.flex = '1';
1718

18-
type DataNode = {
19+
type DataNode = Readonly<{
1920
children: DataNode[];
2021
id: number;
2122
name: string;
22-
};
23+
}>;
2324

24-
type StackElement = {
25+
type StackElement = Readonly<{
2526
nestingLevel: number;
2627
node: DataNode;
27-
};
28+
}>;
2829

29-
type ExtendedData = {
30-
readonly isLeaf: boolean;
31-
readonly name: string;
32-
readonly nestingLevel: number;
33-
};
30+
type TreeData = FixedSizeNodeData &
31+
Readonly<{
32+
isLeaf: boolean;
33+
name: string;
34+
nestingLevel: number;
35+
}>;
3436

3537
let nodeId = 0;
3638

@@ -47,7 +49,6 @@ const createNode = (depth: number = 0) => {
4749
return node;
4850
}
4951

50-
// tslint:disable-next-line:increment-decrement
5152
for (let i = 0; i < 5; i++) {
5253
node.children.push(createNode(depth + 1));
5354
}
@@ -61,7 +62,7 @@ const defaultButtonStyle = {fontFamily: 'Courier New'};
6162

6263
function* treeWalker(
6364
refresh: boolean,
64-
): Generator<FixedSizeNodeData<ExtendedData> | string | symbol, void, boolean> {
65+
): Generator<TreeData | string | symbol, void, boolean> {
6566
const stack: StackElement[] = [];
6667

6768
stack.push({
@@ -84,7 +85,6 @@ function* treeWalker(
8485
: id;
8586

8687
if (node.children.length !== 0 && isOpened) {
87-
// tslint:disable-next-line:increment-decrement
8888
for (let i = node.children.length - 1; i >= 0; i--) {
8989
stack.push({
9090
nestingLevel: nestingLevel + 1,
@@ -95,9 +95,12 @@ function* treeWalker(
9595
}
9696
}
9797

98-
const Node: React.FunctionComponent<FixedSizeNodeComponentProps<
99-
ExtendedData
100-
>> = ({data: {isLeaf, name, nestingLevel}, isOpen, style, toggle}) => (
98+
const Node: FC<FixedSizeNodeComponentProps<TreeData>> = ({
99+
data: {isLeaf, name, nestingLevel},
100+
isOpen,
101+
style,
102+
toggle,
103+
}) => (
101104
<div
102105
style={{
103106
...style,
@@ -117,23 +120,21 @@ const Node: React.FunctionComponent<FixedSizeNodeComponentProps<
117120
</div>
118121
);
119122

120-
interface TreePresenterProps {
121-
readonly itemSize: number;
122-
}
123+
type TreePresenterProps = Readonly<{
124+
itemSize: number;
125+
}>;
123126

124-
const TreePresenter: React.FunctionComponent<TreePresenterProps> = ({
125-
itemSize,
126-
}) => (
127+
const TreePresenter: FC<TreePresenterProps> = ({itemSize}) => (
127128
<AutoSizer disableWidth>
128129
{({height}) => (
129-
<Tree<ExtendedData>
130+
<FixedSizeTree
130131
treeWalker={treeWalker}
131132
itemSize={itemSize}
132133
height={height}
133134
width="100%"
134135
>
135136
{Node}
136-
</Tree>
137+
</FixedSizeTree>
137138
)}
138139
</AutoSizer>
139140
);

0 commit comments

Comments
 (0)