Spaces:
Running
Running
File size: 4,472 Bytes
6bcb42f |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 |
import bindAll from 'lodash.bindall';
import PropTypes from 'prop-types';
import React from 'react';
import omit from 'lodash.omit';
import { connect } from 'react-redux';
/**
* Higher Order Component to give components the ability to react to drag overs
* and drops of objects stored in the assetDrag redux state.
*
* Example: You want to enable MyComponent to receive drops from a drag type
* Wrapped = DropAreaHOC([...dragTypes])(
* <MyComponent />
* )
*
* MyComponent now receives 2 new props
* containerRef: a ref that must be set on the container element
* dragOver: boolean if an asset is being dragged above the component
*
* Use the wrapped component:
* <Wrapped onDrop={yourDropHandler} />
*
* NB: This HOC _only_ works with objects that drag using the assetDrag reducer.
* This _does not_ handle drags for blocks coming from the workspace.
*
* @param {Array.<string>} dragTypes Types to respond to, from DragConstants
* @returns {function} The HOC, specialized for those drag types
*/
const DropAreaHOC = function (dragTypes) {
/**
* Return the HOC, specialized for the dragTypes
* @param {React.Component} WrappedComponent component to receive drop behaviors
* @returns {React.Component} component with drag over/drop behavior
*/
return function (WrappedComponent) {
class DropAreaWrapper extends React.Component {
constructor(props) {
super(props);
bindAll(this, [
'setRef'
]);
this.state = {
dragOver: false
};
this.ref = null;
this.containerBox = null;
}
componentWillReceiveProps(newProps) {
// If `dragging` becomes true, record the drop area rectangle
if (newProps.dragInfo.dragging && !this.props.dragInfo.dragging) {
this.dropAreaRect = this.ref && this.ref.getBoundingClientRect();
// If `dragging` becomes false, call the drop handler
} else if (!newProps.dragInfo.dragging && this.props.dragInfo.dragging && this.state.dragOver) {
this.props.onDrop(this.props.dragInfo);
this.setState({ dragOver: false });
}
// If a drag is in progress (currentOffset) and it matches the relevant drag types,
// test if the drag is within the drop area rect and set the state accordingly.
if (this.dropAreaRect && newProps.dragInfo.currentOffset &&
dragTypes.includes(newProps.dragInfo.dragType)) {
const { x, y } = newProps.dragInfo.currentOffset;
const { top, right, bottom, left } = this.dropAreaRect;
if (x > left && x < right && y > top && y < bottom) {
this.setState({ dragOver: true });
} else {
this.setState({ dragOver: false });
}
}
}
setRef(el) {
this.ref = el;
if (this.props.componentRef) {
this.props.componentRef(this.ref);
}
}
render() {
const componentProps = omit(this.props, ['onDrop', 'dragInfo', 'componentRef']);
return (
<WrappedComponent
containerRef={this.setRef}
dragOver={this.state.dragOver}
{...componentProps}
/>
);
}
}
DropAreaWrapper.propTypes = {
componentRef: PropTypes.func,
dragInfo: PropTypes.shape({
currentOffset: PropTypes.shape({
x: PropTypes.number,
y: PropTypes.number
}),
dragType: PropTypes.string,
dragging: PropTypes.bool,
index: PropTypes.number
}),
onDrop: PropTypes.func
};
const mapStateToProps = state => ({
dragInfo: state.scratchGui.assetDrag
});
const mapDispatchToProps = () => ({});
return connect(
mapStateToProps,
mapDispatchToProps
)(DropAreaWrapper);
};
};
export default DropAreaHOC;
|