diff --git a/rsconcept/frontend/src/components/ui/Flow/DynamicEdge.tsx b/rsconcept/frontend/src/components/ui/Flow/DynamicEdge.tsx new file mode 100644 index 00000000..81c452e8 --- /dev/null +++ b/rsconcept/frontend/src/components/ui/Flow/DynamicEdge.tsx @@ -0,0 +1,36 @@ +import { EdgeProps, getStraightPath } from 'reactflow'; + +import { PARAMETER } from '@/utils/constants'; + +const RADIUS = PARAMETER.graphNodeRadius + PARAMETER.graphNodePadding; + +function DynamicEdge({ id, markerEnd, style, ...props }: EdgeProps) { + const sourceY = props.sourceY - PARAMETER.graphNodeRadius - PARAMETER.graphHandleSize; + const targetY = props.targetY + PARAMETER.graphNodeRadius + PARAMETER.graphHandleSize; + + const dx = props.targetX - props.sourceX; + const dy = targetY - sourceY; + const distance = Math.sqrt(Math.pow(dx, 2) + Math.pow(dy, 2)); + + if (distance <= 2 * RADIUS) { + return null; + } + + const ux = dx / distance; + const uy = dy / distance; + + const [path] = getStraightPath({ + sourceX: props.sourceX + ux * RADIUS, + sourceY: sourceY + uy * RADIUS, + targetX: props.targetX - ux * RADIUS, + targetY: targetY - uy * RADIUS + }); + + return ( + <> + + + ); +} + +export default DynamicEdge; diff --git a/rsconcept/frontend/src/dialogs/DlgShowAST/ASTFlow.tsx b/rsconcept/frontend/src/dialogs/DlgShowAST/ASTFlow.tsx index 729aaba7..a6df36fe 100644 --- a/rsconcept/frontend/src/dialogs/DlgShowAST/ASTFlow.tsx +++ b/rsconcept/frontend/src/dialogs/DlgShowAST/ASTFlow.tsx @@ -13,9 +13,10 @@ interface ASTFlowProps { data: SyntaxTree; onNodeEnter: (node: Node) => void; onNodeLeave: (node: Node) => void; + onChangeDragging: (value: boolean) => void; } -function ASTFlow({ data, onNodeEnter, onNodeLeave }: ASTFlowProps) { +function ASTFlow({ data, onNodeEnter, onNodeLeave, onChangeDragging }: ASTFlowProps) { const [nodes, setNodes, onNodesChange] = useNodesState([]); const [edges, setEdges] = useEdgesState([]); @@ -59,6 +60,8 @@ function ASTFlow({ data, onNodeEnter, onNodeLeave }: ASTFlowProps) { nodesFocusable={false} onNodeMouseEnter={(_, node) => onNodeEnter(node)} onNodeMouseLeave={(_, node) => onNodeLeave(node)} + onNodeDragStart={() => onChangeDragging(true)} + onNodeDragStop={() => onChangeDragging(false)} onNodesChange={onNodesChange} nodeTypes={ASTNodeTypes} edgeTypes={ASTEdgeTypes} diff --git a/rsconcept/frontend/src/dialogs/DlgShowAST/DlgShowAST.tsx b/rsconcept/frontend/src/dialogs/DlgShowAST/DlgShowAST.tsx index 66c9a2f1..9d0da537 100644 --- a/rsconcept/frontend/src/dialogs/DlgShowAST/DlgShowAST.tsx +++ b/rsconcept/frontend/src/dialogs/DlgShowAST/DlgShowAST.tsx @@ -25,6 +25,8 @@ function DlgShowAST({ hideWindow, syntaxTree, expression }: DlgShowASTProps) { const handleHoverIn = useCallback((node: Node) => setHoverID(Number(node.id)), []); const handleHoverOut = useCallback(() => setHoverID(undefined), []); + const [isDragging, setIsDragging] = useState(false); + return ( - {!hoverNode ? expression : null} - {hoverNode ? ( + {!hoverNode || isDragging ? expression : null} + {!isDragging && hoverNode ? (
{expression.slice(0, hoverNode.start)} {expression.slice(hoverNode.start, hoverNode.finish)} @@ -47,7 +49,12 @@ function DlgShowAST({ hideWindow, syntaxTree, expression }: DlgShowASTProps) { ) : null} - + ); diff --git a/rsconcept/frontend/src/dialogs/DlgShowAST/graph/ASTEdge.tsx b/rsconcept/frontend/src/dialogs/DlgShowAST/graph/ASTEdge.tsx deleted file mode 100644 index 5794bf98..00000000 --- a/rsconcept/frontend/src/dialogs/DlgShowAST/graph/ASTEdge.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import { EdgeProps, getStraightPath } from 'reactflow'; - -const NODE_RADIUS = 20; -const EDGE_RADIUS = 25; - -function ASTEdge({ id, markerEnd, style, ...props }: EdgeProps) { - const scale = - EDGE_RADIUS / - Math.sqrt( - Math.pow(props.sourceX - props.targetX, 2) + - Math.pow(Math.abs(props.sourceY - props.targetY) + 2 * NODE_RADIUS, 2) - ); - - const [path] = getStraightPath({ - sourceX: props.sourceX - (props.sourceX - props.targetX) * scale, - sourceY: props.sourceY - (props.sourceY - props.targetY - 2 * NODE_RADIUS) * scale - NODE_RADIUS, - targetX: props.targetX + (props.sourceX - props.targetX) * scale, - targetY: props.targetY + (props.sourceY - props.targetY - 2 * NODE_RADIUS) * scale + NODE_RADIUS - }); - - return ( - <> - - - ); -} - -export default ASTEdge; diff --git a/rsconcept/frontend/src/dialogs/DlgShowAST/graph/ASTEdgeTypes.ts b/rsconcept/frontend/src/dialogs/DlgShowAST/graph/ASTEdgeTypes.ts index c88a8d33..19f9735d 100644 --- a/rsconcept/frontend/src/dialogs/DlgShowAST/graph/ASTEdgeTypes.ts +++ b/rsconcept/frontend/src/dialogs/DlgShowAST/graph/ASTEdgeTypes.ts @@ -1,7 +1,7 @@ import { EdgeTypes } from 'reactflow'; -import ASTEdge from './ASTEdge'; +import DynamicEdge from '@/components/ui/Flow/DynamicEdge'; export const ASTEdgeTypes: EdgeTypes = { - dynamic: ASTEdge + dynamic: DynamicEdge }; diff --git a/rsconcept/frontend/src/dialogs/DlgShowAST/graph/ASTLayout.ts b/rsconcept/frontend/src/dialogs/DlgShowAST/graph/ASTLayout.ts index a848614e..8a124cf5 100644 --- a/rsconcept/frontend/src/dialogs/DlgShowAST/graph/ASTLayout.ts +++ b/rsconcept/frontend/src/dialogs/DlgShowAST/graph/ASTLayout.ts @@ -2,23 +2,19 @@ import dagre from '@dagrejs/dagre'; import { Edge, Node } from 'reactflow'; import { ISyntaxTreeNode } from '@/models/rslang'; - -const NODE_WIDTH = 44; -const NODE_HEIGHT = 44; -const HOR_SEPARATION = 40; -const VERT_SEPARATION = 40; +import { PARAMETER } from '@/utils/constants'; export function applyLayout(nodes: Node[], edges: Edge[]) { const dagreGraph = new dagre.graphlib.Graph().setDefaultEdgeLabel(() => ({})); dagreGraph.setGraph({ rankdir: 'TB', - ranksep: VERT_SEPARATION, - nodesep: HOR_SEPARATION, + ranksep: 40, + nodesep: 40, ranker: 'network-simplex', align: undefined }); nodes.forEach(node => { - dagreGraph.setNode(node.id, { width: NODE_WIDTH, height: NODE_HEIGHT }); + dagreGraph.setNode(node.id, { width: 2 * PARAMETER.graphNodeRadius, height: 2 * PARAMETER.graphNodeRadius }); }); edges.forEach(edge => { @@ -29,7 +25,7 @@ export function applyLayout(nodes: Node[], edges: Edge[]) { nodes.forEach(node => { const nodeWithPosition = dagreGraph.node(node.id); - node.position.x = nodeWithPosition.x - NODE_WIDTH / 2; - node.position.y = nodeWithPosition.y - NODE_HEIGHT / 2; + node.position.x = nodeWithPosition.x - PARAMETER.graphNodeRadius; + node.position.y = nodeWithPosition.y - PARAMETER.graphNodeRadius; }); } diff --git a/rsconcept/frontend/src/dialogs/DlgShowAST/graph/ASTNode.tsx b/rsconcept/frontend/src/dialogs/DlgShowAST/graph/ASTNode.tsx index a8ecb234..6cffbba5 100644 --- a/rsconcept/frontend/src/dialogs/DlgShowAST/graph/ASTNode.tsx +++ b/rsconcept/frontend/src/dialogs/DlgShowAST/graph/ASTNode.tsx @@ -15,6 +15,7 @@ interface ASTNodeInternal { id: string; data: ISyntaxTreeNode; dragging: boolean; + selected: boolean; xPos: number; yPos: number; } @@ -32,10 +33,18 @@ function ASTNode(node: ASTNodeInternal) { />
3 ? 12 : 14 }} + className='font-math mt-1 w-fit text-center translate-x-[calc(-50%+20px)]' + style={{ fontSize: label.length > 3 ? 12 : 14 }} > - {label} +
{label}
+
+ {label} +
); diff --git a/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/TGFlow.tsx b/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/TGFlow.tsx index ff81e8ec..9b019173 100644 --- a/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/TGFlow.tsx +++ b/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/TGFlow.tsx @@ -61,7 +61,9 @@ function TGFlow({ onOpenEdit }: TGFlowProps) { const [edges, setEdges] = useEdgesState([]); const flow = useReactFlow(); const store = useStoreApi(); + const { addSelectedNodes } = store.getState(); + const [showParamsDialog, setShowParamsDialog] = useState(false); const [filterParams, setFilterParams] = useLocalStorage(storage.rsgraphFilter, { noHermits: true, noTemplates: false, @@ -81,16 +83,13 @@ function TGFlow({ onOpenEdit }: TGFlowProps) { allowConstant: true, allowTheorem: true }); - const [showParamsDialog, setShowParamsDialog] = useState(false); - const [focusCst, setFocusCst] = useState(undefined); - const filteredGraph = useGraphFilter(controller.schema, filterParams, focusCst); - - const [hidden, setHidden] = useState([]); - const [coloring, setColoring] = useLocalStorage(storage.rsgraphColoring, 'type'); - const [isDragging, setIsDragging] = useState(false); + const [focusCst, setFocusCst] = useState(undefined); + const filteredGraph = useGraphFilter(controller.schema, filterParams, focusCst); + const [hidden, setHidden] = useState([]); + const [isDragging, setIsDragging] = useState(false); const [hoverID, setHoverID] = useState(undefined); const hoverCst = useMemo(() => { return hoverID && controller.schema?.cstByID.get(hoverID); @@ -100,8 +99,6 @@ function TGFlow({ onOpenEdit }: TGFlowProps) { const [toggleResetView, setToggleResetView] = useState(false); - const { addSelectedNodes } = store.getState(); - const onSelectionChange = useCallback( ({ nodes }: { nodes: Node[] }) => { const ids = nodes.map(node => Number(node.id)); @@ -299,6 +296,8 @@ function TGFlow({ onOpenEdit }: TGFlowProps) { const handleNodeClick = useCallback( (event: CProps.EventMouse, cstID: ConstituentaID) => { if (event.altKey) { + event.preventDefault(); + event.stopPropagation(); handleSetFocus(cstID); } }, @@ -445,7 +444,7 @@ function TGFlow({ onOpenEdit }: TGFlowProps) { hideZero totalCount={controller.schema?.stats?.count_all ?? 0} selectedCount={controller.selected.length} - position='top-[4.3rem] sm:top-[2rem] left-0' + position='top-[4.3rem] left-0' /> {!isDragging && hoverCst && hoverCstDebounced && hoverCst === hoverCstDebounced ? ( @@ -465,7 +464,7 @@ function TGFlow({ onOpenEdit }: TGFlowProps) { ) : null} - +
{selectors} {viewHidden} diff --git a/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/ViewHidden.tsx b/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/ViewHidden.tsx index 93a104c0..b951c7b6 100644 --- a/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/ViewHidden.tsx +++ b/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/ViewHidden.tsx @@ -87,7 +87,7 @@ function ViewHidden({ items, selected, toggleSelection, setFocus, schema, colori 'text-sm', 'cc-scroll-y' )} - style={{ maxHeight: calculateHeight(windowSize.isSmall ? '12.rem + 2px' : '16.4rem + 2px') }} + style={{ maxHeight: calculateHeight(windowSize.isSmall ? '10.4rem + 2px' : '12.5rem + 2px') }} initial={false} animate={!isFolded ? 'open' : 'closed'} variants={animateDropdown} diff --git a/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/graph/TGEdge.tsx b/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/graph/TGEdge.tsx deleted file mode 100644 index d291df5e..00000000 --- a/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/graph/TGEdge.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import { EdgeProps, getStraightPath } from 'reactflow'; - -import { PARAMETER } from '@/utils/constants'; - -function TGEdge({ id, markerEnd, style, ...props }: EdgeProps) { - const sourceY = props.sourceY - PARAMETER.graphNodeRadius; - const targetY = props.targetY + PARAMETER.graphNodeRadius; - - const scale = - (PARAMETER.graphNodePadding + PARAMETER.graphNodeRadius) / - Math.sqrt(Math.pow(props.sourceX - props.targetX, 2) + Math.pow(Math.abs(sourceY - targetY), 2)); - - const [path] = getStraightPath({ - sourceX: props.sourceX - (props.sourceX - props.targetX) * scale, - sourceY: sourceY - (sourceY - targetY) * scale, - targetX: props.targetX + (props.sourceX - props.targetX) * scale, - targetY: targetY + (sourceY - targetY) * scale - }); - - return ( - <> - - - ); -} - -export default TGEdge; diff --git a/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/graph/TGEdgeTypes.ts b/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/graph/TGEdgeTypes.ts index 8c037345..c7a71392 100644 --- a/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/graph/TGEdgeTypes.ts +++ b/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/graph/TGEdgeTypes.ts @@ -1,7 +1,7 @@ import { EdgeTypes } from 'reactflow'; -import TGEdge from './TGEdge'; +import DynamicEdge from '../../../../components/ui/Flow/DynamicEdge'; export const TGEdgeTypes: EdgeTypes = { - termEdge: TGEdge + termEdge: DynamicEdge }; diff --git a/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/graph/TGNode.tsx b/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/graph/TGNode.tsx index 4a44a378..4c70f326 100644 --- a/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/graph/TGNode.tsx +++ b/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/graph/TGNode.tsx @@ -35,12 +35,7 @@ function TGNode(node: TGNodeInternal) {
{node.data.label}