diff --git a/libs/shared/lib/querybuilder/graph/reactflow/handles.tsx b/libs/shared/lib/querybuilder/graph/reactflow/handles.tsx index 9f5f1cdaec06860869971a2d3074014420cffbb3..71a09d2066cd6ebdf0919769e715aa6ee90c7e89 100644 --- a/libs/shared/lib/querybuilder/graph/reactflow/handles.tsx +++ b/libs/shared/lib/querybuilder/graph/reactflow/handles.tsx @@ -22,7 +22,7 @@ export enum Handles { FunctionBase = 'functionHandle_', // + name from FunctionTypes args //source } -/** returns a boolean that check wheter the handle is a function handle */ +/** returns a boolean that check whether the handle is a function handle */ export function isFunctionHandle(handle: string): boolean { return handle.startsWith(Handles.FunctionBase); } diff --git a/libs/shared/lib/querybuilder/graph/reactflow/utils.ts b/libs/shared/lib/querybuilder/graph/reactflow/utils.ts index 3fd693e6f4977ecc8529a03979f3b40facf12a05..2bdf0acdb9a18b0a4af7e672e876f1d4eef39ea1 100644 --- a/libs/shared/lib/querybuilder/graph/reactflow/utils.ts +++ b/libs/shared/lib/querybuilder/graph/reactflow/utils.ts @@ -70,7 +70,7 @@ export function createReactFlowElements(graph: Graph): { // Add the reactflow edges graph.forEachEdge((edge, attributes, source, target): void => { // connection from attributes don't have visible connection lines - if (attributes.type == 'attribute_connection') return; + // if (attributes.type == 'attribute_connection') return; const RFEdge: Edge = { id: edge, diff --git a/libs/shared/lib/querybuilder/panel/querybuilder.stories.tsx b/libs/shared/lib/querybuilder/panel/querybuilder.stories.tsx index b8f69db162ef7d407c1adaede9dfcdbdad571bce..44139105e240429c2949d0e2d215e2be3092d2b6 100644 --- a/libs/shared/lib/querybuilder/panel/querybuilder.stories.tsx +++ b/libs/shared/lib/querybuilder/panel/querybuilder.stories.tsx @@ -13,10 +13,11 @@ import { MultiGraph } from 'graphology'; import { addPill2Graphology } from '../graph/graphology/utils'; import { handles } from '../graph/reactflow/pillHandles'; import { QueryGraph } from '../graph/graphology/model'; +import { Handles } from '../graph/reactflow/handles'; const Component: Meta<typeof QueryBuilder> = { component: QueryBuilder, - title: 'Panels/QueryBuilder', + title: 'QueryBuilder/Panel', decorators: [ (story) => ( <Provider store={mockStore}> @@ -36,7 +37,12 @@ const mockStore = configureStore({ const graph: QueryGraph = new MultiGraph(); addPill2Graphology( '0', - { type: 'entity', x: 100, y: 100, name: 'Entity Pill' }, + { type: 'entity', x: 100, y: 100, name: 'Entity Pill', fadeIn: false }, + graph +); +addPill2Graphology( + '10', + { type: 'entity', x: 200, y: 200, name: 'Entity Pill 2', fadeIn: false }, graph ); // graph.addNode('0', { type: 'entity', x: 100, y: 100, name: 'Entity Pill' }); @@ -48,58 +54,72 @@ addPill2Graphology( y: 140, name: 'Relation Pill', depth: { min: 0, max: 1 }, + fadeIn: false, }, graph ); -addPill2Graphology( - '2', - { - type: 'attribute', - x: 170, - y: 160, - name: 'Attr string', - datatype: 'string', - operator: 'EQ', - value: 'mark', - depth: { min: 0, max: 1 }, - }, - graph -); -addPill2Graphology( - '3', - { - type: 'attribute', - x: 170, - y: 170, - name: 'Attr number', - datatype: 'float', - operator: 'EQ', - depth: { min: 0, max: 1 }, - }, - graph -); -addPill2Graphology( - '4', - { - type: 'attribute', - x: 130, - y: 120, - name: 'Attr bool', - datatype: 'bool', - operator: 'EQ', - value: 'true', - depth: { min: 0, max: 1 }, - }, - graph -); -console.log(graph.getNodeAttributes('2')); -graph.addEdge('2', '1', { type: 'attribute_connection' }); -graph.addEdge('3', '1', { type: 'attribute_connection' }); -graph.addEdge('4', '0', { type: 'attribute_connection' }); +// addPill2Graphology( +// '2', +// { +// type: 'attribute', +// x: 170, +// y: 160, +// name: 'Attr string', +// dataType: 'string', +// matchType: 'EQ', +// value: 'mark', +// depth: { min: 0, max: 1 }, +// fadeIn: false, +// }, +// graph +// ); +// addPill2Graphology( +// '3', +// { +// type: 'attribute', +// x: 170, +// y: 170, +// name: 'Attr number', +// dataType: 'float', +// matchType: 'EQ', +// depth: { min: 0, max: 1 }, +// fadeIn: false, +// }, +// graph +// ); +// addPill2Graphology( +// '4', +// { +// type: 'attribute', +// x: 130, +// y: 120, +// name: 'Attr bool', +// dataType: 'bool', +// matchType: 'EQ', +// value: 'true', +// depth: { min: 0, max: 1 }, +// fadeIn: false, +// }, +// graph +// ); graph.addEdge('0', '1', { - type: 'entity_relation', - targetHandle: handles.relation.fromEntity, + type: 'connection', + sourceHandle: Handles.ToRelation, + targetHandle: Handles.RelationLeft, }); +graph.addEdge('10', '1', { + type: 'connection', + sourceHandle: Handles.ToRelation, + targetHandle: Handles.RelationRight, +}); +// console.log(graph.getNodeAttributes('2')); +// graph.addEdge('2', '1', { type: 'attribute_connection' }); +// graph.addEdge('3', '1', { type: 'attribute_connection' }); +// graph.addEdge('4', '0', { type: 'attribute_connection' }); +// graph.addEdge('0', '1', { +// type: 'entity_relation', +// targetHandle: handles.relation.fromEntity, +// }); // graph.addEdge('1', '0', { // type: 'entity_relation', // sourceHandle: handles.relation.entity, diff --git a/libs/shared/lib/querybuilder/panel/querybuilder.tsx b/libs/shared/lib/querybuilder/panel/querybuilder.tsx index d3de82b59af0ce1f51a2ee52fb5614e124596d6d..bac46691dbdf3bd2f697cdf6765df7157f1356e9 100644 --- a/libs/shared/lib/querybuilder/panel/querybuilder.tsx +++ b/libs/shared/lib/querybuilder/panel/querybuilder.tsx @@ -11,17 +11,21 @@ import ReactFlow, { ReactFlowInstance, } from 'reactflow'; import styles from './querybuilder.module.scss'; + +import { useMemo, useRef } from 'react'; import { - EntityFlowElement, - RelationPill, AttributePill, ConnectionDragLine, ConnectionLine, -} from '@graphpolaris/shared/lib/querybuilder/pills'; - -import { useMemo, useRef } from 'react'; + EntityFlowElement, + RelationPill, +} from '../pills'; import { createReactFlowElements } from '../graph/reactflow/utils'; -import { dragPillStarted, movePillTo } from '../pills/dragging/dragPill'; +import { + dragPillStarted, + dragPillStopped, + movePillTo, +} from '../pills/dragging/dragPill'; const nodeTypes = { entity: EntityFlowElement, @@ -30,6 +34,7 @@ const nodeTypes = { }; const edgeTypes = { connection: ConnectionLine, + attribute_connection: ConnectionLine, }; const onInit = (reactFlowInstance: ReactFlowInstance) => { @@ -37,12 +42,15 @@ const onInit = (reactFlowInstance: ReactFlowInstance) => { }; export const QueryBuilder = (props: any) => { - const nodes = useQuerybuilderNodes(); + const graphologyGraph = useQuerybuilderNodes(); const dispatch = useAppDispatch(); const isDraggingPill = useRef(false); // console.log('inputnodes', nodes); - const elements = useMemo(() => createReactFlowElements(nodes), [nodes]); + const elements = useMemo( + () => createReactFlowElements(graphologyGraph), + [graphologyGraph] + ); const onNodeDrag = ( event: React.MouseEvent<Element, MouseEvent>, @@ -57,15 +65,15 @@ export const QueryBuilder = (props: any) => { // Check if we started dragging, if so, call the drag started usecase if (!isDraggingPill.current) { - dragPillStarted(node.id, nodes); + dragPillStarted(node.id, graphologyGraph); isDraggingPill.current = true; } // Call the drag usecase - movePillTo(node.id, nodes, dx, dy, node.position); + movePillTo(node.id, graphologyGraph, dx, dy, node.position); // Dispatch the new graphology object, so reactflow will get rerendered - dispatch(setQuerybuilderNodes(nodes.export())); + dispatch(setQuerybuilderNodes(graphologyGraph.export())); }; const onNodeDragStop = ( event: React.MouseEvent<Element, MouseEvent>, @@ -74,10 +82,10 @@ export const QueryBuilder = (props: any) => { isDraggingPill.current = false; // Call the drag pill stopped usecase - dragPillStopped(node.id, nodes); + dragPillStopped(node.id, graphologyGraph); // Dispatch the new graphology object, so reactflow will get rerendered - dispatch(setQuerybuilderNodes(nodes.export())); + dispatch(setQuerybuilderNodes(graphologyGraph.export())); }; // console.log(elements); diff --git a/libs/shared/lib/querybuilder/pills/customFlowLines/connection.tsx b/libs/shared/lib/querybuilder/pills/customFlowLines/connection.tsx index 36bab8056be94c13bcba5d6f73a281f812a9af36..50625840f09ca900ea4aa5c0916ba04322ced175 100644 --- a/libs/shared/lib/querybuilder/pills/customFlowLines/connection.tsx +++ b/libs/shared/lib/querybuilder/pills/customFlowLines/connection.tsx @@ -16,45 +16,49 @@ export function ConnectionLine({ style, sourceHandleId, targetHandleId, + source, + target, + sourcePosition, + targetPosition, }: EdgeProps) { //Centering the line - sourceY -= 3; - targetY -= 3; + // sourceY -= 3; + // targetY -= 3; // Correct line positions with hardcoded numbers, because react flow lacks this functionality // if (sourceHandleId == ) sourceX += 2; // if (targetHandleId == Handles.ToAttributeHandle) targetX += 2; - let spos: Position = Position.Bottom; - if (sourceHandleId == handles.relation.fromEntity) { - spos = Position.Left; - sourceX += 7; - sourceY += 3; - } else if (sourceHandleId == handles.relation.toEntity) { - spos = Position.Right; - sourceX -= 2; - sourceY -= 3; - } else if ( - sourceHandleId !== undefined && - sourceHandleId !== null && - sourceHandleId.includes('functionHandle') - ) { - spos = Position.Top; - sourceX -= 4; - sourceY += 3; - } + let spos: Position = sourcePosition; + // if (sourceHandleId == handles.relation.fromEntity) { + // spos = Position.Left; + // sourceX += 7; + // sourceY += 3; + // } else if (sourceHandleId == handles.relation.toEntity) { + // spos = Position.Right; + // sourceX -= 2; + // sourceY -= 3; + // } else if ( + // sourceHandleId !== undefined && + // sourceHandleId !== null && + // sourceHandleId.includes('functionHandle') + // ) { + // spos = Position.Top; + // sourceX -= 4; + // sourceY += 3; + // } - let tpos: Position = Position.Bottom; - if (targetHandleId == handles.relation.fromEntity) { - tpos = Position.Left; - targetX += 7; - targetY += 3; - } else if (targetHandleId == handles.relation.toEntity) { - tpos = Position.Right; - targetX -= 2; - targetY -= 3; - } + let tpos: Position = targetPosition; + // if (targetHandleId == handles.relation.fromEntity) { + // tpos = Position.Left; + // targetX += 7; + // targetY += 3; + // } else if (targetHandleId == handles.relation.toEntity) { + // tpos = Position.Right; + // targetX -= 2; + // targetY -= 3; + // } // Create smoothstep line const path = getSmoothStepPath({ @@ -66,6 +70,8 @@ export function ConnectionLine({ targetPosition: tpos, }); + // console.log(source, target, path); + return ( <g stroke="#2e2e2e"> <path id={id} fill="none" strokeWidth={3} style={style} d={path[0]} /> diff --git a/libs/shared/lib/querybuilder/pills/customFlowPills/attributepill/attributepill.tsx b/libs/shared/lib/querybuilder/pills/customFlowPills/attributepill/attributepill.tsx index 359a562d3dd0fdbcfeb873e9806eea2a97999fb3..b157d949af8de6ce5eaf4c976f6a7572f56be862 100644 --- a/libs/shared/lib/querybuilder/pills/customFlowPills/attributepill/attributepill.tsx +++ b/libs/shared/lib/querybuilder/pills/customFlowPills/attributepill/attributepill.tsx @@ -114,6 +114,20 @@ export const AttributePill = React.memo(({ id, data }: AttributeNode) => { } style={{ backgroundColor: theme.palette.custom.elements.attribute[1] }} /> + <Handle + id={Handles.ToAttribute} + type="source" + position={Position.Left} + className={ + styles.attributeHandleLeft + + ' ' + + (false ? styles.handleConnectedFill : '') + } + style={{ + backgroundColor: theme.palette.custom.elements.attribute[1], + left: 50, + }} + /> <div className={styles.attributeWrapper}> <span className={styles.attributeWrapperSpan}>{data?.name}</span> <Select data={data} /> diff --git a/libs/shared/lib/querybuilder/pills/customFlowPills/entitypill/entitypill.module.scss b/libs/shared/lib/querybuilder/pills/customFlowPills/entitypill/entitypill.module.scss index d4d61cec401aa21c05bef925f314180a7d9b5be7..79e336437fe02d1ddbe85db273c5488ec7bf6bce 100644 --- a/libs/shared/lib/querybuilder/pills/customFlowPills/entitypill/entitypill.module.scss +++ b/libs/shared/lib/querybuilder/pills/customFlowPills/entitypill/entitypill.module.scss @@ -150,20 +150,23 @@ // General style .ToRelationHandle { border-radius: 1px !important; - left: 5px !important; + left: 10px !important; + top: 35% !important; background: rgba(0, 0, 0, 0.3) !important; } .ToAttributeHandle { border-radius: 1px !important; - left: 22.5px !important; + left: 20px !important; + top: 35% !important; background: rgba(0, 0, 0, 0.3) !important; - transform: rotate(45deg) translate(-68%, 0) scale(0.9) !important; + transform: rotate(45deg) scale(0.9) !important; transform-origin: center, center; } .ReceiveFunctionHandle { - left: 33px !important; + left: 37px !important; + top: 35% !important; background: rgba(0, 0, 0, 0.3) !important; } diff --git a/libs/shared/lib/querybuilder/pills/customFlowPills/entitypill/entitypill.tsx b/libs/shared/lib/querybuilder/pills/customFlowPills/entitypill/entitypill.tsx index 7a2bd9490c8b7d17244b7168efd17af85a9c7282..51f8aa0742044a27707d2359d7bc86bdb7b10288 100644 --- a/libs/shared/lib/querybuilder/pills/customFlowPills/entitypill/entitypill.tsx +++ b/libs/shared/lib/querybuilder/pills/customFlowPills/entitypill/entitypill.tsx @@ -12,8 +12,7 @@ import { Handles } from '../../../graph/reactflow/handles'; */ export const EntityFlowElement = React.memo(({ data }: EntityNode) => { const theme = useTheme(); - - console.log('EntityPill', data); + // console.log('EntityPill', data); // TODO: Change flow element width when text overflows const animation = data.fadeIn ? styles.entityFade : ''; @@ -26,7 +25,7 @@ export const EntityFlowElement = React.memo(({ data }: EntityNode) => { <Handle id={Handles.ToRelation} type="source" - position={Position.Left} + position={Position.Bottom} className={ styles.ToRelationHandle + ' ' + @@ -36,7 +35,7 @@ export const EntityFlowElement = React.memo(({ data }: EntityNode) => { <Handle id={Handles.ToAttribute} type="target" - position={Position.Left} + position={Position.Bottom} className={ styles.ToAttributeHandle + ' ' + @@ -46,7 +45,7 @@ export const EntityFlowElement = React.memo(({ data }: EntityNode) => { <Handle id={Handles.ReceiveFunction} type="target" - position={Position.Left} + position={Position.Bottom} className={ styles.ReceiveFunctionHandle + ' ' + diff --git a/libs/shared/lib/querybuilder/pills/customFlowPills/relationpill/relationpill.module.scss b/libs/shared/lib/querybuilder/pills/customFlowPills/relationpill/relationpill.module.scss index e07658643fd923f9c6ed7ffb55629aa31e7d21db..1f8d81b42bb9dd2606b65c17253f93274824547c 100644 --- a/libs/shared/lib/querybuilder/pills/customFlowPills/relationpill/relationpill.module.scss +++ b/libs/shared/lib/querybuilder/pills/customFlowPills/relationpill/relationpill.module.scss @@ -186,7 +186,9 @@ $width: 325; .relationHandleFiller { flex: 1 1 0; } + .relationHandleLeft { + // FIRST ONE position: absolute !important; border-style: solid !important; border-width: 6px 10px 6px 0 !important; @@ -196,7 +198,7 @@ $width: 325; min-height: 0 !important; min-width: 0 !important; width: 0 !important; - height: 0 !important; + height: 0px !important; &::before { content: ''; @@ -212,7 +214,9 @@ $width: 325; left: 2px; } } + .relationHandleAttribute { + // SECOND ONE border-radius: 1px !important; left: 22.5px !important; background: rgba(255, 255, 255, 0.6) !important; @@ -221,7 +225,18 @@ $width: 325; border-width: 1px !important; transform-origin: center, center; } + +.relationHandleFunction { + // THIRD ONE + left: 39px !important; + background: rgba(255, 255, 255, 0.6) !important; + border-color: rgba(22, 110, 110, 1) !important; + border-width: 1px !important; + transform-origin: center, center; +} + .relationHandleRight { + // LAST ONE width: 0 !important; height: 0 !important; position: absolute !important; @@ -247,13 +262,7 @@ $width: 325; right: 2px; } } -.relationHandleFunction { - left: 39px !important; - background: rgba(255, 255, 255, 0.6) !important; - border-color: rgba(22, 110, 110, 1) !important; - border-width: 1px !important; - transform-origin: center, center; -} + .relationInputHolder { display: flex; float: right; diff --git a/libs/shared/lib/querybuilder/pills/customFlowPills/relationpill/relationpill.module.scss.d.ts b/libs/shared/lib/querybuilder/pills/customFlowPills/relationpill/relationpill.module.scss.d.ts index 1348fe7efc3d9733a5bedea25c46bc5d23ec4f8b..a0a450e53cc824b7f609036a0e336970b3f601a4 100644 --- a/libs/shared/lib/querybuilder/pills/customFlowPills/relationpill/relationpill.module.scss.d.ts +++ b/libs/shared/lib/querybuilder/pills/customFlowPills/relationpill/relationpill.module.scss.d.ts @@ -25,8 +25,8 @@ declare const classNames: { readonly relationHandleFiller: 'relationHandleFiller'; readonly relationHandleLeft: 'relationHandleLeft'; readonly relationHandleAttribute: 'relationHandleAttribute'; - readonly relationHandleRight: 'relationHandleRight'; readonly relationHandleFunction: 'relationHandleFunction'; + readonly relationHandleRight: 'relationHandleRight'; readonly relationInputHolder: 'relationInputHolder'; readonly relationInput: 'relationInput'; readonly relationReadonly: 'relationReadonly'; diff --git a/libs/shared/lib/querybuilder/pills/customFlowPills/relationpill/relationpill.tsx b/libs/shared/lib/querybuilder/pills/customFlowPills/relationpill/relationpill.tsx index 84ed39fc7269429dca547937ab328dacdf022d0d..b1de1902c48a34371115cf853e98de7fffaf27e2 100644 --- a/libs/shared/lib/querybuilder/pills/customFlowPills/relationpill/relationpill.tsx +++ b/libs/shared/lib/querybuilder/pills/customFlowPills/relationpill/relationpill.tsx @@ -88,7 +88,10 @@ export const RelationPill = memo(({ data }: RelationNode) => { style={{ backgroundColor: theme.palette.custom.elements.relation[0] }} ></span> */} <div className={styles.relationWrapper}> - <span className={styles.relationHandleFiller}> + <span + className={styles.relationHandleFiller} + // style={{ transform: 'translate(-100px,0)' }} + > <Handle id={Handles.RelationLeft} type="target" @@ -112,6 +115,18 @@ export const RelationPill = memo(({ data }: RelationNode) => { } /> </span> + <span className={styles.relationHandleFiller}> + <Handle + id={Handles.ReceiveFunction} + type="target" + position={Position.Left} + className={ + styles.relationHandleFunction + + ' ' + + (false ? styles.handleConnectedFill : '') + } + /> + </span> <div className={styles.relationDataWrapper}> <span className={styles.relationSpan}>{data?.name}</span> <span className={styles.relationInputHolder}> @@ -196,16 +211,6 @@ export const RelationPill = memo(({ data }: RelationNode) => { } /> </span> - <Handle - id={Handles.ReceiveFunction} - type="target" - position={Position.Right} - className={ - styles.relationHandleFunction + - ' ' + - (false ? styles.handleConnectedFill : '') - } - /> </div> </div> );