import React, {createContext, ReactNode, useContext, useId, useMemo, useRef} from "react"; import { useDrop, useDrag } from "react-dnd"; const DragAndDropContext = createContext([] as any); const DropContext = createContext([] as any); export interface DragAndDropProviderProps { id?: string; onMove?: false|((source: T, dest: T) => void); children?: ReactNode; } export default function DragAndDropProvider({ id, onMove, children }: DragAndDropProviderProps) { const _id = useId(); const value = useMemo(() => ([id || _id, onMove]), [_id, id, onMove]); return {children} ; } export interface DragAndDropItemProps { item?: T; children: ReactNode; } export function DragAndDropItem({ item, children }: DragAndDropItemProps) { const [ id, onMove ] = useContext(DragAndDropContext); //if (!id || !onMove || (item !== 0 && !item)) // return <>{children}; return {children} ; } export function DragAndDropItemInner({ item, children }: DragAndDropItemProps) { const [ id, onMove ] = useContext(DragAndDropContext); const ref = useRef(null); const previewRef = useRef(null); const sym = useMemo(() => Symbol(), []); const [, drop] = useDrop(() => ({ accept: id || sym, hover(item2: any, monitor) { if (!ref.current) { return; } const dragIndex = item2.item; const hoverIndex = item; if (dragIndex === hoverIndex) { return } const hoverBoundingRect = ref.current?.getBoundingClientRect(); const hoverMiddleY = (hoverBoundingRect.bottom - hoverBoundingRect.top) / 2; const clientOffset = monitor.getClientOffset()!; const hoverClientY = clientOffset.y - hoverBoundingRect.top; if (dragIndex < hoverIndex! && hoverClientY < hoverMiddleY) { return } if (dragIndex > hoverIndex! && hoverClientY > hoverMiddleY) { return } onMove(dragIndex, hoverIndex); item2.item = hoverIndex; }, }), [item, onMove]); const [{ isDragging }, drag, preview] = useDrag(() => ({ type: id || sym, item: () => { return { item } }, collect: (monitor) => ({ isDragging: monitor.isDragging(), }), }), [id, item]); const enabled = (item === 0 || item || null); if (enabled) { drag(ref); drop(preview(previewRef)); } const child = React.Children.only(children) as any; return {React.cloneElement(child, { ref: previewRef, style: { ...(child.props.style || {}), opacity: !isDragging ? 1 : .5 } })} ; } export function useDragContext() { return useContext(DropContext) || []; }