Created
October 8, 2020 16:27
-
-
Save resistdesign/8ac31259d0296314f3aa5556d28785bf to your computer and use it in GitHub Desktop.
React Drag And Drop
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import T from 'prop-types'; | |
import React, {PureComponent} from 'react'; | |
export default class DragDropItem extends PureComponent { | |
static propTypes = { | |
active: T.bool, | |
dataType: T.string, | |
data: T.string, | |
draggable: T.bool, | |
dropZone: T.bool, | |
onDragStart: T.func, | |
onDragEnter: T.func, | |
onDragLeave: T.func, | |
onDataDropped: T.func, | |
dataTypeMap: T.objectOf( | |
T.string | |
), | |
dataDropMap: T.objectOf( | |
T.func | |
), | |
ignoreDataTypeCase: T.bool, | |
component: T.oneOfType([ | |
T.func, | |
T.object | |
]), | |
dragIndicatorElement: T.object, | |
dragHoverProps: T.object | |
}; | |
static defaultProps = { | |
active: true, | |
dataType: undefined, | |
draggable: true, | |
dropZone: true | |
}; | |
state = { | |
dragHovering: false | |
}; | |
getExtraProps() { | |
const extraProps = { | |
...this.props | |
}; | |
for (const k in DragDropItem.propTypes) { | |
if (DragDropItem.propTypes.hasOwnProperty(k)) { | |
delete extraProps[k]; | |
} | |
} | |
return extraProps; | |
} | |
getCleanDataTypeName = (dataTypeName = '') => { | |
const {ignoreDataTypeCase} = this.props; | |
return ignoreDataTypeCase ? dataTypeName : dataTypeName.toLowerCase(); | |
}; | |
onDragStart = (event) => { | |
const { | |
dataType, | |
data, | |
dataTypeMap, | |
onDragStart, | |
dragIndicatorElement | |
} = this.props; | |
event.persist(); | |
if (!!dragIndicatorElement) { | |
event.dataTransfer.setDragImage(dragIndicatorElement, 50, 50); | |
} | |
if (typeof dataType === 'string') { | |
event.dataTransfer.setData(this.getCleanDataTypeName(dataType), data); | |
event.dropEffect = 'move'; | |
} | |
if (dataTypeMap instanceof Object) { | |
for (const k in dataTypeMap) { | |
if (dataTypeMap.hasOwnProperty(k)) { | |
event.dataTransfer.setData(this.getCleanDataTypeName(k), dataTypeMap[k]); | |
} | |
} | |
} | |
if (onDragStart instanceof Function) { | |
onDragStart(event); | |
} | |
}; | |
onDragEnter = (event) => { | |
const { | |
onDragEnter | |
} = this.props; | |
this.setState({ | |
dragHovering: true | |
}); | |
if (onDragEnter instanceof Function) { | |
onDragEnter(event); | |
} | |
}; | |
onDragLeave = (event) => { | |
const { | |
onDragLeave | |
} = this.props; | |
this.setState({ | |
dragHovering: false | |
}); | |
if (onDragLeave instanceof Function) { | |
onDragLeave(event); | |
} | |
}; | |
onDragOver = (event) => { | |
const { | |
dataType, | |
dropZone, | |
dataDropMap | |
} = this.props; | |
if (dropZone) { | |
const cleanIncomingTypes = event.dataTransfer.types.map( | |
(t = '') => this.getCleanDataTypeName(t) | |
); | |
if (typeof dataType === 'string') { | |
const cleanDataType = this.getCleanDataTypeName(dataType); | |
if (cleanIncomingTypes.indexOf(cleanDataType) !== -1) { | |
event.preventDefault(); | |
event.stopPropagation(); | |
event.dataTransfer.dropEffect = 'move'; | |
} | |
} | |
if (dataDropMap instanceof Object) { | |
const dropTypes = Object.keys(dataDropMap) | |
.map(key => this.getCleanDataTypeName(key)); | |
for (let i = 0; i < cleanIncomingTypes.length; i++) { | |
const type = cleanIncomingTypes[i]; | |
if (dropTypes.indexOf(type) !== -1) { | |
event.preventDefault(); | |
event.stopPropagation(); | |
event.dataTransfer.dropEffect = 'move'; | |
} | |
} | |
} | |
} | |
}; | |
onDrop = (event) => { | |
const { | |
dataType, | |
dropZone, | |
data: targetData, | |
onDataDropped, | |
dataDropMap | |
} = this.props; | |
if (dropZone) { | |
const cleanIncomingTypes = event.dataTransfer.types.map( | |
(t = '') => this.getCleanDataTypeName(t) | |
); | |
if ( | |
typeof dataType === 'string' && | |
cleanIncomingTypes.indexOf(this.getCleanDataTypeName(dataType)) !== -1 | |
) { | |
const data = event.dataTransfer.getData(this.getCleanDataTypeName(dataType)); | |
event.preventDefault(); | |
event.stopPropagation(); | |
if (onDataDropped instanceof Function) { | |
onDataDropped(data, targetData, event); | |
} | |
} | |
if (dataDropMap instanceof Object) { | |
const cleanDropMap = Object.keys(dataDropMap) | |
.reduce((acc, key) => { | |
acc[this.getCleanDataTypeName(key)] = dataDropMap[key]; | |
return acc; | |
}, {}); | |
for (let i = 0; i < cleanIncomingTypes.length; i++) { | |
const type = cleanIncomingTypes[i]; | |
const onTypeDrop = cleanDropMap[type]; | |
if (onTypeDrop instanceof Function) { | |
const data = event.dataTransfer.getData(type); | |
event.preventDefault(); | |
event.stopPropagation(); | |
onTypeDrop(data, event); | |
} | |
} | |
} | |
} | |
}; | |
render() { | |
const { | |
dragHovering = false | |
} = this.state; | |
const { | |
active, | |
draggable, | |
children, | |
component: RootComponent, | |
dragHoverProps = {} | |
} = this.props; | |
const extraProps = this.getExtraProps(); | |
const currentProps = { | |
draggable: draggable, | |
onDragStart: this.onDragStart, | |
onDragEnter: this.onDragEnter, | |
onDragLeave: this.onDragLeave, | |
onDragOver: this.onDragOver, | |
onDrop: this.onDrop | |
}; | |
const mergedProps = { | |
...extraProps, | |
...(dragHovering ? dragHoverProps : {}), | |
...(active ? currentProps : {}) | |
}; | |
const hasRootComponent = RootComponent instanceof Function || RootComponent instanceof Object; | |
return hasRootComponent ? ( | |
<RootComponent | |
{...mergedProps} | |
> | |
{children} | |
</RootComponent> | |
) : ( | |
<div | |
{...mergedProps} | |
> | |
{children} | |
</div> | |
); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment