import { useEventListener } from '@chakra-ui/react' import { ButtonItem, DraggableStep, DraggableStepType } from 'models' import { createContext, Dispatch, ReactNode, SetStateAction, useContext, useRef, useState, } from 'react' import { Coordinates } from './GraphContext' type BlockInfo = { id: string ref: React.MutableRefObject } const graphDndContext = createContext<{ draggedStepType?: DraggableStepType setDraggedStepType: Dispatch> draggedStep?: DraggableStep setDraggedStep: Dispatch> draggedItem?: ButtonItem setDraggedItem: Dispatch> mouseOverBlock?: BlockInfo setMouseOverBlock: Dispatch> // eslint-disable-next-line @typescript-eslint/ban-ts-comment //@ts-ignore }>({}) export type NodePosition = { absolute: Coordinates; relative: Coordinates } export const GraphDndContext = ({ children }: { children: ReactNode }) => { const [draggedStep, setDraggedStep] = useState() const [draggedStepType, setDraggedStepType] = useState< DraggableStepType | undefined >() const [draggedItem, setDraggedItem] = useState() const [mouseOverBlock, setMouseOverBlock] = useState() return ( {children} ) } export const useDragDistance = ({ ref, onDrag, distanceTolerance = 20, isDisabled = false, }: { ref: React.MutableRefObject onDrag: (position: { absolute: Coordinates; relative: Coordinates }) => void distanceTolerance?: number isDisabled: boolean }) => { const mouseDownPosition = useRef<{ absolute: Coordinates relative: Coordinates }>() const handleMouseUp = () => { if (mouseDownPosition) mouseDownPosition.current = undefined } useEventListener('mouseup', handleMouseUp) const handleMouseDown = (e: MouseEvent) => { if (isDisabled || !ref.current) return e.stopPropagation() const { top, left } = ref.current.getBoundingClientRect() mouseDownPosition.current = { absolute: { x: e.clientX, y: e.clientY }, relative: { x: e.clientX - left, y: e.clientY - top, }, } } useEventListener('mousedown', handleMouseDown, ref.current) const handleMouseMove = (e: MouseEvent) => { if (!mouseDownPosition.current) return const { clientX, clientY } = e if ( Math.abs(mouseDownPosition.current.absolute.x - clientX) > distanceTolerance || Math.abs(mouseDownPosition.current.absolute.y - clientY) > distanceTolerance ) { onDrag(mouseDownPosition.current) } } useEventListener('mousemove', handleMouseMove) } export const computeNearestPlaceholderIndex = ( offsetY: number, placeholderRefs: React.MutableRefObject ) => { const { closestIndex } = placeholderRefs.current.reduce( (prev, elem, index) => { const elementTop = elem.getBoundingClientRect().top const mouseDistanceFromPlaceholder = Math.abs(offsetY - elementTop) return mouseDistanceFromPlaceholder < prev.value ? { closestIndex: index, value: mouseDistanceFromPlaceholder } : prev }, { closestIndex: 0, value: 999999999999 } ) return closestIndex } export const useStepDnd = () => useContext(graphDndContext)