UNPKG

react-sortable-tree-node

Version:
926 lines (823 loc) 40.4 kB
"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;