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