Last active
March 22, 2024 11:39
-
-
Save codemzy/99282424b323d0741e76e1cf7c62f2e4 to your computer and use it in GitHub Desktop.
React Drag-and-Drop Component
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 React from 'react'; | |
// sub-components | |
import DragItem from './DragItem'; | |
import DropZone from './DropZone'; | |
import DropZones from './DropZones'; | |
import DropGuide from './DropGuide'; | |
// context for the drag | |
export const DragContext = React.createContext(); | |
// drag context component | |
function Drag({ draggable = true, handleDrop, children }) { | |
const [dragItem, setDragItem] = React.useState(null); // the item id being dragged | |
const [dragType, setDragType] = React.useState(null); // if multiple types of drag item | |
const [isDragging, setIsDragging] = React.useState(null); // drag is happening | |
const [drop, setDrop] = React.useState(null); // the active dropzone | |
React.useEffect(() => { | |
if (dragItem) { | |
document.body.style.cursor = "grabbing"; // changes mouse to grabbing while dragging | |
} else { | |
document.body.style.cursor = "default"; // back to default when no dragItem | |
} | |
}, [dragItem]); // runs when dragItem state changes | |
const dragStart = function(e, dragId, dragType) { | |
e.stopPropagation(); | |
e.dataTransfer.effectAllowed = 'move'; | |
setDragItem(dragId); | |
setDragType(dragType); | |
}; | |
const drag = function(e, dragId, dragType) { | |
e.stopPropagation(); | |
setIsDragging(true); | |
}; | |
const dragEnd = function() { | |
setDragItem(null); | |
setDragType(null); | |
setIsDragging(false); | |
setDrop(null); | |
}; | |
const onDrop = function(e) { | |
e.preventDefault(); | |
handleDrop({ dragItem, dragType, drop }); | |
setDragItem(null); | |
setDragType(null); | |
setIsDragging(false); | |
setDrop(null); | |
}; | |
return ( | |
<DragContext.Provider value={{ draggable, dragItem, dragType, isDragging, dragStart, drag, dragEnd, drop, setDrop, onDrop }}> | |
{ typeof children === "function" ? children({ activeItem: dragItem, activeType: dragType, isDragging }) : children } | |
</DragContext.Provider> | |
); | |
}; | |
// export Drag and assign sub-components | |
export default Object.assign(Drag, { DragItem, DropZone, DropZones, DropGuide }); |
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 React from 'react'; | |
// context | |
import DragContext from './Drag'; | |
// a draggable item | |
function DragItem({ as, dragId, dragType, ...props }) { | |
const { draggable, dragStart, drag, dragEnd } = React.useContext(DragContext); | |
let Component = as || "div"; | |
return <Component onDragStart={(e) => dragStart(e, dragId, dragType)} onDrag={drag} draggable={draggable} onDragEnd={dragEnd} {...props} />; | |
}; | |
export default DragItem; |
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 React from 'react'; | |
// context | |
import DragContext from './Drag'; | |
// indicates where the drop will go when dragging over a dropzone | |
function DropGuide({ as, dropId, ...props }) { | |
const { drop } = React.useContext(DragContext); | |
let Component = as || "div"; | |
return drop === dropId ? <Component {...props} /> : null; | |
}; | |
export default DropGuide; |
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 React from 'react'; | |
// context | |
import DragContext from './Drag'; | |
// listens for drags over drop zones | |
function DropZone({ as, dropId, dropType, remember, style, children, ...props }) { | |
const { dragItem, dragType, setDrop, drop, onDrop } = React.useContext(DragContext); | |
function handleDragOver(e) { | |
if (e.preventDefault) { | |
e.preventDefault(); | |
} | |
return false; | |
}; | |
function handleLeave() { | |
if (!remember) { | |
setDrop(null); | |
} | |
}; | |
let Component = as || "div"; | |
return ( | |
<Component onDragEnter={(e) => dragItem && dropType === dragType && setDrop(dropId)} onDragOver={handleDragOver} onDrop={onDrop} style={{position: "relative", ...style}} {...props}> | |
{ children } | |
{ drop === dropId && <div style={{position: "absolute", inset: "0px"}} onDragLeave={handleLeave}></div> } | |
</Component> | |
); | |
}; | |
export default DropZone; |
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 React from 'react'; | |
import DropZone from './DropZone'; | |
// context | |
import DragContext from './Drag'; | |
// if we need multiple dropzones | |
function DropZones({ dropType, prevId, nextId, split = "y", remember, children, ...props }) { | |
const { dragType, isDragging } = React.useContext(DragContext); | |
return ( | |
<div style={{position: "relative"}} {...props}> | |
{ children } | |
{ dragType === dropType && isDragging && | |
<div style={{position: "absolute", inset: "0px", display: "flex", flexDirection: split === "x" ? "row" : "column" }}> | |
<DropZone dropId={prevId} style={{ width: "100%", height: "100%" }} dropType={dropType} remember={remember} /> | |
<DropZone dropId={nextId} style={{ width: "100%", height: "100%" }} dropType={dropType} remember={remember} /> | |
</div> | |
} | |
</div> | |
); | |
}; | |
export default DropZones; |
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
export * from './Drag'; | |
export { default } from './Drag'; |
i using this code and I've get this error how can i fix it ??
It looks like the error says you need to end the file with .jsx
file extension. I think with my setup (not using Vite) I can use .js
extension and it knows if it is JSX but Vite does not allow usage of JSX syntax within .js files by default. Switching to Drag.jsx
might fix it.
There is a thread here with more info on Vite config for JSX in js files: https://stackoverflow.com/questions/74620427/how-to-configure-vite-to-allow-jsx-syntax-in-js-files
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
How to install
Download the Gist and rename the file "Drag", then drag it into your
/components
directory.How to use
You can find more about how I built this component in Building a reusable drag and drop component with React with tips and examples for how to use it.