Skip to content

Instantly share code, notes, and snippets.

@resistdesign
Created October 8, 2020 16:27
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save resistdesign/8ac31259d0296314f3aa5556d28785bf to your computer and use it in GitHub Desktop.
Save resistdesign/8ac31259d0296314f3aa5556d28785bf to your computer and use it in GitHub Desktop.
React Drag And Drop
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