From 2484135c91a0537486fbb041b6bd0229fa376db5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Loi=CC=88c=20Correnson?= <loic.correnson@cea.fr> Date: Fri, 3 Jun 2022 08:54:25 +0200 Subject: [PATCH] [dome/dnd] DropTarget introduction --- ivette/src/dome/renderer/newdnd.tsx | 86 +++++++++++++++++++++++++---- ivette/src/sandbox/usednd.tsx | 2 +- 2 files changed, 76 insertions(+), 12 deletions(-) diff --git a/ivette/src/dome/renderer/newdnd.tsx b/ivette/src/dome/renderer/newdnd.tsx index fc40741d610..4fca855045d 100644 --- a/ivette/src/dome/renderer/newdnd.tsx +++ b/ivette/src/dome/renderer/newdnd.tsx @@ -32,6 +32,7 @@ import React from 'react'; import { classes, styles } from 'dome/misc/utils'; import { DraggableCore, + DraggableEvent, DraggableEventHandler } from 'react-draggable'; @@ -40,8 +41,6 @@ import { - `rootX,rootY` is the position where dragging started; - `dragX,dragY` is the current dragging position; - `rect` is the original DOM Rectangle of the dragged HTML node. - - Hence, the relative move during dragging is simply `(dragX-rootX,dragY-rootY)`. */ export interface Dragging { rootX: number; @@ -51,6 +50,50 @@ export interface Dragging { rect: DOMRect; } +let nodeId = 0; +function fresh(): string { + while (1) { + const id = `dome-dnd-${++nodeId}`; + if (!document.getElementById(id)) + return id; + } + return '<crash>'; +} + +type DropZone = () => void; + +export class DnD { + + private registry = new Map<string, DropZone>(); + + onDropZone(node: Element, zone: DropZone): void { + let id = node.id; + if (!id) id = node.id = fresh(); + this.registry.set(id, zone); + } + + offDropZone(node: Element): void { + const id = node.id; + if (id) this.registry.delete(id); + } + + handleEvent(e: DraggableEvent): void { + if (e instanceof MouseEvent) { + document + .elementsFromPoint(e.clientX, e.clientY) + .find((elt) => { + const zone = this.registry.get(elt.id); + if (zone) { + zone(); + return true; + } + return false; + }); + } + } + +} + /** Can be used to conditionally render an element wrt to dragging informations. */ @@ -92,10 +135,12 @@ function RenderOverlay( innerStyle: styles(style, props.styleDragging, position), }; } - return { outerClass: className, outerStyle: style } + return { outerClass: className, outerStyle: style }; } export interface DragSourceProps { + /** DnD controller. */ + dnd?: DnD; /** Disabled dragging. */ disabled?: boolean; /** Class of the element from where a drag can be initiated. */ @@ -104,9 +149,11 @@ export interface DragSourceProps { className?: string; /** Style of the DragSource elements. */ style?: React.CSSProperties; - /** Additional class for the dragged (initial) element (default is `'dome-dragged'`). */ + /** Additional class for the dragged (initial) element. + Default is `'dome-dragged'`. */ classDragged?: string; - /** Additional class for the dragging (moved) element (default is `'dome-dragging'`) */ + /** Additional class for the dragging (moved) element. + Default is `'dome-dragging'`. */ classDragging?: string; /** Additional style for the dragged (initial) element. */ styleDragged?: React.CSSProperties; @@ -135,9 +182,9 @@ export interface DragSourceProps { The content can be rendered conditionnaly by using a function. */ -export function DragSource(props: DragSourceProps): JSX.Element | null { +export function DragSource(props: DragSourceProps): JSX.Element { //--- Props - const { disabled, handle, children } = props; + const { dnd, disabled, handle, children } = props; const { onStart, onDrag, onStop } = props; //--- Dragging State const [dragging, setDragging] = React.useState<Dragging | undefined>(); @@ -153,12 +200,14 @@ export function DragSource(props: DragSourceProps): JSX.Element | null { }, [onStart]); //--- onDrag const handleDrag: DraggableEventHandler = React.useCallback( - (_, { x, y }) => { + (e, { x, y }) => { + if (e && dnd) dnd.handleEvent(e); if (dragging) { - setDragging({ ...dragging, dragX: x, dragY: y }); - if (onDrag) onDrag(dragging); + const newDragging = { ...dragging, dragX: x, dragY: y }; + setDragging(newDragging); + if (onDrag) onDrag(newDragging); } - }, [dragging, onDrag]); + }, [dnd, dragging, onDrag]); //--- onStop const handleStop: DraggableEventHandler = React.useCallback( () => { @@ -183,3 +232,18 @@ export function DragSource(props: DragSourceProps): JSX.Element | null { </DraggableCore> ); } + +export function useDropTarget( + dnd: DnD, zone?: DropZone +): React.RefObject<HTMLDivElement> { + const nodeRef = React.useRef<HTMLDivElement>(null); + const node = nodeRef.current; + React.useEffect(() => { + if (node && zone) { + dnd.onDropZone(node, zone); + return () => dnd.offDropZone(node); + } + return; + }, [dnd, node, zone]); + return nodeRef; +} diff --git a/ivette/src/sandbox/usednd.tsx b/ivette/src/sandbox/usednd.tsx index aa6c4b67b66..00f2d85bc87 100644 --- a/ivette/src/sandbox/usednd.tsx +++ b/ivette/src/sandbox/usednd.tsx @@ -35,7 +35,7 @@ import './sandbox.css'; const delta = (id: string, d: DnD.Dragging): string => { const dx = d.dragX - d.rootX; const dy = d.dragY - d.rootY; - return `${id} ${dx}:${dy}` + return `${id} ${dx}:${dy}`; }; interface ItemProps { -- GitLab