Created
July 27, 2021 05:38
-
-
Save AdithyaBhat17/6a2056c8fcd54f8db659bcb036685261 to your computer and use it in GitHub Desktop.
Drag and sort react hook
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 { useCallback, useState } from 'react'; | |
| export const initialState = { | |
| draggedFrom: null, | |
| draggedTo: null, | |
| isDragging: false, | |
| defaultOrder: [], | |
| updatedOrder: [] | |
| }; | |
| /** | |
| * A custom hook to listen to drag and drop events and sort list items | |
| * @param {{isPersistent?: boolean, defaultOrder: string[] | number[]}} | |
| * @returns {{sortOrder: any[], draggedFrom: number | null, draggedTo: number | null, isDragging: boolean, defaultOrder: string[] | number[], updatedOrder: string[] | number[], onDragStart: (event: React.DragEvent<HTMLDivElement>) => void, onDrop: () => void, onDragOver: (event: React.DragEvent<HTMLDivElement>) => void}} | |
| * @author Adithya NR | |
| */ | |
| export default function useDraggable({ | |
| isPersistent = false, | |
| defaultOrder = [] | |
| }) { | |
| const [dnd, setDnD] = useState(initialState); | |
| const persistedOrder = JSON.parse(sessionStorage.getItem('sortOrder')); | |
| const [sortOrder, setSortOrder] = useState(persistedOrder || defaultOrder); | |
| /** | |
| * | |
| * @param {React.DragEvent<HTMLDivElement>} event | |
| */ | |
| const onDragOver = useCallback( | |
| event => { | |
| // prevent default behaviour of dragover to block dropping list item | |
| event.preventDefault(); | |
| // eslint-disable-next-line no-param-reassign | |
| event.dataTransfer.dropEffect = 'move'; | |
| // initialize a new list | |
| let newList = dnd.defaultOrder; | |
| const { draggedFrom } = dnd; | |
| // get draggedTo position from data-position | |
| const draggedTo = Number(event.currentTarget.dataset.position); | |
| // fetch the dragged item from newList | |
| const itemDragged = newList[draggedFrom]; | |
| // find remaining idle items. | |
| const idleItems = newList.filter((_i, index) => index !== draggedFrom); | |
| // update newList order | |
| newList = [ | |
| ...idleItems.slice(0, draggedTo), | |
| itemDragged, | |
| ...idleItems.slice(draggedTo) | |
| ]; | |
| // if element was dropped to its origin, we don't update the state | |
| if (draggedTo !== dnd.draggedTo) { | |
| setDnD({ | |
| ...dnd, | |
| updatedOrder: newList, | |
| draggedTo | |
| }); | |
| } | |
| }, | |
| [dnd] | |
| ); | |
| /** | |
| * @param {React.DragEvent<HTMLDivElement>} event | |
| */ | |
| const onDragStart = useCallback( | |
| event => { | |
| // fetch initial position from data-position | |
| const initialPosition = Number(event.currentTarget.dataset.position); | |
| event.dataTransfer.effectAllowed = 'move'; | |
| setDnD({ | |
| ...dnd, | |
| draggedFrom: initialPosition, | |
| isDragging: true, | |
| defaultOrder: sortOrder | |
| }); | |
| // for firefox | |
| event.dataTransfer.setData('text/html', ''); | |
| }, | |
| [dnd, sortOrder] | |
| ); | |
| const onDrop = useCallback(() => { | |
| // once the item is dropped, we can now sort the list and clean up the state | |
| setSortOrder(dnd.updatedOrder); | |
| if (isPersistent) { | |
| sessionStorage.setItem('sortOrder', JSON.stringify(dnd.updatedOrder)); | |
| } | |
| setDnD({ | |
| ...dnd, | |
| draggedFrom: null, | |
| draggedTo: null, | |
| isDragging: false | |
| }); | |
| }, [dnd, isPersistent]); | |
| return { | |
| sortOrder, | |
| ...dnd, | |
| onDragStart, | |
| onDragOver, | |
| onDrop | |
| }; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment