UNPKG

@atlaskit/tree

Version:

A React Component for displaying expandable and sortable tree hierarchies

297 lines (296 loc) 13.2 kB
import _extends from "@babel/runtime/helpers/extends"; import _classCallCheck from "@babel/runtime/helpers/classCallCheck"; import _createClass from "@babel/runtime/helpers/createClass"; import _assertThisInitialized from "@babel/runtime/helpers/assertThisInitialized"; import _inherits from "@babel/runtime/helpers/inherits"; import _possibleConstructorReturn from "@babel/runtime/helpers/possibleConstructorReturn"; import _getPrototypeOf from "@babel/runtime/helpers/getPrototypeOf"; import _defineProperty from "@babel/runtime/helpers/defineProperty"; function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); 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 = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; } 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 _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; } } import React, { Component } from 'react'; import { Draggable, Droppable, DragDropContext } from 'react-beautiful-dnd-next'; import { getBox } from 'css-box-model'; import { calculateFinalDropPositions } from './Tree-utils'; import { noop } from '../../utils/handy'; import { flattenTree } from '../../utils/tree'; import { mutateTree } from '../../utils/mutateTree'; import TreeItem from '../TreeItem'; import { getDestinationPath, getItemById, getIndexById } from '../../utils/flat-tree'; import DelayedFunction from '../../utils/delayed-function'; var Tree = /*#__PURE__*/function (_Component) { _inherits(Tree, _Component); var _super = _createSuper(Tree); function Tree() { var _this; _classCallCheck(this, Tree); for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { args[_key] = arguments[_key]; } _this = _super.call.apply(_super, [this].concat(args)); _defineProperty(_assertThisInitialized(_this), "state", { flattenedTree: [], draggedItemId: undefined }); // HTMLElement for each rendered item _defineProperty(_assertThisInitialized(_this), "itemsElement", {}); _defineProperty(_assertThisInitialized(_this), "expandTimer", new DelayedFunction(500)); _defineProperty(_assertThisInitialized(_this), "onDragStart", function (result) { var onDragStart = _this.props.onDragStart; _this.dragState = { source: result.source, destination: result.source, mode: result.mode }; _this.setState({ draggedItemId: result.draggableId }); if (onDragStart) { onDragStart(result.draggableId); } }); _defineProperty(_assertThisInitialized(_this), "onDragUpdate", function (update) { var onExpand = _this.props.onExpand; var flattenedTree = _this.state.flattenedTree; if (!_this.dragState) { return; } _this.expandTimer.stop(); if (update.combine) { var draggableId = update.combine.draggableId; var item = getItemById(flattenedTree, draggableId); if (item && _this.isExpandable(item)) { _this.expandTimer.start(function () { return onExpand(draggableId, item.path); }); } } _this.dragState = _objectSpread(_objectSpread({}, _this.dragState), {}, { destination: update.destination, combine: update.combine }); }); _defineProperty(_assertThisInitialized(_this), "onDropAnimating", function () { _this.expandTimer.stop(); }); _defineProperty(_assertThisInitialized(_this), "onDragEnd", function (result) { var _this$props = _this.props, onDragEnd = _this$props.onDragEnd, tree = _this$props.tree; var flattenedTree = _this.state.flattenedTree; _this.expandTimer.stop(); var finalDragState = _objectSpread(_objectSpread({}, _this.dragState), {}, { source: result.source, destination: result.destination, combine: result.combine }); _this.setState({ draggedItemId: undefined }); var _calculateFinalDropPo = calculateFinalDropPositions(tree, flattenedTree, finalDragState), sourcePosition = _calculateFinalDropPo.sourcePosition, destinationPosition = _calculateFinalDropPo.destinationPosition; onDragEnd(sourcePosition, destinationPosition); _this.dragState = undefined; }); _defineProperty(_assertThisInitialized(_this), "onPointerMove", function () { if (_this.dragState) { _this.dragState = _objectSpread(_objectSpread({}, _this.dragState), {}, { horizontalLevel: _this.getDroppedLevel() }); } }); _defineProperty(_assertThisInitialized(_this), "calculateEffectivePath", function (flatItem, snapshot) { var _this$state = _this.state, flattenedTree = _this$state.flattenedTree, draggedItemId = _this$state.draggedItemId; if (_this.dragState && draggedItemId === flatItem.item.id && (_this.dragState.destination || _this.dragState.combine)) { var _this$dragState = _this.dragState, source = _this$dragState.source, destination = _this$dragState.destination, combine = _this$dragState.combine, horizontalLevel = _this$dragState.horizontalLevel, mode = _this$dragState.mode; // We only update the path when it's dragged by keyboard or drop is animated if (mode === 'SNAP' || snapshot.isDropAnimating) { if (destination) { // Between two items return getDestinationPath(flattenedTree, source.index, destination.index, horizontalLevel); } if (combine) { // Hover on other item while dragging return getDestinationPath(flattenedTree, source.index, getIndexById(flattenedTree, combine.draggableId), horizontalLevel); } } } return flatItem.path; }); _defineProperty(_assertThisInitialized(_this), "isExpandable", function (item) { return !!item.item.hasChildren && !item.item.isExpanded; }); _defineProperty(_assertThisInitialized(_this), "getDroppedLevel", function () { var offsetPerLevel = _this.props.offsetPerLevel; var draggedItemId = _this.state.draggedItemId; if (!_this.dragState || !_this.containerElement) { return undefined; } var containerLeft = getBox(_this.containerElement).contentBox.left; var itemElement = _this.itemsElement[draggedItemId]; if (itemElement) { var currentLeft = getBox(itemElement).contentBox.left; var relativeLeft = Math.max(currentLeft - containerLeft, 0); return Math.floor((relativeLeft + offsetPerLevel / 2) / offsetPerLevel) + 1; } return undefined; }); _defineProperty(_assertThisInitialized(_this), "patchDroppableProvided", function (provided) { return _objectSpread(_objectSpread({}, provided), {}, { innerRef: function innerRef(el) { _this.containerElement = el; provided.innerRef(el); } }); }); _defineProperty(_assertThisInitialized(_this), "setItemRef", function (itemId, el) { if (!!el) { _this.itemsElement[itemId] = el; } }); _defineProperty(_assertThisInitialized(_this), "renderItems", function () { var flattenedTree = _this.state.flattenedTree; return flattenedTree.map(_this.renderItem); }); _defineProperty(_assertThisInitialized(_this), "renderItem", function (flatItem, index) { var isDragEnabled = _this.props.isDragEnabled; // If drag and drop is explicitly disabled for all items, render TreeItem directly with stubbed provided and snapshot if (isDragEnabled === false) { return _this.renderTreeItem({ flatItem: flatItem, path: flatItem.path, provided: { draggableProps: { 'data-react-beautiful-dnd-draggable': '' }, innerRef: function innerRef() {}, dragHandleProps: null }, snapshot: { isDragging: false, isDropAnimating: false } }); } var isDragDisabled = typeof isDragEnabled === 'function' ? !isDragEnabled(flatItem.item) : !isDragEnabled; return /*#__PURE__*/React.createElement(Draggable, { key: flatItem.item.id, draggableId: flatItem.item.id.toString(), index: index, isDragDisabled: isDragDisabled }, _this.renderDraggableItem(flatItem)); }); _defineProperty(_assertThisInitialized(_this), "renderDraggableItem", function (flatItem) { return function (provided, snapshot) { var currentPath = _this.calculateEffectivePath(flatItem, snapshot); if (snapshot.isDropAnimating) { _this.onDropAnimating(); } return _this.renderTreeItem({ flatItem: flatItem, path: currentPath, provided: provided, snapshot: snapshot }); }; }); _defineProperty(_assertThisInitialized(_this), "renderTreeItem", function (_ref) { var flatItem = _ref.flatItem, path = _ref.path, provided = _ref.provided, snapshot = _ref.snapshot; var _this$props2 = _this.props, renderItem = _this$props2.renderItem, onExpand = _this$props2.onExpand, onCollapse = _this$props2.onCollapse, offsetPerLevel = _this$props2.offsetPerLevel; return /*#__PURE__*/React.createElement(TreeItem, { key: flatItem.item.id, item: flatItem.item, path: path, onExpand: onExpand, onCollapse: onCollapse, renderItem: renderItem, provided: provided, snapshot: snapshot, itemRef: _this.setItemRef, offsetPerLevel: offsetPerLevel }); }); return _this; } _createClass(Tree, [{ key: "render", value: function render() { var _this2 = this; var isNestingEnabled = this.props.isNestingEnabled; var renderedItems = this.renderItems(); return /*#__PURE__*/React.createElement(DragDropContext, { onDragStart: this.onDragStart, onDragEnd: this.onDragEnd, onDragUpdate: this.onDragUpdate }, /*#__PURE__*/React.createElement(Droppable, { droppableId: "tree", isCombineEnabled: isNestingEnabled, ignoreContainerClipping: true }, function (provided) { var finalProvided = _this2.patchDroppableProvided(provided); return /*#__PURE__*/React.createElement("div", _extends({ ref: finalProvided.innerRef, style: { pointerEvents: 'auto' }, onTouchMove: _this2.onPointerMove, onMouseMove: _this2.onPointerMove }, finalProvided.droppableProps), renderedItems, provided.placeholder); })); } }], [{ key: "getDerivedStateFromProps", value: function getDerivedStateFromProps(props, state) { var draggedItemId = state.draggedItemId; var tree = props.tree; var finalTree = Tree.closeParentIfNeeded(tree, draggedItemId); var flattenedTree = flattenTree(finalTree); return _objectSpread(_objectSpread({}, state), {}, { flattenedTree: flattenedTree }); } }, { key: "closeParentIfNeeded", value: function closeParentIfNeeded(tree, draggedItemId) { if (!!draggedItemId) { // Closing parent internally during dragging, because visually we can only move one item not a subtree return mutateTree(tree, draggedItemId, { isExpanded: false }); } return tree; } }]); return Tree; }(Component); _defineProperty(Tree, "defaultProps", { tree: { children: [] }, onExpand: noop, onCollapse: noop, onDragStart: noop, onDragEnd: noop, renderItem: noop, offsetPerLevel: 35, isDragEnabled: false, isNestingEnabled: false }); export { Tree as default };