UNPKG

wix-style-react

Version:
462 lines (380 loc) • 17 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 _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 from 'react'; import { findDOMNode } from 'react-dom'; import { DragSource, DropTarget } from 'react-dnd'; import itemTypes from './itemTypes'; import { getValuesByKey, hoverAboveItself } from './utils'; import { NestableListContext } from './NestableListContext'; import { getEmptyImage } from '../DragAndDrop/Draggable/DragUtils'; import classNames from 'classnames'; import { dataAttributes } from '../DragAndDrop/Draggable/constants'; // keep track of horizontal mouse movement var mouse = { lastX: 0 }; function increaseHorizontalLevel(prevPosition, prevIndex) { var nextPosition = prevPosition.slice(0, -1); // append to prevSibling's children nextPosition.push(prevIndex - 1, -1); return nextPosition; } function decreaseHorizontalLevel(prevPosition) { var nextPosition = prevPosition.slice(0, -1); nextPosition[nextPosition.length - 1] += 1; return nextPosition; } function calculateHandleOffset(handleRect, containerRect) { return { x: handleRect.x - containerRect.x, y: handleRect.y - containerRect.y }; } var cardSource = { isDragging: function isDragging(props, monitor) { var ids = getValuesByKey(monitor.getItem().data, 'id', 'children'); return ids.indexOf(props.id) > -1; }, beginDrag: function beginDrag(props, monitor, component) { props.onDragStart && props.onDragStart(props); var node = findDOMNode(component); var clientRect = node.getBoundingClientRect(); var handleOffset = { x: 0, y: 0 }; // needed to fix dnd drag offset data if (component.handleNode) { handleOffset = calculateHandleOffset(component.handleNode.getBoundingClientRect(), clientRect); } return { id: props.id, dragged: false, // needed for workaround of immediately fired dragend event after dragstart index: props.index, position: props.position, data: props.item, groupName: props.groupName, depth: props.depth, // rect for entire component including children clientRect: clientRect, handleOffset: handleOffset }; }, endDrag: function endDrag(props, monitor) { mouse.lastX = 0; props.dropItem(monitor.getItem()); props.onDragEnd && props.onDragEnd(props); } }; var determineHorizontalPosition = function determineHorizontalPosition(_ref) { var monitor = _ref.monitor, props = _ref.props, hoverNode = _ref.hoverNode; var item = monitor.getItem(); // the item being dragged var prevPosition = item.position, dragDepth = item.depth, prevIndex = item.index; // props for component underneath drag var hoverPosition = props.position, hoverSiblings = props.siblings, maxDepth = props.maxDepth, threshold = props.threshold; // determine mouse position var clientOffset = monitor.getClientOffset() || { x: 0, y: 0 }; var initialClientOffset = monitor.getInitialClientOffset() || { x: 0, y: 0 }; // rect for entire component including children var hoverClientRect = hoverNode.getBoundingClientRect(); var isOverSelf = hoverAboveItself(prevPosition, hoverPosition); // set mouse.lastX if it isn't set yet (first hover event) mouse.lastX = mouse.lastX || initialClientOffset.x; var currMouseX = clientOffset.x; var mouseDistanceX = currMouseX - mouse.lastX; var nearLeftEdge = currMouseX < hoverClientRect.left + 10; // nextPosition will be overwritten when moving horizontally var nextPosition = hoverPosition; // moving horizontally if (isOverSelf && (nearLeftEdge || Math.abs(mouseDistanceX) >= threshold)) { // reset lastX for new phase mouse.lastX = currMouseX; // increase horizontal level if (mouseDistanceX > 0 && // has previous sibling prevIndex - 1 >= 0 && // isn't at max depth prevPosition.length + dragDepth - 1 !== maxDepth) { nextPosition = increaseHorizontalLevel(prevPosition, prevIndex); } // decrease horizontal level if (mouseDistanceX < 0 && // is nested prevPosition.length > 1 && // is last item in array prevIndex === hoverSiblings.length - 1) { nextPosition = decreaseHorizontalLevel(prevPosition); } } if (props.preventChangeDepth && nextPosition.length - prevPosition.length === -1 // means that new parent is suitable for current dragged item ) { var isSameParent = nextPosition.every(function (position, depth) { return prevPosition[depth] === position; }); if (!isSameParent) { nextPosition = prevPosition.map(function (position, depth) { return nextPosition[depth] !== undefined ? nextPosition[depth] : 0; }); } } return nextPosition; }; var allowItemMove = function allowItemMove(_ref2) { var prevPosition = _ref2.prevPosition, nextPosition = _ref2.nextPosition, monitor = _ref2.monitor, hoverNode = _ref2.hoverNode, props = _ref2.props; // don't replace items with themselves if (hoverAboveItself(prevPosition, nextPosition)) { return; } // prevent drop if preventChangeDepth and depth is changed if (props.preventChangeDepth && nextPosition.length !== prevPosition.length) { return; } var hoverPosition = props.position; var isOverSelf = hoverAboveItself(prevPosition, hoverPosition); var clientOffset = monitor.getClientOffset() || { x: 0, y: 0 }; // rect for entire component including children var hoverClientRect = hoverNode.getBoundingClientRect(); // rect for item without children var hoverItemClientRect = hoverNode.children[0].getBoundingClientRect(); // get vertical middle var hoverMiddleY = (hoverClientRect.bottom - hoverClientRect.top) / 2; // get pixels to the top var hoverClientY = clientOffset.y - hoverClientRect.top; // dragging child item to another position with same parent if (nextPosition.length === prevPosition.length) { var last = nextPosition.length - 1; var previousIndex = prevPosition[last]; var nextIndex = nextPosition[last]; // only perform the move when the mouse has crossed half of the items height // when dragging downwards, only move when the cursor is below 50% // when dragging upwards, only move when the cursor is above 50% // dragging downwards if (previousIndex < nextIndex && hoverClientY < hoverMiddleY) { return; } // dragging upwards if (previousIndex > nextIndex && hoverClientY > hoverMiddleY) { return; } } else if ( // dragging child item over parent item nextPosition.length < prevPosition.length && nextPosition[nextPosition.length - 1] === prevPosition[prevPosition.length - 2]) { var hoverItemMiddleY = (hoverItemClientRect.bottom - hoverItemClientRect.top) / 2; // cancel if hovering in lower half of parent item if (hoverClientY > hoverItemMiddleY) { return; } } else if (!isOverSelf && clientOffset.y > hoverItemClientRect.bottom) { // cancel if over a nested target that isn't its own child return; } return true; }; var cardTarget = { hover: function hover(props, monitor, component) { // prevent drag and drop between different groups. // currently drag and drop between multiple nestable lists is not supported if (monitor.getItem().groupName !== props.groupName) { return; } var item = monitor.getItem(); // the item being dragged var prevPosition = item.position, dragItem = item.data, dragDepth = item.depth; // props for component underneath drag var hoverPosition = props.position, maxDepth = props.maxDepth; var hoverDepth = hoverPosition.length - 1; var totalDepth = hoverDepth + dragDepth; // don't exceed max depth if (totalDepth > maxDepth) { return; } var hoverNode = findDOMNode(component); var nextPosition = determineHorizontalPosition({ monitor: monitor, props: props, hoverNode: hoverNode }); if (!allowItemMove({ prevPosition: prevPosition, nextPosition: nextPosition, monitor: monitor, hoverNode: hoverNode, props: props })) { return; } // this is where the actual move happens var nextPos = props.moveItem({ dragItem: dragItem, prevPosition: prevPosition, nextPosition: nextPosition }); item.prevPosition = prevPosition; item.prevIndex = item.index; item.dragged = true; // note: we're mutating the monitor item here! // generally it's better to avoid mutations, // but it's good here for the sake of performance // to avoid expensive index searches item.position = nextPos; item.index = nextPos[nextPos.length - 1]; } }; var Item = /*#__PURE__*/function (_React$PureComponent) { _inherits(Item, _React$PureComponent); var _super = _createSuper(Item); function Item() { var _this; _classCallCheck(this, Item); 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", { shouldRenderChildren: true }); _defineProperty(_assertThisInitialized(_this), "unmounted", false); return _this; } _createClass(Item, [{ key: "componentWillUnmount", value: function componentWillUnmount() { this.unmounted = true; } }, { key: "componentDidMount", value: function componentDidMount() { // use empty image as a drag preview so browsers don't draw it // and we can draw whatever we want on the custom drag layer instead. this.props.connectDragPreview(getEmptyImage(), { // IE fallback: specify that we'd rather screenshot the node // when it already knows it's being dragged so we can hide it with CSS. captureDraggingState: true }); this.updateShouldRenderChildren(); } }, { key: "componentDidUpdate", value: function componentDidUpdate() { this.updateShouldRenderChildren(); } }, { key: "updateShouldRenderChildren", value: function updateShouldRenderChildren() { var _this2 = this; var _this$props = this.props, isPlaceholder = _this$props.isPlaceholder, isRenderDraggingChildren = _this$props.isRenderDraggingChildren; var shouldRenderChildren = !isPlaceholder || isRenderDraggingChildren; // start workaround of immediately fired dragend event after dragstart // https://github.com/react-dnd/react-dnd/issues/766#issuecomment-748255082 if (shouldRenderChildren !== this.state.shouldRenderChildren) { if (!this.props.dragged && this.state.shouldRenderChildren !== shouldRenderChildren) { setTimeout(function () { if (!_this2.unmounted) { _this2.setState({ shouldRenderChildren: shouldRenderChildren }); } }, 0); } else { this.setState({ shouldRenderChildren: shouldRenderChildren }); } } // end workaround of immediately fired dragend event after dragstart } }, { key: "render", value: function render() { var _draggableTargetDataP, _this3 = this; var _this$props2 = this.props, item = _this$props2.item, position = _this$props2.position, children = _this$props2.children, isPlaceholder = _this$props2.isPlaceholder, connectDragSource = _this$props2.connectDragSource, connectDropTarget = _this$props2.connectDropTarget, useDragHandle = _this$props2.useDragHandle, renderItem = _this$props2.renderItem, renderPrefix = _this$props2.renderPrefix, theme = _this$props2.theme, readOnly = _this$props2.readOnly, isVeryLastItem = _this$props2.isVeryLastItem, siblings = _this$props2.siblings; // params passed to renderItem callback var renderParams = { item: item, siblings: siblings, isVeryLastItem: isVeryLastItem, isPlaceholder: isPlaceholder, isPreview: false, connectDragSource: function connectDragSource() {}, depth: position.length }; var draggableTargetDataProps = (_draggableTargetDataP = {}, _defineProperty(_draggableTargetDataP, dataAttributes.draggableTarget, true), _defineProperty(_draggableTargetDataP, 'data-hook', 'nestable-item'), _draggableTargetDataP); var renderItemWithDataAttributes = function renderItemWithDataAttributes(params) { var _React$cloneElement; return /*#__PURE__*/React.cloneElement(renderItem(params), (_React$cloneElement = {}, _defineProperty(_React$cloneElement, dataAttributes.draggableSource, true), _defineProperty(_React$cloneElement, dataAttributes.depth, position.length - 1), _defineProperty(_React$cloneElement, dataAttributes.id, item.id), _React$cloneElement)); }; var classes = classNames('nestable-item', theme && theme.item); var _item$draggable = item.draggable, draggable = _item$draggable === void 0 ? true : _item$draggable; if (!draggable || readOnly) { return connectDropTarget( /*#__PURE__*/React.createElement("div", _extends({ className: classes }, draggableTargetDataProps), renderPrefix(renderParams), renderItemWithDataAttributes(renderParams), this.state.shouldRenderChildren && children)); } if (useDragHandle) { renderParams.connectDragSource = function (handle) { var handleWithRef = /*#__PURE__*/React.cloneElement(handle, { ref: function ref(node) { return _this3.handleNode = findDOMNode(node); } }); return connectDragSource(handleWithRef); }; return connectDropTarget( /*#__PURE__*/React.createElement("div", _extends({ className: classes }, draggableTargetDataProps), renderPrefix(renderParams), renderItemWithDataAttributes(renderParams), this.state.shouldRenderChildren && children)); } return connectDropTarget(connectDragSource( /*#__PURE__*/React.createElement("div", _extends({ className: classes }, draggableTargetDataProps), renderPrefix(renderParams), renderItemWithDataAttributes(renderParams), this.state.shouldRenderChildren && children))); } }]); return Item; }(React.PureComponent); export var DragItemSource = DragSource(itemTypes.nestedItem, cardSource, function (connect, monitor) { return { connectDragSource: connect.dragSource(), connectDragPreview: connect.dragPreview(), isPlaceholder: monitor.isDragging(), dragged: monitor.getItem() && monitor.getItem().dragged }; })(Item); export var DropItemTarget = DropTarget(itemTypes.nestedItem, cardTarget, function (connect) { return { connectDropTarget: connect.dropTarget() }; })(DragItemSource); var ItemWithContext = /*#__PURE__*/function (_React$PureComponent2) { _inherits(ItemWithContext, _React$PureComponent2); var _super2 = _createSuper(ItemWithContext); function ItemWithContext() { _classCallCheck(this, ItemWithContext); return _super2.apply(this, arguments); } _createClass(ItemWithContext, [{ key: "render", value: function render() { var _this4 = this; return /*#__PURE__*/React.createElement(NestableListContext.Consumer, null, function (context) { return /*#__PURE__*/React.createElement(DropItemTarget, _extends({}, _this4.props, context)); }); } }]); return ItemWithContext; }(React.PureComponent); export default ItemWithContext;