Skip to content

Commit acd126f

Browse files
committed
Use react-virtualized CellMeasurer
1 parent ad138f4 commit acd126f

File tree

3 files changed

+91
-25
lines changed

3 files changed

+91
-25
lines changed

src/node-renderer-default.js

+6-4
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,11 @@ class NodeRendererDefault extends Component {
3434
className,
3535
style = {},
3636
didDrop,
37-
isOver: _isOver, // Not needed, but preserved for other renderers
38-
parentNode: _parentNode, // Needed for drag-and-drop utils
39-
endDrag: _endDrag, // Needed for drag-and-drop utils
40-
startDrag: _startDrag, // Needed for drag-and-drop utils
37+
isOver: _isOver, // Not needed, but preserved for other renderers
38+
parentNode: _parentNode, // Needed for drag-and-drop utils
39+
endDrag: _endDrag, // Needed for drag-and-drop utils
40+
startDrag: _startDrag, // Needed for drag-and-drop utils
41+
onHeightChange: _onHeightChange, // Not needed, but preserved for other renderers
4142
...otherProps,
4243
} = this.props;
4344

@@ -165,6 +166,7 @@ NodeRendererDefault.propTypes = {
165166
buttons: PropTypes.arrayOf(PropTypes.node),
166167
className: PropTypes.string,
167168
style: PropTypes.object,
169+
onHeightChange: PropTypes.func,
168170

169171
// Drag and drop API functions
170172
// Drag source

src/node-renderer-default.scss

-1
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,6 @@ $row-padding: 10px;
103103
.moveHandle {
104104
@extend %rowItem;
105105

106-
height: 100%;
107106
width: 44px;
108107
background: #D9D9D9 url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0MiIgaGVpZ2h0PSI0MiI+PGcgc3Ryb2tlPSIjRkZGIiBzdHJva2Utd2lkdGg9IjIuOSIgPjxwYXRoIGQ9Ik0xNCAxNS43aDE0LjQiLz48cGF0aCBkPSJNMTQgMjEuNGgxNC40Ii8+PHBhdGggZD0iTTE0IDI3LjFoMTQuNCIvPjwvZz4KPC9zdmc+') no-repeat center;
109108
border: solid #AAA 1px;

src/react-sortable-tree.js

+85-20
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44
* @license Open source under the MIT License
55
*/
66

7-
import React, { Component, PropTypes } from 'react';
8-
import { AutoSizer, List } from 'react-virtualized';
7+
import React, { Component, PropTypes, cloneElement } from 'react';
8+
import { AutoSizer, List, CellMeasurer, CellMeasurerCache } from 'react-virtualized';
99
import isEqual from 'lodash.isequal';
1010
import withScrolling, { createVerticalStrength, createHorizontalStrength } from 'react-dnd-scrollzone';
1111
import 'react-virtualized/styles.css';
@@ -39,6 +39,28 @@ import styles from './react-sortable-tree.scss';
3939

4040
let dndTypeCounter = 1;
4141

42+
const cellMeasurerCache = new CellMeasurerCache({
43+
fixedWidth: true
44+
});
45+
46+
const DynamicRow = ({ parent, rowIndex, children, ...extraProps }) => (
47+
<CellMeasurer
48+
cache={cellMeasurerCache}
49+
columnIndex={0}
50+
parent={parent}
51+
rowIndex={rowIndex}
52+
>
53+
{cloneElement(children, extraProps)}
54+
</CellMeasurer>
55+
);
56+
57+
DynamicRow.propTypes = {
58+
rowIndex: PropTypes.number,
59+
parent: PropTypes.any,
60+
children: PropTypes.node
61+
};
62+
63+
4264
class ReactSortableTree extends Component {
4365
constructor(props) {
4466
super(props);
@@ -78,6 +100,7 @@ class ReactSortableTree extends Component {
78100
this.startDrag = this.startDrag.bind(this);
79101
this.dragHover = this.dragHover.bind(this);
80102
this.endDrag = this.endDrag.bind(this);
103+
this.handleResize = this.handleResize.bind(this);
81104
}
82105

83106
componentWillMount() {
@@ -86,6 +109,10 @@ class ReactSortableTree extends Component {
86109
this.ignoreOneTreeUpdate = false;
87110
}
88111

112+
componentWillUnmount() {
113+
cellMeasurerCache.clearAll();
114+
}
115+
89116
toggleChildrenVisibility({ node: targetNode, path, treeIndex: _treeIndex }) {
90117
const treeData = changeNodeAtPath({
91118
treeData: this.props.treeData,
@@ -121,6 +148,8 @@ class ReactSortableTree extends Component {
121148

122149
this.props.onChange(treeData);
123150

151+
this.handleResize();
152+
124153
if (this.props.onMoveNode) {
125154
this.props.onMoveNode({ treeData, node, treeIndex, path });
126155
}
@@ -317,6 +346,15 @@ class ReactSortableTree extends Component {
317346
});
318347
}
319348

349+
handleResize() {
350+
const { isDynamicHeight } = this.props;
351+
352+
if (isDynamicHeight && this._list) {
353+
cellMeasurerCache.clearAll();
354+
this._list.wrappedInstance.recomputeRowHeights();
355+
}
356+
}
357+
320358
render() {
321359
const {
322360
style,
@@ -325,6 +363,7 @@ class ReactSortableTree extends Component {
325363
rowHeight,
326364
getNodeKey,
327365
isVirtualized,
366+
isDynamicHeight,
328367
} = this.props;
329368
const {
330369
rows,
@@ -347,9 +386,12 @@ class ReactSortableTree extends Component {
347386
const ScrollZoneVirtualList = this.scrollZoneVirtualList;
348387
// Render list with react-virtualized
349388
list = (
350-
<AutoSizer>
389+
<AutoSizer onResize={this.handleResize}>
351390
{({height, width}) => (
352391
<ScrollZoneVirtualList
392+
ref={(ref) => {
393+
this._list = ref;
394+
}}
353395
{...scrollToInfo}
354396
verticalStrength={this.vStrength}
355397
horizontalStrength={this.hStrength}
@@ -362,14 +404,16 @@ class ReactSortableTree extends Component {
362404
style={innerStyle}
363405
rowCount={rows.length}
364406
estimatedRowSize={typeof rowHeight !== 'function' ? rowHeight : undefined}
365-
rowHeight={rowHeight}
366-
rowRenderer={({ index, key, style: rowStyle }) => this.renderRow(
407+
deferredMeasurementCache={isDynamicHeight ? cellMeasurerCache : undefined}
408+
rowHeight={isDynamicHeight ? cellMeasurerCache.rowHeight : rowHeight}
409+
rowRenderer={({ index, key, style: rowStyle, parent }) => this.renderRow(
367410
rows[index],
368411
index,
369412
key,
370413
rowStyle,
371414
() => (rows[index - 1] || null),
372-
matchKeys
415+
matchKeys,
416+
parent
373417
)}
374418
{...this.props.reactVirtualizedListProps}
375419
/>
@@ -407,7 +451,8 @@ class ReactSortableTree extends Component {
407451
key,
408452
style,
409453
getPrevRow,
410-
matchKeys
454+
matchKeys,
455+
parent
411456
) {
412457
const TreeNodeRenderer = this.treeNodeRenderer;
413458
const NodeContentRenderer = this.nodeContentRenderer;
@@ -426,6 +471,34 @@ class ReactSortableTree extends Component {
426471
isSearchFocus,
427472
});
428473

474+
const content = (
475+
<NodeContentRenderer
476+
node={node}
477+
parentNode={parentNode}
478+
path={path}
479+
isSearchMatch={isSearchMatch}
480+
isSearchFocus={isSearchFocus}
481+
treeIndex={treeIndex}
482+
startDrag={this.startDrag}
483+
endDrag={this.endDrag}
484+
toggleChildrenVisibility={this.toggleChildrenVisibility}
485+
scaffoldBlockPxWidth={this.props.scaffoldBlockPxWidth}
486+
onHeightChange={() => cellMeasurerCache.clear(treeIndex, 0)}
487+
{...nodeProps}
488+
/>
489+
);
490+
491+
let row;
492+
if (this.props.isDynamicHeight) {
493+
row = (
494+
<DynamicRow parent={parent} rowIndex={listIndex}>
495+
{content}
496+
</DynamicRow>
497+
);
498+
} else {
499+
row = content;
500+
}
501+
429502
return (
430503
<TreeNodeRenderer
431504
style={style}
@@ -446,19 +519,7 @@ class ReactSortableTree extends Component {
446519
maxDepth={this.props.maxDepth}
447520
dragHover={this.dragHover}
448521
>
449-
<NodeContentRenderer
450-
node={node}
451-
parentNode={parentNode}
452-
path={path}
453-
isSearchMatch={isSearchMatch}
454-
isSearchFocus={isSearchFocus}
455-
treeIndex={treeIndex}
456-
startDrag={this.startDrag}
457-
endDrag={this.endDrag}
458-
toggleChildrenVisibility={this.toggleChildrenVisibility}
459-
scaffoldBlockPxWidth={this.props.scaffoldBlockPxWidth}
460-
{...nodeProps}
461-
/>
522+
{row}
462523
</TreeNodeRenderer>
463524
);
464525
}
@@ -525,6 +586,10 @@ ReactSortableTree.propTypes = {
525586
// NOTE: Auto-scrolling while dragging, and scrolling to the `searchFocusOffset` will be disabled.
526587
isVirtualized: PropTypes.bool,
527588

589+
// Set to false to disable CellMeasurer.
590+
// NOTE: rowHeight will be ignored.
591+
isDynamicHeight: PropTypes.bool,
592+
528593
// Override the default component for rendering nodes (but keep the scaffolding generator)
529594
// This is an advanced option for complete customization of the appearance.
530595
// It is best to copy the component in `node-renderer-default.js` to use as a base, and customize as needed.

0 commit comments

Comments
 (0)