react-sortable-tree-node
Version:
react-sortable-tree-node
926 lines (823 loc) • 40.4 kB
JavaScript
"use strict";
function _typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = exports.SortableTreeWithoutDndContext = void 0;
var _react = _interopRequireWildcard(require("react"));
var _propTypes = _interopRequireDefault(require("prop-types"));
var _reactVirtualized = require("react-virtualized");
var _lodash = _interopRequireDefault(require("lodash.isequal"));
var _reactDndScrollzone = _interopRequireWildcard(require("react-dnd-scrollzone"));
var _reactLifecyclesCompat = require("react-lifecycles-compat");
require("react-virtualized/styles.css");
var _treeNode = _interopRequireDefault(require("./tree-node"));
var _nodeRendererDefault = _interopRequireDefault(require("./node-renderer-default"));
var _treePlaceholder = _interopRequireDefault(require("./tree-placeholder"));
var _placeholderRendererDefault = _interopRequireDefault(require("./placeholder-renderer-default"));
var _treeDataUtils = require("./utils/tree-data-utils");
var _memoizedTreeDataUtils = require("./utils/memoized-tree-data-utils");
var _genericUtils = require("./utils/generic-utils");
var _defaultHandlers = require("./utils/default-handlers");
var _dndManager = _interopRequireDefault(require("./utils/dnd-manager"));
var _classnames = _interopRequireDefault(require("./utils/classnames"));
require("./react-sortable-tree.css");
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || _typeof(obj) !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); }
function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }
function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; }
function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } else if (call !== void 0) { throw new TypeError("Derived constructors may only return object or undefined"); } return _assertThisInitialized(self); }
function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; }
function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } }
function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) { symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); } keys.push.apply(keys, symbols); } return keys; }
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
var treeIdCounter = 1;
var mergeTheme = function mergeTheme(props) {
var merged = _objectSpread(_objectSpread({}, props), {}, {
style: _objectSpread(_objectSpread({}, props.theme.style), props.style),
innerStyle: _objectSpread(_objectSpread({}, props.theme.innerStyle), props.innerStyle),
reactVirtualizedListProps: _objectSpread(_objectSpread({}, props.theme.reactVirtualizedListProps), props.reactVirtualizedListProps)
});
var overridableDefaults = {
nodeContentRenderer: _nodeRendererDefault.default,
placeholderRenderer: _placeholderRendererDefault.default,
rowHeight: 62,
scaffoldBlockPxWidth: 44,
slideRegionSize: 100,
treeNodeRenderer: _treeNode.default
};
Object.keys(overridableDefaults).forEach(function (propKey) {
// If prop has been specified, do not change it
// If prop is specified in theme, use the theme setting
// If all else fails, fall back to the default
if (props[propKey] === null) {
merged[propKey] = typeof props.theme[propKey] !== 'undefined' ? props.theme[propKey] : overridableDefaults[propKey];
}
});
return merged;
};
var ReactSortableTree = /*#__PURE__*/function (_Component) {
_inherits(ReactSortableTree, _Component);
var _super = _createSuper(ReactSortableTree);
function ReactSortableTree(props) {
var _this;
_classCallCheck(this, ReactSortableTree);
_this = _super.call(this, props);
var _mergeTheme = mergeTheme(props),
dndType = _mergeTheme.dndType,
nodeContentRenderer = _mergeTheme.nodeContentRenderer,
treeNodeRenderer = _mergeTheme.treeNodeRenderer,
isVirtualized = _mergeTheme.isVirtualized,
slideRegionSize = _mergeTheme.slideRegionSize;
_this.dndManager = new _dndManager.default(_assertThisInitialized(_this)); // Wrapping classes for use with react-dnd
_this.treeId = "rst__".concat(treeIdCounter);
treeIdCounter += 1;
_this.dndType = dndType || _this.treeId;
_this.nodeContentRenderer = _this.dndManager.wrapSource(nodeContentRenderer);
_this.treePlaceholderRenderer = _this.dndManager.wrapPlaceholder(_treePlaceholder.default);
_this.treeNodeRenderer = _this.dndManager.wrapTarget(treeNodeRenderer); // Prepare scroll-on-drag options for this list
if (isVirtualized) {
_this.scrollZoneVirtualList = (0, _reactDndScrollzone.default)(_reactVirtualized.List);
_this.vStrength = (0, _reactDndScrollzone.createVerticalStrength)(slideRegionSize);
_this.hStrength = (0, _reactDndScrollzone.createHorizontalStrength)(slideRegionSize);
}
_this.state = {
draggingTreeData: null,
draggedNode: null,
draggedMinimumTreeIndex: null,
draggedDepth: null,
searchMatches: [],
searchFocusTreeIndex: null,
dragging: false,
// props that need to be used in gDSFP or static functions will be stored here
instanceProps: {
treeData: [],
ignoreOneTreeUpdate: false,
searchQuery: null,
searchFocusOffset: null
}
};
_this.toggleChildrenVisibility = _this.toggleChildrenVisibility.bind(_assertThisInitialized(_this));
_this.moveNode = _this.moveNode.bind(_assertThisInitialized(_this));
_this.startDrag = _this.startDrag.bind(_assertThisInitialized(_this));
_this.dragHover = _this.dragHover.bind(_assertThisInitialized(_this));
_this.endDrag = _this.endDrag.bind(_assertThisInitialized(_this));
_this.drop = _this.drop.bind(_assertThisInitialized(_this));
_this.handleDndMonitorChange = _this.handleDndMonitorChange.bind(_assertThisInitialized(_this));
return _this;
}
_createClass(ReactSortableTree, [{
key: "componentDidMount",
value: function componentDidMount() {
ReactSortableTree.loadLazyChildren(this.props, this.state);
var stateUpdate = ReactSortableTree.search(this.props, this.state, true, true, false);
this.setState(stateUpdate); // Hook into react-dnd state changes to detect when the drag ends
// TODO: This is very brittle, so it needs to be replaced if react-dnd
// offers a more official way to detect when a drag ends
this.clearMonitorSubscription = this.context.dragDropManager.getMonitor().subscribeToStateChange(this.handleDndMonitorChange);
}
}, {
key: "componentDidUpdate",
value: // listen to dragging
function componentDidUpdate(prevProps, prevState) {
// if it is not the same then call the onDragStateChanged
if (this.state.dragging !== prevState.dragging) {
if (this.props.onDragStateChanged) {
this.props.onDragStateChanged({
isDragging: this.state.dragging,
draggedNode: this.state.draggedNode
});
}
}
}
}, {
key: "componentWillUnmount",
value: function componentWillUnmount() {
this.clearMonitorSubscription();
}
}, {
key: "getRows",
value: function getRows(treeData) {
return (0, _memoizedTreeDataUtils.memoizedGetFlatDataFromTree)({
ignoreCollapsed: true,
getNodeKey: this.props.getNodeKey,
treeData: treeData
});
}
}, {
key: "handleDndMonitorChange",
value: function handleDndMonitorChange() {
var monitor = this.context.dragDropManager.getMonitor(); // If the drag ends and the tree is still in a mid-drag state,
// it means that the drag was canceled or the dragSource dropped
// elsewhere, and we should reset the state of this tree
if (!monitor.isDragging() && this.state.draggingTreeData) {
this.endDrag();
}
}
}, {
key: "toggleChildrenVisibility",
value: function toggleChildrenVisibility(_ref) {
var targetNode = _ref.node,
path = _ref.path;
var instanceProps = this.state.instanceProps;
var treeData = (0, _treeDataUtils.changeNodeAtPath)({
treeData: instanceProps.treeData,
path: path,
newNode: function newNode(_ref2) {
var node = _ref2.node;
return _objectSpread(_objectSpread({}, node), {}, {
expanded: !node.expanded
});
},
getNodeKey: this.props.getNodeKey
});
this.props.onChange(treeData);
this.props.onVisibilityToggle({
treeData: treeData,
node: targetNode,
expanded: !targetNode.expanded,
path: path
});
}
}, {
key: "moveNode",
value: function moveNode(_ref3) {
var node = _ref3.node,
prevPath = _ref3.path,
prevTreeIndex = _ref3.treeIndex,
depth = _ref3.depth,
minimumTreeIndex = _ref3.minimumTreeIndex;
var _insertNode = (0, _treeDataUtils.insertNode)({
treeData: this.state.draggingTreeData,
newNode: node,
depth: depth,
minimumTreeIndex: minimumTreeIndex,
expandParent: true,
getNodeKey: this.props.getNodeKey
}),
treeData = _insertNode.treeData,
treeIndex = _insertNode.treeIndex,
path = _insertNode.path,
nextParentNode = _insertNode.parentNode;
this.props.onChange(treeData);
this.props.onMoveNode({
treeData: treeData,
node: node,
treeIndex: treeIndex,
path: path,
nextPath: path,
nextTreeIndex: treeIndex,
prevPath: prevPath,
prevTreeIndex: prevTreeIndex,
nextParentNode: nextParentNode
});
} // returns the new state after search
}, {
key: "startDrag",
value: function startDrag(_ref4) {
var _this2 = this;
var path = _ref4.path;
this.setState(function (prevState) {
var _removeNode = (0, _treeDataUtils.removeNode)({
treeData: prevState.instanceProps.treeData,
path: path,
getNodeKey: _this2.props.getNodeKey
}),
draggingTreeData = _removeNode.treeData,
draggedNode = _removeNode.node,
draggedMinimumTreeIndex = _removeNode.treeIndex;
return {
draggingTreeData: draggingTreeData,
draggedNode: draggedNode,
draggedDepth: path.length - 1,
draggedMinimumTreeIndex: draggedMinimumTreeIndex,
dragging: true
};
});
}
}, {
key: "dragHover",
value: function dragHover(_ref5) {
var draggedNode = _ref5.node,
draggedDepth = _ref5.depth,
draggedMinimumTreeIndex = _ref5.minimumTreeIndex;
var instanceProps = this.state.instanceProps; // Ignore this hover if it is at the same position as the last hover
if (this.state.draggedDepth === draggedDepth && this.state.draggedMinimumTreeIndex === draggedMinimumTreeIndex) {
return;
} // Fall back to the tree data if something is being dragged in from
// an external element
var draggingTreeData = this.state.draggingTreeData || instanceProps.treeData;
var addedResult = (0, _memoizedTreeDataUtils.memoizedInsertNode)({
treeData: draggingTreeData,
newNode: draggedNode,
depth: draggedDepth,
minimumTreeIndex: draggedMinimumTreeIndex,
expandParent: true,
getNodeKey: this.props.getNodeKey
});
var rows = this.getRows(addedResult.treeData);
var expandedParentPath = rows[addedResult.treeIndex].path;
this.setState({
draggedNode: draggedNode,
draggedDepth: draggedDepth,
draggedMinimumTreeIndex: draggedMinimumTreeIndex,
draggingTreeData: (0, _treeDataUtils.changeNodeAtPath)({
treeData: draggingTreeData,
path: expandedParentPath.slice(0, -1),
newNode: function newNode(_ref6) {
var node = _ref6.node;
return _objectSpread(_objectSpread({}, node), {}, {
expanded: true
});
},
getNodeKey: this.props.getNodeKey
}),
// reset the scroll focus so it doesn't jump back
// to a search result while dragging
searchFocusTreeIndex: null,
dragging: true
});
}
}, {
key: "endDrag",
value: function endDrag(dropResult) {
var _this3 = this;
var instanceProps = this.state.instanceProps;
var resetTree = function resetTree() {
return _this3.setState({
draggingTreeData: null,
draggedNode: null,
draggedMinimumTreeIndex: null,
draggedDepth: null,
dragging: false
});
}; // Drop was cancelled
if (!dropResult) {
resetTree();
} else if (dropResult.treeId !== this.treeId) {
// The node was dropped in an external drop target or tree
var node = dropResult.node,
path = dropResult.path,
treeIndex = dropResult.treeIndex;
var shouldCopy = this.props.shouldCopyOnOutsideDrop;
if (typeof shouldCopy === 'function') {
shouldCopy = shouldCopy({
node: node,
prevTreeIndex: treeIndex,
prevPath: path
});
}
var treeData = this.state.draggingTreeData || instanceProps.treeData; // If copying is enabled, a drop outside leaves behind a copy in the
// source tree
if (shouldCopy) {
treeData = (0, _treeDataUtils.changeNodeAtPath)({
treeData: instanceProps.treeData,
// use treeData unaltered by the drag operation
path: path,
newNode: function newNode(_ref7) {
var copyNode = _ref7.node;
return _objectSpread({}, copyNode);
},
// create a shallow copy of the node
getNodeKey: this.props.getNodeKey
});
}
this.props.onChange(treeData);
this.props.onMoveNode({
treeData: treeData,
node: node,
treeIndex: null,
path: null,
nextPath: null,
nextTreeIndex: null,
prevPath: path,
prevTreeIndex: treeIndex
});
}
}
}, {
key: "drop",
value: function drop(dropResult) {
this.moveNode(dropResult);
}
}, {
key: "canNodeHaveChildren",
value: function canNodeHaveChildren(node) {
var canNodeHaveChildren = this.props.canNodeHaveChildren;
if (canNodeHaveChildren) {
return canNodeHaveChildren(node);
}
return true;
} // Load any children in the tree that are given by a function
// calls the onChange callback on the new treeData
}, {
key: "renderRow",
value: function renderRow(row, _ref8) {
var listIndex = _ref8.listIndex,
style = _ref8.style,
getPrevRow = _ref8.getPrevRow,
matchKeys = _ref8.matchKeys,
swapFrom = _ref8.swapFrom,
swapDepth = _ref8.swapDepth,
swapLength = _ref8.swapLength;
var node = row.node,
parentNode = row.parentNode,
path = row.path,
lowerSiblingCounts = row.lowerSiblingCounts,
treeIndex = row.treeIndex;
var _mergeTheme2 = mergeTheme(this.props),
canDrag = _mergeTheme2.canDrag,
generateNodeProps = _mergeTheme2.generateNodeProps,
scaffoldBlockPxWidth = _mergeTheme2.scaffoldBlockPxWidth,
searchFocusOffset = _mergeTheme2.searchFocusOffset,
rowDirection = _mergeTheme2.rowDirection;
var TreeNodeRenderer = this.treeNodeRenderer;
var NodeContentRenderer = this.nodeContentRenderer;
var nodeKey = path[path.length - 1];
var isSearchMatch = (nodeKey in matchKeys);
var isSearchFocus = isSearchMatch && matchKeys[nodeKey] === searchFocusOffset;
var callbackParams = {
node: node,
parentNode: parentNode,
path: path,
lowerSiblingCounts: lowerSiblingCounts,
treeIndex: treeIndex,
isSearchMatch: isSearchMatch,
isSearchFocus: isSearchFocus
};
var nodeProps = !generateNodeProps ? {} : generateNodeProps(callbackParams);
var rowCanDrag = typeof canDrag !== 'function' ? canDrag : canDrag(callbackParams);
var sharedProps = {
treeIndex: treeIndex,
scaffoldBlockPxWidth: scaffoldBlockPxWidth,
node: node,
path: path,
treeId: this.treeId,
rowDirection: rowDirection
};
return /*#__PURE__*/_react.default.createElement(TreeNodeRenderer, _extends({
style: style,
key: nodeKey,
listIndex: listIndex,
getPrevRow: getPrevRow,
lowerSiblingCounts: lowerSiblingCounts,
swapFrom: swapFrom,
swapLength: swapLength,
swapDepth: swapDepth
}, sharedProps), /*#__PURE__*/_react.default.createElement(NodeContentRenderer, _extends({
parentNode: parentNode,
isSearchMatch: isSearchMatch,
isSearchFocus: isSearchFocus,
canDrag: rowCanDrag,
toggleChildrenVisibility: this.toggleChildrenVisibility
}, sharedProps, nodeProps)));
}
}, {
key: "render",
value: function render() {
var _this4 = this;
var _mergeTheme3 = mergeTheme(this.props),
style = _mergeTheme3.style,
className = _mergeTheme3.className,
innerStyle = _mergeTheme3.innerStyle,
rowHeight = _mergeTheme3.rowHeight,
isVirtualized = _mergeTheme3.isVirtualized,
placeholderRenderer = _mergeTheme3.placeholderRenderer,
reactVirtualizedListProps = _mergeTheme3.reactVirtualizedListProps,
getNodeKey = _mergeTheme3.getNodeKey,
rowDirection = _mergeTheme3.rowDirection;
var _this$state = this.state,
searchMatches = _this$state.searchMatches,
searchFocusTreeIndex = _this$state.searchFocusTreeIndex,
draggedNode = _this$state.draggedNode,
draggedDepth = _this$state.draggedDepth,
draggedMinimumTreeIndex = _this$state.draggedMinimumTreeIndex,
instanceProps = _this$state.instanceProps;
var treeData = this.state.draggingTreeData || instanceProps.treeData;
var rowDirectionClass = rowDirection === 'rtl' ? 'rst__rtl' : null;
var rows;
var swapFrom = null;
var swapLength = null;
if (draggedNode && draggedMinimumTreeIndex !== null) {
var addedResult = (0, _memoizedTreeDataUtils.memoizedInsertNode)({
treeData: treeData,
newNode: draggedNode,
depth: draggedDepth,
minimumTreeIndex: draggedMinimumTreeIndex,
expandParent: true,
getNodeKey: getNodeKey
});
var swapTo = draggedMinimumTreeIndex;
swapFrom = addedResult.treeIndex;
swapLength = 1 + (0, _memoizedTreeDataUtils.memoizedGetDescendantCount)({
node: draggedNode
});
rows = (0, _genericUtils.slideRows)(this.getRows(addedResult.treeData), swapFrom, swapTo, swapLength);
} else {
rows = this.getRows(treeData);
} // Get indices for rows that match the search conditions
var matchKeys = {};
searchMatches.forEach(function (_ref9, i) {
var path = _ref9.path;
matchKeys[path[path.length - 1]] = i;
}); // Seek to the focused search result if there is one specified
var scrollToInfo = searchFocusTreeIndex !== null ? {
scrollToIndex: searchFocusTreeIndex
} : {};
var containerStyle = style;
var list;
if (rows.length < 1) {
var Placeholder = this.treePlaceholderRenderer;
var PlaceholderContent = placeholderRenderer;
list = /*#__PURE__*/_react.default.createElement(Placeholder, {
treeId: this.treeId,
drop: this.drop
}, /*#__PURE__*/_react.default.createElement(PlaceholderContent, null));
} else if (isVirtualized) {
containerStyle = _objectSpread({
height: '100%'
}, containerStyle);
var ScrollZoneVirtualList = this.scrollZoneVirtualList; // Render list with react-virtualized
list = /*#__PURE__*/_react.default.createElement(_reactVirtualized.AutoSizer, null, function (_ref10) {
var height = _ref10.height,
width = _ref10.width;
return /*#__PURE__*/_react.default.createElement(ScrollZoneVirtualList, _extends({}, scrollToInfo, {
verticalStrength: _this4.vStrength,
horizontalStrength: _this4.hStrength,
speed: 30,
scrollToAlignment: "start",
className: "rst__virtualScrollOverride",
width: width,
onScroll: function onScroll(_ref11) {
var scrollTop = _ref11.scrollTop;
_this4.scrollTop = scrollTop;
},
height: height,
style: innerStyle,
rowCount: rows.length,
estimatedRowSize: typeof rowHeight !== 'function' ? rowHeight : undefined,
rowHeight: typeof rowHeight !== 'function' ? rowHeight : function (_ref12) {
var index = _ref12.index;
return rowHeight({
index: index,
treeIndex: index,
node: rows[index].node,
path: rows[index].path
});
},
rowRenderer: function rowRenderer(_ref13) {
var index = _ref13.index,
rowStyle = _ref13.style;
return _this4.renderRow(rows[index], {
listIndex: index,
style: rowStyle,
getPrevRow: function getPrevRow() {
return rows[index - 1] || null;
},
matchKeys: matchKeys,
swapFrom: swapFrom,
swapDepth: draggedDepth,
swapLength: swapLength
});
}
}, reactVirtualizedListProps));
});
} else {
// Render list without react-virtualized
list = rows.map(function (row, index) {
return _this4.renderRow(row, {
listIndex: index,
style: {
height: typeof rowHeight !== 'function' ? rowHeight : rowHeight({
index: index,
treeIndex: index,
node: row.node,
path: row.path
})
},
getPrevRow: function getPrevRow() {
return rows[index - 1] || null;
},
matchKeys: matchKeys,
swapFrom: swapFrom,
swapDepth: draggedDepth,
swapLength: swapLength
});
});
}
return /*#__PURE__*/_react.default.createElement("div", {
className: (0, _classnames.default)('rst__tree', className, rowDirectionClass),
style: containerStyle
}, list);
}
}], [{
key: "getDerivedStateFromProps",
value: function getDerivedStateFromProps(nextProps, prevState) {
var instanceProps = prevState.instanceProps;
var newState = {}; // make sure we have the most recent version of treeData
instanceProps.treeData = nextProps.treeData;
if (!(0, _lodash.default)(instanceProps.treeData, nextProps.treeData)) {
if (instanceProps.ignoreOneTreeUpdate) {
instanceProps.ignoreOneTreeUpdate = false;
} else {
newState.searchFocusTreeIndex = null;
ReactSortableTree.loadLazyChildren(nextProps, prevState);
Object.assign(newState, ReactSortableTree.search(nextProps, prevState, false, false, false));
}
newState.draggingTreeData = null;
newState.draggedNode = null;
newState.draggedMinimumTreeIndex = null;
newState.draggedDepth = null;
newState.dragging = false;
} else if (!(0, _lodash.default)(instanceProps.searchQuery, nextProps.searchQuery)) {
Object.assign(newState, ReactSortableTree.search(nextProps, prevState, true, true, false));
} else if (instanceProps.searchFocusOffset !== nextProps.searchFocusOffset) {
Object.assign(newState, ReactSortableTree.search(nextProps, prevState, true, true, true));
}
instanceProps.searchQuery = nextProps.searchQuery;
instanceProps.searchFocusOffset = nextProps.searchFocusOffset;
newState.instanceProps = instanceProps;
return newState;
}
}, {
key: "search",
value: function search(props, state, seekIndex, expand, singleSearch) {
var onChange = props.onChange,
getNodeKey = props.getNodeKey,
searchFinishCallback = props.searchFinishCallback,
searchQuery = props.searchQuery,
searchMethod = props.searchMethod,
searchFocusOffset = props.searchFocusOffset,
onlyExpandSearchedNodes = props.onlyExpandSearchedNodes;
var instanceProps = state.instanceProps; // Skip search if no conditions are specified
if (!searchQuery && !searchMethod) {
if (searchFinishCallback) {
searchFinishCallback([]);
}
return {
searchMatches: []
};
}
var newState = {}; // if onlyExpandSearchedNodes collapse the tree and search
var _find = (0, _treeDataUtils.find)({
getNodeKey: getNodeKey,
treeData: onlyExpandSearchedNodes ? (0, _treeDataUtils.toggleExpandedForAll)({
treeData: instanceProps.treeData,
expanded: false
}) : instanceProps.treeData,
searchQuery: searchQuery,
searchMethod: searchMethod || _defaultHandlers.defaultSearchMethod,
searchFocusOffset: searchFocusOffset,
expandAllMatchPaths: expand && !singleSearch,
expandFocusMatchPaths: !!expand
}),
expandedTreeData = _find.treeData,
searchMatches = _find.matches; // Update the tree with data leaving all paths leading to matching nodes open
if (expand) {
newState.ignoreOneTreeUpdate = true; // Prevents infinite loop
onChange(expandedTreeData);
}
if (searchFinishCallback) {
searchFinishCallback(searchMatches);
}
var searchFocusTreeIndex = null;
if (seekIndex && searchFocusOffset !== null && searchFocusOffset < searchMatches.length) {
searchFocusTreeIndex = searchMatches[searchFocusOffset].treeIndex;
}
newState.searchMatches = searchMatches;
newState.searchFocusTreeIndex = searchFocusTreeIndex;
return newState;
}
}, {
key: "loadLazyChildren",
value: function loadLazyChildren(props, state) {
var instanceProps = state.instanceProps;
(0, _treeDataUtils.walk)({
treeData: instanceProps.treeData,
getNodeKey: props.getNodeKey,
callback: function callback(_ref14) {
var node = _ref14.node,
path = _ref14.path,
lowerSiblingCounts = _ref14.lowerSiblingCounts,
treeIndex = _ref14.treeIndex;
// If the node has children defined by a function, and is either expanded
// or set to load even before expansion, run the function.
if (node.children && typeof node.children === 'function' && (node.expanded || props.loadCollapsedLazyChildren)) {
// Call the children fetching function
node.children({
node: node,
path: path,
lowerSiblingCounts: lowerSiblingCounts,
treeIndex: treeIndex,
// Provide a helper to append the new data when it is received
done: function done(childrenArray) {
return props.onChange((0, _treeDataUtils.changeNodeAtPath)({
treeData: instanceProps.treeData,
path: path,
newNode: function newNode(_ref15) {
var oldNode = _ref15.node;
return (// Only replace the old node if it's the one we set off to find children
// for in the first place
oldNode === node ? _objectSpread(_objectSpread({}, oldNode), {}, {
children: childrenArray
}) : oldNode
);
},
getNodeKey: props.getNodeKey
}));
}
});
}
}
});
}
}]);
return ReactSortableTree;
}(_react.Component);
exports.SortableTreeWithoutDndContext = ReactSortableTree;
ReactSortableTree.propTypes = {
// Tree data in the following format:
// [{title: 'main', subtitle: 'sub'}, { title: 'value2', expanded: true, children: [{ title: 'value3') }] }]
// `title` is the primary label for the node
// `subtitle` is a secondary label for the node
// `expanded` shows children of the node if true, or hides them if false. Defaults to false.
// `children` is an array of child nodes belonging to the node.
treeData: _propTypes.default.arrayOf(_propTypes.default.object).isRequired,
// Style applied to the container wrapping the tree (style defaults to {height: '100%'})
style: _propTypes.default.shape({}),
// Class name for the container wrapping the tree
className: _propTypes.default.string,
// Style applied to the inner, scrollable container (for padding, etc.)
innerStyle: _propTypes.default.shape({}),
// Used by react-virtualized
// Either a fixed row height (number) or a function that returns the
// height of a row given its index: `({ index: number }): number`
rowHeight: _propTypes.default.oneOfType([_propTypes.default.number, _propTypes.default.func]),
// Size in px of the region near the edges that initiates scrolling on dragover
slideRegionSize: _propTypes.default.number,
// Custom properties to hand to the react-virtualized list
// https://github.com/bvaughn/react-virtualized/blob/master/docs/List.md#prop-types
reactVirtualizedListProps: _propTypes.default.shape({}),
// The width of the blocks containing the lines representing the structure of the tree.
scaffoldBlockPxWidth: _propTypes.default.number,
// Maximum depth nodes can be inserted at. Defaults to infinite.
maxDepth: _propTypes.default.number,
// The method used to search nodes.
// Defaults to a function that uses the `searchQuery` string to search for nodes with
// matching `title` or `subtitle` values.
// NOTE: Changing `searchMethod` will not update the search, but changing the `searchQuery` will.
searchMethod: _propTypes.default.func,
// Used by the `searchMethod` to highlight and scroll to matched nodes.
// Should be a string for the default `searchMethod`, but can be anything when using a custom search.
searchQuery: _propTypes.default.any,
// eslint-disable-line react/forbid-prop-types
// Outline the <`searchFocusOffset`>th node and scroll to it.
searchFocusOffset: _propTypes.default.number,
// Get the nodes that match the search criteria. Used for counting total matches, etc.
searchFinishCallback: _propTypes.default.func,
// Generate an object with additional props to be passed to the node renderer.
// Use this for adding buttons via the `buttons` key,
// or additional `style` / `className` settings.
generateNodeProps: _propTypes.default.func,
// Set to false to disable virtualization.
// NOTE: Auto-scrolling while dragging, and scrolling to the `searchFocusOffset` will be disabled.
isVirtualized: _propTypes.default.bool,
treeNodeRenderer: _propTypes.default.func,
// Override the default component for rendering nodes (but keep the scaffolding generator)
// This is an advanced option for complete customization of the appearance.
// It is best to copy the component in `node-renderer-default.js` to use as a base, and customize as needed.
nodeContentRenderer: _propTypes.default.func,
// Override the default component for rendering an empty tree
// This is an advanced option for complete customization of the appearance.
// It is best to copy the component in `placeholder-renderer-default.js` to use as a base,
// and customize as needed.
placeholderRenderer: _propTypes.default.func,
theme: _propTypes.default.shape({
style: _propTypes.default.shape({}),
innerStyle: _propTypes.default.shape({}),
reactVirtualizedListProps: _propTypes.default.shape({}),
scaffoldBlockPxWidth: _propTypes.default.number,
slideRegionSize: _propTypes.default.number,
rowHeight: _propTypes.default.oneOfType([_propTypes.default.number, _propTypes.default.func]),
treeNodeRenderer: _propTypes.default.func,
nodeContentRenderer: _propTypes.default.func,
placeholderRenderer: _propTypes.default.func
}),
// Determine the unique key used to identify each node and
// generate the `path` array passed in callbacks.
// By default, returns the index in the tree (omitting hidden nodes).
getNodeKey: _propTypes.default.func,
// Called whenever tree data changed.
// Just like with React input elements, you have to update your
// own component's data to see the changes reflected.
onChange: _propTypes.default.func.isRequired,
// Called after node move operation.
onMoveNode: _propTypes.default.func,
// Determine whether a node can be dragged. Set to false to disable dragging on all nodes.
canDrag: _propTypes.default.oneOfType([_propTypes.default.func, _propTypes.default.bool]),
// Determine whether a node can be dropped based on its path and parents'.
canDrop: _propTypes.default.func,
// Determine whether a node can have children
canNodeHaveChildren: _propTypes.default.func,
// When true, or a callback returning true, dropping nodes to react-dnd
// drop targets outside of this tree will not remove them from this tree
shouldCopyOnOutsideDrop: _propTypes.default.oneOfType([_propTypes.default.func, _propTypes.default.bool]),
// Called after children nodes collapsed or expanded.
onVisibilityToggle: _propTypes.default.func,
dndType: _propTypes.default.string,
// Called to track between dropped and dragging
onDragStateChanged: _propTypes.default.func,
// Specify that nodes that do not match search will be collapsed
onlyExpandSearchedNodes: _propTypes.default.bool,
// rtl support
rowDirection: _propTypes.default.string
};
ReactSortableTree.defaultProps = {
canDrag: true,
canDrop: null,
canNodeHaveChildren: function canNodeHaveChildren() {
return true;
},
className: '',
dndType: null,
generateNodeProps: null,
getNodeKey: _defaultHandlers.defaultGetNodeKey,
innerStyle: {},
isVirtualized: true,
maxDepth: null,
treeNodeRenderer: null,
nodeContentRenderer: null,
onMoveNode: function onMoveNode() {},
onVisibilityToggle: function onVisibilityToggle() {},
placeholderRenderer: null,
reactVirtualizedListProps: {},
rowHeight: null,
scaffoldBlockPxWidth: null,
searchFinishCallback: null,
searchFocusOffset: null,
searchMethod: null,
searchQuery: null,
shouldCopyOnOutsideDrop: false,
slideRegionSize: null,
style: {},
theme: {},
onDragStateChanged: function onDragStateChanged() {},
onlyExpandSearchedNodes: false,
rowDirection: 'ltr'
};
ReactSortableTree.contextTypes = {
dragDropManager: _propTypes.default.shape({})
};
(0, _reactLifecyclesCompat.polyfill)(ReactSortableTree); // Export the tree component without the react-dnd DragDropContext,
// for when component is used with other components using react-dnd.
// see: https://github.com/gaearon/react-dnd/issues/186
var _default = _dndManager.default.wrapRoot(ReactSortableTree);
exports.default = _default;