From 3d4badf9c74a068252a8e43f4b1a2df3d3f1ad42 Mon Sep 17 00:00:00 2001
From: Leonardo Christino <leomilho@gmail.com>
Date: Wed, 3 Apr 2024 17:50:39 +0200
Subject: [PATCH] feat(qb): larger handle for mouse events

also fixes qb's pills growing due to large attribute names
---
 .../lib/graph-layout/graphology-layouts.ts    |  1 -
 .../lib/querybuilder/pills/FilterHandle.tsx   | 52 +++++++++++++++++--
 .../customFlowPills/entitypill/entitypill.tsx | 11 ++--
 .../relationpill/relation-handles.tsx         |  8 +--
 .../pills/pilldropdown/pilldropdown.tsx       |  5 +-
 5 files changed, 61 insertions(+), 16 deletions(-)

diff --git a/libs/shared/lib/graph-layout/graphology-layouts.ts b/libs/shared/lib/graph-layout/graphology-layouts.ts
index 4074a90cc..dd82b7093 100644
--- a/libs/shared/lib/graph-layout/graphology-layouts.ts
+++ b/libs/shared/lib/graph-layout/graphology-layouts.ts
@@ -197,7 +197,6 @@ export class GraphologyForceAtlas2Webworker extends GraphologyLayout {
     super.layout(graph, boundingBox);
 
     const sensibleSettings = forceAtlas2.inferSettings(graph);
-    console.log(sensibleSettings);
 
     const layout = new FA2Layout(graph, {
       settings: {
diff --git a/libs/shared/lib/querybuilder/pills/FilterHandle.tsx b/libs/shared/lib/querybuilder/pills/FilterHandle.tsx
index dec750f44..9dcf0c610 100644
--- a/libs/shared/lib/querybuilder/pills/FilterHandle.tsx
+++ b/libs/shared/lib/querybuilder/pills/FilterHandle.tsx
@@ -10,9 +10,13 @@ const selector = (s: any) => ({
 export const FilterHandle = (
   props: {
     handle: QueryGraphEdgeHandle;
+    handleTop?: 'auto' | 'fixed';
+    hidden?: boolean;
   } & Omit<HandleProps, 'id'> &
-    Omit<React.HTMLAttributes<HTMLDivElement>, 'id'>
+    Omit<React.HTMLAttributes<HTMLDivElement>, 'id'>,
 ) => {
+  const outerSize = 8;
+  const innerSize = 3;
   const { nodeInternals, edges } = useStore(selector);
   const nodeId = useNodeId();
 
@@ -51,8 +55,50 @@ export const FilterHandle = (
       if (sourceWithLogic === targetHandleOfTypeLogic) return true;
       else return false;
     },
-    [nodeInternals, edges, nodeId, props.handle]
+    [nodeInternals, edges, nodeId, props.handle],
   );
 
-  return <Handle id={id} {...props} isValidConnection={isValidConnection}></Handle>;
+  const style: React.CSSProperties = {
+    width: outerSize * 2,
+    height: outerSize * 2,
+    top: props.handleTop === 'auto' ? `auto` : `calc(2rem - ${outerSize}px)`,
+  };
+  if (props.position === Position.Left) {
+    style.left = outerSize / 2;
+  } else {
+    style.right = outerSize / 2;
+  }
+
+  const handleStyle: React.CSSProperties = {};
+  if (props.position === Position.Left) {
+    handleStyle.left = 0;
+  } else {
+    handleStyle.right = 0;
+  }
+
+  const innerStyle: React.CSSProperties = { width: innerSize * 2, height: innerSize * 2 };
+  innerStyle.top = outerSize / 2 + innerSize / 4;
+  if (props.position === Position.Left) {
+    innerStyle.left = outerSize / 2 + innerSize / 4;
+  } else {
+    innerStyle.right = outerSize / 2 + innerSize / 4;
+  }
+
+  const innerProps = { ...props };
+  delete innerProps.className;
+  delete innerProps.style;
+  delete innerProps.handleTop;
+
+  return (
+    <div className="absolute  " style={style}>
+      <Handle
+        id={id}
+        {...innerProps}
+        className={'!rounded-none !bg-transparent !w-full !h-full !border-0'}
+        style={handleStyle}
+        isValidConnection={isValidConnection}
+      ></Handle>
+      <div className={'absolute pointer-events-none ' + props.className} style={innerStyle}></div>
+    </div>
+  );
 };
diff --git a/libs/shared/lib/querybuilder/pills/customFlowPills/entitypill/entitypill.tsx b/libs/shared/lib/querybuilder/pills/customFlowPills/entitypill/entitypill.tsx
index d8f27749d..d0ec8779a 100644
--- a/libs/shared/lib/querybuilder/pills/customFlowPills/entitypill/entitypill.tsx
+++ b/libs/shared/lib/querybuilder/pills/customFlowPills/entitypill/entitypill.tsx
@@ -18,7 +18,7 @@ export const EntityFlowElement = React.memo((node: SchemaReactflowEntityNode) =>
   const graph = useQuerybuilderGraph();
   const attributeEdges = useMemo(
     () => graph.edges.filter((edge) => edge.source === node.id && !!edge?.attributes?.sourceHandleData.attributeType),
-    [graph]
+    [graph],
   );
 
   const [hovered, setHovered] = useState(false);
@@ -48,19 +48,22 @@ export const EntityFlowElement = React.memo((node: SchemaReactflowEntityNode) =>
 
   return (
     <div className="p-3 bg-transparent" onMouseEnter={onMouseEnter} onMouseLeave={onMouseLeave}>
-      <div className={`rounded-sm shadow min-w-[8rem] text-[0.8rem] bg-gradient-to-r pt-1 from-[#FFA952] to-[#D66700]`}>
+      <div className={`rounded-sm shadow min-w-[9rem] max-w-[9rem] text-[0.8rem] bg-gradient-to-r pt-1 from-[#FFA952] to-[#D66700]`}>
         <div className={`pt-1 ${data.selected ? 'bg-secondary-400' : 'bg-secondary-50'}`}>
           <FilterHandle
             handle={data.leftRelationHandleId}
             type="target"
             position={Position.Left}
-            className={'!top-8 !left-2 !bg-danger-700 !rounded-none'}
+            // className={'!top-8 !left-2 !bg-danger-700 !rounded-none'}
+            // outerClassName={'!bg-blue-700 !rounded-none'}
+            className={'!bg-accent-700'}
           />
           <FilterHandle
             handle={data.rightRelationHandleId}
             type="source"
             position={Position.Right}
-            className={'!top-8 !right-2 !bg-accent-700 !rounded-none'}
+            // outerClassName={'!bg-blue-700 !rounded-none'}
+            className={'!bg-accent-700'}
           />
           <div className="text-center py-1">{data.name}</div>
           {data?.attributes && (
diff --git a/libs/shared/lib/querybuilder/pills/customFlowPills/relationpill/relation-handles.tsx b/libs/shared/lib/querybuilder/pills/customFlowPills/relationpill/relation-handles.tsx
index 61ea6fc06..b8e3049fb 100644
--- a/libs/shared/lib/querybuilder/pills/customFlowPills/relationpill/relation-handles.tsx
+++ b/libs/shared/lib/querybuilder/pills/customFlowPills/relationpill/relation-handles.tsx
@@ -26,15 +26,13 @@ export const LeftHandle = (props: Props) => {
       handle={props.handle}
       type={props.type}
       position={Position.Left}
-      className="!top- !left-2"
-      // style={{ transform: `translate(${offsetX}px, ${offsetY}px)` }}
       onDoubleClickCapture={(e) => {
         e.preventDefault();
         e.stopPropagation();
         if (props.onDoubleClick) props.onDoubleClick();
       }}
     >
-      <svg className="pointer-events-none" height={10} style={{ transform: `translate(-2px, -3px)` }}>
+      <svg className="pointer-events-none" height={10} style={{ transform: `translate(3px, 4px)` }}>
         <polygon points={getArrow[props.point]} fill={tailwindColors.relation[200]} />
       </svg>
     </FilterHandle>
@@ -47,15 +45,13 @@ export const RightHandle = (props: Props) => {
       handle={props.handle}
       type={props.type}
       position={Position.Right}
-      className="!top-8 !right-2"
-      // style={{ transform: `translate(${offsetX}px, ${offsetY}px)` }}
       onDoubleClickCapture={(e) => {
         e.preventDefault();
         e.stopPropagation();
         if (props.onDoubleClick) props.onDoubleClick();
       }}
     >
-      <svg height={10} className="pointer-events-none" style={{ transform: `translate(-2px, -3px)` }}>
+      <svg height={10} className="pointer-events-none" style={{ transform: `translate(6px, 4px)` }}>
         <polygon points={getArrow[props.point]} fill={tailwindColors.relation[200]} />
       </svg>
     </FilterHandle>
diff --git a/libs/shared/lib/querybuilder/pills/pilldropdown/pilldropdown.tsx b/libs/shared/lib/querybuilder/pills/pilldropdown/pilldropdown.tsx
index 5e704d6d2..312a11d05 100644
--- a/libs/shared/lib/querybuilder/pills/pilldropdown/pilldropdown.tsx
+++ b/libs/shared/lib/querybuilder/pills/pilldropdown/pilldropdown.tsx
@@ -63,7 +63,7 @@ export const PillDropdown = (props: PillDropdownProps) => {
                 props.onHandleMouseDown(attribute, i, event);
               }}
             >
-              <p>{attribute.handleData.attributeName}</p>
+              <p className="truncate text-[0.6rem]">{attribute.handleData.attributeName}</p>
               {attribute.handleData?.attributeDimension && (
                 // <div className="!text-xs text-secondary-500">{IconMap[attribute.handleData.attributeDimension]}</div>
                 // <div className="!text-xs text-secondary-500">
@@ -74,7 +74,8 @@ export const PillDropdown = (props: PillDropdownProps) => {
                 handle={handleDataFromReactflowToDataId(props.node, attribute)}
                 type="source"
                 position={Position.Right}
-                className={styles.handle + ' ' + '!top-auto mt-2 !right-[0.5rem] bg-secondary-500'}
+                className={styles.handle + ' bg-secondary-500 rounded-full'}
+                handleTop="auto"
               ></FilterHandle>
             </div>
           );
-- 
GitLab