Skip to content

Commit 0269218

Browse files
committed
feat: new tree list
1 parent 67fc179 commit 0269218

File tree

84 files changed

+1178
-347
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

84 files changed

+1178
-347
lines changed

.yalc/@stoplight/tree-list/LICENSE

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Not Licensed
Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
# @stoplight/tree-list
2+
3+
[![@stoplight/tree-list](https://img.shields.io/npm/v/@stoplight/tree-list.svg)](https://www.npmjs.com/package/@stoplight/tree-list) [![Maintainability](https://api.codeclimate.com/v1/badges/5873a5e9df9590071bc9/maintainability)](https://codeclimate.com/repos/5c0607a04434ef5e6a0005d4/maintainability) [![Test Coverage](https://api.codeclimate.com/v1/badges/5873a5e9df9590071bc9/test_coverage)](https://codeclimate.com/repos/5c0607a04434ef5e6a0005d4/test_coverage)
4+
5+
<!-- SUMMARY -->
6+
7+
- Explore the components: [Storybook](https://stoplightio.github.io/tree-list)
8+
- View the changelog: [Releases](https://github.com/stoplightio/tree-list/releases)
9+
- [Features](#features)
10+
- [Installation](#installation)
11+
- [Usage](#usage)
12+
- [TreeList Props](#treelist-props)
13+
- [TreeList Store](#treelist-store)
14+
- [Contributing](#contributing)
15+
16+
### Features
17+
18+
- Component for rendering a flat list of nodes in a tree like interface.
19+
- Drag and drop support
20+
- Context menu support
21+
22+
### Installation
23+
24+
Supported in modern browsers.
25+
26+
```bash
27+
# latest stable
28+
yarn add @stoplight/tree-list
29+
```
30+
31+
### Usage
32+
33+
```jsx
34+
import { TreeList, TreeStore } from "@stoplight/tree-list";
35+
36+
const nodes = [
37+
{
38+
id: '1',
39+
level: 0,
40+
name: 'Folder',
41+
canHaveChildren: true,
42+
},
43+
{
44+
id: '2',
45+
level: 1,
46+
name: 'Nested Folder',
47+
canHaveChildren: true,
48+
},
49+
{
50+
id: '3',
51+
level: 2,
52+
name: 'Nested File',
53+
},
54+
{
55+
id: '4',
56+
name: 'File',
57+
level: 0,
58+
}
59+
];
60+
61+
const store = new TreeStore({ nodes );
62+
63+
<TreeList
64+
store={store}
65+
onNodeClick={(e, node) => {
66+
store.toggleExpanded(node);
67+
}}
68+
/>
69+
```
70+
71+
### TreeList Props
72+
73+
`nodeRenderer` is called every time when TreeList renders a row. It has two arguments: current node and current state. It should return a React component.
74+
75+
`onNodeClick`, `onNodeDoubleClick`, `onNodeToggle` - are called when an according event occur. They have two arguments: current node and an event object.
76+
77+
`generateContextMenu` is called when a TreeNode is right clicked. It is given the tree node and the tree API and expects to return an array of ContextMenu items.
78+
79+
`canDrag` is called to determine if a given node can be dragged.
80+
81+
`canDrop` is called to determine if a given node can be dropped on a different parent node during the drag process.
82+
83+
`handleDrop` is called when a given node is dropped onto a new parent node.
84+
85+
### TreeList Store
86+
87+
The tree list store is exported as `TreeStore` in the main entry.
88+
89+
`nodes` is an array of INode:
90+
91+
```ts
92+
interface INode {
93+
id: string;
94+
level: number;
95+
metadata?: object;
96+
canHaveChildren?: boolean;
97+
98+
name?: string;
99+
icon?: IIcon['icon'];
100+
}
101+
```
102+
103+
`create` adds an editable input to the tree list and returns a promise that resolves once the user hits the `<enter>` key. The promise is rejected if the user clicks out of the input (onBlur) or if the user hits the `<esc>` key.
104+
It also accepts a custom validator which is called on input when a tree node upon creation. It should return an error to be shown or `null` if no error.
105+
106+
`rename` turns a given tree node into an editable input and returns a promise that resolves once the user hits the `<enter>` key. The promise is rejected if the user clicks out of the input (onBlur) or if the user hits the `<esc>` key.
107+
It also accepts a custom validator which is called on input when a tree node upon renaming. It should return an error to be shown or `null` if no error.
108+
109+
`cancel` is used to reset any editable inputs back to tree nodes.
110+
111+
`toggleExpand` can be used to manipulate expanded state of given node.
112+
113+
**Here's some example usage:**
114+
115+
```tsx
116+
import { TreeList, TreeStore } from "@stoplight/tree-list";
117+
118+
const store = new TreeStore();
119+
const customStore = new CustomStore(); // An optional custom store that contains methods to update tree store
120+
121+
function Tree() {
122+
// Create a reference to the tree list API
123+
return (
124+
<div>
125+
<button
126+
onClick={() => store.create()}
127+
>
128+
Create
129+
</button>
130+
131+
<button
132+
onClick={() => store.cancel()}
133+
>
134+
Cancel
135+
</button>
136+
137+
<TreeList
138+
store={store}
139+
generateContextMenu={getContextMenu}
140+
/>
141+
</div>
142+
);
143+
}
144+
145+
function getContextMenu(node: TreeListNode) {
146+
const menuItems = [
147+
{
148+
title: "Rename",
149+
onClick() {
150+
store.rename(node).then((newNode) => {
151+
customStore.renameNode(node.id, node.name);
152+
});
153+
},
154+
},
155+
{
156+
title: "Delete",
157+
onClick() {
158+
customStore.deleteNode(node.id);
159+
},
160+
}
161+
];
162+
163+
if (node.canHaveChildren) {
164+
menuItems.unshift({
165+
title: 'Create',
166+
onClick() {
167+
store.create(node).then((newNode, parentNode) => {
168+
customStore.addNode(newNode, parentNode);
169+
});
170+
}
171+
});
172+
}
173+
174+
return menuItems;
175+
}
176+
```
177+
178+
### Contributing
179+
180+
1. Clone repo.
181+
2. Create / checkout `feature/{name}`, `chore/{name}`, or `fix/{name}` branch.
182+
3. Install deps: `yarn`.
183+
4. Make your changes.
184+
5. Run tests: `yarn test.prod`.
185+
6. Stage relevant files to git.
186+
7. Commit: `yarn commit`. _NOTE: Commits that don't follow the [conventional](https://github.com/marionebl/commitlint/tree/master/%40commitlint/config-conventional) format will be rejected. `yarn commit` creates this format for you, or you can put it together manually and then do a regular `git commit`._
187+
8. Push: `git push`.
188+
9. Open PR targeting the `master` branch.

.yalc/@stoplight/tree-list/index.cjs.js

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.yalc/@stoplight/tree-list/index.es.js

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
{
2+
"name": "@stoplight/tree-list",
3+
"version": "0.0.1-2d0c8d7d",
4+
"description": "A React component that renders a file tree like interface.",
5+
"keywords": [],
6+
"sideEffects": false,
7+
"files": [
8+
"**/*"
9+
],
10+
"author": "Stoplight <support@stoplight.io>",
11+
"repository": {
12+
"type": "git",
13+
"url": "https://github.com/stoplightio/tree-list"
14+
},
15+
"license": "UNLICENSED",
16+
"engines": {
17+
"node": ">=10.0"
18+
},
19+
"peerDependencies": {
20+
"@stoplight/ui-kit": ">=2",
21+
"mobx": "5.x.x",
22+
"react": ">=16.8",
23+
"react-dom": ">=16.8"
24+
},
25+
"dependencies": {
26+
"@stoplight/lifecycle": "^2.1.3",
27+
"@stoplight/react-error-boundary": "^1.0.0",
28+
"classnames": "^2.2.6",
29+
"mobx-react-lite": "^1.5.0",
30+
"strict-event-emitter-types": "^2.0.0"
31+
},
32+
"main": "index.cjs.js",
33+
"module": "index.es.js",
34+
"typings": "src/index.d.ts"
35+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
export declare const smallList: () => ({
2+
id: string;
3+
level: number;
4+
name: string;
5+
type: string;
6+
canHaveChildren?: undefined;
7+
} | {
8+
id: string;
9+
level: number;
10+
name: string;
11+
type: string;
12+
canHaveChildren: boolean;
13+
})[];
14+
export declare const largeList: () => any[];
15+
export declare const stressList: () => {
16+
id: string;
17+
name: string;
18+
level: number;
19+
type: string;
20+
}[];
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export {};
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import { Tree } from 'src/tree/tree';
2+
export declare const smallTree: Tree;
3+
export declare const largeTree: Tree;
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import '../styles/_storybook.scss';
2+
import '../styles/_tree-list.scss';
3+
import './TreeList';
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import { TreeStore } from '../store';
2+
import { ITreeListContextMenuItem, TreeListNode } from '../types';
3+
export declare const generateContextMenu: (store: TreeStore) => (node: TreeListNode) => ITreeListContextMenuItem[];

0 commit comments

Comments
 (0)