UNPKG

8.61 kBMarkdownView Raw
1[![build status](https://secure.travis-ci.org/reactabular/treetabular.svg)](http://travis-ci.org/reactabular/treetabular) [![bitHound Score](https://www.bithound.io/github/reactabular/treetabular/badges/score.svg)](https://www.bithound.io/github/reactabular/treetabular) [![codecov](https://codecov.io/gh/reactabular/treetabular/branch/master/graph/badge.svg)](https://codecov.io/gh/reactabular/treetabular)
2
3# Treetabular - Tree utilities
4
5`treetabular` provides tree helpers for Reactabular. It relies on a flat structure like this:
6
7```javascript
8const tree = [
9 {
10 id: 123,
11 name: 'Demo'
12 },
13 {
14 id: 456,
15 name: 'Another',
16 parent: 123
17 },
18 {
19 id: 789,
20 name: 'Yet Another',
21 parent: 123
22 },
23 {
24 id: 532,
25 name: 'Foobar'
26 }
27];
28```
29
30If there's a `parent` relation, the children must follow their parent right after it.
31
32> You can find suggested default styling for the package at `style.css` in the package root.
33
34## API
35
36```javascript
37import * as tree from 'treetabular';
38
39// Or you can cherry-pick
40import { filter } from 'treetabular';
41import { filter as filterTree } from 'treetabular';
42```
43
44### Transformations
45
46**`tree.collapseAll = ({ property = 'showingChildren' }) => (rows) => [<collapsedRow>]`**
47
48Collapses rows by setting `showingChildren` of each row to `false`.
49
50**`tree.expandAll = ({ property = 'showingChildren' }) => (rows) => [<expandedRow>]`**
51
52Expands rows by setting `showingChildren` of each row to `true`.
53
54**`tree.filter = ({ fieldName, parentField = 'parent' }) => (rows) => [<filteredRow>]`**
55
56Filters the given rows using `fieldName`. This is handy if you want only rows that are visible assuming visibility logic has been defined.
57
58### Queries
59
60**`tree.getLevel = ({ index, parentField = 'parent' }) => (rows) => <level>`**
61
62Returns the nesting level of the row at the given `index` within `rows`.
63
64**`tree.getChildren = ({ index, idField = 'id', parentField = 'parent' }) => (rows) => [<child>]`**
65
66Returns children based on given `rows` and `index`. This includes children of children.
67
68**`tree.getImmediateChildren = ({ index, idField = 'id', parentField = 'parent' }) => (rows) => [<child>]`**
69
70Returns immediate children based on given `rows` and `index`.
71
72**`tree.getParents = ({ index, parentField = 'parent' }) => (rows) => [<parent>]`**
73
74Returns parents based on given `rows` and `index`.
75
76**`tree.hasChildren = ({ index, idField = 'id', parentField = 'parent '}) => (rows) => <boolean>`**
77
78Returns a boolean based on whether or not the row at the given `index` has children.
79
80**`tree.search = ({ columns, query, idField = 'id', parentField = 'parent' }) => (rows) => [<searchedRow>]`**
81
82Searches against a tree structure while matching against children too. If children are found, associated parents are returned as well.
83
84> This depends on [resolve.index](http://reactabular.js.org/#/data/resolving)!
85
86**`tree.sort = ({ columns, sortingColumns, strategy, idField = 'id' }) => (rows) => [<sortedRow>]`**
87
88Sorts a tree (packs/unpacks internally to maintain root level sorting).
89
90### Packing
91
92**`tree.pack = ({ parentField = 'parent', childrenField = 'children', idField = 'id' }) => (rows) => [<packedRow>]`**
93
94Packs children inside root level nodes. This is useful with sorting and filtering.
95
96**`tree.unpack = ({ parentField = 'parent', childrenField = 'children', idField = 'id', parent }) => (rows) => [<unpackedRow>]`**
97
98Unpacks children from root level nodes. This is useful with sorting and filtering.
99
100### Drag and Drop
101
102**`tree.moveRows = ({ sourceRowId, targetRowId, retain = [], idField = 'id', parentField = 'parent' }) => (rows) => [<movedRow>]`**
103
104Allows moving tree rows while `retain`ing given fields at their original rows.
105
106### UI
107
108**`tree.toggleChildren = ({ getRows, getShowingChildren, toggleShowingChildren, props, idField, parentField }) => (value, extra) => <React element>`**
109
110Makes it possible to toggle node children through a user interface.
111
112> This depends on [resolve.index](https://www.npmjs.com/package/table-resolver#resolveindex)!
113
114## Example
115
116```jsx
117/*
118import React from 'react';
119import cloneDeep from 'lodash/cloneDeep';
120import { compose } from 'redux';
121import { Table, resolve } from 'reactabular';
122import * as tree from 'treetabular';
123import VisibilityToggles from 'reactabular-visibility-toggles';
124import * as search from 'searchtabular';
125import * as sort from 'sortabular';
126
127import {
128 generateParents, generateRows
129} from './helpers';
130*/
131
132const schema = {
133 type: 'object',
134 properties: {
135 id: {
136 type: 'string'
137 },
138 name: {
139 type: 'string'
140 },
141 age: {
142 type: 'integer'
143 }
144 },
145 required: ['id', 'name', 'age']
146};
147
148class TreeTable extends React.Component {
149 constructor(props) {
150 super(props);
151
152 const columns = this.getColumns();
153 const rows = resolve.resolve(
154 {
155 columns,
156 method: resolve.index
157 }
158 )(
159 generateParents(generateRows(100, schema))
160 );
161
162 this.state = {
163 searchColumn: 'all',
164 query: {},
165 sortingColumns: null,
166 rows,
167 columns
168 };
169
170 this.onExpandAll = this.onExpandAll.bind(this);
171 this.onCollapseAll = this.onCollapseAll.bind(this);
172 this.onToggleColumn = this.onToggleColumn.bind(this);
173 }
174 getColumns() {
175 const sortable = sort.sort({
176 // Point the transform to your rows. React state can work for this purpose
177 // but you can use a state manager as well.
178 getSortingColumns: () => this.state.sortingColumns || {},
179
180 // The user requested sorting, adjust the sorting state accordingly.
181 // This is a good chance to pass the request through a sorter.
182 onSort: selectedColumn => {
183 const sortingColumns = sort.byColumns({
184 sortingColumns: this.state.sortingColumns,
185 selectedColumn
186 });
187
188 this.setState({ sortingColumns });
189 }
190 });
191
192 return [
193 {
194 property: 'name',
195 props: {
196 style: { width: 200 }
197 },
198 header: {
199 label: 'Name',
200 transforms: [sortable]
201 },
202 cell: {
203 formatters: [
204 tree.toggleChildren({
205 getRows: () => this.state.rows,
206 getShowingChildren: ({ rowData }) => rowData.showingChildren,
207 toggleShowingChildren: rowIndex => {
208 const rows = cloneDeep(this.state.rows);
209
210 rows[rowIndex].showingChildren = !rows[rowIndex].showingChildren;
211
212 this.setState({ rows });
213 },
214 // Inject custom class name per row here etc.
215 props: {}
216 })
217 ]
218 },
219 visible: true
220 },
221 {
222 property: 'age',
223 props: {
224 style: { width: 300 }
225 },
226 header: {
227 label: 'Age',
228 transforms: [sortable]
229 },
230 visible: true
231 }
232 ];
233 }
234 render() {
235 const {
236 searchColumn, columns, sortingColumns, query
237 } = this.state;
238 const visibleColumns = columns.filter(column => column.visible);
239 const rows = compose(
240 tree.filter({ fieldName: 'showingChildren' }),
241 tree.sort({
242 columns,
243 sortingColumns
244 }),
245 tree.search({ columns, query })
246 )(this.state.rows);
247
248 return (
249 <div>
250 <VisibilityToggles
251 columns={columns}
252 onToggleColumn={this.onToggleColumn}
253 />
254
255 <button onClick={this.onExpandAll}>Expand all</button>
256 <button onClick={this.onCollapseAll}>Collapse all</button>
257
258 <div className="search-container">
259 <span>Search</span>
260 <search.Field
261 column={searchColumn}
262 query={query}
263 columns={visibleColumns}
264 rows={rows}
265 onColumnChange={searchColumn => this.setState({ searchColumn })}
266 onChange={query => this.setState({ query })}
267 />
268 </div>
269
270 <Table.Provider
271 className="pure-table pure-table-striped"
272 columns={visibleColumns}
273 >
274 <Table.Header />
275
276 <Table.Body rows={rows} rowKey="id" />
277 </Table.Provider>
278 </div>
279 );
280 }
281 onExpandAll() {
282 this.setState({
283 rows: tree.expandAll()(this.state.rows)
284 });
285 }
286 onCollapseAll() {
287 this.setState({
288 rows: tree.collapseAll()(this.state.rows)
289 });
290 }
291 onToggleColumn(columnIndex) {
292 const columns = cloneDeep(this.state.columns);
293
294 columns[columnIndex].visible = !columns[columnIndex].visible;
295
296 this.setState({ columns });
297 }
298}
299
300<TreeTable />
301```
302
303## License
304
305MIT. See LICENSE for details.