strapi-plugin-content-manager
Version:
A powerful UI to easily manage your data.
185 lines (155 loc) • 5.42 kB
JavaScript
/**
*
* DraggableAttr
*/
/* eslint-disable react/no-find-dom-node */
import React from 'react';
import { findDOMNode } from 'react-dom';
import {
DragSource,
DropTarget,
} from 'react-dnd';
import { flow, upperFirst } from 'lodash';
import PropTypes from 'prop-types';
import { FormattedMessage } from 'react-intl';
import cn from 'classnames';
import styles from './styles.scss';
const draggableAttrSource = {
beginDrag: (props) => {
props.updateSiblingHoverState();
return {
id: props.id,
index: props.index,
};
},
endDrag: (props) => {
props.updateSiblingHoverState();
return {};
},
};
const draggableAttrTarget = {
hover: (props, monitor, component) => {
const dragIndex = monitor.getItem().index;
const hoverIndex = props.index;
// Don't replace items with themselves
if (dragIndex === hoverIndex) {
return;
}
// Determine rectangle on screen
const hoverBoundingRect = findDOMNode(component).getBoundingClientRect();
// Get vertical middle
const hoverMiddleY = (hoverBoundingRect.bottom - hoverBoundingRect.top) / 2;
// Determine mouse position
const clientOffset = monitor.getClientOffset();
// Get pixels to the top
const hoverClientY = clientOffset.y - hoverBoundingRect.top;
// 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 (dragIndex < hoverIndex && hoverClientY < hoverMiddleY) {
return;
}
// Dragging upwards
if (dragIndex > hoverIndex && hoverClientY > hoverMiddleY) {
return;
}
// Time to actually perform the action
props.moveAttr(dragIndex, hoverIndex, props.keys);
// 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.
monitor.getItem().index = hoverIndex;
},
};
class DraggableAttr extends React.Component {
state = { isOver: false, dragStart: false };
componentDidUpdate(prevProps) {
const { isDraggingSibling } = this.props;
if (isDraggingSibling !== prevProps.isDraggingSibling && isDraggingSibling) {
this.handleMouseLeave();
}
}
handleClickEdit = (e) => {
e.preventDefault();
e.stopPropagation();
this.props.onClickEditListItem(this.props.index);
}
handleMouseEnter = () => {
if (!this.props.isDraggingSibling) {
this.setState({ isOver: true });
}
};
handleMouseLeave = () => this.setState({ isOver: false });
handleRemove = (e) => {
e.preventDefault();
e.stopPropagation();
this.props.onRemove(this.props.index, this.props.keys);
}
render() {
const { label, name, isDragging, isEditing, connectDragSource, connectDropTarget } = this.props;
const { isOver, dragStart } = this.state;
const opacity = isDragging ? 0.2 : 1;
const overClass = isOver ? styles.draggableAttrOvered : '';
const className = dragStart ? styles.dragged : styles.draggableAttr;
return (
connectDragSource(
connectDropTarget(
<div
className={cn(className, isEditing && styles.editingAttr, overClass)}
onDragStart={() => this.setState({ dragStart: true })}
onDragEnd={() => this.setState({ dragStart: false })}
onMouseEnter={this.handleMouseEnter}
onMouseLeave={this.handleMouseLeave}
onClick={this.handleClickEdit}
style={{ opacity }}
>
<i className="fa fa-th" aria-hidden="true" />
<span>{name}</span>
{ isOver && !isDragging && (
<div className={styles.info}>
<FormattedMessage id="content-manager.components.DraggableAttr.edit" />
</div>
)}
{ !isOver && upperFirst(name) !== label && (
<div className={styles.info}>
{label}
</div>
)}
{isEditing && !isOver? (
<span className={styles.editIcon} onClick={this.handleClickEdit} />
) : (
<span className={cn( dragStart ? styles.removeIconDragged : styles.removeIcon)} onClick={this.handleRemove} />
)}
</div>
),
)
);
}
}
DraggableAttr.defaultProps = {
isEditing: false,
onRemove: () => {},
};
DraggableAttr.propTypes = {
connectDragSource: PropTypes.func.isRequired,
connectDropTarget: PropTypes.func.isRequired,
index: PropTypes.number.isRequired,
isDragging: PropTypes.bool.isRequired,
isDraggingSibling: PropTypes.bool.isRequired,
isEditing: PropTypes.bool,
keys: PropTypes.string.isRequired,
label: PropTypes.string.isRequired,
name: PropTypes.string.isRequired,
onClickEditListItem: PropTypes.func.isRequired,
onRemove: PropTypes.func,
};
const withDropTarget = DropTarget('draggableAttr', draggableAttrTarget, connect => ({
connectDropTarget: connect.dropTarget(),
}));
const withDragSource = DragSource('draggableAttr', draggableAttrSource, (connect, monitor) => ({
connectDragSource: connect.dragSource(),
isDragging: monitor.isDragging(),
}));
export default flow([withDropTarget, withDragSource])(DraggableAttr);