From 1ba733d783ca9c2c4b82045793e4c90f16b1be15 Mon Sep 17 00:00:00 2001
From: Sivan Duijn <sivanduijn@gmail.com>
Date: Sun, 6 Mar 2022 17:28:43 +0100
Subject: [PATCH] feat(querybuilder): split logic up in usecases

---
 .../components/querybuilder/querybuilder.tsx  | 32 +++++++++++++------
 libs/querybuilder/usecases/src/index.ts       |  4 ++-
 .../src/lib/createReactFlowElements.ts        |  5 ++-
 .../src/lib/dragging/dragAttribute.ts         | 12 +++++++
 .../src/lib/dragging/dragAttributesAlong.ts   | 27 ++++++++++++++++
 .../{connPointsOnPills.ts => pillHandles.ts}  |  0
 6 files changed, 66 insertions(+), 14 deletions(-)
 create mode 100644 libs/querybuilder/usecases/src/lib/dragging/dragAttribute.ts
 create mode 100644 libs/querybuilder/usecases/src/lib/dragging/dragAttributesAlong.ts
 rename libs/querybuilder/usecases/src/lib/{connPointsOnPills.ts => pillHandles.ts} (100%)

diff --git a/apps/web-graphpolaris/src/components/querybuilder/querybuilder.tsx b/apps/web-graphpolaris/src/components/querybuilder/querybuilder.tsx
index 8e42ebf76..031156107 100644
--- a/apps/web-graphpolaris/src/components/querybuilder/querybuilder.tsx
+++ b/apps/web-graphpolaris/src/components/querybuilder/querybuilder.tsx
@@ -1,4 +1,8 @@
-import { createReactFlowElements } from '@graphpolaris/querybuilder/usecases';
+import {
+  createReactFlowElements,
+  DragAttributePill,
+  DragAttributesAlong,
+} from '@graphpolaris/querybuilder/usecases';
 import {
   setQuerybuilderNodes,
   useAppDispatch,
@@ -15,6 +19,7 @@ import ReactFlow, {
   FlowElement,
   Background,
   Node,
+  isNode,
 } from 'react-flow-renderer';
 import styles from './querybuilder.module.scss';
 import ConnectionLine from './customFlowLines/connection';
@@ -46,20 +51,27 @@ const QueryBuilder = (props: {}) => {
     event: React.MouseEvent<Element, MouseEvent>,
     node: Node<any>
   ) => {
-    const attr = nodes.getNodeAttributes(node.id);
-    const dx = node.position.x - attr.x;
-    const dy = node.position.y - attr.y;
+    const pNode = elements.find((e) => e.id == node.id);
+    if (!(pNode && isNode(pNode))) return;
 
-    nodes.forEachInNeighbor(node.id, (nb) => {
-      if (nodes.getNodeAttribute(nb, 'type') == 'attribute') {
-        nodes.updateNodeAttribute(nb, 'x', (x) => x + dx);
-        nodes.updateNodeAttribute(nb, 'y', (y) => y + dy);
-      }
-    });
+    const dx = node.position.x - pNode.position.x;
+    const dy = node.position.y - pNode.position.y;
 
     nodes.setNodeAttribute(node.id, 'x', node.position.x);
     nodes.setNodeAttribute(node.id, 'y', node.position.y);
 
+    switch (nodes.getNodeAttribute(node.id, 'type')) {
+      case 'attribute':
+        DragAttributePill(node.id, nodes, dx, dy);
+        break;
+      case 'entity':
+        DragAttributesAlong(node.id, nodes, dx, dy);
+        break;
+      case 'relation':
+        DragAttributesAlong(node.id, nodes, dx, dy);
+        break;
+    }
+
     dispatch(setQuerybuilderNodes(nodes.export()));
   };
 
diff --git a/libs/querybuilder/usecases/src/index.ts b/libs/querybuilder/usecases/src/index.ts
index 587b11ec5..b84282029 100644
--- a/libs/querybuilder/usecases/src/index.ts
+++ b/libs/querybuilder/usecases/src/index.ts
@@ -1,4 +1,6 @@
 export * from './lib/attribute/getAttributeBoolOperators';
 export * from './lib/attribute/checkInput';
 export * from './lib/createReactFlowElements';
-export * from './lib/connPointsOnPills';
+export * from './lib/pillHandles';
+export * from './lib/dragging/dragAttribute';
+export * from './lib/dragging/dragAttributesAlong';
diff --git a/libs/querybuilder/usecases/src/lib/createReactFlowElements.ts b/libs/querybuilder/usecases/src/lib/createReactFlowElements.ts
index 76b9e83c4..1757b3291 100644
--- a/libs/querybuilder/usecases/src/lib/createReactFlowElements.ts
+++ b/libs/querybuilder/usecases/src/lib/createReactFlowElements.ts
@@ -12,7 +12,6 @@ export function createReactFlowElements(graph: Graph): Elements<Node | Edge> {
     switch (attributes.type) {
       case 'entity':
         data = {
-          name: attributes.name,
           isConnected: graph
             .neighbors(node)
             .some((nb) => graph.getNodeAttribute(nb, 'type') == 'relation'),
@@ -20,7 +19,6 @@ export function createReactFlowElements(graph: Graph): Elements<Node | Edge> {
         break;
       case 'relation':
         data = {
-          name: attributes.name,
           isFromEntityConnected: graph
             .inNeighbors(node)
             .some((nb) => graph.getNodeAttribute(nb, 'type') == 'entity'),
@@ -38,7 +36,6 @@ export function createReactFlowElements(graph: Graph): Elements<Node | Edge> {
         if (ERNeighbors.length > 0)
           attributeOfA = graph.getNodeAttribute(ERNeighbors[0], 'type');
         data = {
-          name: attributes.name,
           datatype: attributes.datatype,
           operator: attributes.operator,
           attributeOfA: attributeOfA,
@@ -46,6 +43,8 @@ export function createReactFlowElements(graph: Graph): Elements<Node | Edge> {
         break;
       }
     }
+    // Each pill should have a name and type
+    data = { ...data, name: attributes.name };
 
     const RFNode: Node = {
       id: node,
diff --git a/libs/querybuilder/usecases/src/lib/dragging/dragAttribute.ts b/libs/querybuilder/usecases/src/lib/dragging/dragAttribute.ts
new file mode 100644
index 000000000..332ca29cd
--- /dev/null
+++ b/libs/querybuilder/usecases/src/lib/dragging/dragAttribute.ts
@@ -0,0 +1,12 @@
+import Graph from 'graphology';
+
+export function DragAttributePill(
+  id: string,
+  nodes: Graph,
+  dx: number,
+  dy: number
+) {
+  // if the attribute is still connected to an entity or relation pill, disconnect
+  const es = nodes.outEdges(id);
+  es.forEach((e) => nodes.dropEdge(e));
+}
diff --git a/libs/querybuilder/usecases/src/lib/dragging/dragAttributesAlong.ts b/libs/querybuilder/usecases/src/lib/dragging/dragAttributesAlong.ts
new file mode 100644
index 000000000..56697ab3f
--- /dev/null
+++ b/libs/querybuilder/usecases/src/lib/dragging/dragAttributesAlong.ts
@@ -0,0 +1,27 @@
+import Graph from 'graphology';
+
+/**
+ * Changes the position of connected attributes.
+ * @param id The id of the node which could have attributes connected to it (entity or relation)
+ * @param nodes The graphology query builder object
+ * @param dx The change in x
+ * @param dy The change in y
+ * @returns True if any attribute positions were changed
+ */
+export function DragAttributesAlong(
+  id: string,
+  nodes: Graph,
+  dx: number,
+  dy: number
+): boolean {
+  let didChangeAttributes = false;
+  nodes.forEachInNeighbor(id, (nb) => {
+    if (nodes.getNodeAttribute(nb, 'type') == 'attribute') {
+      nodes.updateNodeAttribute(nb, 'x', (x) => x + dx);
+      nodes.updateNodeAttribute(nb, 'y', (y) => y + dy);
+      didChangeAttributes = true;
+    }
+  });
+
+  return didChangeAttributes;
+}
diff --git a/libs/querybuilder/usecases/src/lib/connPointsOnPills.ts b/libs/querybuilder/usecases/src/lib/pillHandles.ts
similarity index 100%
rename from libs/querybuilder/usecases/src/lib/connPointsOnPills.ts
rename to libs/querybuilder/usecases/src/lib/pillHandles.ts
-- 
GitLab