mirror of
https://github.com/IRBorisov/ConceptPortal.git
synced 2025-06-25 20:40:36 +03:00
F: Improve diagram flow management
This commit is contained in:
parent
caf11aa329
commit
6bc7993797
100
rsconcept/frontend/src/components/flow/diagram-flow.tsx
Normal file
100
rsconcept/frontend/src/components/flow/diagram-flow.tsx
Normal file
|
@ -0,0 +1,100 @@
|
|||
'use client';
|
||||
|
||||
import { type ReactNode, useState } from 'react';
|
||||
import { Background, ReactFlow, type ReactFlowProps } from 'reactflow';
|
||||
|
||||
export { useReactFlow, useStoreApi } from 'reactflow';
|
||||
|
||||
import { cn } from '../utils';
|
||||
|
||||
type DiagramFlowProps = {
|
||||
children?: ReactNode;
|
||||
height?: number | string;
|
||||
showGrid?: boolean;
|
||||
gridSize?: number;
|
||||
onKeyDown?: (event: React.KeyboardEvent<HTMLDivElement>) => void;
|
||||
onKeyUp?: (event: React.KeyboardEvent<HTMLDivElement>) => void;
|
||||
} & ReactFlowProps;
|
||||
|
||||
export function DiagramFlow({
|
||||
children,
|
||||
className,
|
||||
style,
|
||||
height,
|
||||
showGrid = false,
|
||||
gridSize = 20,
|
||||
onKeyDown,
|
||||
onKeyUp,
|
||||
|
||||
nodesDraggable = true,
|
||||
nodesFocusable,
|
||||
edgesFocusable,
|
||||
|
||||
onNodesChange,
|
||||
onEdgesChange,
|
||||
onNodeClick,
|
||||
onNodeDoubleClick,
|
||||
onNodeContextMenu,
|
||||
onNodeMouseEnter,
|
||||
onNodeMouseLeave,
|
||||
|
||||
onNodeDragStart,
|
||||
onNodeDrag,
|
||||
onNodeDragStop,
|
||||
onContextMenu,
|
||||
...restProps
|
||||
}: DiagramFlowProps) {
|
||||
const [spaceMode, setSpaceMode] = useState(false);
|
||||
|
||||
function handleKeyDown(event: React.KeyboardEvent<HTMLDivElement>) {
|
||||
if (event.code === 'Space') {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
setSpaceMode(true);
|
||||
}
|
||||
onKeyDown?.(event);
|
||||
}
|
||||
|
||||
function handleKeyUp(event: React.KeyboardEvent<HTMLDivElement>) {
|
||||
if (event.code === 'Space') {
|
||||
setSpaceMode(false);
|
||||
}
|
||||
onKeyUp?.(event);
|
||||
}
|
||||
|
||||
function handleContextMenu(event: React.MouseEvent<HTMLDivElement>) {
|
||||
event.preventDefault();
|
||||
onContextMenu?.(event);
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
tabIndex={-1}
|
||||
className={cn('relative cc-mask-sides w-[100dvw]', spaceMode && 'space-mode', className)}
|
||||
style={{ ...style, height: height }}
|
||||
onKeyDown={handleKeyDown}
|
||||
onKeyUp={handleKeyUp}
|
||||
>
|
||||
<ReactFlow
|
||||
{...restProps}
|
||||
onNodesChange={spaceMode ? undefined : onNodesChange}
|
||||
onEdgesChange={spaceMode ? undefined : onEdgesChange}
|
||||
onNodeClick={spaceMode ? undefined : onNodeClick}
|
||||
onNodeDoubleClick={spaceMode ? undefined : onNodeDoubleClick}
|
||||
nodesDraggable={!spaceMode && nodesDraggable}
|
||||
nodesFocusable={!spaceMode && nodesFocusable}
|
||||
edgesFocusable={!spaceMode && edgesFocusable}
|
||||
onNodeDragStart={spaceMode ? undefined : onNodeDragStart}
|
||||
onNodeDrag={spaceMode ? undefined : onNodeDrag}
|
||||
onNodeDragStop={spaceMode ? undefined : onNodeDragStop}
|
||||
onNodeContextMenu={spaceMode ? undefined : onNodeContextMenu}
|
||||
onNodeMouseEnter={spaceMode ? undefined : onNodeMouseEnter}
|
||||
onNodeMouseLeave={spaceMode ? undefined : onNodeMouseLeave}
|
||||
onContextMenu={handleContextMenu}
|
||||
>
|
||||
{showGrid ? <Background gap={gridSize} /> : null}
|
||||
{children}
|
||||
</ReactFlow>
|
||||
</div>
|
||||
);
|
||||
}
|
|
@ -28,12 +28,12 @@ export function BlockNode(node: BlockInternalNode) {
|
|||
return (
|
||||
<>
|
||||
<NodeResizeControl minWidth={BLOCK_NODE_MIN_WIDTH} minHeight={BLOCK_NODE_MIN_HEIGHT}>
|
||||
<IconResize size={8} className='absolute bottom-[2px] right-[2px] cc-graph-interactive' />
|
||||
<IconResize size={12} className='absolute bottom-[3px] right-[3px] cc-graph-interactive' />
|
||||
</NodeResizeControl>
|
||||
{showCoordinates ? (
|
||||
<div
|
||||
className={clsx(
|
||||
'absolute top-full mt-1 right-[1px]',
|
||||
'absolute top-full mt-[4px] right-[1px]',
|
||||
'text-[7px]/[8px] font-math',
|
||||
'text-muted-foreground hover:text-foreground'
|
||||
)}
|
||||
|
@ -43,24 +43,24 @@ export function BlockNode(node: BlockInternalNode) {
|
|||
) : null}
|
||||
<div
|
||||
className={clsx(
|
||||
'cc-node-block h-full w-full',
|
||||
'cc-node-block h-full w-full cursor-pointer',
|
||||
isDragging && isParent && dropTarget !== node.data.block.id && 'border-destructive',
|
||||
((isParent && !isDragging) || dropTarget === node.data.block.id) && 'border-primary',
|
||||
isChild && 'border-accent-orange'
|
||||
)}
|
||||
>
|
||||
<div className='absolute top-0 left-0 w-full h-2 cc-graph-interactive cursor-pointer' />
|
||||
<div className='absolute top-0 right-0 h-full w-2 cc-graph-interactive cursor-pointer' />
|
||||
<div className='absolute bottom-0 right-0 w-full h-2 cc-graph-interactive cursor-pointer' />
|
||||
<div className='absolute bottom-0 left-0 h-full w-2 cc-graph-interactive cursor-pointer' />
|
||||
<div className='absolute -top-[8px] left-0 w-full h-[16px] cc-graph-interactive' />
|
||||
<div className='absolute top-0 -right-[8px] h-full w-[16px] cc-graph-interactive' />
|
||||
<div className='absolute -bottom-[8px] right-0 w-full h-[16px] cc-graph-interactive' />
|
||||
<div className='absolute bottom-0 -left-[8px] h-full w-[16px] cc-graph-interactive' />
|
||||
|
||||
<div
|
||||
className={clsx(
|
||||
'w-fit mx-auto -translate-y-1/2 -mt-[8px]',
|
||||
'px-2',
|
||||
'px-[8px]',
|
||||
'bg-background rounded-lg',
|
||||
'text-[18px]/[20px] line-clamp-2 text-center text-ellipsis',
|
||||
'cc-graph-interactive cursor-pointer'
|
||||
'cc-graph-interactive'
|
||||
)}
|
||||
data-tooltip-id={globalIDs.operation_tooltip}
|
||||
data-tooltip-hidden={node.dragging}
|
||||
|
|
|
@ -42,7 +42,7 @@ export function NodeCore({ node }: NodeCoreProps) {
|
|||
data-tooltip-hidden={node.dragging}
|
||||
onMouseEnter={() => setHover(node.data.operation)}
|
||||
>
|
||||
<div className='absolute z-pop top-0 right-0 flex flex-col gap-1 p-[2px]'>
|
||||
<div className='absolute z-pop top-0 right-0 flex flex-col gap-[4px] p-[2px]'>
|
||||
<Indicator
|
||||
noPadding
|
||||
title={hasFile ? 'Связанная КС' : 'Нет связанной КС'}
|
||||
|
@ -59,7 +59,7 @@ export function NodeCore({ node }: NodeCoreProps) {
|
|||
{showCoordinates ? (
|
||||
<div
|
||||
className={clsx(
|
||||
'absolute top-full mt-1 right-[1px]',
|
||||
'absolute top-full mt-[4px] right-[1px]',
|
||||
'text-[7px]/[8px] font-math',
|
||||
'text-muted-foreground hover:text-foreground',
|
||||
node.selected && 'translate-y-[6px]'
|
||||
|
|
|
@ -11,10 +11,9 @@ import { PARAMETER } from '@/utils/constants';
|
|||
|
||||
import { useOssEdit } from '../oss-edit-context';
|
||||
|
||||
import { flowOptions } from './oss-flow';
|
||||
import { OssFlowContext } from './oss-flow-context';
|
||||
|
||||
const VIEW_PADDING = 0.2;
|
||||
|
||||
const Z_BLOCK = 1;
|
||||
const Z_SCHEMA = 10;
|
||||
|
||||
|
@ -86,7 +85,7 @@ export const OssFlowState = ({ children }: React.PropsWithChildren) => {
|
|||
setNodes(newNodes);
|
||||
setEdges(newEdges);
|
||||
|
||||
setTimeout(() => fitView({ duration: PARAMETER.zoomDuration, padding: VIEW_PADDING }), PARAMETER.refreshTimeout);
|
||||
setTimeout(() => fitView(flowOptions.fitViewOptions), PARAMETER.refreshTimeout);
|
||||
}, [schema, setNodes, setEdges, edgeAnimate, edgeStraight, fitView]);
|
||||
|
||||
useEffect(() => {
|
||||
|
@ -94,7 +93,7 @@ export const OssFlowState = ({ children }: React.PropsWithChildren) => {
|
|||
}, [schema, edgeAnimate, edgeStraight, resetGraph]);
|
||||
|
||||
function resetView() {
|
||||
setTimeout(() => fitView({ duration: PARAMETER.zoomDuration, padding: VIEW_PADDING }), PARAMETER.refreshTimeout);
|
||||
setTimeout(() => fitView(flowOptions.fitViewOptions), PARAMETER.refreshTimeout);
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
'use client';
|
||||
|
||||
import { useState } from 'react';
|
||||
import { Background, ReactFlow, useReactFlow, useStoreApi } from 'reactflow';
|
||||
import clsx from 'clsx';
|
||||
|
||||
import { DiagramFlow, useReactFlow, useStoreApi } from '@/components/flow/diagram-flow';
|
||||
import { useMainHeight } from '@/stores/app-layout';
|
||||
import { useDialogsStore } from '@/stores/dialogs';
|
||||
import { PARAMETER } from '@/utils/constants';
|
||||
import { promptText } from '@/utils/labels';
|
||||
|
||||
import { useDeleteBlock } from '../../../backend/use-delete-block';
|
||||
|
@ -25,8 +26,18 @@ import { ToolbarOssGraph } from './toolbar-oss-graph';
|
|||
import { useDragging } from './use-dragging';
|
||||
import { useGetLayout } from './use-get-layout';
|
||||
|
||||
const ZOOM_MAX = 2;
|
||||
const ZOOM_MIN = 0.5;
|
||||
export const flowOptions = {
|
||||
fitView: true,
|
||||
fitViewOptions: { padding: 0.3, duration: PARAMETER.zoomDuration },
|
||||
edgesFocusable: false,
|
||||
nodesFocusable: false,
|
||||
nodesConnectable: false,
|
||||
maxZoom: 2,
|
||||
minZoom: 0.5,
|
||||
gridSize: GRID_SIZE,
|
||||
snapToGrid: true,
|
||||
snapGrid: [GRID_SIZE, GRID_SIZE] as [number, number]
|
||||
} as const;
|
||||
|
||||
export function OssFlow() {
|
||||
const mainHeight = useMainHeight();
|
||||
|
@ -46,7 +57,6 @@ export function OssFlow() {
|
|||
const { deleteBlock } = useDeleteBlock();
|
||||
|
||||
const [mouseCoords, setMouseCoords] = useState<Position2D>({ x: 0, y: 0 });
|
||||
const [spacePressed, setSpacePressed] = useState(false);
|
||||
|
||||
const showCreateOperation = useDialogsStore(state => state.showCreateOperation);
|
||||
const showCreateBlock = useDialogsStore(state => state.showCreateBlock);
|
||||
|
@ -131,12 +141,6 @@ export function OssFlow() {
|
|||
}
|
||||
|
||||
function handleKeyDown(event: React.KeyboardEvent<HTMLDivElement>) {
|
||||
if (event.code === 'Space') {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
setSpacePressed(true);
|
||||
return;
|
||||
}
|
||||
if (isProcessing) {
|
||||
return;
|
||||
}
|
||||
|
@ -173,25 +177,13 @@ export function OssFlow() {
|
|||
}
|
||||
}
|
||||
|
||||
function handleKeyUp(event: React.KeyboardEvent<HTMLDivElement>) {
|
||||
if (event.code === 'Space') {
|
||||
setSpacePressed(false);
|
||||
}
|
||||
}
|
||||
|
||||
function handleMouseMove(event: React.MouseEvent<HTMLDivElement>) {
|
||||
const targetPosition = screenToFlowPosition({ x: event.clientX, y: event.clientY });
|
||||
setMouseCoords(targetPosition);
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
tabIndex={-1}
|
||||
className='relative'
|
||||
onKeyDown={handleKeyDown}
|
||||
onKeyUp={handleKeyUp}
|
||||
onMouseMove={showCoordinates ? handleMouseMove : undefined}
|
||||
>
|
||||
<div tabIndex={-1} className='relative' onMouseMove={showCoordinates ? handleMouseMove : undefined}>
|
||||
{showCoordinates ? <CoordinateDisplay mouseCoords={mouseCoords} className='absolute top-1 right-2' /> : null}
|
||||
<ToolbarOssGraph
|
||||
className='absolute z-pop top-8 right-1/2 translate-x-1/2'
|
||||
|
@ -203,43 +195,25 @@ export function OssFlow() {
|
|||
|
||||
<ContextMenu isOpen={isContextMenuOpen} onHide={hideContextMenu} {...menuProps} />
|
||||
|
||||
<div
|
||||
className={clsx(
|
||||
'relative w-[100vw] cc-mask-sides',
|
||||
spacePressed ? 'space-mode' : '',
|
||||
!containMovement && 'cursor-relocate'
|
||||
)}
|
||||
style={{ height: mainHeight, fontFamily: 'Rubik' }}
|
||||
>
|
||||
<ReactFlow
|
||||
nodes={nodes}
|
||||
edges={edges}
|
||||
onNodesChange={spacePressed ? undefined : onNodesChange}
|
||||
onEdgesChange={spacePressed ? undefined : onEdgesChange}
|
||||
edgesFocusable={false}
|
||||
nodesFocusable={false}
|
||||
fitView
|
||||
nodeTypes={OssNodeTypes}
|
||||
maxZoom={ZOOM_MAX}
|
||||
minZoom={ZOOM_MIN}
|
||||
nodesConnectable={false}
|
||||
snapToGrid={true}
|
||||
snapGrid={[GRID_SIZE, GRID_SIZE]}
|
||||
onClick={hideContextMenu}
|
||||
onNodeDoubleClick={spacePressed ? undefined : handleNodeDoubleClick}
|
||||
onNodeContextMenu={handleContextMenu}
|
||||
onContextMenu={event => {
|
||||
event.preventDefault();
|
||||
hideContextMenu();
|
||||
}}
|
||||
nodesDraggable={!spacePressed}
|
||||
onNodeDragStart={spacePressed ? undefined : handleDragStart}
|
||||
onNodeDrag={spacePressed ? undefined : handleDrag}
|
||||
onNodeDragStop={spacePressed ? undefined : handleDragStop}
|
||||
>
|
||||
{showGrid ? <Background gap={GRID_SIZE} /> : null}
|
||||
</ReactFlow>
|
||||
</div>
|
||||
<DiagramFlow
|
||||
{...flowOptions}
|
||||
className={clsx(!containMovement && 'cursor-relocate')}
|
||||
height={mainHeight}
|
||||
onKeyDown={handleKeyDown}
|
||||
nodes={nodes}
|
||||
edges={edges}
|
||||
onNodesChange={onNodesChange}
|
||||
onEdgesChange={onEdgesChange}
|
||||
nodeTypes={OssNodeTypes}
|
||||
showGrid={showGrid}
|
||||
onClick={hideContextMenu}
|
||||
onNodeDoubleClick={handleNodeDoubleClick}
|
||||
onNodeContextMenu={handleContextMenu}
|
||||
onContextMenu={hideContextMenu}
|
||||
onNodeDragStart={handleDragStart}
|
||||
onNodeDrag={handleDrag}
|
||||
onNodeDragStop={handleDragStop}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -63,6 +63,9 @@ export function useDragging({ hideContextMenu }: DraggingProps) {
|
|||
if (containMovement) {
|
||||
applyContainMovement([target.id, ...selected.map(id => String(id))], false);
|
||||
} else {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
const new_parent = dropTarget.evaluate(event);
|
||||
const allSelected = [...selected.filter(id => id != Number(target.id)), Number(target.id)];
|
||||
const operations = allSelected
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
'use client';
|
||||
|
||||
import { useEffect } from 'react';
|
||||
import { type Edge, MarkerType, type Node, ReactFlow, useEdgesState, useNodesState } from 'reactflow';
|
||||
import { type Edge, MarkerType, type Node, useEdgesState, useNodesState } from 'reactflow';
|
||||
|
||||
import { DiagramFlow } from '@/components/flow/diagram-flow';
|
||||
|
||||
import { type SyntaxTree } from '../../models/rslang';
|
||||
|
||||
|
@ -9,6 +11,16 @@ import { ASTEdgeTypes } from './graph/ast-edge-types';
|
|||
import { applyLayout } from './graph/ast-layout';
|
||||
import { ASTNodeTypes } from './graph/ast-node-types';
|
||||
|
||||
const flowOptions = {
|
||||
fitView: true,
|
||||
fitViewOptions: { padding: 0.25 },
|
||||
edgesFocusable: false,
|
||||
nodesFocusable: false,
|
||||
nodesConnectable: false,
|
||||
maxZoom: 2,
|
||||
minZoom: 0.5
|
||||
} as const;
|
||||
|
||||
interface ASTFlowProps {
|
||||
data: SyntaxTree;
|
||||
onNodeEnter: (node: Node) => void;
|
||||
|
@ -53,11 +65,11 @@ export function ASTFlow({ data, onNodeEnter, onNodeLeave, onChangeDragging }: AS
|
|||
}, [data, setNodes, setEdges]);
|
||||
|
||||
return (
|
||||
<ReactFlow
|
||||
<DiagramFlow
|
||||
{...flowOptions}
|
||||
className='h-full w-full'
|
||||
nodes={nodes}
|
||||
edges={edges}
|
||||
edgesFocusable={false}
|
||||
nodesFocusable={false}
|
||||
onNodeMouseEnter={(_, node) => onNodeEnter(node)}
|
||||
onNodeMouseLeave={(_, node) => onNodeLeave(node)}
|
||||
onNodeDragStart={() => onChangeDragging(true)}
|
||||
|
@ -65,11 +77,6 @@ export function ASTFlow({ data, onNodeEnter, onNodeLeave, onChangeDragging }: AS
|
|||
onNodesChange={onNodesChange}
|
||||
nodeTypes={ASTNodeTypes}
|
||||
edgeTypes={ASTEdgeTypes}
|
||||
fitView
|
||||
maxZoom={2}
|
||||
minZoom={0.5}
|
||||
nodesConnectable={false}
|
||||
onContextMenu={event => event.preventDefault()}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -34,7 +34,9 @@ export function ASTNode(node: ASTNodeInternal) {
|
|||
<Handle type='source' position={Position.Bottom} className='opacity-0' />
|
||||
<div
|
||||
className={clsx(
|
||||
'font-math mt-1 w-fit text-center translate-x-[calc(-50%+20px)]',
|
||||
'mt-[4px] w-fit translate-x-[calc(-50%+20px)]',
|
||||
'font-math text-center ',
|
||||
'pointer-events-none',
|
||||
label.length > LABEL_THRESHOLD ? 'text-[12px]/[16px]' : 'text-[14px]/[20px]'
|
||||
)}
|
||||
>
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
'use client';
|
||||
|
||||
import { useEffect } from 'react';
|
||||
import { type Edge, ReactFlow, useEdgesState, useNodesState } from 'reactflow';
|
||||
import { type Edge, useEdgesState, useNodesState } from 'reactflow';
|
||||
|
||||
import { DiagramFlow } from '@/components/flow/diagram-flow';
|
||||
|
||||
import { type TypificationGraph } from '../../models/typification-graph';
|
||||
|
||||
|
@ -9,8 +11,15 @@ import { TMGraphEdgeTypes } from './graph/mgraph-edge-types';
|
|||
import { applyLayout } from './graph/mgraph-layout';
|
||||
import { TMGraphNodeTypes } from './graph/mgraph-node-types';
|
||||
|
||||
const ZOOM_MAX = 2;
|
||||
const ZOOM_MIN = 0.5;
|
||||
const flowOptions = {
|
||||
fitView: true,
|
||||
fitViewOptions: { padding: 0.25 },
|
||||
edgesFocusable: false,
|
||||
nodesFocusable: false,
|
||||
nodesConnectable: false,
|
||||
maxZoom: 2,
|
||||
minZoom: 0.5
|
||||
} as const;
|
||||
|
||||
interface MGraphFlowProps {
|
||||
data: TypificationGraph;
|
||||
|
@ -57,19 +66,14 @@ export function MGraphFlow({ data }: MGraphFlowProps) {
|
|||
}, [data, setNodes, setEdges]);
|
||||
|
||||
return (
|
||||
<ReactFlow
|
||||
<DiagramFlow
|
||||
{...flowOptions}
|
||||
className='h-full w-full'
|
||||
nodes={nodes}
|
||||
edges={edges}
|
||||
edgesFocusable={false}
|
||||
nodesFocusable={false}
|
||||
onNodesChange={onNodesChange}
|
||||
nodeTypes={TMGraphNodeTypes}
|
||||
edgeTypes={TMGraphEdgeTypes}
|
||||
fitView
|
||||
maxZoom={ZOOM_MAX}
|
||||
minZoom={ZOOM_MIN}
|
||||
nodesConnectable={false}
|
||||
onContextMenu={event => event.preventDefault()}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -54,7 +54,7 @@ export function TGNode(node: TGNodeInternal) {
|
|||
<div
|
||||
className={clsx(
|
||||
'w-full h-full cursor-default flex items-center justify-center rounded-full',
|
||||
isFocused && 'border-2 border-selected',
|
||||
isFocused && 'border-[2px] border-selected',
|
||||
label.length > LABEL_THRESHOLD ? 'text-[12px]/[16px]' : 'text-[14px]/[20px]'
|
||||
)}
|
||||
style={{
|
||||
|
@ -76,13 +76,14 @@ export function TGNode(node: TGNodeInternal) {
|
|||
{description ? (
|
||||
<div
|
||||
className={clsx(
|
||||
'mt-1 w-[150px] px-1 text-center translate-x-[calc(-50%+20px)]',
|
||||
'mt-[4px] w-[150px] px-[4px] text-center translate-x-[calc(-50%+20px)]',
|
||||
'pointer-events-none',
|
||||
description.length > DESCRIPTION_THRESHOLD ? 'text-[10px]/[12px]' : 'text-[12px]/[16px]'
|
||||
)}
|
||||
onContextMenu={handleContextMenu}
|
||||
onDoubleClick={handleDoubleClick}
|
||||
>
|
||||
<div className='absolute top-0 px-1 left-0 text-center w-full line-clamp-3 hover:line-clamp-none'>
|
||||
<div className='absolute top-0 px-[4px] left-0 text-center w-full line-clamp-3 hover:line-clamp-none'>
|
||||
{description}
|
||||
</div>
|
||||
<div aria-hidden className='line-clamp-3 hover:line-clamp-none cc-text-outline'>
|
||||
|
|
|
@ -1,18 +1,9 @@
|
|||
'use client';
|
||||
|
||||
import { useEffect, useRef } from 'react';
|
||||
import {
|
||||
type Edge,
|
||||
MarkerType,
|
||||
type Node,
|
||||
ReactFlow,
|
||||
useEdgesState,
|
||||
useNodesState,
|
||||
useOnSelectionChange,
|
||||
useReactFlow,
|
||||
useStoreApi
|
||||
} from 'reactflow';
|
||||
import { type Edge, MarkerType, type Node, useEdgesState, useNodesState, useOnSelectionChange } from 'reactflow';
|
||||
|
||||
import { DiagramFlow, useReactFlow, useStoreApi } from '@/components/flow/diagram-flow';
|
||||
import { useMainHeight } from '@/stores/app-layout';
|
||||
import { PARAMETER } from '@/utils/constants';
|
||||
|
||||
|
@ -32,9 +23,15 @@ import { ToolbarTermGraph } from './toolbar-term-graph';
|
|||
import { useFilteredGraph } from './use-filtered-graph';
|
||||
import { ViewHidden } from './view-hidden';
|
||||
|
||||
const ZOOM_MAX = 3;
|
||||
const ZOOM_MIN = 0.25;
|
||||
export const VIEW_PADDING = 0.3;
|
||||
export const flowOptions = {
|
||||
fitView: true,
|
||||
fitViewOptions: { padding: 0.3, duration: PARAMETER.zoomDuration },
|
||||
edgesFocusable: false,
|
||||
nodesFocusable: false,
|
||||
nodesConnectable: false,
|
||||
maxZoom: 3,
|
||||
minZoom: 0.25
|
||||
} as const;
|
||||
|
||||
export function TGFlow() {
|
||||
const mainHeight = useMainHeight();
|
||||
|
@ -116,9 +113,7 @@ export function TGFlow() {
|
|||
setNodes(newNodes);
|
||||
setEdges(newEdges);
|
||||
|
||||
setTimeout(() => {
|
||||
fitView({ duration: PARAMETER.zoomDuration, padding: VIEW_PADDING });
|
||||
}, PARAMETER.minimalTimeout);
|
||||
setTimeout(() => fitView(flowOptions.fitViewOptions), PARAMETER.minimalTimeout);
|
||||
}, [schema, filteredGraph, setNodes, setEdges, filter.noText, fitView, viewportInitialized, focusCst]);
|
||||
|
||||
const prevSelected = useRef<number[]>([]);
|
||||
|
@ -190,22 +185,16 @@ export function TGFlow() {
|
|||
<ViewHidden items={hidden} />
|
||||
</div>
|
||||
|
||||
<div className='relative outline-hidden w-[100dvw] cc-mask-sides' style={{ height: mainHeight }}>
|
||||
<ReactFlow
|
||||
nodes={nodes}
|
||||
onNodesChange={onNodesChange}
|
||||
edges={edges}
|
||||
fitView
|
||||
edgesFocusable={false}
|
||||
nodesFocusable={false}
|
||||
nodesConnectable={false}
|
||||
nodeTypes={TGNodeTypes}
|
||||
edgeTypes={TGEdgeTypes}
|
||||
maxZoom={ZOOM_MAX}
|
||||
minZoom={ZOOM_MIN}
|
||||
onContextMenu={event => event.preventDefault()}
|
||||
/>
|
||||
</div>
|
||||
<DiagramFlow
|
||||
{...flowOptions}
|
||||
height={mainHeight}
|
||||
nodes={nodes}
|
||||
onNodesChange={onNodesChange}
|
||||
edges={edges}
|
||||
nodeTypes={TGNodeTypes}
|
||||
edgeTypes={TGEdgeTypes}
|
||||
onContextMenu={event => event.preventDefault()}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -25,7 +25,7 @@ import { useMutatingRSForm } from '../../../backend/use-mutating-rsform';
|
|||
import { useTermGraphStore } from '../../../stores/term-graph';
|
||||
import { useRSEdit } from '../rsedit-context';
|
||||
|
||||
import { VIEW_PADDING } from './tg-flow';
|
||||
import { flowOptions } from './tg-flow';
|
||||
|
||||
export function ToolbarTermGraph() {
|
||||
const isProcessing = useMutatingRSForm();
|
||||
|
@ -76,7 +76,7 @@ export function ToolbarTermGraph() {
|
|||
|
||||
function handleFitView() {
|
||||
setTimeout(() => {
|
||||
fitView({ duration: PARAMETER.zoomDuration, padding: VIEW_PADDING });
|
||||
fitView(flowOptions.fitViewOptions);
|
||||
}, PARAMETER.minimalTimeout);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user