// DragItem.tsx import React, { DragEventHandler, useRef } from "react"; export interface IDragItem<T> { type: string; data: T; } let _dragItem: IDragItem<any> | null = null; export function getDragItem<T>(): IDragItem<T> | null { return _dragItem; } export function setDragItem(type: string, data: any) { _dragItem = { type, data }; } export function clearDragItem() { _dragItem = null; } export function DragItem(props: { type: string, data?: any; children: any; className?: string, style?: React.CSSProperties }) { const { type, data, children, className, style } = props; const ref = useRef<HTMLDivElement>(null); const onDragStart: DragEventHandler<HTMLDivElement> = (e) => { setDragItem(type, data); const div = (e.target as HTMLDivElement); div.classList.add("dragging"); // let img = new Image(); // img.src = 'example.gif'; // ev.dataTransfer.setDragImage(img, 10, 10); // e.dataTransfer.dropEffect = "copy"; // copy | move | link }; const onDragEnd: DragEventHandler<HTMLDivElement> = (e) => { clearDragItem(); const div = (e.target as HTMLDivElement); div.classList.remove("dragging"); }; return <div ref={ref} className={className} style={style} draggable onDragStart={onDragStart} onDragEnd={onDragEnd}>{children}</div>; } // DropZone.tsx export interface IOnDroppedEvent<T, U> { x: number; y: number; pageX: number; pageY: number; item: T, data: U, target: EventTarget; } export function DropZone<T, U>(props: { types: string[], children: any, onDropped: (e: IOnDroppedEvent<T, U>) => void, className?: string, style?: React.CSSProperties, item: T }) { const { types, onDropped, children, className, style, item } = props; const ref = useRef<HTMLDivElement>(null); const onDrop: DragEventHandler<HTMLDivElement> = (e) => { e.preventDefault(); const dragItem = getDragItem<U>(); if (!dragItem) return false; if (!types.includes(dragItem.type)) return false; e.stopPropagation(); const div = ref.current!; const rect = div.getBoundingClientRect(); const event: IOnDroppedEvent<T, U> = { x: e.pageX - rect.left, y: e.pageY - rect.top, pageX: e.pageX, pageY: e.pageY, item: item, data: dragItem.data, target: e.target }; onDropped(event); div.classList.remove("drag-over"); }; const onDragOver: DragEventHandler<HTMLDivElement> = (e) => { const dragItem = getDragItem<U>(); if (!dragItem) return false; if (!types.includes(dragItem.type)) return false; const div = ref.current!; div.classList.add("drag-over"); e.preventDefault(); // allow drop e.stopPropagation(); return false; }; const onDragEnter: DragEventHandler<HTMLDivElement> = (e) => { const dragItem = getDragItem<U>(); if (!dragItem) return false; if (!types.includes(dragItem.type)) return false; const div = ref.current!; div.classList.add("drag-over"); e.preventDefault(); // allow drop e.stopPropagation(); return false; }; const onDragLeave: DragEventHandler<HTMLDivElement> = (e) => { const div = ref.current!; div.classList.remove("drag-over"); return false; }; return <div ref={ref} className={className} style={style} onDrop={onDrop} onDragOver={onDragOver} onDragEnter={onDragEnter} onDragLeave={onDragLeave}>{children}</div>; }
568200cookie-checkReact: Drag Drop (Custom Implementation)