react-dazzle-reload
Version:
The simple yet flexible dashbording solution for React
248 lines (208 loc) • 5.96 kB
JavaScript
import React, { Component, createElement } from 'react';
import PropTypes from 'prop-types';
import { findDOMNode } from 'react-dom';
import { DragSource, DropTarget } from 'react-dnd';
import { WIDGET } from './ItemTypes';
import { removeWidget, sortWidget } from '../util';
import DefaultFrame from './DefaultFrame';
const boxSource = {
beginDrag(props) {
return {
widgetName: props.widgetName,
widgetId: props.widgetId,
rowIndex: props.rowIndex,
columnIndex: props.columnIndex,
widgetIndex: props.widgetIndex,
};
},
};
const cardTarget = {
hover(props, monitor, component) {
const dragIndex = monitor.getItem().widgetIndex;
const hoverIndex = props.widgetIndex;
// 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
const { layout, columnIndex, rowIndex } = props;
if (monitor.getItem().rowIndex === rowIndex && monitor.getItem().columnIndex === columnIndex) {
const newLayout = sortWidget(layout, {
rowIndex,
columnIndex,
widgetIndex: dragIndex,
}, {
rowIndex,
columnIndex,
widgetIndex: hoverIndex,
}, monitor.getItem());
props.onMove(newLayout);
// 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().widgetIndex = hoverIndex; // eslint-disable-line no-param-reassign
}
},
};
/**
* Frame component which surrounds each widget.
*/
@DropTarget(WIDGET, cardTarget, connect => ({
connectDropTarget: connect.dropTarget(),
}))
@DragSource(WIDGET, boxSource, (connect, monitor) => ({
connectDragSource: connect.dragSource(),
isDragging: monitor.isDragging(),
}))
class WidgetFrame extends Component {
render() {
const {
frameComponent,
children,
widgetId,
editable,
title,
frameSettings,
connectDragSource,
connectDropTarget,
isDragging,
rowIndex,
columnIndex,
widgetIndex,
} = this.props;
let selected = null;
if (frameComponent) {
// if user provided a custom frame, use it
selected = createElement(frameComponent, {
children,
widgetId,
editable,
title,
settings: frameSettings,
onRemove: this.remove,
onEdit: this.edit,
rowIndex,
columnIndex,
widgetIndex,
isDragging,
});
} else {
// else use the default frame
selected = (
<DefaultFrame
title={title}
editable={editable}
children={children}
onRemove={this.remove}
onEdit={this.edit}
/>
);
}
const opacity = isDragging ? 0 : 1;
const widgetFrame = (
<div style={{ opacity }}>
{selected}
</div>
);
return editable ? connectDragSource(connectDropTarget(widgetFrame)) : widgetFrame;
}
edit = () => {
const { layout, rowIndex, columnIndex, widgetIndex } = this.props;
this.props.onEdit(layout.rows[rowIndex].columns[columnIndex].widgets[widgetIndex].key);
}
remove = () => {
const { layout, rowIndex, columnIndex, widgetIndex } = this.props;
const newLayout = removeWidget(layout, rowIndex, columnIndex, widgetIndex);
this.props.onRemove(newLayout, rowIndex, columnIndex, widgetIndex);
}
}
WidgetFrame.propTypes = {
/**
* Childrens of the widget frame.
*/
children: PropTypes.element,
/**
* Layout of the dahsboard.
*/
layout: PropTypes.object,
/**
* Index of the column these widgets should be placed.
*/
columnIndex: PropTypes.number,
/**
* Index of the row these widgets should be placed.
*/
rowIndex: PropTypes.number,
/**
* Index of the widget.
*/
widgetIndex: PropTypes.number,
/**
* Indicates weatehr dashboard is in ediable mode or not.
*/
editable: PropTypes.bool,
/**
* User provided widget frame that should be used instead of the default one.
*/
frameComponent: PropTypes.func,
/**
* User provided settings for be use by custom widget frame.
*/
frameSettings: PropTypes.object,
/**
* Name of the widget.
*/
widgetName: PropTypes.string,
/**
* Custom id of the widget.
*/
widgetId: PropTypes.string,
/**
* Title of the widget.
*/
title: PropTypes.string,
/**
* Weather the component is being dragged.
*/
isDragging: PropTypes.bool,
/**
* ReactDnd's connectDragSource().
*/
connectDragSource: PropTypes.func,
/**
* ReactDnd's connectDropTarget().
*/
connectDropTarget: PropTypes.func,
/**
* Function that should be called when a widget is about to be removed.
*/
onRemove: PropTypes.func,
/**
* Function called when to edit a widget.
*/
onEdit: PropTypes.func,
};
WidgetFrame.defaultProps = {
frameSettings: {},
};
export default WidgetFrame;