Skip to content

Commit

Permalink
[DOP-22355] add transform node (#67)
Browse files Browse the repository at this point in the history
* [DOP-22355] add transform node

* [DOP-22355] fix after review

---------

Co-authored-by: Zabilsya <kvcherniko@mts.ru>
  • Loading branch information
Zabilsya and Zabilsya authored Feb 4, 2025
1 parent ab4fdb0 commit b728846
Show file tree
Hide file tree
Showing 21 changed files with 203 additions and 35 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import React, { memo } from 'react';

/** TODO: [DOP-22361] Add component content */
export const FilterRows = memo(() => {
return <div>FilterRowsNode</div>;
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import React, { useMemo } from 'react';
import { CanvasNode } from '@shared/ui';
import { Handle, Position } from '@xyflow/react';
import { FilterOutlined } from '@ant-design/icons';

import { FilterRows } from '../FilterRows';
import { TransferCanvasEdge } from '../TransferConnectionsCanvas';

import { FilterRowsNodeProps } from './types';

export const FilterRowsNode = ({}: FilterRowsNodeProps) => {
const icon = useMemo(() => {
return <FilterOutlined />;
}, []);

const children = useMemo(() => {
return (
<>
<Handle type="target" position={Position.Left} id={TransferCanvasEdge.FILTER_ROWS_TARGET} />
<FilterRows />
<Handle type="source" position={Position.Right} id={TransferCanvasEdge.FILTER_ROWS_SOURCE} />
</>
);
}, []);

return (
<CanvasNode title="Filter rows" icon={icon}>
{children}
</CanvasNode>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './FilterRowsNode';
export * from './types';
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { Node, NodeProps } from '@xyflow/react';

import { TransferCanvasTransformNodeType } from '../TransferConnectionsCanvas';

export interface FilterRowsNodeData
extends Node<Record<string, unknown>, TransferCanvasTransformNodeType.FILTER_ROWS> {}

export interface FilterRowsNodeProps extends NodeProps<FilterRowsNodeData> {}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import { SourceParams } from '../SourceParams';
import { TransferCanvasEdge } from '../TransferConnectionsCanvas';

import { SourceParamsNodeProps } from './types';
import classes from './styles.module.less';

export const SourceParamsNode = ({ data }: SourceParamsNodeProps) => {
const connectionType = Form.useWatch<ConnectionType>(['source_params', 'type']);
Expand All @@ -28,7 +27,7 @@ export const SourceParamsNode = ({ data }: SourceParamsNodeProps) => {
}, [data.groupId, data.initialSourceConnectionType]);

return (
<CanvasNode className={classes.root} title="Source" icon={icon}>
<CanvasNode title="Source" icon={icon}>
{children}
</CanvasNode>
);
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { Node, NodeProps } from '@xyflow/react';

import { SourceParamsProps } from '../SourceParams';
import { TransferCanvasNode } from '../TransferConnectionsCanvas';
import { TransferCanvasDefaultNodeType } from '../TransferConnectionsCanvas';

export interface SourceParamsNodeData extends Node<SourceParamsProps, TransferCanvasNode.SOURCE> {}
export interface SourceParamsNodeData extends Node<SourceParamsProps, TransferCanvasDefaultNodeType.SOURCE> {}

export interface SourceParamsNodeProps extends NodeProps<SourceParamsNodeData> {}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import { TargetParams } from '../TargetParams';
import { TransferCanvasEdge } from '../TransferConnectionsCanvas';

import { TargetParamsNodeProps } from './types';
import classes from './styles.module.less';

export const TargetParamsNode = ({ data }: TargetParamsNodeProps) => {
const connectionType = Form.useWatch<ConnectionType>(['target_params', 'type']);
Expand All @@ -28,7 +27,7 @@ export const TargetParamsNode = ({ data }: TargetParamsNodeProps) => {
}, [data.groupId, data.initialTargetConnectionType]);

return (
<CanvasNode className={classes.root} title="Target" icon={icon}>
<CanvasNode title="Target" icon={icon}>
{children}
</CanvasNode>
);
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { Node, NodeProps } from '@xyflow/react';

import { TargetParamsProps } from '../TargetParams';
import { TransferCanvasNode } from '../TransferConnectionsCanvas';
import { TransferCanvasDefaultNodeType } from '../TransferConnectionsCanvas';

export interface TargetParamsNodeData extends Node<TargetParamsProps, TransferCanvasNode.TARGET> {}
export interface TargetParamsNodeData extends Node<TargetParamsProps, TransferCanvasDefaultNodeType.TARGET> {}

export interface TargetParamsNodeProps extends NodeProps<TargetParamsNodeData> {}
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,13 @@ import React, { useMemo } from 'react';
import { Canvas, Fieldset } from '@shared/ui';
import { ReactFlowProvider, useEdgesState, useNodesState } from '@xyflow/react';

import { TransformButtons } from '../TransformButtons';

import { TransferCanvasProps } from './types';
import { getInitialNodes } from './utils';
import { INITIAL_EDGES, NODE_TYPES } from './constants';
import classes from './styles.module.less';

import '@xyflow/react/dist/style.css';

export const TransferConnectionsCanvas = (props: TransferCanvasProps) => {
Expand All @@ -29,7 +32,9 @@ export const TransferConnectionsCanvas = (props: TransferCanvasProps) => {
edges={edges}
onNodesChange={onNodesChange}
onEdgesChange={onEdgesChange}
/>
>
<TransformButtons />
</Canvas>
</div>
</Fieldset>
</ReactFlowProvider>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,21 @@ import { Edge, NodeTypes } from '@xyflow/react';

import { SourceParamsNode } from '../SourceParamsNode';
import { TargetParamsNode } from '../TargetParamsNode';
import { FilterRowsNode } from '../FilterRowsNode';

import { TransferCanvasNode } from './types';
import { TransferCanvasDefaultNodeType, TransferCanvasTransformNodeType } from './types';

export const INITIAL_EDGES: Edge[] = [
{
id: 'edge-1',
source: TransferCanvasNode.SOURCE,
target: TransferCanvasNode.TARGET,
source: TransferCanvasDefaultNodeType.SOURCE,
target: TransferCanvasDefaultNodeType.TARGET,
animated: true,
},
];

export const NODE_TYPES: NodeTypes = {
[TransferCanvasNode.SOURCE]: SourceParamsNode,
[TransferCanvasNode.TARGET]: TargetParamsNode,
[TransferCanvasDefaultNodeType.SOURCE]: SourceParamsNode,
[TransferCanvasDefaultNodeType.TARGET]: TargetParamsNode,
[TransferCanvasTransformNodeType.FILTER_ROWS]: FilterRowsNode,
};
Original file line number Diff line number Diff line change
@@ -1,18 +1,26 @@
import { FilterRowsNodeData } from '../FilterRowsNode';
import { SourceParamsNodeData } from '../SourceParamsNode';
import { TargetParamsNodeData } from '../TargetParamsNode';

import { GetInitialNodesProps } from './utils';

export interface TransferCanvasProps extends GetInitialNodesProps {}
export interface TransferCanvasProps
extends Pick<GetInitialNodesProps, 'groupId' | 'initialSourceConnectionType' | 'initialTargetConnectionType'> {}

export type TransferCanvasNodeData = SourceParamsNodeData | TargetParamsNodeData;
export type TransferCanvasNodeData = SourceParamsNodeData | TargetParamsNodeData | FilterRowsNodeData;

export enum TransferCanvasNode {
export enum TransferCanvasDefaultNodeType {
SOURCE = 'SOURCE',
TARGET = 'TARGET',
}

export enum TransferCanvasTransformNodeType {
FILTER_ROWS = 'FILTER_ROWS',
}

export enum TransferCanvasEdge {
SOURCE = 'SOURCE',
TARGET = 'TARGET',
FILTER_ROWS_SOURCE = 'FILTER_ROWS_SOURCE',
FILTER_ROWS_TARGET = 'FILTER_ROWS_TARGET',
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { TransferCanvasNodeData, TransferCanvasNode } from '../../types';
import { TransferCanvasNodeData, TransferCanvasDefaultNodeType } from '../../types';

import { GetInitialNodesProps } from './types';

Expand All @@ -8,15 +8,15 @@ export const getInitialNodes = ({
initialTargetConnectionType,
}: GetInitialNodesProps): TransferCanvasNodeData[] => [
{
id: TransferCanvasNode.SOURCE,
type: TransferCanvasNode.SOURCE,
id: TransferCanvasDefaultNodeType.SOURCE,
type: TransferCanvasDefaultNodeType.SOURCE,
data: { groupId, initialSourceConnectionType },
position: { x: 0, y: 0 },
},
{
id: TransferCanvasNode.TARGET,
type: TransferCanvasNode.TARGET,
id: TransferCanvasDefaultNodeType.TARGET,
type: TransferCanvasDefaultNodeType.TARGET,
data: { groupId, initialTargetConnectionType },
position: { x: 600, y: 0 },
position: { x: 500, y: 0 },
},
];
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import React, { memo } from 'react';
import { Button, Typography } from 'antd';

import { TransferCanvasTransformNodeType } from '../TransferConnectionsCanvas';

import classes from './styles.module.less';
import { useHandleNodes } from './hooks';

const { Text } = Typography;

export const TransformButtons = memo(() => {
const { transformNodeTypes, handleAddTransformNode } = useHandleNodes();

return (
<div className={classes.root}>
<Text strong>You can add some data transforms:</Text>
<div className={classes.buttons}>
<Button
type="primary"
onClick={() => handleAddTransformNode(TransferCanvasTransformNodeType.FILTER_ROWS)}
disabled={transformNodeTypes ? transformNodeTypes[TransferCanvasTransformNodeType.FILTER_ROWS] : false}
>
Filter rows
</Button>
</div>
</div>
);
});
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './useHandleNodes';
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { useReactFlow } from '@xyflow/react';
import { useState } from 'react';

import { TransferCanvasDefaultNodeType, TransferCanvasTransformNodeType } from '../../../TransferConnectionsCanvas';

/** Hook for handling nodes and edges data (add, delete) */
export const useHandleNodes = () => {
const [transformNodeTypes, setTransformNodeTypes] = useState<Record<TransferCanvasTransformNodeType, true>>();
const { getNodes, getEdges, setNodes, setEdges } = useReactFlow();

const addNewNode = (nodeType: TransferCanvasTransformNodeType) => {
const newNode = {
id: nodeType,
type: nodeType,
position: { x: 0, y: 0 },
data: {},
};

const nodes = getNodes();

const firstNodes = nodes.slice(0, nodes.length - 1);
const lastNode = nodes[nodes.length - 1];
const newNodes = firstNodes.concat(newNode).concat(lastNode);

setNodes(newNodes.map((node, index) => ({ ...node, position: { x: index * 500, y: 0 } })));
};

const addNewEdge = (nodeType: TransferCanvasTransformNodeType) => {
const edges = getEdges();

const newEdge = {
id: `edge-${edges.length + 1}`,
source: nodeType,
target: TransferCanvasDefaultNodeType.TARGET,
animated: true,
};

const firstEdges = edges.slice(0, edges.length - 1);
const lastEdge = { ...edges[edges.length - 1], target: nodeType };
const newEdges = firstEdges.concat(lastEdge).concat(newEdge);

setEdges(newEdges);
};

const handleAddTransformNode = (nodeType: TransferCanvasTransformNodeType) => {
setTransformNodeTypes((state) => ({ ...state, [nodeType]: true }));
addNewNode(nodeType);
addNewEdge(nodeType);
};

return { transformNodeTypes, handleAddTransformNode };
};
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './TransformButtons';
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
.root {
display: flex;
flex-direction: column;
gap: 8px;
position: absolute;
bottom: 0;
left: 50%;
transform: translateX(-50%);
background-color: @white;
margin-bottom: 15px;
border-radius: 16px;
padding: 16px;
z-index: 5;

.buttons {
display: flex;
align-items: center;
gap: 12px;
}
}
17 changes: 13 additions & 4 deletions src/shared/ui/Canvas/index.tsx
Original file line number Diff line number Diff line change
@@ -1,30 +1,39 @@
import { Background, BackgroundVariant, Controls, Edge, MiniMap, Node, ReactFlow } from '@xyflow/react';
import React from 'react';
import { Background, BackgroundVariant, Controls, Edge, MiniMap, Node, ReactFlow, useReactFlow } from '@xyflow/react';
import React, { useEffect } from 'react';

import classes from './styles.module.less';
import { CanvasProps } from './types';

export const Canvas = <N extends Node, E extends Edge>(props: CanvasProps<N, E>) => {
export const Canvas = <N extends Node, E extends Edge>({ children, nodes, ...props }: CanvasProps<N, E>) => {
const { fitView } = useReactFlow();

/** Set appropriate zoom of canvas when nodes count has changed */
useEffect(() => {
fitView({ duration: 200 });
}, [nodes?.length, fitView]);

return (
<ReactFlow
className={classes.root}
attributionPosition="top-right"
nodesFocusable
elementsSelectable
nodesConnectable={false}
nodes={nodes}
nodesDraggable
panOnScroll={false}
panOnDrag
zoomOnScroll
zoomOnPinch
zoomOnDoubleClick={false}
fitViewOptions={{ duration: 200, padding: 1 }}
fitViewOptions={{ duration: 200, padding: 0.5 }}
fitView
{...props}
>
<Background bgColor="#f0f2f5" variant={BackgroundVariant.Cross} size={3} />
<Controls />
<MiniMap />
{children}
</ReactFlow>
);
};
7 changes: 5 additions & 2 deletions src/shared/ui/Canvas/styles.module.less
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
.root {
:global(.react-flow__minimap) {
transform: scale(70%);
svg {
width: 140px;
height: 105px;
}
}

:global(.react-flow__node) {
width: fit-content;
width: 300px;
background-color: @white;
border: 1px solid fade(@black, 25%);
border-radius: 16px;
Expand Down

0 comments on commit b728846

Please sign in to comment.