wix-style-react
Version:
462 lines (380 loc) • 17 kB
JavaScript
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;