diff --git a/docs/useDocs/intro.md b/docs/useDocs/intro.md index b3bca47..df7342a 100644 --- a/docs/useDocs/intro.md +++ b/docs/useDocs/intro.md @@ -6,5 +6,3 @@ group: title: 为什么选择 ReactFlow ? description: --- - -dsfsaf diff --git a/docs/useDocs/quickDoc.md b/docs/useDocs/quickDoc.md index ce0fff5..896bba0 100644 --- a/docs/useDocs/quickDoc.md +++ b/docs/useDocs/quickDoc.md @@ -8,4 +8,34 @@ order: 1 description: --- -asd aws +## 快速入门 + +如果您想尽快启动并运行,那么您来对地方了! 此页面将在几分钟内带您从零到一创建一个有效的 ProFlow 应用程序。如果您想深入的了解 ProFlow 的全部内容,请查看示例,或深入了解 API 文档。 + +## 安装 + +若要在本地开始,应具备以下几点: + +- [Nodejs](https://nodejs.org/en) 安装 +- npm 或其他包管理工具,比如 [yarn](https://yarnpkg.com/) 或 [pnpm](https://pnpm.io/) +- 以及 [React](https://reactjs.org/)的基础知识 + +首先启动一个 React 应用,推荐使用 [vite](https://vitejs.dev/), 但选择权在你手中。 + +```bash +pnpm create vite@latest my-pro-flow-app --template react +``` + +ProFlow 在 npm 上发布为 [@ant-design/pro-flow](https://www.npmjs.com/package/@ant-design/pro-flow) ,推荐使用 pnpm 安装。 + +```bash +pnpm i @ant-design/pro-flow -S +``` + +最后 React 服务,就可以开始了。 + +```bash +pnpm run dev +``` + +## 创建第一个 ProFlow diff --git a/src/Background/demos/double.tsx b/src/Background/demos/double.tsx new file mode 100644 index 0000000..fb8a8fd --- /dev/null +++ b/src/Background/demos/double.tsx @@ -0,0 +1,26 @@ +import { ProFlow } from '@/index'; +import { createStyles } from 'antd-style'; +import { memo } from 'react'; +import Background, { BackgroundVariant } from '..'; + +const useStyles = createStyles(({ css }) => ({ + container: css` + width: 100%; + height: 600px; + `, +})); + +const BackgroundDemo = memo(() => { + const { styles } = useStyles(); + + return ( +
+ + + + +
+ ); +}); + +export default BackgroundDemo; diff --git a/src/Background/demos/index.tsx b/src/Background/demos/index.tsx new file mode 100644 index 0000000..c2ac14a --- /dev/null +++ b/src/Background/demos/index.tsx @@ -0,0 +1,39 @@ +import { ProFlow } from '@/index'; +import { createStyles } from 'antd-style'; +import { memo, useState } from 'react'; +import { Panel } from 'reactflow'; +import Background, { BackgroundVariant } from '..'; + +const useStyles = createStyles(({ css }) => ({ + container: css` + width: 100%; + height: 600px; + `, +})); + +const BackgroundDemo = memo(() => { + const [variant, setVariant] = useState(BackgroundVariant.Cross); + const { styles } = useStyles(); + + return ( +
+ + +
variant:
+ + + +
+ +
+
+ ); +}); + +export default BackgroundDemo; diff --git a/src/Background/index.md b/src/Background/index.md new file mode 100644 index 0000000..37614bb --- /dev/null +++ b/src/Background/index.md @@ -0,0 +1,35 @@ +--- +group: 辅助 +title: Background 画布背景 +description: 配合ProFLow组件使用,控制背景展示 +--- + +## Default + + + +## Double Background + + + +## APIs + +| 属性名 | 类型 | 描述 | 默认值 | 必选 | +| --------- | --------------------------- | ------------------------------------------------------------------------ | ------ | ---- | +| id | `string` | 如果要显示多个背景,则需要 | - | - | +| className | `string` | 自定义类名 | - | - | +| variant | `BackgroundVariant` | 背景图案类型 | - | - | +| gap | `number \|[number, number]` | 模式之间的差距。您可以传递一个包含两个数字的数组来指定 x 间隙和 y 间隙。 | - | - | +| size | `number` | ”点“的半径或”十字“的尺寸 | - | - | +| lineWidth | `number` | ”线“或”十字“的宽度 | - | - | +| offset | `number` | 图案偏移 | - | - | +| color | `string` | 图案颜色 | - | - | +| style | `CSSProperties` | 样式属性 | - | - | + +### BackgroundVariant + +| 属性名 | 类型 | 描述 | 默认值 | 必选 | +| ------ | -------- | ---- | ------ | ---- | +| lines | `string` | 线 | - | - | +| dots | `string` | 点 | - | - | +| cross | `string` | 十字 | - | - | diff --git a/src/Background/index.tsx b/src/Background/index.tsx new file mode 100644 index 0000000..f48d0a1 --- /dev/null +++ b/src/Background/index.tsx @@ -0,0 +1,22 @@ +import { CSSProperties } from 'react'; +import { Background, BackgroundVariant } from 'reactflow'; + +interface BackgroundProps { + variant?: BackgroundVariant; + gap?: number | [number, number]; + size?: number; + lineWidth?: number; + offset?: number; + color?: string; + style?: CSSProperties; + className?: string; + id?: string; +} + +export default (props: BackgroundProps) => { + const { gap = 10, color = '#bac3d4' } = props; + + return ; +}; + +export { BackgroundVariant }; diff --git a/src/ControlInput/index.md b/src/ControlInput/index.md index 065e60c..ddfef30 100644 --- a/src/ControlInput/index.md +++ b/src/ControlInput/index.md @@ -1,6 +1,6 @@ --- title: ControlInput 可控输入框 -group: 输入控件 +group: 控件 description: 针对编辑场景优化的输入框控件 --- diff --git a/src/EditableText/index.md b/src/EditableText/index.md index 0abdfd6..006d733 100644 --- a/src/EditableText/index.md +++ b/src/EditableText/index.md @@ -1,5 +1,5 @@ --- -group: 输入控件 +group: 控件 title: EditableText 可编辑文本 description: EditableText is a component that allows users to edit text inline. It displays the text in a non-editable state by default, but when the user clicks the edit icon, it switches to an editable input field where the user can make changes. Once the user is done editing, they can click outside the input field or press the enter key to save the changes. The component uses the ControlInput component to display the input field and passes the value and onChange props to it. --- diff --git a/src/FlowPanel/demos/index.tsx b/src/FlowPanel/demos/index.tsx new file mode 100644 index 0000000..144fd43 --- /dev/null +++ b/src/FlowPanel/demos/index.tsx @@ -0,0 +1,29 @@ +import { ProFlow } from '@/index'; +import { createStyles } from 'antd-style'; +import { memo } from 'react'; +import FlowPanel from '..'; + +const useStyles = createStyles(({ css }) => ({ + container: css` + width: 100%; + height: 600px; + `, +})); + +const FlowControllerDemo = memo(() => { + const { styles } = useStyles(); + return ( +
+ + top-left + top-center + top-right + bottom-left + bottom-center + bottom-right + +
+ ); +}); + +export default FlowControllerDemo; diff --git a/src/FlowPanel/index.md b/src/FlowPanel/index.md new file mode 100644 index 0000000..82ef992 --- /dev/null +++ b/src/FlowPanel/index.md @@ -0,0 +1,24 @@ +--- +group: 辅助 +title: FlowPanel 画布面板 +description: 配合ProFLow组件使用,提供一个展示在画布上的面板 +--- + +## Default + + + +## APIs + +| 属性名 | 类型 | 描述 | 默认值 | 必选 | +| --------- | ----------------- | -------------------- | ------ | ---- | +| className | `string` | 自定义类名 | - | - | +| visible | `boolean` | 是否展示 | - | - | +| position | `MiniMapPosition` | 控制器在画布中的坐标 | - | - | + +### MiniMapPosition + +| 属性名 | 类型 | 描述 | 默认值 | 必选 | +| ------ | -------- | ------------------------------ | ------ | ---- | +| x | `number` | x 坐标,正数向左偏移,负数反之 | 0 | - | +| y | `number` | y 坐标,正数向上偏移,负数反之 | 0 | - | diff --git a/src/FlowPanel/index.tsx b/src/FlowPanel/index.tsx new file mode 100644 index 0000000..c4a6e95 --- /dev/null +++ b/src/FlowPanel/index.tsx @@ -0,0 +1,16 @@ +import { Panel, PanelPosition } from 'reactflow'; + +interface PanelProps { + position?: PanelPosition; + children: React.ReactNode; + className?: string; + style?: React.CSSProperties; +} + +export default (props: PanelProps) => { + const { position = 'top-left', children } = props; + + return {children}; +}; + +export { PanelPosition }; diff --git a/src/FlowStoreProvider/index.md b/src/FlowStoreProvider/index.md index 15c8d46..a79fc8a 100644 --- a/src/FlowStoreProvider/index.md +++ b/src/FlowStoreProvider/index.md @@ -2,7 +2,7 @@ title: FlowStoreProvider 流数据容器 atomId: FlowStoreProvider nav: 组件 -group: 容器组件 +group: 辅助 --- # FlowStoreProvider diff --git a/src/ProFlow/FlowView.tsx b/src/FlowView/FlowView.tsx similarity index 82% rename from src/ProFlow/FlowView.tsx rename to src/FlowView/FlowView.tsx index 7eb3360..37cb539 100644 --- a/src/ProFlow/FlowView.tsx +++ b/src/FlowView/FlowView.tsx @@ -1,10 +1,10 @@ -import { ProFlowProps } from '@/constants'; +import { FlowViewProps } from '@/constants'; import { FC, useContext } from 'react'; import FlowView from '.'; import { FlowViewProvider } from './provider/FlowViewProvider'; import { FlowViewContext } from './provider/provider'; -const ProFlow: FC = (props) => { +const ProFlow: FC = (props) => { const { isUseProvider } = useContext(FlowViewContext); if (isUseProvider) { diff --git a/src/FlowView/components/DefaultNode.tsx b/src/FlowView/components/DefaultNode.tsx new file mode 100644 index 0000000..1dce259 --- /dev/null +++ b/src/FlowView/components/DefaultNode.tsx @@ -0,0 +1,12 @@ +import { DefaultNodeData } from '@/constants'; +import { FC } from 'react'; +import { useStyles } from '../styles'; + +const DefaultNode: FC = (props) => { + const { styles, cx } = useStyles(); + const { className, children } = props; + + return
{children}
; +}; + +export default DefaultNode; diff --git a/src/ProFlow/constants.tsx b/src/FlowView/constants.tsx similarity index 88% rename from src/ProFlow/constants.tsx rename to src/FlowView/constants.tsx index 1f1a2d2..e8da36a 100644 --- a/src/ProFlow/constants.tsx +++ b/src/FlowView/constants.tsx @@ -1,5 +1,5 @@ import { Node } from 'reactflow'; -import { ProFlowNode, ProFlowNodeData } from '../constants'; +import { DefaultNodeType, FlowNodeType, NodeTypeDataMap } from '../constants'; export enum NodeSelect { SELECT = 'SELECT', @@ -15,7 +15,7 @@ export interface InitialNode extends Node { height?: number; } -export interface NodeMapItem { +export interface NodeMapItem> { id: string; key?: string; left?: string[]; @@ -29,6 +29,7 @@ export interface NodeMapItem { danger?: boolean; dangerCount?: number; type?: 'input' | 'output' | 'default'; + flowNodeType?: T; className?: string; select?: NodeSelect; isGroup?: boolean; @@ -38,7 +39,7 @@ export interface NodeMapItem { qualityScore?: string; subDanger?: boolean; logo?: string; - data: ProFlowNodeData | ProFlowNode[]; + data: NodeTypeDataMap[T]; nodeType?: string; zoom?: number; label?: string; diff --git a/src/ProFlow/demos/ProFlowDemo.tsx b/src/FlowView/demos/ProFlowDemo.tsx similarity index 92% rename from src/ProFlow/demos/ProFlowDemo.tsx rename to src/FlowView/demos/ProFlowDemo.tsx index abf8d96..0bc8765 100644 --- a/src/ProFlow/demos/ProFlowDemo.tsx +++ b/src/FlowView/demos/ProFlowDemo.tsx @@ -1,4 +1,4 @@ -import { EdgeType, NodeSelect, ProFlowEdge, ProFlowNode } from '@/index'; +import { EdgeType, FlowViewEdge, FlowViewNode, NodeSelect } from '@/index'; import { ProFlow } from '@ant-design/pro-flow'; import { Progress } from 'antd'; import { createStyles } from 'antd-style'; @@ -49,14 +49,13 @@ const DangerLogo = styled.div` } `; -const nodes: ProFlowNode[] = [ +const nodes: FlowViewNode[] = [ { id: 'a1', label: '123', + type: 'default', data: { - title: 'XXX数据源', - describe: 'cksadjfnf', - logo: 'https://mdn.alipayobjects.com/huamei_ntgeqc/afts/img/A*jWDsQ5GTmHMAAAAAAAAAAAAADvuvAQ/original', + children:
default node, 123123
, }, }, { @@ -118,8 +117,8 @@ const nodes: ProFlowNode[] = [ }, { id: 'd1', - group: true, label: '456', + type: 'lineageGroup', data: [ { id: 'a5', @@ -181,7 +180,7 @@ const nodes: ProFlowNode[] = [ }, ]; -const edges: ProFlowEdge[] = [ +const edges: FlowViewEdge[] = [ { id: 'a1-b1', source: 'a1', @@ -240,11 +239,11 @@ const edges: ProFlowEdge[] = [ ]; const ProFlowDemo = () => { - const [_nodes, setNodes] = useState([...nodes]); - const [_edges, setEdges] = useState([...edges]); + const [_nodes, setNodes] = useState([...nodes]); + const [_edges, setEdges] = useState([...edges]); const { styles } = useStyles(); - const handleHighlight = (node: ProFlowNode) => { + const handleHighlight = (node: FlowViewNode) => { _nodes.forEach((_node) => { if (_node.id === node.id) { _node.select = NodeSelect.SELECT; diff --git a/src/ProFlow/helper.tsx b/src/FlowView/helper.tsx similarity index 72% rename from src/ProFlow/helper.tsx rename to src/FlowView/helper.tsx index b76a2e1..57392b4 100644 --- a/src/ProFlow/helper.tsx +++ b/src/FlowView/helper.tsx @@ -1,9 +1,18 @@ -import BloodNodeGroup from '@/BloodGroupNode'; -import BloodNode from '@/BloodNode'; -import { EdgeType, ProFlowEdge, ProFlowNode, ProFlowNodeData } from '@/constants'; +import LineageNodeGroup from '@/LineageGroupNode'; +import LineageNode from '@/LineageNode'; +import { + DefaultNodeData, + EdgeType, + FlowViewEdge, + FlowViewNode, + LineageGroupNodeData, + LineageNodeData, + NodeHandler, +} from '@/constants'; import Dagre from '@dagrejs/dagre'; import { cx } from 'antd-style'; import { Edge, Node, Position } from 'reactflow'; +import DefaultNode from './components/DefaultNode'; import { EDGE_DANGER, EDGE_SELECT, @@ -18,6 +27,7 @@ import { NodeSelect, } from './constants'; +// 这里的type是指节点的连接点在哪里,input是在左边,output是在右边,default是左右两边都有 function getTypeFromEdge(node: NodeMapItem) { if (node.left?.length && node.right?.length) { return 'default'; @@ -31,16 +41,18 @@ function getTypeFromEdge(node: NodeMapItem) { return 'default'; } -export function convertMappingFrom(nodes: ProFlowNode[], edges: ProFlowEdge[], zoom: number) { +export function convertMappingFrom(nodes: FlowViewNode[], edges: FlowViewEdge[], zoom: number) { const mapping: NodeMapping = {}; nodes.forEach((node) => { + const { type = 'lineage' } = node; + mapping[node.id] = { id: node.id, - group: node.group, - width: node.group ? 355 : 322, - height: node.group ? 1100 : 85, + // width: width ? width : node.group ? 355 : 322, + // height: height ? height : node.group ? 1100 : 85, data: node.data, select: node.select, + flowNodeType: type, right: [], left: [], zoom, @@ -141,7 +153,7 @@ function getEdgeClsFromNodeSelect(select: NodeSelect) { // } // } -export function getRenderEdges(edges: ProFlowEdge[]) { +export function getRenderEdges(edges: FlowViewEdge[]) { return edges.map((edge) => { const { source, target, select = NodeSelect.DEFAULT, type } = edge; @@ -161,9 +173,41 @@ export function getRenderEdges(edges: ProFlowEdge[]) { // }) } +const NodeComponentHandler: NodeHandler = { + default: (node: NodeMapItem) => , + lineage: (node: NodeMapItem) => { + const { select = NodeSelect.DEFAULT } = node; + + return ( + + ); + }, + lineageGroup: (node: NodeMapItem) => { + const { select = NodeSelect.DEFAULT } = node; + + return ( + + ); + }, +}; + export const getRenderData = ( mapping: NodeMapping, - edges: ProFlowEdge[], + edges: FlowViewEdge[], ): { nodes: Node[]; edges: Edge[]; @@ -174,7 +218,7 @@ export const getRenderData = ( Object.keys(mapping).forEach((id) => { const node = mapping[id]; - const { select = NodeSelect.DEFAULT } = node; + const { flowNodeType } = node; renderNodes.push({ sourcePosition: Position.Right, @@ -186,26 +230,7 @@ export const getRenderData = ( height: node.group ? 1100 : 83, className: cx(INIT_NODE), data: { - label: node.group ? ( - - ) : ( - - ), + label: NodeComponentHandler[flowNodeType!](node), }, }); }); diff --git a/src/ProFlow/hooks/useFlowView.ts b/src/FlowView/hooks/useFlowView.ts similarity index 94% rename from src/ProFlow/hooks/useFlowView.ts rename to src/FlowView/hooks/useFlowView.ts index fad59cc..9c6c18c 100644 --- a/src/ProFlow/hooks/useFlowView.ts +++ b/src/FlowView/hooks/useFlowView.ts @@ -13,7 +13,7 @@ export const useMiniMap = () => { const { setMiniMapPosition: setPosition } = useContext(FlowViewContext); const setMiniMapPosition = (x: number, y: number) => { - setPosition!([x, y]); + setPosition!({ x, y }); }; return { diff --git a/src/FlowView/index.md b/src/FlowView/index.md new file mode 100644 index 0000000..b3a38ec --- /dev/null +++ b/src/FlowView/index.md @@ -0,0 +1,90 @@ +--- +group: 画布 +title: FlowView 基础画布容器 +description: +--- + +## default + + + +## API + +| 属性名 | 类型 | 描述 | 默认值 | 必选 | +| ---------- | ----------------- | -------- | ------ | ---- | +| nodes | `FlowViewNode` | 边数据 | - | - | +| edges | `FlowViewEdge` | 节点数据 | - | - | +| className | `string` | 边数据 | - | - | +| style | `CSSProperties` | 节点数据 | - | - | +| miniMap | `boolean` | 边数据 | - | - | +| background | `boolean` | 节点数据 | - | - | +| children | `React.ReactNode` | 边数据 | - | - | + +### FlowViewNode + +| 属性名 | 类型 | 描述 | 默认值 | 必选 | +| ------ | ---------------------------------------------- | ------------ | ------ | ---- | +| id | `string` | 边数据 | - | - | +| select | `NodeSelect` | 节点数据 | - | - | +| data | `NodeTypeDataMap[T]` | 边数据 | - | - | +| type | `T = 'default' \| 'lineage' \| 'lineageGroup'` | 节点类型() | - | - | +| label | `string` | 边数据 | - | - | +| width | `number` | 节点数据 | - | - | +| height | `number` | 边数据 | - | - | + +#### NodeTypeDataMap[T] + +```ts +export interface NodeTypeDataMap { + default: DefaultNodeData; + lineage: LineageNodeData; + lineageGroup: LineageGroupNodeData[]; +} +``` + +#### DefaultNodeData + +| 属性名 | 类型 | 描述 | 默认值 | 必选 | +| --------- | ----------------- | ------ | ------ | ---- | +| className | `string` | 类名 | - | - | +| children | `React.ReactNode` | 子组件 | - | - | + +#### LineageNodeData + +| 属性名 | 类型 | 描述 | 默认值 | 必选 | +| --------- | ----------------------------------------------------- | ---- | ------ | ---- | +| title | `string` | 类名 | - | - | +| describe | `string` | 描述 | - | - | +| logo | `string` | logo | - | - | +| titleSlot | `{ type: 'left' \| 'right', value: React.ReactNode }` | 插槽 | - | - | + +#### LineageGroupNodeData + +| 属性名 | 类型 | 描述 | 默认值 | 必选 | +| ------ | ----------------- | ------ | ------ | ---- | +| id | `string` | 类名 | - | - | +| data | `LineageNodeData` | 子组件 | - | - | + +#### NodeSelect + +```ts +export enum NodeSelect { + SELECT = 'SELECT', + SUB_SELECT = 'SUB_SELECT', + DANGER = 'DANGER', + SUB_DANGER = 'SUB_DANGER', + WARNING = 'WARNING', + SUB_WARNING = 'SUB_WARNING', + DEFAULT = 'DEFAULT', +} +``` + +### FlowViewEdge + +| 属性名 | 类型 | 描述 | 默认值 | 必选 | +| ------ | ----------------------------------- | ----------- | ------ | ---- | +| id | `string` | 唯一 id | - | - | +| source | `string` | 来源节点 id | - | - | +| target | `string` | 目标节点 id | - | - | +| select | `NodeSelect` | 选中状态 | - | - | +| type | `EdgeType = 'default' \| 'radius' ` | 边缘类型 | - | - | diff --git a/src/ProFlow/index.tsx b/src/FlowView/index.tsx similarity index 77% rename from src/ProFlow/index.tsx rename to src/FlowView/index.tsx index 6bf330b..5264cde 100644 --- a/src/ProFlow/index.tsx +++ b/src/FlowView/index.tsx @@ -5,9 +5,10 @@ import React, { useMemo, type MouseEvent as ReactMouseEvent, } from 'react'; -import ReactFlow, { Background, BackgroundVariant, Edge, Node, useViewport } from 'reactflow'; +import ReactFlow, { Edge, Node, useViewport } from 'reactflow'; import 'reactflow/dist/style.css'; -import { ProFlowController, ProFlowProps, RadiusEdge } from '../index'; +import Background, { BackgroundVariant } from '../Background'; +import { FlowViewProps, ProFlowController, RadiusEdge } from '../index'; import { convertMappingFrom, getRenderData } from './helper'; import { FlowViewContext } from './provider/provider'; import { useStyles } from './styles'; @@ -16,7 +17,7 @@ const MIN_ZOOM = 0.1; export const FlowContext = createContext({}); const initFn = () => {}; -const FlowView: React.FC> = (props) => { +const FlowView: React.FC> = (props) => { const { onNodeDragStart = initFn, onPaneClick = initFn, @@ -25,6 +26,7 @@ const FlowView: React.FC> = (props) => { edges, miniMap = true, children, + background = true, } = props; const { styles, cx } = useStyles(); const { zoom } = useViewport(); @@ -53,7 +55,7 @@ const FlowView: React.FC> = (props) => { // console.log(reactFlowInstance); const handleNodeDragStart = useCallback( (event: ReactMouseEvent, node: Node, nodes: Node[]) => { - // TODO: 应当把事件中的 node 转换为 ProFlowNode 透出给用户 + // TODO: 应当把事件中的 node 转换为 FlowViewNode 透出给用户 // const {node} = transformNode(node); onNodeDragStart(event, node, nodes); }, @@ -62,7 +64,7 @@ const FlowView: React.FC> = (props) => { const handlePaneClick = useCallback( (event: ReactMouseEvent) => { - // TODO: 应当把事件中的 node 转换为 ProFlowNode 透出给用户 + // TODO: 应当把事件中的 node 转换为 FlowViewNode 透出给用户 // const {node} = transformNode(node); onPaneClick(event); }, @@ -71,7 +73,7 @@ const FlowView: React.FC> = (props) => { const handleNodeClick = useCallback( (event: ReactMouseEvent, node: Node) => { - // TODO: 应当把事件中的 node 转换为 ProFlowNode 透出给用户 + // TODO: 应当把事件中的 node 转换为 FlowViewNode 透出给用户 // const {node} = transformNode(node); onNodeClick(event, node); }, @@ -97,14 +99,15 @@ const FlowView: React.FC> = (props) => { {miniMap && ( )} - {children} + {background && ( + + )} ); }; diff --git a/src/ProFlow/provider/FlowViewProvider.tsx b/src/FlowView/provider/FlowViewProvider.tsx similarity index 84% rename from src/ProFlow/provider/FlowViewProvider.tsx rename to src/FlowView/provider/FlowViewProvider.tsx index 2c366b3..39b0c49 100644 --- a/src/ProFlow/provider/FlowViewProvider.tsx +++ b/src/FlowView/provider/FlowViewProvider.tsx @@ -1,10 +1,11 @@ +import { MiniMapPosition } from '@/constants'; import { FC, ReactNode, useState } from 'react'; import { ReactFlowProvider, useReactFlow } from 'reactflow'; import { FlowViewContext } from './provider'; // 数据处理层 const ProviderInner: FC<{ children: ReactNode }> = ({ children }) => { - const [miniMapPosition, setMiniMapPosition] = useState<[number, number]>([0, 0]); + const [miniMapPosition, setMiniMapPosition] = useState({ x: 0, y: 0 }); const reactFlowInstance = useReactFlow(); return ( diff --git a/src/ProFlow/provider/provider.ts b/src/FlowView/provider/provider.ts similarity index 62% rename from src/ProFlow/provider/provider.ts rename to src/FlowView/provider/provider.ts index 5570bfb..1dd70a0 100644 --- a/src/ProFlow/provider/provider.ts +++ b/src/FlowView/provider/provider.ts @@ -1,11 +1,12 @@ +import { MiniMapPosition } from '@/constants'; import { createContext } from 'react'; import { ReactFlowInstance } from 'reactflow'; interface FlowViewContextProps { isUseProvider?: boolean; reactFlowInstance?: ReactFlowInstance; - miniMapPosition?: [number, number]; - setMiniMapPosition?: React.Dispatch>; + miniMapPosition?: MiniMapPosition; + setMiniMapPosition?: React.Dispatch>; } export const FlowViewContext = createContext({}); diff --git a/src/FlowView/styles.tsx b/src/FlowView/styles.tsx new file mode 100644 index 0000000..d513251 --- /dev/null +++ b/src/FlowView/styles.tsx @@ -0,0 +1,169 @@ +import { createStyles } from 'antd-style'; +import { + EDGE_DANGER, + EDGE_SELECT, + EDGE_SUB_DANGER, + EDGE_SUB_SELECT, + EDGE_SUB_WARNING, + EDGE_WARNING, + INIT_NODE, +} from './constants'; + +export const useStyles = createStyles(({ css, cx }) => ({ + container: css` + width: 100%; + height: 100%; + + .react-flow__attribution { + display: none; + } + + .${INIT_NODE} { + padding: 0; + box-sizing: border-box; + width: 320px; + height: 83px; + border: none; + border-radius: 8px; + cursor: pointer; + z-index: 1; + } + + .${EDGE_SELECT} path { + stroke: #1677ff; + stroke-width: 2; + z-index: 100; + } + + .${EDGE_SUB_SELECT} path { + stroke: #1677ff; + stroke-width: 1; + z-index: 100; + } + + .${EDGE_DANGER} path { + stroke: #f7636e; + stroke-width: 2; + z-index: 100; + } + + .${EDGE_SUB_DANGER} path { + stroke: #f7636e; + stroke-width: 1; + z-index: 100; + } + + .${EDGE_WARNING} path { + stroke: #ef9d3b; + stroke-width: 2; + z-index: 100; + } + + .${EDGE_SUB_WARNING} path { + stroke: #ef9d3b; + stroke-width: 1; + z-index: 100; + } + + .selectable:focus { + box-shadow: none !important; + } + + .selected { + box-shadow: none !important; + } + `, + nodeWrap: cx( + css` + width: 320px; + height: 85px; + display: flex; + z-index: 10 !important; + position: absolute; + border-radius: 8px; + padding: 16px 12px; + box-sizing: border-box; + /* border: 3px solid white; */ + flex: 1; + background: #ffffff; + border: 1px solid rgba(255, 255, 255, 0.04); + box-shadow: 0 4px 6px -2px rgba(25, 15, 15, 0.05), 0 0 1px 0 rgba(0, 0, 0, 0.08); + + .img { + width: 48px; + height: 48px; + } + + .content { + width: 230px; + margin-left: 13px; + display: flex; + flex-direction: column; + justify-content: space-between; + align-items: flex-start; + + .title { + width: 100%; + display: flex; + flex: 1; + + span { + font-size: 16px; + color: rgba(0, 0, 0, 85%); + line-height: 22px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + text-align: left; + } + + .dangerLogo { + width: 28px; + height: 16px; + background: #ffeef1; + border-radius: 7px; + margin-left: 8px; + margin-top: 3px; + display: flex; + justify-content: center; + align-items: center; + img { + width: 8px; + height: 9px; + } + } + } + + .des { + font-size: 14px; + color: rgba(0, 0, 0, 45%); + line-height: 20px; + white-space: nowrap; + width: 100%; + overflow: hidden; + text-overflow: ellipsis; + text-align: left; + } + } + `, + ), + nodeSelected: css` + box-shadow: 0 0 0 3px #1677ff, 0 1px 4px 1px rgba(0, 0, 0, 8%) !important; + `, + nodeSubSelected: css` + box-shadow: 0 0 0 1px #1677ff, 0 1px 4px 1px rgba(0, 0, 0, 8%) !important; + `, + nodeDanger: css` + box-shadow: 0 0 0 3px #f7636e, 0 1px 4px 1px rgba(0, 0, 0, 8%); + `, + nodeSubDanger: css` + box-shadow: 0 0 0 1px #f7636e, 0 1px 4px 1px rgba(0, 0, 0, 8%) !important; + `, + nodeWarning: css` + box-shadow: 0 0 0 3px #ef9d3b, 0 1px 4px 1px rgba(0, 0, 0, 8%) !important; + `, + nodeSubWarning: css` + box-shadow: 0 0 0 1px #ef9d3b, 0 1px 4px 1px rgba(0, 0, 0, 8%) !important; + `, + nodeDefault: css``, +})); diff --git a/src/BloodGroupNode/constants.ts b/src/LineageGroupNode/constants.ts similarity index 100% rename from src/BloodGroupNode/constants.ts rename to src/LineageGroupNode/constants.ts diff --git a/src/LineageGroupNode/demos/index.tsx b/src/LineageGroupNode/demos/index.tsx new file mode 100644 index 0000000..b985472 --- /dev/null +++ b/src/LineageGroupNode/demos/index.tsx @@ -0,0 +1,210 @@ +import { FlowViewEdge, FlowViewNode, NodeSelect } from '@/index'; +import { ProFlow } from '@ant-design/pro-flow'; +import { createStyles } from 'antd-style'; +import { useState } from 'react'; + +const useStyles = createStyles(({ css }) => ({ + container: css` + width: 100%; + height: 600px; + .ant-progress-text { + text-align: center !important; + } + `, +})); + +const nodes: FlowViewNode[] = [ + { + id: 'd1', + label: 'group1', + type: 'lineageGroup', + data: [ + { + id: 'a5', + data: { + title: 'XXX数据源', + describe: 'cksadjfnf', + logo: 'https://mdn.alipayobjects.com/huamei_ntgeqc/afts/img/A*jWDsQ5GTmHMAAAAAAAAAAAAADvuvAQ/original', + }, + }, + { + id: 'a6', + data: { + title: 'XXX_API', + logo: 'https://mdn.alipayobjects.com/huamei_ntgeqc/afts/img/A*kgyiRKi04eUAAAAAAAAAAAAADvuvAQ/original', + describe: 'XXX_XXX_XXX_API', + }, + }, + { + id: 'a7', + data: { + title: 'XXXX产品', + logo: 'https://mdn.alipayobjects.com/huamei_ntgeqc/afts/img/A*ezaYT4wYRBwAAAAAAAAAAAAADvuvAQ/original', + describe: '2031030213014', + }, + }, + { + id: 'a8', + data: { + title: 'XXX数据源', + describe: 'cksadjfnf', + logo: 'https://mdn.alipayobjects.com/huamei_ntgeqc/afts/img/A*jWDsQ5GTmHMAAAAAAAAAAAAADvuvAQ/original', + }, + }, + { + id: 'a9', + data: { + title: 'XXX_API', + logo: 'https://mdn.alipayobjects.com/huamei_ntgeqc/afts/img/A*kgyiRKi04eUAAAAAAAAAAAAADvuvAQ/original', + describe: 'XXX_XXX_XXX_API', + }, + }, + { + id: 'a10', + data: { + title: 'XXXX产品', + logo: 'https://mdn.alipayobjects.com/huamei_ntgeqc/afts/img/A*ezaYT4wYRBwAAAAAAAAAAAAADvuvAQ/original', + describe: '2031030213014', + }, + }, + { + id: 'a11', + data: { + title: 'XXXX产品', + logo: 'https://mdn.alipayobjects.com/huamei_ntgeqc/afts/img/A*ezaYT4wYRBwAAAAAAAAAAAAADvuvAQ/original', + describe: '2031030213014', + }, + }, + ], + }, + { + id: 'd2', + label: 'group2', + type: 'lineageGroup', + data: [ + { + id: 'a5', + data: { + title: 'XXX数据源', + describe: 'cksadjfnf', + logo: 'https://mdn.alipayobjects.com/huamei_ntgeqc/afts/img/A*jWDsQ5GTmHMAAAAAAAAAAAAADvuvAQ/original', + }, + }, + { + id: 'a6', + data: { + title: 'XXX_API', + logo: 'https://mdn.alipayobjects.com/huamei_ntgeqc/afts/img/A*kgyiRKi04eUAAAAAAAAAAAAADvuvAQ/original', + describe: 'XXX_XXX_XXX_API', + }, + }, + { + id: 'a7', + data: { + title: 'XXXX产品', + logo: 'https://mdn.alipayobjects.com/huamei_ntgeqc/afts/img/A*ezaYT4wYRBwAAAAAAAAAAAAADvuvAQ/original', + describe: '2031030213014', + }, + }, + { + id: 'a8', + data: { + title: 'XXX数据源', + describe: 'cksadjfnf', + logo: 'https://mdn.alipayobjects.com/huamei_ntgeqc/afts/img/A*jWDsQ5GTmHMAAAAAAAAAAAAADvuvAQ/original', + }, + }, + { + id: 'a9', + data: { + title: 'XXX_API', + logo: 'https://mdn.alipayobjects.com/huamei_ntgeqc/afts/img/A*kgyiRKi04eUAAAAAAAAAAAAADvuvAQ/original', + describe: 'XXX_XXX_XXX_API', + }, + }, + { + id: 'a10', + data: { + title: 'XXXX产品', + logo: 'https://mdn.alipayobjects.com/huamei_ntgeqc/afts/img/A*ezaYT4wYRBwAAAAAAAAAAAAADvuvAQ/original', + describe: '2031030213014', + }, + }, + { + id: 'a11', + data: { + title: 'XXXX产品', + logo: 'https://mdn.alipayobjects.com/huamei_ntgeqc/afts/img/A*ezaYT4wYRBwAAAAAAAAAAAAADvuvAQ/original', + describe: '2031030213014', + }, + }, + ], + }, +]; + +const edges = [ + { + id: 'b12', + source: 'd1', + target: 'd2', + }, +]; + +const ProFlowDemo = () => { + const [_nodes, setNodes] = useState(nodes); + const [_edges, setEdges] = useState(edges); + const { styles } = useStyles(); + + const handleHighlight = (node: FlowViewNode) => { + setNodes( + _nodes.map((_node) => { + if (_node.id === node.id) { + _node.select = NodeSelect.SELECT; + } + return _node; + }), + ); + setEdges( + _edges.map((edge) => { + if (edge.source === node.id || edge.target === node.id) { + edge.select = NodeSelect.SUB_SELECT; + } + return { + ...edge, + }; + }), + ); + }; + + const handleUnHighlight = () => { + setNodes( + _nodes.map((_node) => { + _node.select = NodeSelect.DEFAULT; + return _node; + }), + ); + setEdges( + _edges.map((edge) => { + edge.select = NodeSelect.DEFAULT; + return edge; + }), + ); + }; + + return ( +
+ handleHighlight(node)} + onPaneClick={handleUnHighlight} + nodes={_nodes} + edges={_edges} + > +
+ ); +}; + +const FlowDemo = () => { + return ; +}; + +export default FlowDemo; diff --git a/src/LineageGroupNode/index.md b/src/LineageGroupNode/index.md new file mode 100644 index 0000000..6f33a65 --- /dev/null +++ b/src/LineageGroupNode/index.md @@ -0,0 +1,25 @@ +--- +group: 节点 +title: LineageGroupNode 血缘组节点 +description: 无法单独使用。在FlowView中将节点类型设置为'lineage'后使用。 +--- + +## Default + + + +## API + +| 属性名 | 类型 | 描述 | 默认值 | 必选 | +| ------ | ----------------- | -------- | ------ | ---- | +| id | `string` | 节点标题 | - | - | +| data | `LineageNodeData` | 节点描述 | - | - | + +### LineageNodeData + +| 属性名 | 类型 | 描述 | 默认值 | 必选 | +| --------- | --------------------------------------------------- | ------------------------------------------------------------------- | ------ | ---- | +| title | `string` | 节点标题 | - | - | +| describe | `string` | 节点描述 | - | - | +| logo | `string` | 节点 logo 的 url | - | - | +| titleSlot | `{type: 'left' \| 'right', value: React.ReactNode}` | 标题右侧的插槽,left 跟随在 title 文字结尾处,right 在 title 行尾部 | - | - | diff --git a/src/BloodGroupNode/index.tsx b/src/LineageGroupNode/index.tsx similarity index 68% rename from src/BloodGroupNode/index.tsx rename to src/LineageGroupNode/index.tsx index 0216b89..c8ad1da 100644 --- a/src/BloodGroupNode/index.tsx +++ b/src/LineageGroupNode/index.tsx @@ -1,22 +1,22 @@ -import { ArtboardTitle, getClsFromSelectType } from '@/BloodNode'; -import { NodeMapItem, NodeSelect } from '@/ProFlow/constants'; -import { ProFlowNode, ProFlowNodeData } from '@/constants'; +import { NodeMapItem, NodeSelect } from '@/FlowView/constants'; +import { ArtboardTitle } from '@/LineageNode'; +import { LineageGroupNodeData, LineageNodeData } from '@/constants'; +import { getClsFromSelectType } from '@/utils'; import { cx } from 'antd-style'; import React from 'react'; import { useStyles } from './styles'; -export interface BloodNodeGroupProps { +export interface LineageNodeGroupProps { id?: string; zoom?: number; label?: string; select?: NodeSelect; - data: ProFlowNode[]; - group: boolean; + data: LineageGroupNodeData[]; } -const convertMappingNode = (nodeList: ProFlowNode[]): NodeMapItem[] => { - return nodeList.map((node) => { - return node; +const convertMappingNode = (nodeList: LineageGroupNodeData[]): NodeMapItem[] => { + return nodeList.map((_node) => { + return { ..._node, type: 'default', flowNodeType: 'lineage' }; }); }; @@ -35,8 +35,7 @@ const GroupItem = (node: NodeMapItem) => { ); }; -const BloodNodeGroup: React.FC = ({ - group, +const LineageNodeGroup: React.FC = ({ data, select = NodeSelect.SELECT, zoom = 1, @@ -44,15 +43,11 @@ const BloodNodeGroup: React.FC = ({ }) => { const { styles } = useStyles(); - if (!group) { - return null; - } - - if ((data as ProFlowNode[]).length < 7) { + if ((data as LineageGroupNodeData[]).length < 7) { return
数组长度必须大于等于7!
; } - const nodeList = convertMappingNode(data as ProFlowNode[]); + const nodeList = convertMappingNode(data as LineageGroupNodeData[]); return ( <> @@ -63,7 +58,7 @@ const BloodNodeGroup: React.FC = ({ )}
{nodeList!.map((_node, index) => { - const data = _node.data as ProFlowNodeData; + const data = _node.data as LineageNodeData; _node.position = { x: 0, y: 100 * index, @@ -86,4 +81,4 @@ const BloodNodeGroup: React.FC = ({ ); }; -export default BloodNodeGroup; +export default LineageNodeGroup; diff --git a/src/BloodGroupNode/styles.ts b/src/LineageGroupNode/styles.ts similarity index 100% rename from src/BloodGroupNode/styles.ts rename to src/LineageGroupNode/styles.ts diff --git a/src/BloodNode/demos/DataViewList.tsx b/src/LineageNode/demos/DataViewList.tsx similarity index 100% rename from src/BloodNode/demos/DataViewList.tsx rename to src/LineageNode/demos/DataViewList.tsx diff --git a/src/LineageNode/demos/index.tsx b/src/LineageNode/demos/index.tsx new file mode 100644 index 0000000..ee81fa3 --- /dev/null +++ b/src/LineageNode/demos/index.tsx @@ -0,0 +1,168 @@ +import { FlowViewEdge, FlowViewNode, NodeSelect } from '@/index'; +import { ProFlow } from '@ant-design/pro-flow'; +import { Progress } from 'antd'; +import { createStyles } from 'antd-style'; +import React, { useState } from 'react'; +import styled from 'styled-components'; + +const useStyles = createStyles(({ css }) => ({ + container: css` + width: 100%; + height: 600px; + .ant-progress-text { + text-align: center !important; + } + `, +})); + +const ApiScore: React.FC<{ score: number }> = ({ score }) => { + return ( + 60 ? '#30a46c' : '#e5484d'} + format={() => `${score}`} + size={[28, 6]} + /> + ); +}; + +const DangerLogo = styled.div` + width: 28px; + height: 16px; + background: #ffeef1; + border-radius: 7px; + + margin-top: 3px; + display: flex; + justify-content: center; + align-items: center; + img { + width: 8px; + height: 9px; + } +`; + +const nodes: FlowViewNode[] = [ + { + id: 'b1', + label: 'titleSlot-left', + data: { + title: 'XXX_API1', + logo: 'https://mdn.alipayobjects.com/huamei_ntgeqc/afts/img/A*kgyiRKi04eUAAAAAAAAAAAAADvuvAQ/original', + describe: 'XXX_XXX_XXX_API', + titleSlot: { + type: 'left', + value: ( + + + + ), + }, + }, + }, + { + id: 'b2', + label: 'titleSlot-right', + data: { + title: 'XXX_APIddddddddddddddddddddddddddddddddddddddddddddddddddd_b2', + logo: 'https://mdn.alipayobjects.com/huamei_ntgeqc/afts/img/A*kgyiRKi04eUAAAAAAAAAAAAADvuvAQ/original', + describe: 'XXX_XXX_XXX_API', + titleSlot: { + type: 'right', + value: , + }, + }, + }, + { + id: 'b3', + label: 'default', + data: { + title: 'XXX_API_b3', + logo: 'https://mdn.alipayobjects.com/huamei_ntgeqc/afts/img/A*kgyiRKi04eUAAAAAAAAAAAAADvuvAQ/original', + describe: 'XXX_XXX_XXX_API', + }, + }, +]; + +const edges = [ + { + id: 'b12', + source: 'b1', + target: 'b2', + }, + { + id: 'b23', + source: 'b2', + target: 'b3', + }, +]; + +const ProFlowDemo = () => { + const [_nodes, setNodes] = useState(nodes); + const [_edges, setEdges] = useState(edges); + const { styles } = useStyles(); + + const handleHighlight = (node: FlowViewNode) => { + setNodes( + _nodes.map((_node) => { + if (_node.id === node.id) { + _node.select = NodeSelect.SELECT; + } + return _node; + }), + ); + setEdges( + _edges.map((edge) => { + if (edge.source === node.id || edge.target === node.id) { + edge.select = NodeSelect.SUB_SELECT; + } + return { + ...edge, + }; + }), + ); + }; + + const handleUnHighlight = () => { + setNodes( + _nodes.map((_node) => { + _node.select = NodeSelect.DEFAULT; + return _node; + }), + ); + setEdges( + _edges.map((edge) => { + edge.select = NodeSelect.DEFAULT; + return edge; + }), + ); + }; + + return ( +
+ handleHighlight(node)} + onPaneClick={handleUnHighlight} + nodes={_nodes} + edges={_edges} + > +
+ ); +}; + +const FlowDemo = () => { + return ; +}; + +export default FlowDemo; diff --git a/src/LineageNode/index.md b/src/LineageNode/index.md new file mode 100644 index 0000000..1e93446 --- /dev/null +++ b/src/LineageNode/index.md @@ -0,0 +1,18 @@ +--- +group: 节点 +title: LineageNode 血缘节点 +description: 无法单独使用。在FlowView中将节点类型设置为'lineage'后使用。 +--- + +## Default + + + +## API + +| 属性名 | 类型 | 描述 | 默认值 | 必选 | +| --------- | --------------------------------------------------- | ------------------------------------------------------------------- | ------ | ---- | +| title | `string` | 节点标题 | - | - | +| describe | `string` | 节点描述 | - | - | +| logo | `string` | 节点 logo 的 url | - | - | +| titleSlot | `{type: 'left' \| 'right', value: React.ReactNode}` | 标题右侧的插槽,left 跟随在 title 文字结尾处,right 在 title 行尾部 | - | - | diff --git a/src/BloodNode/index.tsx b/src/LineageNode/index.tsx similarity index 78% rename from src/BloodNode/index.tsx rename to src/LineageNode/index.tsx index 00d750d..e072091 100644 --- a/src/BloodNode/index.tsx +++ b/src/LineageNode/index.tsx @@ -1,12 +1,5 @@ -import { - NODE_DANGER, - NODE_SELECT, - NODE_SUB_DANGER, - NODE_SUB_SELECT, - NODE_SUB_WARNING, - NODE_WARNING, - NodeSelect, -} from '@/ProFlow/constants'; +import { NodeSelect } from '@/FlowView/constants'; +import { getClsFromSelectType } from '@/utils'; import React from 'react'; import styled from 'styled-components'; import { useStyles } from './styles'; @@ -59,25 +52,6 @@ const TitleSlotRight = styled.div` top: 9px; `; -export function getClsFromSelectType(select: NodeSelect) { - switch (select) { - case NodeSelect.SELECT: - return NODE_SELECT; - case NodeSelect.SUB_SELECT: - return NODE_SUB_SELECT; - case NodeSelect.DANGER: - return NODE_DANGER; - case NodeSelect.SUB_DANGER: - return NODE_SUB_DANGER; - case NodeSelect.WARNING: - return NODE_WARNING; - case NodeSelect.SUB_WARNING: - return NODE_SUB_WARNING; - default: - return 'nodeDefault'; - } -} - const BloodNode: React.FC> = ({ title, logo, diff --git a/src/BloodNode/styles.ts b/src/LineageNode/styles.ts similarity index 100% rename from src/BloodNode/styles.ts rename to src/LineageNode/styles.ts diff --git a/src/ProFlow/index.md b/src/ProFlow/index.md deleted file mode 100644 index 6e538e2..0000000 --- a/src/ProFlow/index.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -group: 容器组件 -title: ProFlow 基础画布容器 -description: ---- - -我们在 ProFlow 中提供了 BloodMap 的基础节点,方便定制样式。 - -## 预览模式 - - diff --git a/src/ProFlow/styles.tsx b/src/ProFlow/styles.tsx deleted file mode 100644 index 3e364d2..0000000 --- a/src/ProFlow/styles.tsx +++ /dev/null @@ -1,76 +0,0 @@ -import { createStyles } from 'antd-style'; -import { - EDGE_DANGER, - EDGE_SELECT, - EDGE_SUB_DANGER, - EDGE_SUB_SELECT, - EDGE_SUB_WARNING, - EDGE_WARNING, - INIT_NODE, -} from './constants'; - -export const useStyles = createStyles(({ css }) => ({ - container: css` - width: 100%; - height: 100%; - - .react-flow__attribution { - display: none; - } - - .${INIT_NODE} { - padding: 0; - box-sizing: border-box; - width: 320px; - height: 83px; - border: none; - border-radius: 8px; - cursor: pointer; - z-index: 1; - } - - .${EDGE_SELECT} path { - stroke: #1677ff; - stroke-width: 2; - z-index: 100; - } - - .${EDGE_SUB_SELECT} path { - stroke: #1677ff; - stroke-width: 1; - z-index: 100; - } - - .${EDGE_DANGER} path { - stroke: #f7636e; - stroke-width: 2; - z-index: 100; - } - - .${EDGE_SUB_DANGER} path { - stroke: #f7636e; - stroke-width: 1; - z-index: 100; - } - - .${EDGE_WARNING} path { - stroke: #ef9d3b; - stroke-width: 2; - z-index: 100; - } - - .${EDGE_SUB_WARNING} path { - stroke: #ef9d3b; - stroke-width: 1; - z-index: 100; - } - - .selectable:focus { - box-shadow: none !important; - } - - .selected { - box-shadow: none !important; - } - `, -})); diff --git a/src/ProFlowController/demos/FlowControllerDemo.tsx b/src/ProFlowController/demos/FlowControllerDemo.tsx index 29fc2c7..2d4b609 100644 --- a/src/ProFlowController/demos/FlowControllerDemo.tsx +++ b/src/ProFlowController/demos/FlowControllerDemo.tsx @@ -1,8 +1,6 @@ +import { ProFlow, ProFlowController } from '@/index'; import { createStyles } from 'antd-style'; import { memo } from 'react'; -// import { Background, BackgroundVariant } from 'reactflow'; -// import ProFlowController from '..'; -// import ProFlow from '../../ProFlow'; const useStyles = createStyles(({ css }) => ({ container: css` @@ -15,10 +13,9 @@ const FlowControllerDemo = memo(() => { const { styles } = useStyles(); return (
- {/* - - - */} + + +
); }); diff --git a/src/ProFlowController/index.md b/src/ProFlowController/index.md new file mode 100644 index 0000000..9076979 --- /dev/null +++ b/src/ProFlowController/index.md @@ -0,0 +1,24 @@ +--- +group: 辅助 +title: ProFlowController 画布控制器 +description: 配合ProFLow组件使用,提供可控制画布的小地图 +--- + +## Default + + + +## APIs + +| 属性名 | 类型 | 描述 | 默认值 | 必选 | +| --------- | ----------------- | -------------------- | ------ | ---- | +| className | `string` | 自定义类名 | - | - | +| visible | `boolean` | 是否展示 | - | - | +| position | `MiniMapPosition` | 控制器在画布中的坐标 | - | - | + +### MiniMapPosition + +| 属性名 | 类型 | 描述 | 默认值 | 必选 | +| ------ | -------- | ------------------------------ | ------ | ---- | +| x | `number` | x 坐标,正数向左偏移,负数反之 | 0 | - | +| y | `number` | y 坐标,正数向上偏移,负数反之 | 0 | - | diff --git a/src/ProFlowController/index.tsx b/src/ProFlowController/index.tsx index 896f954..ff2f37f 100644 --- a/src/ProFlowController/index.tsx +++ b/src/ProFlowController/index.tsx @@ -3,6 +3,7 @@ import { Button, Space, Tooltip } from 'antd'; import { createStyles } from 'antd-style'; import React from 'react'; import { MiniMap, useReactFlow, useViewport } from 'reactflow'; +import { MiniMapPosition } from '..'; const useStyles = createStyles(({ css }, props: { x: number; y: number }) => { const { x, y } = props; @@ -45,14 +46,14 @@ const useStyles = createStyles(({ css }, props: { x: number; y: number }) => { interface ProFlowControllerProps { visible?: boolean; className?: string; - position?: [number, number]; + position?: MiniMapPosition; } const ProFlowController: React.FC> = (props) => { - const { visible = false, className = '', position = [0, 0] } = props; + const { visible = false, className = '', position = { x: 0, y: 0 } } = props; const reactFlow = useReactFlow(); const { zoom } = useViewport(); - const { styles, cx } = useStyles({ x: position[0], y: position[1] }); + const { styles, cx } = useStyles(position); const handleZoomIn = () => { reactFlow.zoomIn(); diff --git a/src/RadiusEdge/demos/index.tsx b/src/RadiusEdge/demos/index.tsx index 6422847..427088e 100644 --- a/src/RadiusEdge/demos/index.tsx +++ b/src/RadiusEdge/demos/index.tsx @@ -1,4 +1,4 @@ -import { EdgeType, ProFlow, ProFlowNode } from '@ant-design/pro-flow'; +import { EdgeType, FlowViewNode, ProFlow } from '@ant-design/pro-flow'; import { createStyles } from 'antd-style'; import { memo } from 'react'; @@ -9,7 +9,7 @@ const useStyles = createStyles(({ css }) => ({ `, })); -const nodes: ProFlowNode[] = [ +const nodes: FlowViewNode[] = [ { id: 'a1', data: { diff --git a/src/RadiusEdge/index.md b/src/RadiusEdge/index.md deleted file mode 100644 index e33c88e..0000000 --- a/src/RadiusEdge/index.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -group: 线段组件 -title: RadiusEdge 圆角线段 -description: ---- - -## 圆角线段 - - diff --git a/src/constants.tsx b/src/constants.tsx index 0284f50..b0f1d4a 100644 --- a/src/constants.tsx +++ b/src/constants.tsx @@ -1,5 +1,6 @@ -import { type CSSProperties, type MouseEvent as ReactMouseEvent } from 'react'; +import React, { type CSSProperties, type MouseEvent as ReactMouseEvent } from 'react'; import { Node } from 'reactflow'; +import { NodeMapItem } from './FlowView/constants'; export enum NodeSelect { SELECT = 'SELECT', @@ -16,15 +17,50 @@ export enum EdgeType { radius = 'radius', } -export interface ProFlowNode { +export interface DefaultNodeData { + className?: string; + children?: React.ReactNode; +} + +export interface LineageNodeData { + title: string; + describe: string; + logo: string; + titleSlot?: { + type: 'left' | 'right'; + value: React.ReactNode; + }; +} + +export interface LineageGroupNodeData { + id: string; + data: LineageNodeData; +} + +export interface NodeTypeDataMap { + default: DefaultNodeData; + lineage: LineageNodeData; + lineageGroup: LineageGroupNodeData[]; +} + +export type FlowNodeType = keyof NodeTypeDataMap; + +export type NodeHandler = { + [T in FlowNodeType]: (node: NodeMapItem) => React.ReactNode; +}; + +export type DefaultNodeType = T extends FlowNodeType ? T : 'lineage'; +export interface FlowViewNode> { id: string; - group?: boolean; select?: NodeSelect; - data: ProFlowNodeData | ProFlowNode[]; + data: NodeTypeDataMap[T]; + type?: T; label?: string; + width?: number; + height?: number; } -export interface ProFlowEdge { +export interface FlowViewEdge { id: string; source: string; target: string; @@ -32,35 +68,20 @@ export interface ProFlowEdge { type?: EdgeType; } -export interface ProFlowNodeData { - title: string; - describe: string; - logo: string; - titleSlot?: { - type: 'left' | 'right'; - value: React.ReactNode; - }; -} - -export interface ProFlowProps { +export interface FlowViewProps { onNodeDragStart?: (event: ReactMouseEvent, node: Node, nodes: Node[]) => void; onPaneClick?: (event: ReactMouseEvent) => void; onNodeClick?: (event: ReactMouseEvent, node: Node) => void; - nodes: ProFlowNode[]; - edges: ProFlowEdge[]; + nodes: FlowViewNode[]; + edges: FlowViewEdge[]; className?: string; style?: CSSProperties; miniMap?: boolean; + background?: boolean; children?: React.ReactNode; } -export interface ProFlowProps { - onNodeDragStart?: (event: ReactMouseEvent, node: Node, nodes: Node[]) => void; - onPaneClick?: (event: ReactMouseEvent) => void; - onNodeClick?: (event: ReactMouseEvent, node: Node) => void; - nodes: ProFlowNode[]; - edges: ProFlowEdge[]; - className?: string; - style?: CSSProperties; - miniMap?: boolean; +export interface MiniMapPosition { + x: number; + y: number; } diff --git a/src/index.ts b/src/index.ts index 7ceb1a0..e7177ee 100644 --- a/src/index.ts +++ b/src/index.ts @@ -4,10 +4,10 @@ export * from './ControlInput'; export { default as EditableText } from './EditableText'; export * from './FlowEditor'; export { FlowStoreProvider, type FlowEditorStoreProviderProps } from './FlowStoreProvider'; +export { default as ProFlow } from './FlowView/FlowView'; export * from './Input'; export { NodeField, useNodeFieldStyles } from './NodeField'; export type { ExtraAction } from './NodeField'; -export { default as ProFlow } from './ProFlow/FlowView'; export { default as ProFlowController } from './ProFlowController'; export { default as RadiusEdge } from './RadiusEdge'; export * from './constants'; diff --git a/src/utils/index.ts b/src/utils/index.ts new file mode 100644 index 0000000..3d333ee --- /dev/null +++ b/src/utils/index.ts @@ -0,0 +1,28 @@ +import { + NODE_DANGER, + NODE_SELECT, + NODE_SUB_DANGER, + NODE_SUB_SELECT, + NODE_SUB_WARNING, + NODE_WARNING, +} from '@/FlowView/constants'; +import { NodeSelect } from '..'; + +export function getClsFromSelectType(select: NodeSelect) { + switch (select) { + case NodeSelect.SELECT: + return NODE_SELECT; + case NodeSelect.SUB_SELECT: + return NODE_SUB_SELECT; + case NodeSelect.DANGER: + return NODE_DANGER; + case NodeSelect.SUB_DANGER: + return NODE_SUB_DANGER; + case NodeSelect.WARNING: + return NODE_WARNING; + case NodeSelect.SUB_WARNING: + return NODE_SUB_WARNING; + default: + return 'nodeDefault'; + } +}