From ccef3ccf6b943db22c9a0a5f3ba9e2ce95006d9d Mon Sep 17 00:00:00 2001 From: fritz-c Date: Wed, 22 Mar 2017 14:58:34 +0900 Subject: [PATCH] Make components stateful for compatibility with react-dnd. Fixes #65 --- src/node-renderer-default.js | 244 ++++++++++++++++++----------------- src/tree-node.js | 239 ++++++++++++++++++---------------- 2 files changed, 249 insertions(+), 234 deletions(-) diff --git a/src/node-renderer-default.js b/src/node-renderer-default.js index 987c1797..608f4bfd 100644 --- a/src/node-renderer-default.js +++ b/src/node-renderer-default.js @@ -1,4 +1,4 @@ -import React, { PropTypes } from 'react'; +import React, { Component, PropTypes } from 'react'; import { getIEVersion } from './utils/browser-utils'; import baseStyles from './node-renderer-default.scss'; import { isDescendant } from './utils/tree-data-utils'; @@ -15,139 +15,143 @@ if (getIEVersion < 10) { }; } -const NodeRendererDefault = ({ - scaffoldBlockPxWidth, - toggleChildrenVisibility, - connectDragPreview, - connectDragSource, - isDragging, - canDrop, - node, - draggedNode, - path, - treeIndex, - isSearchMatch, - isSearchFocus, - buttons, - className, - style = {}, - didDrop, - isOver: _isOver, // Not needed, but preserved for other renderers - parentNode: _parentNode, // Needed for drag-and-drop utils - endDrag: _endDrag, // Needed for drag-and-drop utils - startDrag: _startDrag, // Needed for drag-and-drop utils - ...otherProps, -}) => { - let handle; - if (typeof node.children === 'function' && node.expanded) { - // Show a loading symbol on the handle when the children are expanded - // and yet still defined by a function (a callback to fetch the children) - handle = ( -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- ); - } else { - // Show the handle used to initiate a drag-and-drop - handle = connectDragSource(( -
- ), { dropEffect: 'copy' }); - } +class NodeRendererDefault extends Component { + render() { + const { + scaffoldBlockPxWidth, + toggleChildrenVisibility, + connectDragPreview, + connectDragSource, + isDragging, + canDrop, + node, + draggedNode, + path, + treeIndex, + isSearchMatch, + isSearchFocus, + buttons, + className, + style = {}, + didDrop, + isOver: _isOver, // Not needed, but preserved for other renderers + parentNode: _parentNode, // Needed for drag-and-drop utils + endDrag: _endDrag, // Needed for drag-and-drop utils + startDrag: _startDrag, // Needed for drag-and-drop utils + ...otherProps, + } = this.props; - const isDraggedDescendant = draggedNode && isDescendant(draggedNode, node); - const isLandingPadActive = !didDrop && isDragging; + let handle; + if (typeof node.children === 'function' && node.expanded) { + // Show a loading symbol on the handle when the children are expanded + // and yet still defined by a function (a callback to fetch the children) + handle = ( +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ); + } else { + // Show the handle used to initiate a drag-and-drop + handle = connectDragSource(( +
+ ), { dropEffect: 'copy' }); + } - return ( -
- {toggleChildrenVisibility && node.children && node.children.length > 0 && ( -
-
- )} -
- {/* Set the row preview to be used during drag and drop */} - {connectDragPreview( -
} - style={{ - opacity: isDraggedDescendant ? 0.5 : 1, - ...style, - }} - > - {handle} +
+ )} -
-
- - {typeof node.title === 'function' ? - node.title({node, path, treeIndex }) : - node.title - } - +
+ {/* Set the row preview to be used during drag and drop */} + {connectDragPreview( +
+ {handle} - {node.subtitle && - - {typeof node.subtitle === 'function' ? - node.subtitle({node, path, treeIndex }) : - node.subtitle +
+
+ + {typeof node.title === 'function' ? + node.title({node, path, treeIndex }) : + node.title } - } -
-
- {buttons && buttons.map((btn, index) => ( -
- {btn} -
- ))} + {node.subtitle && + + {typeof node.subtitle === 'function' ? + node.subtitle({node, path, treeIndex }) : + node.subtitle + } + + } +
+ +
+ {buttons && buttons.map((btn, index) => ( +
+ {btn} +
+ ))} +
-
- )} + )} +
-
- ); -}; + ); + } +} NodeRendererDefault.propTypes = { node: PropTypes.object.isRequired, diff --git a/src/tree-node.js b/src/tree-node.js index e54de4fb..97d99bad 100644 --- a/src/tree-node.js +++ b/src/tree-node.js @@ -1,138 +1,142 @@ -import React, { PropTypes, Children, cloneElement } from 'react'; +import React, { Component, PropTypes, Children, cloneElement } from 'react'; import styles from './tree-node.scss'; -const TreeNode = ({ - children, - listIndex, - swapFrom, - swapLength, - swapDepth, - scaffoldBlockPxWidth, - lowerSiblingCounts, - connectDropTarget, - isOver, - draggedNode, - canDrop, - treeIndex, - customCanDrop: _customCanDrop, // Delete from otherProps - dragHover: _dragHover, // Delete from otherProps - getNodeKey: _getNodeKey, // Delete from otherProps - getPrevRow: _getPrevRow, // Delete from otherProps - maxDepth: _maxDepth, // Delete from otherProps - node: _node, // Delete from otherProps - path: _path, // Delete from otherProps - treeData: _treeData, // Delete from otherProps - ...otherProps, -}) => { - // Construct the scaffold representing the structure of the tree - const scaffoldBlockCount = lowerSiblingCounts.length; - const scaffold = []; - lowerSiblingCounts.forEach((lowerSiblingCount, i) => { - let lineClass = ''; - if (lowerSiblingCount > 0) { - // At this level in the tree, the nodes had sibling nodes further down +class TreeNode extends Component { + render() { + const { + children, + listIndex, + swapFrom, + swapLength, + swapDepth, + scaffoldBlockPxWidth, + lowerSiblingCounts, + connectDropTarget, + isOver, + draggedNode, + canDrop, + treeIndex, + customCanDrop: _customCanDrop, // Delete from otherProps + dragHover: _dragHover, // Delete from otherProps + getNodeKey: _getNodeKey, // Delete from otherProps + getPrevRow: _getPrevRow, // Delete from otherProps + maxDepth: _maxDepth, // Delete from otherProps + node: _node, // Delete from otherProps + path: _path, // Delete from otherProps + treeData: _treeData, // Delete from otherProps + ...otherProps, + } = this.props; - if (listIndex === 0) { - // Top-left corner of the tree + // Construct the scaffold representing the structure of the tree + const scaffoldBlockCount = lowerSiblingCounts.length; + const scaffold = []; + lowerSiblingCounts.forEach((lowerSiblingCount, i) => { + let lineClass = ''; + if (lowerSiblingCount > 0) { + // At this level in the tree, the nodes had sibling nodes further down + + if (listIndex === 0) { + // Top-left corner of the tree + // +-----+ + // | | + // | +--+ + // | | | + // +--+--+ + lineClass = `${styles.lineHalfHorizontalRight} ${styles.lineHalfVerticalBottom}`; + } else if (i === scaffoldBlockCount - 1) { + // Last scaffold block in the row, right before the row content + // +--+--+ + // | | | + // | +--+ + // | | | + // +--+--+ + lineClass = `${styles.lineHalfHorizontalRight} ${styles.lineFullVertical}`; + } else { + // Simply connecting the line extending down to the next sibling on this level + // +--+--+ + // | | | + // | | | + // | | | + // +--+--+ + lineClass = styles.lineFullVertical; + } + } else if (listIndex === 0) { + // Top-left corner of the tree, but has no siblings // +-----+ // | | // | +--+ - // | | | - // +--+--+ - lineClass = `${styles.lineHalfHorizontalRight} ${styles.lineHalfVerticalBottom}`; + // | | + // +-----+ + lineClass = styles.lineHalfHorizontalRight; } else if (i === scaffoldBlockCount - 1) { - // Last scaffold block in the row, right before the row content + // The last or only node in this level of the tree // +--+--+ // | | | // | +--+ - // | | | - // +--+--+ - lineClass = `${styles.lineHalfHorizontalRight} ${styles.lineFullVertical}`; - } else { - // Simply connecting the line extending down to the next sibling on this level - // +--+--+ - // | | | - // | | | - // | | | - // +--+--+ - lineClass = styles.lineFullVertical; - } - } else if (listIndex === 0) { - // Top-left corner of the tree, but has no siblings - // +-----+ - // | | - // | +--+ - // | | - // +-----+ - lineClass = styles.lineHalfHorizontalRight; - } else if (i === scaffoldBlockCount - 1) { - // The last or only node in this level of the tree - // +--+--+ - // | | | - // | +--+ - // | | - // +-----+ - lineClass = `${styles.lineHalfVerticalTop} ${styles.lineHalfHorizontalRight}`; - } - - scaffold.push( -
- ); - - if (treeIndex !== listIndex && i === swapDepth) { - // This row has been shifted, and is at the depth of - // the line pointing to the new destination - let highlightLineClass = ''; - - if (listIndex === swapFrom + swapLength - 1) { - // This block is on the bottom (target) line - // This block points at the target block (where the row will go when released) - highlightLineClass = styles.highlightBottomLeftCorner; - } else if (treeIndex === swapFrom) { - // This block is on the top (source) line - highlightLineClass = styles.highlightTopLeftCorner; - } else { - // This block is between the bottom and top - highlightLineClass = styles.highlightLineVertical; + // | | + // +-----+ + lineClass = `${styles.lineHalfVerticalTop} ${styles.lineHalfHorizontalRight}`; } scaffold.push(
); - } - }); - return connectDropTarget( -
- {scaffold} + if (treeIndex !== listIndex && i === swapDepth) { + // This row has been shifted, and is at the depth of + // the line pointing to the new destination + let highlightLineClass = ''; + + if (listIndex === swapFrom + swapLength - 1) { + // This block is on the bottom (target) line + // This block points at the target block (where the row will go when released) + highlightLineClass = styles.highlightBottomLeftCorner; + } else if (treeIndex === swapFrom) { + // This block is on the top (source) line + highlightLineClass = styles.highlightTopLeftCorner; + } else { + // This block is between the bottom and top + highlightLineClass = styles.highlightLineVertical; + } + scaffold.push( +
+ ); + } + }); + + return connectDropTarget(
- {Children.map(children, child => cloneElement(child, { - isOver, - canDrop, - draggedNode, - }))} + {scaffold} + +
+ {Children.map(children, child => cloneElement(child, { + isOver, + canDrop, + draggedNode, + }))} +
-
- ); -}; + ); + } +} TreeNode.propTypes = { treeIndex: PropTypes.number.isRequired, @@ -152,6 +156,13 @@ TreeNode.propTypes = { isOver: PropTypes.bool.isRequired, canDrop: PropTypes.bool, draggedNode: PropTypes.object, + + customCanDrop: PropTypes.func, // used in drag-and-drop-utils + dragHover: PropTypes.func.isRequired, // used in drag-and-drop-utils + getNodeKey: PropTypes.func, // used in drag-and-drop-utils + getPrevRow: PropTypes.func, // used in drag-and-drop-utils + maxDepth: PropTypes.number, // used in drag-and-drop-utils + treeData: PropTypes.arrayOf(PropTypes.object), // used in drag-and-drop-utils }; export default TreeNode;