├── .changelogrc.js
├── .commitlintrc.js
├── .dumirc.ts
├── .editorconfig
├── .eslintignore
├── .eslintrc.js
├── .fatherrc.ts
├── .github
├── ISSUE_TEMPLATE
│ ├── bug_report.md
│ ├── feature_request.md
│ └── question.md
└── workflows
│ ├── deploy.yml
│ ├── preview.yml
│ ├── release.yml
│ └── test.yml
├── .gitignore
├── .gitpod.yml
├── .husky
├── commit-msg
└── pre-commit
├── .npmrc
├── .prettierignore
├── .prettierrc.js
├── .releaserc.js
├── .stylelintrc.js
├── CHANGELOG.md
├── LICENSE
├── README.md
├── README.zh-CN.md
├── docs
├── apiDocs
│ ├── demos
│ │ ├── css
│ │ │ └── viewer.style.ts
│ │ ├── data
│ │ │ └── viewer.ts
│ │ └── useFlowView.tsx
│ ├── useFlowEditor.zh-CN.md
│ └── useFlowViewer.zh-CN.md
├── caseShow
│ ├── cicdPipeLine.md
│ ├── dataFlow.md
│ ├── demos
│ │ ├── dataflow
│ │ │ ├── data.tsx
│ │ │ ├── helper.ts
│ │ │ ├── index.tsx
│ │ │ └── styled.ts
│ │ ├── index.style.ts
│ │ ├── pipeline
│ │ │ ├── multiPipe
│ │ │ │ ├── data.tsx
│ │ │ │ ├── pipeNode.tsx
│ │ │ │ └── pipelineDemo.tsx
│ │ │ ├── taskPipeline
│ │ │ │ ├── data.tsx
│ │ │ │ ├── index.tsx
│ │ │ │ ├── nodes
│ │ │ │ │ ├── stageNode.tsx
│ │ │ │ │ ├── styled.ts
│ │ │ │ │ ├── taskContent.tsx
│ │ │ │ │ └── taskNode.tsx
│ │ │ │ └── styled.ts
│ │ │ └── techPipe
│ │ │ │ ├── data.ts
│ │ │ │ ├── pipeNode.tsx
│ │ │ │ ├── techPipeLine.style.ts
│ │ │ │ └── techPipeline.tsx
│ │ ├── pressureTest.tsx
│ │ ├── tinaFlow
│ │ │ ├── comp
│ │ │ │ ├── box
│ │ │ │ │ ├── index.less
│ │ │ │ │ └── index.tsx
│ │ │ │ ├── comp
│ │ │ │ │ └── index.tsx
│ │ │ │ ├── ctx.tsx
│ │ │ │ ├── group
│ │ │ │ │ ├── index.less
│ │ │ │ │ └── index.tsx
│ │ │ │ └── icon
│ │ │ │ │ └── index.tsx
│ │ │ └── index.tsx
│ │ └── workflow
│ │ │ ├── index.tsx
│ │ │ ├── nodes
│ │ │ ├── network.tsx
│ │ │ └── textNode.tsx
│ │ │ ├── sidebar.tsx
│ │ │ └── styled.ts
│ ├── pipeline.md
│ ├── presureTest.md
│ ├── techPileline.md
│ └── workFlow.md
├── changelog.md
├── guide
│ ├── autoLayout.zh-CN.md
│ ├── baseIntro.zh-CN.md
│ ├── briefIntro.zh-CN.md
│ ├── customEdge.zh-CN.md
│ ├── customNode.zh-CN.md
│ ├── demos
│ │ ├── autoLayout
│ │ │ ├── data
│ │ │ │ ├── demo1data.ts
│ │ │ │ └── demo2data.ts
│ │ │ ├── demo1.tsx
│ │ │ ├── demo2.tsx
│ │ │ └── index.style.ts
│ │ ├── baseIntro
│ │ │ ├── coreEdge.tsx
│ │ │ ├── coreHandle.tsx
│ │ │ ├── coreNode.tsx
│ │ │ ├── css
│ │ │ │ ├── customerNode.style.ts
│ │ │ │ └── index.style.ts
│ │ │ ├── customerNode.tsx
│ │ │ └── data
│ │ │ │ └── coreEdgeData.ts
│ │ ├── customEdge
│ │ │ ├── ButtonEdge.tsx
│ │ │ ├── btn.style.ts
│ │ │ ├── data.ts
│ │ │ └── index.tsx
│ │ ├── customNode
│ │ │ ├── components
│ │ │ │ ├── CustomNode.tsx
│ │ │ │ └── custom.style.ts
│ │ │ ├── css
│ │ │ │ ├── index.style.ts
│ │ │ │ └── multi.style.ts
│ │ │ ├── data
│ │ │ │ ├── index.ts
│ │ │ │ └── multi.ts
│ │ │ ├── index.tsx
│ │ │ └── multiHandle.tsx
│ │ ├── flowEditor
│ │ │ ├── EditorNode.tsx
│ │ │ ├── StringNode.tsx
│ │ │ ├── base.tsx
│ │ │ ├── btnGroup.tsx
│ │ │ ├── css
│ │ │ │ ├── dragAddNode.style.ts
│ │ │ │ ├── index.style.ts
│ │ │ │ ├── probase.style.ts
│ │ │ │ └── sidebar.style.ts
│ │ │ ├── dragAddNode.tsx
│ │ │ ├── editFlowDemo.tsx
│ │ │ ├── proBase.tsx
│ │ │ └── sidebar.tsx
│ │ ├── flowViewIntro
│ │ │ ├── autoFlow.tsx
│ │ │ ├── baseMiniMap.tsx
│ │ │ ├── css
│ │ │ │ └── index.style.ts
│ │ │ ├── data
│ │ │ │ ├── data.ts
│ │ │ │ ├── data2.tsx
│ │ │ │ └── data3.ts
│ │ │ ├── dragableNode.tsx
│ │ │ ├── groupNode.tsx
│ │ │ ├── noAutoFlow.tsx
│ │ │ └── noMiniMap.tsx
│ │ └── quickUse
│ │ │ ├── baseFlow.tsx
│ │ │ ├── css
│ │ │ └── index.style.ts
│ │ │ ├── data.ts
│ │ │ ├── emptyFLow.tsx
│ │ │ └── selectFlow.tsx
│ ├── flowEditor.zh-CN.md
│ ├── flowViewIntro.zh-CN.md
│ ├── installUse.zh-CN.md
│ └── quickUse.zh-CN.md
└── index.md
├── package.json
├── src
├── Background
│ ├── components
│ │ └── SwimBg.tsx
│ ├── demos
│ │ ├── double.tsx
│ │ ├── index.tsx
│ │ └── swim.tsx
│ ├── index.tsx
│ └── index.zh-CN.md
├── BasicGroupNode
│ ├── constants.ts
│ ├── demos
│ │ ├── data.ts
│ │ └── index.tsx
│ ├── index.tsx
│ ├── index.zh-CN.md
│ └── styles.ts
├── BasicNode
│ ├── demos
│ │ ├── DataViewList.tsx
│ │ └── default
│ │ │ ├── data.tsx
│ │ │ └── index.tsx
│ ├── index.tsx
│ ├── index.zh-CN.md
│ └── styles.ts
├── CanvasLoading
│ ├── demos
│ │ ├── _Content.tsx
│ │ └── index.tsx
│ ├── index.md
│ └── index.tsx
├── ControlInput
│ ├── demos
│ │ └── index.tsx
│ ├── index.tsx
│ └── index.txt
├── EditNode
│ ├── CollapseContext.tsx
│ ├── demos
│ │ ├── FieldCollapse.tsx
│ │ ├── Preview.tsx
│ │ └── PreviewField.tsx
│ ├── index.md
│ └── index.tsx
├── EditableText
│ ├── demos
│ │ └── index.tsx
│ ├── index.tsx
│ └── index.txt
├── FlowEditor
│ ├── container
│ │ ├── FlowEditor.tsx
│ │ ├── FlowEditorProvider.tsx
│ │ ├── StoreUpdater
│ │ │ ├── Common.tsx
│ │ │ └── index.tsx
│ │ └── index.tsx
│ ├── demos
│ │ ├── index.style.ts
│ │ └── index.tsx
│ ├── features
│ │ ├── ContextMenu
│ │ │ ├── index.tsx
│ │ │ └── useMenuAction.tsx
│ │ └── ControlAction
│ │ │ └── index.tsx
│ ├── hooks
│ │ ├── useFlowEditor.ts
│ │ └── useHotkeyManager.ts
│ ├── index.ts
│ ├── index.zh-CN.md
│ ├── store
│ │ ├── actions.ts
│ │ ├── index.ts
│ │ ├── initialState.ts
│ │ ├── reducers
│ │ │ ├── __snapshots__
│ │ │ │ └── edge.test.ts.snap
│ │ │ ├── edge.test.ts
│ │ │ ├── edge.ts
│ │ │ ├── node.test.ts
│ │ │ └── node.ts
│ │ ├── selectors.ts
│ │ └── slices
│ │ │ ├── edgesSlice.ts
│ │ │ ├── generalActionSlice.ts
│ │ │ ├── index.ts
│ │ │ └── nodesSlice.ts
│ ├── types
│ │ ├── index.ts
│ │ ├── meta.ts
│ │ └── node.ts
│ └── utils
│ │ ├── convertChange.ts
│ │ ├── edge.test.ts
│ │ ├── edge.ts
│ │ ├── memoEqual.ts
│ │ ├── merge.ts
│ │ ├── nodeTree.ts
│ │ ├── uuid.ts
│ │ └── yjs.ts
├── FlowPanel
│ ├── demos
│ │ └── index.tsx
│ ├── index.tsx
│ └── index.zh-CN.md
├── FlowStoreProvider
│ ├── demos
│ │ └── FlowStoreProvider.tsx
│ ├── index.tsx
│ └── index.txt
├── FlowView
│ ├── FlowView.tsx
│ ├── components
│ │ └── DefaultNode.tsx
│ ├── constants.tsx
│ ├── demos
│ │ ├── CustomerNode.tsx
│ │ ├── ProFlowDemo.tsx
│ │ ├── data.tsx
│ │ └── index.style.ts
│ ├── helper.tsx
│ ├── hooks
│ │ ├── useFlowState.ts
│ │ └── useFlowView.ts
│ ├── index.tsx
│ ├── index.zh-CN.md
│ ├── provider
│ │ ├── FlowViewProvider.tsx
│ │ └── provider.ts
│ └── styles.tsx
├── Input
│ ├── index.tsx
│ └── style.ts
├── Inspector
│ ├── demos
│ │ └── index.tsx
│ ├── index.style.ts
│ ├── index.tsx
│ └── index.zh-CN.md
├── MiniMap
│ ├── demos
│ │ └── FlowControllerDemo.tsx
│ ├── index.tsx
│ └── index.zh-CN.md
├── NodeField
│ ├── demos
│ │ ├── actions.tsx
│ │ └── index.tsx
│ ├── index.tsx
│ └── index.txt
├── RadiusEdge
│ ├── demos
│ │ └── index.tsx
│ └── index.tsx
├── constants.tsx
├── index.ts
└── utils
│ └── index.ts
├── testDagre.js
├── tests
└── test-setup.ts
├── tsconfig-check.json
├── tsconfig.json
└── vitest.config.ts
/.changelogrc.js:
--------------------------------------------------------------------------------
1 | // 详情配置查看 https://github.com/arvinxx/gitmoji-commit-workflow/tree/master/packages/changelog#readme
2 | module.exports = {
3 | displayTypes: ['feat', 'fix', 'styles', 'pref', 'build'],
4 | titleLanguage: 'zh-CN',
5 | showAuthor: true,
6 | showAuthorAvatar: true,
7 | showSummary: true,
8 | reduceHeadingLevel: true,
9 | newlineTimestamp: true,
10 | addBackToTop: true,
11 | };
12 |
--------------------------------------------------------------------------------
/.commitlintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: ['gitmoji'],
3 | rules: {
4 | 'footer-leading-blank': [1, 'always'],
5 | },
6 | };
7 |
--------------------------------------------------------------------------------
/.dumirc.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'dumi';
2 | import { homepage } from './package.json';
3 |
4 | const isProd = process.env.NODE_ENV === 'production';
5 |
6 | // 不是预览模式 同时是生产环境
7 | const isProdSite = process.env.PREVIEW !== '1' && isProd;
8 |
9 | export default defineConfig({
10 | outputPath: 'docs-dist',
11 | favicons: [
12 | 'https://mdn.alipayobjects.com/huamei_d2ejos/afts/img/A*j10nRoiMh0MAAAAAAAAAAAAADvl6AQ/original',
13 | ],
14 | themeConfig: {
15 | name: 'ProFlow',
16 | logo: 'https://mdn.alipayobjects.com/huamei_d2ejos/afts/img/A*j10nRoiMh0MAAAAAAAAAAAAADvl6AQ/original',
17 | socialLinks: {
18 | github: homepage,
19 | },
20 | footer: 'Made with ❤️ by 蚂蚁集团 - AFX & 数字科技',
21 | },
22 | });
23 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # http://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | indent_style = space
6 | indent_size = 2
7 | end_of_line = lf
8 | charset = utf-8
9 | trim_trailing_whitespace = true
10 | insert_final_newline = true
11 |
12 | [*.md]
13 | trim_trailing_whitespace = false
14 |
15 | [Makefile]
16 | indent_style = tab
17 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | /lambda/mock/**
2 | /scripts
3 | /config
4 | /example
5 | _test_
6 | __test__
7 |
8 | /node_modules
9 | jest*
10 | /es
11 | /lib
12 | /docs
13 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = require('@umijs/lint/dist/config/eslint');
2 |
--------------------------------------------------------------------------------
/.fatherrc.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'father';
2 |
3 | export default defineConfig({
4 | esm: { output: 'es' },
5 | });
6 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: '报告Bug 🐛'
3 | about: 报告 pro-flow-editor 的 bug
4 | title: '🐛[BUG]'
5 | labels: '🐛 BUG'
6 | assignees: ''
7 | ---
8 |
9 | ### 🐛 bug 描述
10 |
11 |
14 |
15 | ### 📷 复现步骤
16 |
17 |
20 |
21 | ### 🏞 期望结果
22 |
23 |
26 |
27 | ### 💻 复现代码
28 |
29 |
33 |
34 | [可复现 demo](https://codesandbox.io/s/html2ksetch-demo-m53be?file=/src/Demo.tsx)
35 |
36 | ### © 版本信息
37 |
38 | - pro-flow-editor 版本: [e.g. 1.0.0]
39 | - 浏览器环境
40 | - 开发环境 [e.g. mac OS]
41 |
42 | ### 🚑 其他信息
43 |
44 |
47 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: '功能需求 ✨'
3 | about: 对 pro-flow-editor 的需求或建议
4 | title: '👑 [需求]'
5 | labels: '👑 Feature'
6 | assignees: ''
7 | ---
8 |
9 | ### 🥰 需求描述
10 |
11 |
14 |
15 | ### 🧐 解决方案
16 |
17 |
20 |
21 | ### 🚑 其他信息
22 |
23 |
26 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/question.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: '疑问或需要帮助 ❓'
3 | about: 对 pro-flow-editor 使用的疑问或需要帮助
4 | title: '🧐[问题]'
5 | labels: '🧐 Question'
6 | assignees: ''
7 | ---
8 |
9 | ### 🧐 问题描述
10 |
11 |
14 |
15 | ### 💻 示例代码
16 |
17 |
20 |
21 | ### 🚑 其他信息
22 |
23 |
26 |
--------------------------------------------------------------------------------
/.github/workflows/deploy.yml:
--------------------------------------------------------------------------------
1 | name: Deploy CI
2 |
3 | on:
4 | push:
5 | branches:
6 | - master
7 |
8 | jobs:
9 | build-and-deploy:
10 | runs-on: ubuntu-latest
11 | steps:
12 | - name: Checkout 🛎️
13 | uses: actions/checkout@v3
14 |
15 | - name: Install pnpm
16 | uses: pnpm/action-setup@v2
17 | with:
18 | version: 8
19 |
20 | - name: Setup Node.js environment
21 | uses: actions/setup-node@v3
22 | with:
23 | node-version: '18'
24 |
25 | - name: Install and Build 🔧
26 | run: |
27 | pnpm i
28 | pnpm run lint
29 | pnpm run docs:build
30 |
31 | - name: Deploy 🚀
32 | uses: JamesIves/github-pages-deploy-action@v4
33 | with:
34 | token: ${{ secrets.GITHUB_TOKEN }}
35 | branch: gh-pages # The branch the action should deploy to.
36 | folder: docs-dist # The folder the action should deploy.
37 | clean: true # Automatically remove deleted files from the deploy branch
38 |
--------------------------------------------------------------------------------
/.github/workflows/preview.yml:
--------------------------------------------------------------------------------
1 | name: Surge PR Preview
2 |
3 | on:
4 | pull_request:
5 |
6 | workflow_dispatch:
7 |
8 | jobs:
9 | preview:
10 | runs-on: ubuntu-latest
11 | steps:
12 | - uses: actions/checkout@v3
13 |
14 | - name: Install pnpm
15 | uses: pnpm/action-setup@v2
16 | with:
17 | version: 8
18 |
19 | - uses: afc163/surge-preview@v1
20 | id: preview_step
21 | with:
22 | surge_token: ${{ secrets.SURGE_TOKEN }}
23 | github_token: ${{ secrets.GITHUB_TOKEN }}
24 | build: |
25 | pnpm i
26 | pnpm run docs:preview
27 | dist: docs-dist
28 |
29 | - name: Get the preview_url
30 | run: echo "url => ${{ steps.preview_step.outputs.preview_url }}"
31 |
32 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: Release CI
2 | on:
3 | push:
4 | branches:
5 | - master
6 | - alpha
7 | - beta
8 | - rc
9 |
10 | jobs:
11 | test:
12 | name: Test
13 | runs-on: ubuntu-latest
14 | steps:
15 | - uses: actions/checkout@v3
16 |
17 | - name: Install pnpm
18 | uses: pnpm/action-setup@v2
19 | with:
20 | version: 8
21 |
22 | - name: Setup Node.js environment
23 | uses: actions/setup-node@v3
24 | with:
25 | node-version: '18'
26 |
27 | - name: Install deps
28 | run: pnpm install
29 |
30 | - name: Test
31 | run: pnpm run test
32 |
33 | release:
34 | needs: test
35 | name: Release
36 | runs-on: ubuntu-latest
37 | steps:
38 | - uses: actions/checkout@v3
39 |
40 | - name: Install pnpm
41 | uses: pnpm/action-setup@v2
42 | with:
43 | version: 8
44 |
45 | - name: Setup Node.js environment
46 | uses: actions/setup-node@v3
47 | with:
48 | node-version: '18'
49 |
50 | - name: Install deps
51 | run: pnpm install
52 |
53 | - name: build
54 | run: pnpm run build
55 |
56 | - name: release
57 | run: pnpm run release
58 | env:
59 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
60 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
61 |
--------------------------------------------------------------------------------
/.github/workflows/test.yml:
--------------------------------------------------------------------------------
1 | name: Test CI
2 | on: [push, pull_request]
3 | jobs:
4 | test:
5 | runs-on: ubuntu-latest
6 |
7 | steps:
8 | - uses: actions/checkout@v3
9 |
10 | - name: Install pnpm
11 | uses: pnpm/action-setup@v2
12 | with:
13 | version: 8
14 |
15 | - name: Setup Node.js environment
16 | uses: actions/setup-node@v3
17 | with:
18 | node-version: '18'
19 |
20 | - name: Install deps
21 | run: pnpm install
22 |
23 | - name: lint
24 | run: pnpm run ci
25 |
26 | - name: Test and coverage
27 | run: pnpm run test:coverage
28 |
29 | # - name: Upload coverage to Codecov
30 | # uses: codecov/codecov-action@v3
31 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | **/node_modules
5 | # roadhog-api-doc ignore
6 | /src/utils/request-temp.js
7 | _roadhog-api-doc
8 |
9 | # production
10 | **/dist
11 | /.vscode
12 | /es
13 | /lib
14 |
15 | # misc
16 | .DS_Store
17 | storybook-static
18 | npm-debug.log*
19 | yarn-error.log
20 |
21 | /coverage
22 | .idea
23 | package-lock.json
24 | *bak
25 | .vscode
26 |
27 | # visual studio code
28 | .history
29 | *.log
30 | functions/*
31 | lambda/mock/index.js
32 | .temp/**
33 |
34 | # umi
35 | .dumi/tmp*
36 |
37 | # screenshot
38 | screenshot
39 | .firebase
40 | example/.temp/*
41 | .eslintcache
42 | techUI*
43 | docs-dist
44 |
--------------------------------------------------------------------------------
/.gitpod.yml:
--------------------------------------------------------------------------------
1 | tasks:
2 | - init: pnpm install
3 | command: pnpm run start
4 |
--------------------------------------------------------------------------------
/.husky/commit-msg:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 | . "$(dirname -- "$0")/_/husky.sh"
3 |
4 | npx --no -- commitlint --edit ${1}
5 |
--------------------------------------------------------------------------------
/.husky/pre-commit:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 | . "$(dirname -- "$0")/_/husky.sh"
3 |
4 | npx --no-install lint-staged
5 |
--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
1 | lockfile=false
2 | resolution-mode=highest
3 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | **/*.svg
2 | .umi
3 | .umi-production
4 | /dist
5 | .dockerignore
6 | .DS_Store
7 | .eslintignore
8 | *.png
9 | *.toml
10 | docker
11 | .editorconfig
12 | Dockerfile*
13 | .gitignore
14 | .prettierignore
15 | LICENSE
16 | .eslintcache
17 | *.lock
18 | yarn-error.log
19 |
--------------------------------------------------------------------------------
/.prettierrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | pluginSearchDirs: false,
3 | plugins: [
4 | require.resolve('prettier-plugin-organize-imports'),
5 | require.resolve('prettier-plugin-packagejson'),
6 | ],
7 | printWidth: 100,
8 | proseWrap: 'never',
9 | singleQuote: true,
10 | trailingComma: 'all',
11 | overrides: [
12 | {
13 | files: '*.md',
14 | options: {
15 | proseWrap: 'preserve',
16 | },
17 | },
18 | ],
19 | };
20 |
--------------------------------------------------------------------------------
/.releaserc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: ['semantic-release-config-gitmoji'],
3 | };
4 |
--------------------------------------------------------------------------------
/.stylelintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = require('@umijs/lint/dist/config/stylelint');
2 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Arvin Xu
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/docs/apiDocs/demos/css/viewer.style.ts:
--------------------------------------------------------------------------------
1 | import { createStyles } from 'antd-style';
2 |
3 | const useStyles = createStyles(() => {
4 | return {
5 | container: {
6 | width: '100%',
7 | height: '500px',
8 | },
9 | };
10 | });
11 | export default useStyles;
12 |
--------------------------------------------------------------------------------
/docs/apiDocs/demos/data/viewer.ts:
--------------------------------------------------------------------------------
1 | export const nodes = [
2 | {
3 | id: 'a1',
4 | data: {
5 | title: 'a1 节点',
6 | logo: 'https://mdn.alipayobjects.com/huamei_ntgeqc/afts/img/A*kgyiRKi04eUAAAAAAAAAAAAADvuvAQ/original',
7 | describe: 'XXX_XXX_XXX_API',
8 | },
9 | },
10 | {
11 | id: 'a2',
12 | data: {
13 | title: 'XXX_API_b4',
14 | logo: 'https://mdn.alipayobjects.com/huamei_ntgeqc/afts/img/A*kgyiRKi04eUAAAAAAAAAAAAADvuvAQ/original',
15 | describe: 'XXX_XXX_XXX_API',
16 | },
17 | },
18 | {
19 | id: 'a3',
20 | data: {
21 | title: 'XXX_API_b4',
22 | logo: 'https://mdn.alipayobjects.com/huamei_ntgeqc/afts/img/A*kgyiRKi04eUAAAAAAAAAAAAADvuvAQ/original',
23 | describe: 'XXX_XXX_XXX_API',
24 | },
25 | },
26 | ];
27 |
28 | export const edges = [
29 | {
30 | id: 'a1-a2',
31 | source: 'a1',
32 | target: 'a2',
33 | },
34 | {
35 | id: 'a1-a3',
36 | source: 'a1',
37 | target: 'a3',
38 | type: 'radius',
39 | },
40 | ];
41 |
--------------------------------------------------------------------------------
/docs/apiDocs/demos/useFlowView.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | FlowPanel,
3 | FlowView,
4 | FlowViewProvider,
5 | SelectType,
6 | useFlowViewer,
7 | } from '@ant-design/pro-flow';
8 | import { Button } from 'antd';
9 | import useStyles from './css/viewer.style';
10 | import { edges, nodes } from './data/viewer';
11 |
12 | function App() {
13 | const { selectNode, selectEdges, selectNodes, zoomToNode, fullScreen } = useFlowViewer();
14 | const { styles } = useStyles();
15 |
16 | return (
17 |
18 |
{
20 | selectNodes(['a1', 'a2', 'a3'], SelectType.SUB_SELECT);
21 | selectNode(node.id, SelectType.SELECT);
22 | selectEdges(['a1-a2', 'a1-a3'], SelectType.SUB_SELECT);
23 | }}
24 | onPaneClick={() => {
25 | selectNodes(['a1', 'a2', 'a3'], SelectType.DEFAULT);
26 | selectEdges(['a1-a2', 'a1-a3'], SelectType.DEFAULT);
27 | }}
28 | nodes={nodes}
29 | edges={edges}
30 | >
31 |
32 |
33 |
40 |
47 |
54 |
61 |
62 |
63 |
64 |
65 | );
66 | }
67 |
68 | function ProApp() {
69 | return (
70 |
71 |
72 |
73 | );
74 | }
75 |
76 | export default ProApp;
77 |
--------------------------------------------------------------------------------
/docs/caseShow/cicdPipeLine.md:
--------------------------------------------------------------------------------
1 | ---
2 | nav: 案例展示
3 | order: 100
4 | group:
5 | title: pipeLine
6 | order: 1
7 | title: CI/CD PipeLine
8 | description:
9 | ---
10 |
11 | ## Default
12 |
13 |
14 |
--------------------------------------------------------------------------------
/docs/caseShow/dataFlow.md:
--------------------------------------------------------------------------------
1 | ---
2 | nav:
3 | title: 案例展示
4 | order: 100
5 | group:
6 | title: 场景展示
7 | order: 1
8 | title: 数据流程图
9 | order: 1
10 | description:
11 | ---
12 |
13 | ## 数据流程图
14 |
15 | 案例特点:
16 |
17 | - 点击选中节点,自动高亮相关链路
18 | - 无级缩放的 label
19 | - 节点拖拽
20 | - Danger 节点链路高亮
21 | - 选中节点唤起抽屉
22 | - 点击画布或其他节点重置选中态
23 | - 初始化自动布局
24 |
25 |
26 |
--------------------------------------------------------------------------------
/docs/caseShow/demos/dataflow/styled.ts:
--------------------------------------------------------------------------------
1 | import { createStyles } from 'antd-style';
2 |
3 | const useStyles = createStyles(({ css }) => {
4 | return {
5 | dangerLogo: {
6 | width: '28px',
7 | height: '16px',
8 | background: '#ffeef1',
9 | borderRadius: '7px',
10 | marginTop: '3px',
11 | display: 'flex',
12 | justifyContent: 'center',
13 | alignItems: 'center',
14 | img: { width: '8px', height: '9px' },
15 | },
16 | container: css`
17 | width: 100%;
18 | height: 600px;
19 | .ant-progress-text {
20 | text-align: center !important;
21 | }
22 | `,
23 | CustomWrap: {
24 | width: '300px',
25 | height: '100px',
26 | backgroundColor: 'red',
27 | },
28 | };
29 | });
30 | export default useStyles;
31 |
--------------------------------------------------------------------------------
/docs/caseShow/demos/index.style.ts:
--------------------------------------------------------------------------------
1 | import { createStyles } from 'antd-style';
2 |
3 | const useStyles = createStyles(() => {
4 | return {
5 | pipeNodeWrap: {
6 | width: '260px',
7 | minHeight: '100px',
8 | backgroundColor: 'white',
9 | padding: '16px',
10 | boxSizing: 'border-box',
11 | borderRadius: '8px',
12 | },
13 | handle: {
14 | top: '0',
15 | },
16 | stepTitle: {
17 | overflow: 'hidden',
18 | color: '#8c8c8c',
19 | whiteSpace: 'nowrap',
20 | textOverflow: 'ellipsis',
21 | },
22 | pipeNode: {
23 | marginTop: '10px',
24 | width: '232px',
25 | boxSizing: 'border-box',
26 | border: '1px solid rgba(0, 0, 0, 0.08)',
27 | borderRadius: '8px',
28 | backgroundColor: 'white',
29 | },
30 | mainBox: {
31 | width: '100%',
32 | padding: '12px',
33 | height: '70px',
34 | backgroundColor: 'white',
35 | display: 'flex',
36 | borderBottom: 'none',
37 | borderRadius: '8px',
38 | boxSizing: 'border-box',
39 | position: 'relative',
40 | },
41 | logo: {
42 | img: { width: '16px', height: '16px', marginTop: '4px' },
43 | },
44 | wrap: {
45 | marginLeft: '8px',
46 | display: 'flex',
47 | flexDirection: 'column',
48 | },
49 | title: {
50 | color: '#000',
51 | fontWeight: '500',
52 | fontSize: '14px',
53 | lineHeight: '22px',
54 | whiteSpace: 'nowrap',
55 | },
56 | des: {
57 | marginTop: '8px',
58 | color: '#00000073',
59 | fontSize: '10px',
60 | },
61 | children: {
62 | display: 'flex',
63 | flexDirection: 'column',
64 | alignItems: 'center',
65 | paddingBottom: '10px',
66 | },
67 | childrenBox: {
68 | width: '200px',
69 | padding: '12px',
70 | height: '70px',
71 | backgroundColor: 'white',
72 | display: 'flex',
73 | border: '1px solid rgba(0, 0, 0, 0.08)',
74 | borderRadius: '8px',
75 | boxSizing: 'border-box',
76 | marginTop: '10px',
77 | },
78 | container: {
79 | width: '100%',
80 | height: '600px',
81 | },
82 | };
83 | });
84 | export default useStyles;
85 |
--------------------------------------------------------------------------------
/docs/caseShow/demos/pipeline/multiPipe/pipelineDemo.tsx:
--------------------------------------------------------------------------------
1 | /**
2 | * compact: true
3 | */
4 | import {
5 | Background,
6 | FlowView,
7 | FlowViewProvider,
8 | useEdgesState,
9 | useFlowViewer,
10 | useNodesState,
11 | } from '@ant-design/pro-flow';
12 | import { useCallback } from 'react';
13 | import useStyles from '../../index.style';
14 | import { edges, nodes } from './data';
15 | import { PipeNode } from './pipeNode';
16 |
17 | const nodeTypes = { pipeNode: PipeNode };
18 |
19 | function App() {
20 | const flowViewer = useFlowViewer();
21 | const { styles } = useStyles();
22 | const [_nodes, , onNodesChange] = useNodesState([...nodes]);
23 | const [_edges, , onEdgesChange] = useEdgesState([...edges]);
24 |
25 | const handleClick = useCallback(
26 | (e, n) => {
27 | flowViewer?.zoomToNode(n.id, 1000);
28 | },
29 | [flowViewer],
30 | );
31 |
32 | return (
33 |
34 |
43 |
44 |
45 |
46 | );
47 | }
48 |
49 | function ProApp() {
50 | return (
51 |
52 |
53 |
54 | );
55 | }
56 |
57 | export default ProApp;
58 |
--------------------------------------------------------------------------------
/docs/caseShow/demos/pipeline/taskPipeline/index.tsx:
--------------------------------------------------------------------------------
1 | /**
2 | * compact: true
3 | */
4 |
5 | import { Background, FlowView, applyEdgeChanges, applyNodeChanges } from '@ant-design/pro-flow';
6 | import { useCallback, useState } from 'react';
7 | import { edges, nodes } from './data';
8 | import StageNode from './nodes/stageNode';
9 | import TaskNode from './nodes/taskNode';
10 | import useStyles from './styled';
11 |
12 | const nodeTypes = { TaskNode, StageNode };
13 |
14 | function App() {
15 | const { styles } = useStyles();
16 | const [_nodes, setNodes] = useState(nodes);
17 | const [_edges, setEdges] = useState(edges);
18 |
19 | const onNodesChange = useCallback(
20 | (changes) => setNodes((nds) => applyNodeChanges(changes, nds)),
21 | [setNodes],
22 | );
23 |
24 | const onEdgesChange = useCallback(
25 | (changes) => setEdges((eds) => applyEdgeChanges(changes, eds)),
26 | [setEdges],
27 | );
28 |
29 | return (
30 |
31 |
42 |
43 |
44 |
45 | );
46 | }
47 |
48 | function ProApp() {
49 | return ;
50 | }
51 |
52 | export default ProApp;
53 |
--------------------------------------------------------------------------------
/docs/caseShow/demos/pipeline/taskPipeline/nodes/stageNode.tsx:
--------------------------------------------------------------------------------
1 | import { FlowViewNode } from '@ant-design/pro-flow';
2 | import { FC } from 'react';
3 | import useStyles from './styled';
4 | import TaskNode from './taskNode';
5 |
6 | const StageNode: FC = (node) => {
7 | // const [open, setOpen] = useState(false);
8 | const { styles } = useStyles();
9 | const { data } = node;
10 | const taskNodes = data.children;
11 |
12 | return (
13 |
14 |
15 |
{data.title}
16 | {data.extra}
17 |
18 |
19 | {taskNodes.map((taskNode: any) => {
20 | return ;
21 | })}
22 |
23 |
24 | );
25 | };
26 |
27 | export default StageNode;
28 |
--------------------------------------------------------------------------------
/docs/caseShow/demos/pipeline/taskPipeline/nodes/styled.ts:
--------------------------------------------------------------------------------
1 | import { createStyles } from 'antd-style';
2 |
3 | const useStyles = createStyles(() => {
4 | return {
5 | wrap: {
6 | width: '240px',
7 | backgroundColor: '#FAFAFA',
8 | marginBottom: '10px',
9 | borderRadius: '8px',
10 | position: 'relative',
11 | },
12 | taskNode: {
13 | width: '240px',
14 | minHeight: '42px',
15 | backgroundColor: 'white',
16 | padding: '8px 12px',
17 | borderRadius: '8px',
18 | boxSizing: 'border-box',
19 | border: '1px solid #e8e8e8',
20 | display: 'flex',
21 | alignItems: 'center',
22 | justifyContent: 'space-between',
23 | '.title': {
24 | width: '100px',
25 | marginLeft: '10px',
26 | },
27 | '.group': {
28 | width: '100px',
29 | display: 'flex',
30 | alignItems: 'center',
31 | justifyContent: 'space-between',
32 |
33 | '.extra': {
34 | width: '60px',
35 | },
36 |
37 | '.span': {
38 | width: '20px',
39 | display: 'block',
40 | textAlign: 'center',
41 | },
42 | },
43 | },
44 |
45 | stageNode: {
46 | width: '256px',
47 | height: ' 100%',
48 | backgroundColor: '#FAFAFA',
49 | display: 'flex',
50 | flexDirection: 'column',
51 | alignItems: 'center',
52 | borderRadius: '8px',
53 |
54 | '.wrap': {
55 | width: '100%',
56 | display: 'flex',
57 | flexWrap: 'nowrap',
58 | justifyContent: 'space-between',
59 | padding: '10px',
60 | },
61 |
62 | '.title': {
63 | width: '100px',
64 | marginLeft: '10px',
65 | },
66 | },
67 |
68 | taskContent: {
69 | margin: '0',
70 | padding: '8px 12px',
71 | listStyle: 'none',
72 | border: '1px solid #f0f0f0',
73 | borderTop: 'none',
74 | '> li': {
75 | display: 'flex',
76 | alignItems: 'center',
77 | marginBottom: '8px',
78 | '> div': {
79 | marginLeft: '8px',
80 | display: 'flex',
81 | justifyContent: 'space-between',
82 | width: '100%',
83 | },
84 | },
85 | },
86 | };
87 | });
88 | export default useStyles;
89 |
--------------------------------------------------------------------------------
/docs/caseShow/demos/pipeline/taskPipeline/nodes/taskContent.tsx:
--------------------------------------------------------------------------------
1 | import { CheckCircleFilled, ClockCircleFilled, CloseCircleFilled } from '@ant-design/icons';
2 | import useStyles from './styled';
3 |
4 | export const TaskContent = () => {
5 | const { styles } = useStyles();
6 |
7 | return (
8 |
9 | -
10 |
11 |
12 | 七尼尼 已完成
13 | 43s
14 |
15 |
16 |
17 | -
18 |
19 |
20 | lydon 已完成
21 | 43s
22 |
23 |
24 |
25 | -
26 |
27 |
28 | 青霓 审批不通过
29 | 43s
30 |
31 |
32 |
33 | -
34 |
35 |
36 | 青霓 未开始
37 |
38 |
39 |
40 |
41 | );
42 | };
43 |
--------------------------------------------------------------------------------
/docs/caseShow/demos/pipeline/taskPipeline/nodes/taskNode.tsx:
--------------------------------------------------------------------------------
1 | import { CheckCircleFilled } from '@ant-design/icons';
2 | import { Handle, Position } from '@ant-design/pro-flow';
3 | import { useState } from 'react';
4 | import useStyles from './styled';
5 |
6 | export interface TaskNodeC {
7 | id: string;
8 | title: string;
9 | status: string;
10 | extra: React.JSX.Element;
11 | isOpen: boolean;
12 | children: React.JSX.Element;
13 | }
14 |
15 | const TaskNode = (props: { data: TaskNodeC }) => {
16 | const { data } = props;
17 | const [open, setOpen] = useState(data.isOpen);
18 | const { styles } = useStyles();
19 |
20 | return (
21 |
22 |
33 |
37 |
38 |
{data.title}
39 | {data.children && (
40 |
44 | {data.extra &&
{data.extra}
}
45 | {data.extra &&
|}
46 |
setOpen(!open)}>{open ? 'on' : 'off'}
47 |
48 | )}
49 |
50 | {open && data.children}
51 |
62 |
63 | );
64 | };
65 |
66 | export default TaskNode;
67 |
--------------------------------------------------------------------------------
/docs/caseShow/demos/pipeline/taskPipeline/styled.ts:
--------------------------------------------------------------------------------
1 | import { createStyles } from 'antd-style';
2 |
3 | const useStyles = createStyles(() => {
4 | return {
5 | techUIpipeNodeWrap: {
6 | width: '260px',
7 | minHeight: '100px',
8 | backgroundColor: '#f6f8fa',
9 | padding: '12px 6px',
10 | boxSizing: 'border-box',
11 | borderRadius: '8px',
12 | },
13 |
14 | pipeNode: {
15 | width: '100%',
16 | borderRadius: '4px',
17 | backgroundColor: '#fff',
18 | paddingBottom: '12px',
19 | },
20 | mainBox: {
21 | width: '100%',
22 | padding: '12px',
23 | height: '45px',
24 | backgroundColor: 'white',
25 | display: 'flex',
26 | borderBottom: 'none',
27 | borderRadius: '8px',
28 | boxSizing: 'border-box',
29 | position: 'relative',
30 | },
31 | logo: {
32 | display: 'flex',
33 | alignItems: 'center',
34 | img: { width: '16px', height: '16px' },
35 | },
36 | title: {
37 | marginLeft: '8px',
38 | fontSize: '14px',
39 | fontWeight: '500',
40 | color: '#000',
41 | lineHeight: '22px',
42 | whiteSpace: 'nowrap',
43 | },
44 | lineTitle: {
45 | marginLeft: '8px',
46 | fontSize: '14px',
47 | fontWeight: '500',
48 | color: '#000',
49 | lineHeight: '22px',
50 | whiteSpace: 'nowrap',
51 | },
52 | subLogo: {
53 | position: 'absolute',
54 | right: '6px',
55 | top: '8px',
56 | img: { width: '80px', height: '25px' },
57 | },
58 | des: {
59 | marginTop: '16px',
60 | color: '#00000073',
61 | fontSize: '10px',
62 | },
63 | children: {
64 | display: 'flex',
65 | flexDirection: 'column',
66 | alignItems: 'center',
67 | paddingBottom: '8px',
68 | },
69 | childrenBox: {
70 | width: '220px',
71 | height: '30px',
72 | paddingLeft: '5px',
73 | backgroundColor: 'white',
74 | display: 'flex',
75 | alignItems: 'center',
76 | border: '1px solid #f1f1f1',
77 | borderRadius: '3px',
78 | boxSizing: 'border-box',
79 | marginTop: '3px',
80 | position: 'relative',
81 | },
82 | wrap: {
83 | marginLeft: '8px',
84 | display: 'flex',
85 | flexDirection: 'column',
86 | },
87 | container: {
88 | width: '100%',
89 | height: '600px',
90 | },
91 | };
92 | });
93 | export default useStyles;
94 |
--------------------------------------------------------------------------------
/docs/caseShow/demos/pipeline/techPipe/pipeNode.tsx:
--------------------------------------------------------------------------------
1 | import { Handle, Position } from '@ant-design/pro-flow';
2 | import useStyles from './techPipeLine.style';
3 |
4 | interface PipeNodeChild {
5 | title: string;
6 | logo?: string;
7 | id: string;
8 | }
9 |
10 | interface PipeNode {
11 | title: string;
12 | logo: string;
13 | des?: string;
14 | children?: PipeNodeChild[];
15 | }
16 |
17 | const PipeNode: FC<{
18 | data: PipeNode;
19 | }> = ({ data }) => {
20 | const { title, des, logo, children = [] } = data;
21 | const { styles } = useStyles();
22 |
23 | return (
24 |
25 |
26 |
27 |
28 |

29 |
30 |
{title}
31 |
32 |

38 |
39 |
40 |
41 | {children.length > 0 && (
42 |
43 | {children.map((item, index) => (
44 |
45 |
46 |
54 |
55 | {item.logo && (
56 |
57 |

58 |
59 | )}
60 |
61 |
64 |
72 |
73 |
74 | ))}
75 |
76 | )}
77 |
78 | {des &&
{des}
}
79 |
80 |
81 | );
82 | };
83 |
84 | export default PipeNode;
85 |
--------------------------------------------------------------------------------
/docs/caseShow/demos/pipeline/techPipe/techPipeLine.style.ts:
--------------------------------------------------------------------------------
1 | import { createStyles } from 'antd-style';
2 |
3 | const useStyles = createStyles(() => {
4 | return {
5 | techUIpipeNodeWrap: {
6 | width: '260px',
7 | minHeight: '100px',
8 | backgroundColor: '#f6f8fa',
9 | padding: '12px 6px',
10 | boxSizing: 'border-box',
11 | borderRadius: '8px',
12 | },
13 |
14 | pipeNode: {
15 | width: '100%',
16 | borderRadius: '4px',
17 | backgroundColor: '#fff',
18 | paddingBottom: '12px',
19 | },
20 | mainBox: {
21 | width: '100%',
22 | padding: '12px',
23 | height: '45px',
24 | backgroundColor: 'white',
25 | display: 'flex',
26 | borderBottom: 'none',
27 | borderRadius: '8px',
28 | boxSizing: 'border-box',
29 | position: 'relative',
30 | },
31 | logo: {
32 | display: 'flex',
33 | alignItems: 'center',
34 | img: { width: '16px', height: '16px' },
35 | },
36 | title: {
37 | marginLeft: '8px',
38 | fontSize: '14px',
39 | fontWeight: '500',
40 | color: '#000',
41 | lineHeight: '22px',
42 | whiteSpace: 'nowrap',
43 | },
44 | lineTitle: {
45 | marginLeft: '8px',
46 | fontSize: '14px',
47 | fontWeight: '500',
48 | color: '#000',
49 | lineHeight: '22px',
50 | whiteSpace: 'nowrap',
51 | },
52 | subLogo: {
53 | position: 'absolute',
54 | right: '6px',
55 | top: '8px',
56 | img: { width: '80px', height: '25px' },
57 | },
58 | des: {
59 | marginTop: '16px',
60 | color: '#00000073',
61 | fontSize: '10px',
62 | },
63 | children: {
64 | display: 'flex',
65 | flexDirection: 'column',
66 | alignItems: 'center',
67 | paddingBottom: '8px',
68 | },
69 | childrenBox: {
70 | width: '220px',
71 | height: '30px',
72 | paddingLeft: '5px',
73 | backgroundColor: 'white',
74 | display: 'flex',
75 | alignItems: 'center',
76 | border: '1px solid #f1f1f1',
77 | borderRadius: '3px',
78 | boxSizing: 'border-box',
79 | marginTop: '3px',
80 | position: 'relative',
81 | },
82 | wrap: {
83 | marginLeft: '8px',
84 | display: 'flex',
85 | flexDirection: 'column',
86 | },
87 | container: {
88 | width: '100%',
89 | height: '600px',
90 | },
91 | };
92 | });
93 | export default useStyles;
94 |
--------------------------------------------------------------------------------
/docs/caseShow/demos/pipeline/techPipe/techPipeline.tsx:
--------------------------------------------------------------------------------
1 | /**
2 | * compact: true
3 | */
4 |
5 | import {
6 | Background,
7 | FlowView,
8 | FlowViewProvider,
9 | useEdgesState,
10 | useFlowViewer,
11 | useNodesState,
12 | } from '@ant-design/pro-flow';
13 | import { useCallback } from 'react';
14 | import { edges, nodes } from './data';
15 | import PipeNode from './pipeNode';
16 | import useStyles from './techPipeLine.style';
17 |
18 | const nodeTypes = { pipeNode: PipeNode };
19 |
20 | function App() {
21 | const flowViewer = useFlowViewer();
22 | const { styles } = useStyles();
23 | const [_nodes, , onNodesChange] = useNodesState([...nodes]);
24 | const [_edges, , onEdgesChange] = useEdgesState([...edges]);
25 |
26 | const handleClick = useCallback(
27 | (e, n) => {
28 | flowViewer?.zoomToNode(n.id, 1000);
29 | },
30 | [flowViewer],
31 | );
32 |
33 | return (
34 |
35 |
44 |
45 |
46 |
47 | );
48 | }
49 |
50 | function ProApp() {
51 | return (
52 |
53 |
54 |
55 | );
56 | }
57 |
58 | export default ProApp;
59 |
--------------------------------------------------------------------------------
/docs/caseShow/demos/pressureTest.tsx:
--------------------------------------------------------------------------------
1 | /**
2 | * compact: true
3 | * defaultShowCode: true
4 | */
5 | import { FlowEdge, FlowNode, FlowView } from '@ant-design/pro-flow';
6 |
7 | const nodes: FlowNode[] = [];
8 | const edges: FlowEdge[] = [];
9 |
10 | nodes.push({
11 | id: `a${0}`,
12 | data: {
13 | title: `节点${0}`,
14 | logo: 'https://mdn.alipayobjects.com/huamei_ntgeqc/afts/img/A*kgyiRKi04eUAAAAAAAAAAAAADvuvAQ/original',
15 | describe: 'XXX_XXX_XXX_API',
16 | },
17 | });
18 |
19 | for (let i = 1; i < 1000; i += 1) {
20 | nodes.push({
21 | id: `a${i}`,
22 | data: {
23 | title: `节点${i}`,
24 | logo: 'https://mdn.alipayobjects.com/huamei_ntgeqc/afts/img/A*kgyiRKi04eUAAAAAAAAAAAAADvuvAQ/original',
25 | describe: 'XXX_XXX_XXX_API',
26 | },
27 | });
28 |
29 | if ((i - 1) % 10 === 0) {
30 | edges.push({
31 | id: `a${i}-${Math.random()}-${Date.now() * Math.random()}`,
32 | source: 'a0',
33 | target: `a${i}`,
34 | });
35 | } else {
36 | edges.push({
37 | id: `a${i}-${Math.random()}-${Date.now() * Math.random()}`,
38 | source: `a${i - 1}`,
39 | target: `a${i}`,
40 | });
41 | }
42 | }
43 |
44 | function App() {
45 | return (
46 |
52 |
53 |
54 | );
55 | }
56 |
57 | export default App;
58 |
--------------------------------------------------------------------------------
/docs/caseShow/demos/tinaFlow/comp/comp/index.tsx:
--------------------------------------------------------------------------------
1 | import { Handle, Position, useFlowViewer } from '@ant-design/pro-flow';
2 | import { FC, useState } from 'react';
3 | import { IconFont } from '../icon';
4 |
5 | interface CompNodeProps {
6 | className?: string;
7 | }
8 |
9 | export const CompNode: FC = function CompNode(props) {
10 | const s = useState({ height: '100px', width: '200px' });
11 | // const { updateNodeMeta } = useFlowEditor();
12 | const { selectNode, selectEdges, selectNodes, zoomToNode, fullScreen, instance } =
13 | useFlowViewer();
14 |
15 | // @ts-ignore
16 | window.instance = instance;
17 | return (
18 |
19 |
27 | compcompcompcomp
28 | {
31 | if (s[0].width === '200px') {
32 | s[1]({ height: '200px', width: '250px' });
33 | props?.data?.onResize(250, 200);
34 | } else {
35 | s[1]({ height: '100px', width: '200px' });
36 | props?.data?.onResize(200, 100);
37 | }
38 | console.log('props: ', props);
39 | // updateNodeMeta(props.id, "height", 200);
40 | // updateNodeMeta(props.id, "width", 250);
41 | }}
42 | />
43 |
44 | );
45 | };
46 |
--------------------------------------------------------------------------------
/docs/caseShow/demos/tinaFlow/comp/ctx.tsx:
--------------------------------------------------------------------------------
1 | import { FlowViewEdge, FlowViewNode, useFlowViewer } from '@ant-design/pro-flow';
2 | import { FC, createContext, useCallback, useContext, useState } from 'react';
3 |
4 | interface ICtx {
5 | nodes: FlowViewNode[];
6 | edges: FlowViewEdge[];
7 | init: (nodes: FlowViewNode[], edges: FlowViewEdge[]) => void;
8 | updateNodes: (
9 | items: [string, Partial | ((node: FlowViewNode) => FlowViewNode)][],
10 | ) => void;
11 | removeNodes: (ids: string[]) => void;
12 | }
13 |
14 | const Ctx = createContext({
15 | nodes: [],
16 | edges: [],
17 | init: () => {},
18 | updateNodes: () => {},
19 | removeNodes: () => {},
20 | });
21 |
22 | export const MindProvider: FC<{ children: React.ReactNode }> = ({ children }) => {
23 | const nodesState = useState([]);
24 | const edgesState = useState([]);
25 |
26 | const updateNodes = useCallback((items) => {
27 | nodesState[1]((nodes) => {
28 | const _nodes = [...nodes];
29 | items.forEach(([id, item]) => {
30 | const idx = _nodes.findIndex((n) => n.id === id);
31 | const node = _nodes[idx];
32 | if (node) {
33 | if (typeof item === 'function') {
34 | _nodes[idx] = { ...node, ...item(node) };
35 | } else {
36 | _nodes[idx] = { ...node, ...item };
37 | }
38 | }
39 | });
40 | // @ts-ignore
41 | window.nodes = _nodes;
42 | return _nodes;
43 | });
44 | }, []);
45 |
46 | const removeNodes = useCallback((ids) => {
47 | nodesState[1]((nodes) => {
48 | return nodes.filter((node) => !ids.includes(node.id));
49 | });
50 | edgesState[1]((edges) => {
51 | return edges.filter((edge) => !ids.includes(edge.source) && !ids.includes(edge.target));
52 | });
53 | }, []);
54 |
55 | const init = useCallback((nodes, edges) => {
56 | nodesState[1](nodes);
57 | edgesState[1](edges);
58 | }, []);
59 |
60 | return (
61 |
64 | {children}
65 |
66 | );
67 | };
68 |
69 | export function useMind() {
70 | const { selectNode, selectEdges, selectNodes, zoomToNode, fullScreen, instance } =
71 | useFlowViewer();
72 |
73 | // @ts-ignore
74 | window.instance = instance;
75 | return useContext(Ctx);
76 | }
77 |
--------------------------------------------------------------------------------
/docs/caseShow/demos/tinaFlow/comp/group/index.less:
--------------------------------------------------------------------------------
1 | @w: 155px;
2 |
3 | .group {
4 | background-color: #fff;
5 | border-radius: 8px;
6 | box-shadow: 0 4px 6px -2px rgba(25, 15, 15, 0.05), 0 0 1px 0 rgba(0, 0, 0, 0.08);
7 | padding: 0 12px 16px;
8 | width: @w * 2 + 8px + 12px * 2;
9 | box-sizing: border-box;
10 | .header {
11 | display: flex;
12 | padding: 8px 0;
13 | img {
14 | width: 16px;
15 | vertical-align: middle;
16 | margin-right: 8px;
17 | }
18 | .title {
19 | flex: 1 1 auto;
20 | }
21 | }
22 |
23 | .body {
24 | display: flex;
25 | flex-wrap: wrap;
26 | gap: 8px;
27 | .item {
28 | width: @w;
29 | border: 1px solid #eee;
30 | box-sizing: border-box;
31 | border-radius: 8px;
32 | box-shadow: 0 4px 6px -2px rgba(25, 15, 15, 0.05), 0 0 1px 0 rgba(0, 0, 0, 0.08);
33 | padding: 12px 8px;
34 | .itemTitle {
35 | font-size: 14px;
36 | }
37 | .itemDesc {
38 | color: #666;
39 | font-size: 12px;
40 | }
41 | }
42 | }
43 | }
44 |
45 | .handle {
46 | top: 41.5px;
47 | left: -9px;
48 | }
49 |
--------------------------------------------------------------------------------
/docs/caseShow/demos/tinaFlow/comp/group/index.tsx:
--------------------------------------------------------------------------------
1 | import { Handle, NodeProps, Position } from '@ant-design/pro-flow';
2 | import React, { FC, useEffect } from 'react';
3 |
4 | import { useMind } from '../ctx';
5 | import { IconFont } from '../icon';
6 | import './index.less';
7 |
8 | interface GroupProps extends NodeProps {
9 | className?: string;
10 | }
11 |
12 | export const Group: FC = function Group(props) {
13 | const { updateNodes } = useMind();
14 |
15 | const [count, setCount] = React.useState(props.data?.count);
16 |
17 | useEffect(() => {
18 | const row = Math.ceil(count / 2);
19 | updateNodes([[props.id, { height: 38.5 + row * 63 + (row - 1) * 8 + 16, width: 342 }]]);
20 | }, [count]);
21 |
22 | return (
23 |
24 |
30 |
31 |
32 |

36 |
bizData
37 |
38 | {
41 | setCount((count) => count + 1);
42 | }}
43 | />
44 |
45 |
46 |
47 |
48 | {new Array(count).fill('').map((item, i) => (
49 |
50 |
SaveMoneyContainer
51 |
直塞发奖弹窗
52 |
53 | ))}
54 |
55 |
56 | );
57 | };
58 |
--------------------------------------------------------------------------------
/docs/caseShow/demos/tinaFlow/comp/icon/index.tsx:
--------------------------------------------------------------------------------
1 | import { createFromIconfontCN } from '@ant-design/icons';
2 |
3 | // 使用你在 iconfont.cn 上的项目URL
4 | export const IconFont = createFromIconfontCN({
5 | scriptUrl: '//at.alicdn.com/t/a/font_4664976_ncmcql8595.js', // 示例URL
6 | });
7 |
--------------------------------------------------------------------------------
/docs/caseShow/demos/workflow/index.tsx:
--------------------------------------------------------------------------------
1 | /**
2 | * compact: true
3 | * defaultShowCode: true
4 | */
5 | import {
6 | BasicNode,
7 | EditNode,
8 | FlowEditor,
9 | FlowEditorProvider,
10 | useFlowEditor,
11 | } from '@ant-design/pro-flow';
12 | import { useCallback, useEffect } from 'react';
13 | import { StringRender } from '/docs/guide/demos/flowEditor/StringNode';
14 |
15 | import TextNode from './nodes/textNode';
16 | import Sidebar from './sidebar';
17 | import useStyles from './styled';
18 |
19 | let id = 0;
20 | const getId = () => `node_${id++}`;
21 |
22 | const nodeTypes = {
23 | TextNode: TextNode,
24 | StringNode: StringRender,
25 | BasicNode: BasicNode,
26 | EditNode: EditNode,
27 | };
28 | const ProFlowDemo = () => {
29 | const editor = useFlowEditor();
30 | const { styles } = useStyles();
31 |
32 | const onDragOver = useCallback((event) => {
33 | event.preventDefault();
34 | event.dataTransfer.dropEffect = 'move';
35 | }, []);
36 |
37 | const onDrop = useCallback(
38 | (event) => {
39 | event.preventDefault();
40 | if (!editor) return;
41 |
42 | const type = event.dataTransfer.getData('application/reactflow');
43 | if (typeof type === 'undefined' || !type) {
44 | return;
45 | }
46 |
47 | const position = editor.screenToFlowPosition({
48 | x: event.clientX,
49 | y: event.clientY,
50 | });
51 | const newNode = {
52 | id: getId(),
53 | type,
54 | position,
55 | content: {
56 | a: '123',
57 | },
58 | data: {
59 | title: `${type} node`,
60 | content: '123',
61 | },
62 | };
63 |
64 | editor.addNode(newNode);
65 | },
66 | [editor],
67 | );
68 |
69 | useEffect(() => {
70 | editor.addNode({
71 | id: 'a1',
72 | type: 'TextNode',
73 | position: { x: 200, y: 100 },
74 | data: {
75 | title: '123',
76 | aaa: '456',
77 | },
78 | });
79 | }, [editor]);
80 |
81 | return (
82 |
83 |
92 |
93 |
94 |
95 | );
96 | };
97 |
98 | const FlowDemo = () => {
99 | return (
100 |
101 |
102 |
103 | );
104 | };
105 |
106 | export default FlowDemo;
107 |
--------------------------------------------------------------------------------
/docs/caseShow/demos/workflow/nodes/network.tsx:
--------------------------------------------------------------------------------
1 | import { FC } from 'react';
2 | import useStyles from '../styled';
3 |
4 | interface NetworkNode {
5 | title: string;
6 | logo: string;
7 | des?: string;
8 | }
9 |
10 | const NetworkNode: FC<{
11 | data: NetworkNode;
12 | }> = ({ data }) => {
13 | const { styles } = useStyles();
14 |
15 | return ;
16 | };
17 |
18 | export default NetworkNode;
19 |
--------------------------------------------------------------------------------
/docs/caseShow/demos/workflow/nodes/textNode.tsx:
--------------------------------------------------------------------------------
1 | import { Handle, Position } from '@ant-design/pro-flow';
2 | import useStyles from '../styled';
3 |
4 | const TextNode = () => {
5 | const { styles } = useStyles();
6 |
7 | return (
8 |
9 |
文本节点
10 |
11 |
12 |
13 | );
14 | };
15 |
16 | export default TextNode;
17 |
--------------------------------------------------------------------------------
/docs/caseShow/demos/workflow/sidebar.tsx:
--------------------------------------------------------------------------------
1 | import useStyles from './styled';
2 |
3 | export default () => {
4 | const { styles, cx } = useStyles();
5 |
6 | const onDragStart = (event, nodeType) => {
7 | event.dataTransfer.setData('application/reactflow', nodeType);
8 | event.dataTransfer.effectAllowed = 'move';
9 | };
10 |
11 | return (
12 |
13 |
您可以将这些节点拖到左侧的画布中
14 |
onDragStart(event, 'StringNode')}
17 | draggable
18 | >
19 | String Node
20 |
21 |
onDragStart(event, 'TextNode')}
24 | draggable
25 | >
26 | Text Node
27 |
28 |
onDragStart(event, 'BasicNode')}
31 | draggable
32 | >
33 | BasicNode Node
34 |
35 |
onDragStart(event, 'EditNode')}
38 | draggable
39 | >
40 | EditNode Node
41 |
42 |
43 | );
44 | };
45 |
--------------------------------------------------------------------------------
/docs/caseShow/demos/workflow/styled.ts:
--------------------------------------------------------------------------------
1 | import { createStyles } from 'antd-style';
2 |
3 | const useStyles = createStyles(() => {
4 | return {
5 | container: {
6 | width: '100%',
7 | height: '600px',
8 | display: 'flex',
9 | flexDirection: 'column',
10 | },
11 | aside: {
12 | width: '200px',
13 | height: '100%',
14 | position: 'absolute',
15 | right: 0,
16 | top: 0,
17 | zIndex: 10000000,
18 | borderRight: '1px solid #eee',
19 | padding: '15px 10px',
20 | fontSize: '12px',
21 | background: '#fcfcfc',
22 | marginBottom: '10px',
23 | },
24 | description: {
25 | marginBottom: '10px',
26 | },
27 | dndnode: {
28 | height: '20px',
29 | padding: '4px',
30 | border: '1px solid #1a192b',
31 | borderRadius: '2px',
32 | marginBottom: '10px',
33 | display: 'flex',
34 | justifyContent: 'center',
35 | alignItems: 'center',
36 | cursor: 'grab',
37 | },
38 | 'dndnode.input': {
39 | borderColor: '#0041d0',
40 | },
41 | 'dndnode.output': {
42 | borderColor: '#ff0072',
43 | },
44 | 'reactflow-wrapper': {
45 | flexGrow: '1',
46 | height: '100%',
47 | },
48 | selectall: {
49 | marginTop: '10px',
50 | },
51 | textNode: {
52 | width: '300px',
53 | height: '300px',
54 | background: '#fff',
55 | borderRadius: '10px',
56 | border: '1px solid #ddd',
57 | display: 'flex',
58 | justifyContent: 'center',
59 | flexWrap: 'wrap',
60 | h1: {
61 | margin: 0,
62 | padding: '10px',
63 | borderBottom: '1px solid #ddd',
64 | fontSize: '',
65 | },
66 | textarea: {
67 | width: '280px',
68 | height: '200px',
69 | },
70 | },
71 | };
72 | });
73 | export default useStyles;
74 |
--------------------------------------------------------------------------------
/docs/caseShow/pipeline.md:
--------------------------------------------------------------------------------
1 | ---
2 | nav:
3 | title: 案例展示
4 | order: 100
5 | group:
6 | title: 场景展示
7 | order: 1
8 | title: 流水线&管道图
9 | order: 3
10 | description:
11 | ---
12 |
13 | ## 流水线
14 |
15 |
16 |
17 | ## 管道图
18 |
19 |
20 |
21 | ## 任务管道图
22 |
23 |
24 |
--------------------------------------------------------------------------------
/docs/caseShow/presureTest.md:
--------------------------------------------------------------------------------
1 | ---
2 | nav: 案例展示
3 | order: 100
4 | group:
5 | title: 压力测试
6 | order: 1
7 | title: 1000节点
8 | description:
9 | ---
10 |
11 | ## Default
12 |
13 |
14 |
--------------------------------------------------------------------------------
/docs/caseShow/techPileline.md:
--------------------------------------------------------------------------------
1 | ---
2 | nav:
3 | title: 案例展示
4 | order: 100
5 | group:
6 | title: pipeLine
7 | order: 10
8 | title: MultiConnect
9 |
10 | description:
11 | ---
12 |
13 | ## Default
14 |
15 |
16 |
--------------------------------------------------------------------------------
/docs/caseShow/workFlow.md:
--------------------------------------------------------------------------------
1 | ---
2 | nav:
3 | title: 案例展示
4 | order: 100
5 | group:
6 | title: 场景展示
7 | order: 1
8 | title: 工作流编辑器
9 | order: 2
10 | description:
11 | ---
12 |
13 | ## 工作流编辑器
14 |
15 | 案例特点:
16 |
17 | - 拖拽节点放入画布
18 | - 框选多个节点
19 | - 复制粘贴(快捷键)
20 | - 撤销重做(快捷键)
21 | - 单个/框选删除
22 | - 自由拖拽
23 | - 连接节点
24 |
25 |
26 |
--------------------------------------------------------------------------------
/docs/changelog.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: 更新日志
3 | nav:
4 | title: 更新日志
5 | order: 999
6 | ---
7 |
8 |
9 |
--------------------------------------------------------------------------------
/docs/guide/autoLayout.zh-CN.md:
--------------------------------------------------------------------------------
1 | ---
2 | nav:
3 | title: 快速上手
4 | order: 10
5 | group:
6 | title: 进阶使用
7 | order: 3
8 | title: 自动布局
9 | order: 1
10 | description:
11 | ---
12 |
13 | ## 自动布局
14 |
15 | 当你使用 FlowView 组件传入 nodes 和 edges 列表后,FlowView 会自动梳理节点之间的逻辑关系,并给出一个自动布局结果渲染在画布上,比如下面这段关系渲染后的结果如下。
16 |
17 |
18 |
19 | ## 自定义布局
20 |
21 | 如果希望自己设置坐标,可以给节点添加 position 属性来控制。
22 |
23 | ```js
24 | const nodes = [
25 | {
26 | id: 'a1',
27 | position: {
28 | x: 100,
29 | y: 300,
30 | },
31 | data: {
32 | title: '节点1',
33 | },
34 | },
35 | {
36 | id: 'a2',
37 | position: {
38 | x: 300,
39 | y: 600,
40 | },
41 | data: {
42 | title: '节点2',
43 | },
44 | },
45 | ...
46 | ];
47 | ```
48 |
49 |
50 |
--------------------------------------------------------------------------------
/docs/guide/baseIntro.zh-CN.md:
--------------------------------------------------------------------------------
1 | ---
2 | nav:
3 | title: 快速上手
4 | order: 10
5 | group:
6 | title: 基础介绍
7 | order: 1
8 | title: 术语和定义
9 | order: 2
10 | description:
11 | ---
12 |
13 | # 术语和定义
14 |
15 | ## Nodes - 节点
16 |
17 | ProFlow 中的节点是一个 React 组件。这意味着它可以渲染您喜欢的任何内容。每个节点都有一个 x 和 y 坐标,这告诉它它在视口中的位置。在不设置 type 的情况下,FlowView 组件默认会把组件设置为[BasicNode](/components/lineage-node)类型的节点。你也可以自定义节点类型。
18 |
19 |
20 |
21 | ### Custom Nodes - 自定义节点
22 |
23 | [自定义节点使用说明](/components/customNodeDoc)
24 |
25 | ## Edges - 边缘
26 |
27 | 一条 Edge 连接两个节点。每个边缘都需要一个`source` 和 一个`target`。 ProFlow 内置了五种边缘类型:
28 |
29 | - 'straight'
30 | - 'step'
31 | - 'smoothstep'
32 | - 'bezier'
33 | - 'radius' 。
34 |
35 | FlowView 把 `smoothstep` 设置为默认类型。 你也可以自定义边缘类型。
36 |
37 |
38 |
39 | ## Handles - 连接桩
40 |
41 | `Handle` 可以翻译为 “**连接桩**”,是边缘连接到节点的位置。`Handle`可以放置在任何地方。
42 |
43 | 可以用以下方式式引入 `Handle` 与 `Position`,来自定义 `Handle` 在节点中的位置。
44 |
45 | ```ts
46 | import { Handle, type Position } from '@ant-design/pro-flow';
47 | ```
48 |
49 |
50 |
--------------------------------------------------------------------------------
/docs/guide/briefIntro.zh-CN.md:
--------------------------------------------------------------------------------
1 | ---
2 | nav:
3 | title: 快速上手
4 | order: 10
5 | group:
6 | title: 基础介绍
7 | order: 1
8 | title: 简介
9 | order: 1
10 | description:
11 | ---
12 |
13 | # 简介
14 |
15 | `ProFlow` 是一款功能强大、易用灵活的流程编辑器框架,帮助你轻松构建复杂的工作流和流程产品。
16 |
17 | ## 特性 1: 更现代化的设计
18 |
19 | 更现代化的 UI 组件设计,使得编辑出的流程图更像是一个产品,而不是简易流程图。
20 |
21 |
24 |
25 |
28 |
29 | ## 特性 2: 开发者友好的 API
30 |
31 | ### 更符合 React 的开发方式,上手成本低,开发效率高,细节控制精准,灵活度极高。
32 |
33 | ```js
34 | import { FlowView, FlowViewProvider } from '@ant-design/pro-flow';
35 | import { nodes, edeges } from './data.ts';
36 |
37 | const ProFlowDemo = () => {
38 | const flowInstance = useFlowView();
39 | const { setMiniMapPosition } = useMiniMap();
40 |
41 | return (
42 |
43 |
44 |
45 | );
46 | };
47 |
48 | const FlowDemo = () => {
49 | return (
50 |
51 |
52 |
53 | );
54 | };
55 | ```
56 |
57 | ### 更细颗粒度的操控画布与节点
58 |
59 | ```js
60 | import { useFlowEditor } from '@ant-design/pro-flow';
61 |
62 | export default () => {
63 | const editor = useFlowEditor();
64 | console.log(editor);
65 | };
66 | ```
67 |
68 | ### 高灵活度
69 |
70 | ProFlow 支持自定义节点类型。自定义的节点为 React 组件,所以可以实现任何你需要的东西。
71 |
72 | ## 特性 3. 完善的基本功能
73 |
74 | ### 无极缩放的 label
75 |
76 |
79 |
80 | ### 节点与边的主辅级选中态
81 |
82 |
85 |
86 | ### 自动布局,排布节点
87 |
88 | 你只需关注要展示的节点,与节点之间的关系。FlowView 会自动根据节点间的关系给出一个布局效果。
89 |
90 | 
91 |
92 | ### 多线重叠时的高亮展示
93 |
94 | 当有多条边缘重叠在一起时,会自动将高亮的边缘放在画布图层的最顶层。
95 |
96 | 
97 |
--------------------------------------------------------------------------------
/docs/guide/customEdge.zh-CN.md:
--------------------------------------------------------------------------------
1 | ---
2 | nav:
3 | title: 快速上手
4 | order: 10
5 | group:
6 | title: 进阶使用
7 | order: 3
8 | title: 自定义边缘
9 | order: 3
10 | description:
11 | ---
12 |
13 | ## 自定义边缘
14 |
15 | FlowView 内置了 5 中不同的边缘类型:`straight`,`step`,`smoothstep`,`bezier`和`radius`。
16 |
17 | ```ts
18 | const edges = [{
19 | id: 'e1',
20 | type: 'radius',
21 | ...
22 | }]
23 |
24 | ```
25 |
26 | 你也可以像下面的示例一样,自定义边缘。
27 |
28 | ## 实现自定义边缘
29 |
30 |
31 |
--------------------------------------------------------------------------------
/docs/guide/customNode.zh-CN.md:
--------------------------------------------------------------------------------
1 | ---
2 | nav:
3 | title: 快速上手
4 | order: 10
5 | group:
6 | title: 进阶使用
7 | order: 3
8 | title: 自定义节点
9 | order: 2
10 | description:
11 | ---
12 |
13 | ## 自定义节点
14 |
15 | ProFlow 的一个强大功能是能够添加自定义节点。在自定义节点中,您可以渲染所需的所有内容。例如,您可以定义多个源句柄和目标句柄,并呈现表单输入或图表。在本节中,我们将实现一个带有输入字段的节点,该输入字段更新应用程序另一部分中的一些文本。
16 |
17 | ## 实现自定义节点
18 |
19 | 让我们开始实现:声明一个自定义节点`CustomNode`组件。
20 |
21 | ```js
22 | function CustomNode({ data }) {
23 | return 组件源码见下方示例
;
24 | }
25 | ```
26 |
27 | ## 添加节点类型
28 |
29 | 你可以将新的节点类型添加到 prop 中传递给 FlowView。
30 |
31 | :::warning
32 | **在组件外部定义或缓存`nodeTypes`非常重要。** 否则 React 会在每次渲染时创建一个新的对象,这会导致性能问题和错误。
33 | :::
34 |
35 | ```js
36 | const nodeTypes = { customNode: CustomNode };
37 |
38 | function App() {
39 | return (
40 |
41 |
42 |
43 | );
44 | }
45 | ```
46 |
47 | 定义新节点类型后,可以在 nodes 列表中使用这个类型了。
48 |
49 | ```js
50 | const nodes = [
51 | {
52 | id: 'n1',
53 | type: 'customNode',
54 | data,
55 | },
56 | ];
57 | ```
58 |
59 | 然后你就可以得到如下的自定义节点效果。
60 |
61 | ## 使用多个 Handle
62 |
63 | 当一个节点拥有多个 Handle 时,在连接时只传递节点 Id 是不够的,还需要传递特定的 HandleId。
64 |
65 | ```js
66 | const initialEdge = [
67 | {
68 | id: 'edge-1',
69 | source: 'a1',
70 | target: 'a2',
71 | type: edgeType,
72 | sourceHandle: 'a1-1-source',
73 | targetHandle: 'a2-1-target',
74 | },
75 | ...
76 | ];
77 | ```
78 |
79 | :::warning
80 | 自定义节点可以通过传入 width 和 height 属性来调节自动布局后的节点位置。
81 |
82 | 自定义节点 width 越大,两个节点的水平间距就越大。height 同理。
83 | :::
84 |
85 | ```js
86 | const nodes = [
87 | {
88 | id: 'a1',
89 | type: 'customNode',
90 | width: 250,
91 | height: 150,
92 | data,
93 | },
94 | ];
95 | ```
96 |
97 | 效果如下:
98 |
--------------------------------------------------------------------------------
/docs/guide/demos/autoLayout/data/demo1data.ts:
--------------------------------------------------------------------------------
1 | export const nodes = [
2 | {
3 | id: 'a1',
4 | data: {
5 | title: '节点1',
6 | },
7 | },
8 | {
9 | id: 'a2',
10 | data: {
11 | title: '节点2',
12 | },
13 | },
14 | {
15 | id: 'a3',
16 | data: {
17 | title: '节点3',
18 | },
19 | },
20 | {
21 | id: 'a4',
22 | data: {
23 | title: '节点4',
24 | },
25 | },
26 | {
27 | id: 'a5',
28 | data: {
29 | title: '节点5',
30 | },
31 | },
32 | {
33 | id: 'a6',
34 | data: {
35 | title: '节点6',
36 | },
37 | },
38 | ];
39 |
40 | export const edges = [
41 | {
42 | source: 'a1',
43 | target: 'a2',
44 | },
45 | {
46 | source: 'a1',
47 | target: 'a3',
48 | },
49 | {
50 | source: 'a2',
51 | target: 'a3',
52 | },
53 | {
54 | source: 'a3',
55 | target: 'a6',
56 | },
57 | {
58 | source: 'a2',
59 | target: 'a4',
60 | },
61 | {
62 | source: 'a3',
63 | target: 'a5',
64 | },
65 | {
66 | source: 'a2',
67 | target: 'a6',
68 | },
69 | ];
70 |
--------------------------------------------------------------------------------
/docs/guide/demos/autoLayout/data/demo2data.ts:
--------------------------------------------------------------------------------
1 | export const nodes = [
2 | {
3 | id: 'a1',
4 | position: {
5 | x: 100,
6 | y: 300,
7 | },
8 | data: {
9 | title: '节点1',
10 | },
11 | },
12 | {
13 | id: 'a2',
14 | position: {
15 | x: 300,
16 | y: 600,
17 | },
18 | data: {
19 | title: '节点2',
20 | },
21 | },
22 | {
23 | id: 'a3',
24 | data: {
25 | title: '节点3',
26 | },
27 | },
28 | {
29 | id: 'a4',
30 | data: {
31 | title: '节点4',
32 | },
33 | },
34 | {
35 | id: 'a5',
36 | data: {
37 | title: '节点5',
38 | },
39 | },
40 | {
41 | id: 'a6',
42 | data: {
43 | title: '节点6',
44 | },
45 | },
46 | ];
47 |
48 | export const edges = [
49 | {
50 | source: 'a1',
51 | target: 'a2',
52 | },
53 | {
54 | source: 'a1',
55 | target: 'a3',
56 | },
57 | {
58 | source: 'a2',
59 | target: 'a3',
60 | },
61 | {
62 | source: 'a3',
63 | target: 'a6',
64 | },
65 | {
66 | source: 'a2',
67 | target: 'a4',
68 | },
69 | {
70 | source: 'a3',
71 | target: 'a5',
72 | },
73 | {
74 | source: 'a2',
75 | target: 'a6',
76 | },
77 | ];
78 |
--------------------------------------------------------------------------------
/docs/guide/demos/autoLayout/demo1.tsx:
--------------------------------------------------------------------------------
1 | /**
2 | * compact: true
3 | * defaultShowCode: true
4 | */
5 | import { FlowView } from '@ant-design/pro-flow';
6 | import { edges, nodes } from './data/demo1data';
7 | import useStyles from './index.style';
8 |
9 | function App() {
10 | const { styles } = useStyles();
11 |
12 | return (
13 |
14 |
15 |
16 | );
17 | }
18 |
19 | export default App;
20 |
--------------------------------------------------------------------------------
/docs/guide/demos/autoLayout/demo2.tsx:
--------------------------------------------------------------------------------
1 | /**
2 | * compact: true
3 | * defaultShowCode: true
4 | */
5 | import { FlowView } from '@ant-design/pro-flow';
6 | import { edges, nodes } from './data/demo2data';
7 | import useStyles from './index.style';
8 |
9 | function App() {
10 | const { styles } = useStyles();
11 |
12 | return (
13 |
14 |
15 |
16 | );
17 | }
18 |
19 | export default App;
20 |
--------------------------------------------------------------------------------
/docs/guide/demos/autoLayout/index.style.ts:
--------------------------------------------------------------------------------
1 | import { createStyles } from 'antd-style';
2 |
3 | const useStyles = createStyles(() => {
4 | return {
5 | container: {
6 | width: '100%',
7 | height: '400px',
8 | },
9 | };
10 | });
11 | export default useStyles;
12 |
--------------------------------------------------------------------------------
/docs/guide/demos/baseIntro/coreEdge.tsx:
--------------------------------------------------------------------------------
1 | /**
2 | * compact: true
3 | */
4 |
5 | import { FlowPanel, FlowView } from '@ant-design/pro-flow';
6 | import { Button } from 'antd';
7 | import { useState } from 'react';
8 | import useStyles from './css/index.style';
9 | import { getEdges, nodes } from './data/coreEdgeData';
10 |
11 | const edgeTypes = ['straight', 'step', 'bezier', 'smoothstep', 'radius'];
12 |
13 | function App() {
14 | const [edgeType, setEdgeType] = useState('straight');
15 | const { styles } = useStyles();
16 |
17 | return (
18 |
19 |
20 |
21 | {edgeTypes.map((type) => (
22 |
30 | ))}
31 |
32 |
33 |
34 | );
35 | }
36 |
37 | export default App;
38 |
--------------------------------------------------------------------------------
/docs/guide/demos/baseIntro/coreHandle.tsx:
--------------------------------------------------------------------------------
1 | /**
2 | * compact: true
3 | */
4 | import { FlowView, Handle, Position } from '@ant-design/pro-flow';
5 | import { FC } from 'react';
6 | import useStyles from './css/index.style';
7 |
8 | const CustomNode: FC<{
9 | data: {
10 | title: string;
11 | };
12 | }> = (props) => {
13 | const { data } = props;
14 |
15 | return (
16 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 | );
36 | };
37 |
38 | const nodes = [
39 | {
40 | id: 'b1',
41 | type: 'customNode',
42 | data: {
43 | title: '一堆 Handle',
44 | },
45 | },
46 | ];
47 |
48 | const nodeTypes = { customNode: CustomNode };
49 |
50 | function App() {
51 | const { styles } = useStyles();
52 |
53 | return (
54 |
55 |
56 |
57 | );
58 | }
59 |
60 | export default App;
61 |
--------------------------------------------------------------------------------
/docs/guide/demos/baseIntro/coreNode.tsx:
--------------------------------------------------------------------------------
1 | /**
2 | * compact: true
3 | */
4 | import { FlowView } from '@ant-design/pro-flow';
5 | import useStyles from './css/index.style';
6 |
7 | const nodes = [
8 | {
9 | id: 'a1',
10 | data: {
11 | title: 'XXX_API_b3',
12 | logo: 'https://mdn.alipayobjects.com/huamei_ntgeqc/afts/img/A*kgyiRKi04eUAAAAAAAAAAAAADvuvAQ/original',
13 | description: 'XXX_XXX_XXX_API',
14 | },
15 | },
16 | ];
17 |
18 | function App() {
19 | const { styles } = useStyles();
20 |
21 | return (
22 |
23 |
24 |
25 | );
26 | }
27 |
28 | export default App;
29 |
--------------------------------------------------------------------------------
/docs/guide/demos/baseIntro/css/customerNode.style.ts:
--------------------------------------------------------------------------------
1 | import { createStyles } from 'antd-style';
2 |
3 | const useStyles = createStyles(() => {
4 | return {
5 | customerWrap: {
6 | width: '260px',
7 | minHeight: '100px',
8 | backgroundColor: '#f6f8fa',
9 | padding: '16px',
10 | boxSizing: 'border-box',
11 | borderRadius: '8px',
12 | },
13 | handle: {
14 | top: '0',
15 | },
16 | stepTitle: {
17 | overflow: 'hidden',
18 | color: '#8c8c8c',
19 | whiteSpace: 'nowrap',
20 | textOverflow: 'ellipsis',
21 | },
22 | pipeNode: {
23 | marginTop: '10px',
24 | width: '232px',
25 | boxSizing: 'border-box',
26 | border: '1px solid rgba(0, 0, 0, 0.08)',
27 | borderRadius: '8px',
28 | },
29 | mainBox: {
30 | width: '100%',
31 | padding: '12px',
32 | height: '70px',
33 | backgroundColor: 'white',
34 | display: 'flex',
35 | borderBottom: 'none',
36 | borderRadius: '8px',
37 | boxSizing: 'border-box',
38 | },
39 | logo: {
40 | img: { width: '16px', height: '16px', marginTop: '4px' },
41 | },
42 | wrap: {
43 | marginLeft: '8px',
44 | display: 'flex',
45 | flexDirection: 'column',
46 | },
47 | title: {
48 | color: '#000',
49 | fontWeight: '500',
50 | fontSize: '14px',
51 | lineHeight: '22px',
52 | whiteSpace: 'nowrap',
53 | },
54 | des: {
55 | marginTop: '8px',
56 | color: '#00000073',
57 | fontSize: '12px',
58 | },
59 | children: {
60 | display: 'flex',
61 | flexDirection: 'column',
62 | alignItems: 'center',
63 | paddingBottom: '10px',
64 | },
65 | childrenBox: {
66 | width: '200px',
67 | padding: '12px',
68 | height: '70px',
69 | backgroundColor: 'white',
70 | display: 'flex',
71 | border: '1px solid rgba(0, 0, 0, 0.08)',
72 | borderRadius: '8px',
73 | boxSizing: 'border-box',
74 | marginTop: '10px',
75 | },
76 | container: {
77 | width: '100%',
78 | height: '600px',
79 | },
80 | };
81 | });
82 | export default useStyles;
83 |
--------------------------------------------------------------------------------
/docs/guide/demos/baseIntro/css/index.style.ts:
--------------------------------------------------------------------------------
1 | import { createStyles } from 'antd-style';
2 |
3 | const useStyles = createStyles(() => {
4 | return {
5 | container: {
6 | width: '100%',
7 | height: '600px',
8 | },
9 | };
10 | });
11 | export default useStyles;
12 |
--------------------------------------------------------------------------------
/docs/guide/demos/baseIntro/data/coreEdgeData.ts:
--------------------------------------------------------------------------------
1 | export const nodes = [
2 | {
3 | id: 'a1',
4 | data: {
5 | title: '节点1',
6 | },
7 | },
8 | {
9 | id: 'a2',
10 | data: {
11 | title: '节点2',
12 | },
13 | },
14 | {
15 | id: 'a3',
16 | data: {
17 | title: '节点3',
18 | },
19 | },
20 | {
21 | id: 'a4',
22 | data: {
23 | title: '节点4',
24 | },
25 | },
26 | {
27 | id: 'a5',
28 | data: {
29 | title: '节点5',
30 | },
31 | },
32 | {
33 | id: 'a6',
34 | data: {
35 | title: '节点6',
36 | },
37 | },
38 | ];
39 |
40 | export const getEdges = (type: string) => [
41 | {
42 | source: 'a1',
43 | target: 'a2',
44 | type,
45 | label: type,
46 | },
47 | {
48 | source: 'a1',
49 | target: 'a3',
50 | type,
51 | label: type,
52 | },
53 | {
54 | source: 'a2',
55 | target: 'a3',
56 | type,
57 | label: type,
58 | },
59 | {
60 | source: 'a3',
61 | target: 'a6',
62 | type,
63 | label: type,
64 | },
65 | {
66 | source: 'a2',
67 | target: 'a4',
68 | type,
69 | label: type,
70 | },
71 | {
72 | source: 'a3',
73 | target: 'a5',
74 | type,
75 | label: type,
76 | },
77 | {
78 | source: 'a2',
79 | target: 'a6',
80 | type,
81 | label: type,
82 | },
83 | ];
84 |
--------------------------------------------------------------------------------
/docs/guide/demos/customEdge/ButtonEdge.tsx:
--------------------------------------------------------------------------------
1 | import { BaseEdge, EdgeLabelRenderer, EdgeProps, getBezierPath } from '@ant-design/pro-flow';
2 | import useStyles from './btn.style';
3 |
4 | const onEdgeClick = (evt, id) => {
5 | evt.stopPropagation();
6 | alert(` ${id}`);
7 | };
8 |
9 | export default function CustomEdge(edge: EdgeProps) {
10 | const {
11 | id,
12 | sourceX,
13 | sourceY,
14 | targetX,
15 | targetY,
16 | sourcePosition,
17 | targetPosition,
18 | style = {},
19 | markerEnd,
20 | } = edge;
21 |
22 | const [edgePath, labelX, labelY] = getBezierPath({
23 | sourceX,
24 | sourceY,
25 | sourcePosition,
26 | targetX,
27 | targetY,
28 | targetPosition,
29 | });
30 |
31 | const { styles } = useStyles();
32 | return (
33 | <>
34 |
40 |
41 |
52 |
55 |
56 |
57 | >
58 | );
59 | }
60 |
--------------------------------------------------------------------------------
/docs/guide/demos/customEdge/btn.style.ts:
--------------------------------------------------------------------------------
1 | import { createStyles } from 'antd-style';
2 |
3 | const useStyles = createStyles(({ css }) => {
4 | return {
5 | edgebutton: css`
6 | width: '0px';
7 | background: '#eee';
8 | border: '1px solid #fff';
9 | cursor: 'pointer';
10 | border-radius: '30px';
11 | font-size: '12px';
12 | line-height: '1';
13 | margin-bottom: 8px;
14 | cursor: pointer;
15 |
16 | &:hover {
17 | box-shadow: '0 0 6px 2px rgba(0, 0, 0, 0.08)';
18 | }
19 | `,
20 | customEdge: css`
21 | background-color: red;
22 | &:hover {
23 | path {
24 | stroke: red;
25 | }
26 | }
27 | `,
28 | container: {
29 | width: '100%',
30 | height: '400px',
31 | },
32 | path: {
33 | stroke: 'red',
34 | },
35 | };
36 | });
37 | export default useStyles;
38 |
--------------------------------------------------------------------------------
/docs/guide/demos/customEdge/data.ts:
--------------------------------------------------------------------------------
1 | export const nodes = [
2 | {
3 | id: 'a1',
4 | label: 'a1',
5 | data: {
6 | title: 'XXX_API_ddddddddddddddddddddddddddddddddddddddddddddddddddddddb1',
7 | logo: 'https://mdn.alipayobjects.com/huamei_ntgeqc/afts/img/A*kgyiRKi04eUAAAAAAAAAAAAADvuvAQ/original',
8 | description: 'XXX_XXX_XXX_API',
9 | },
10 | },
11 | {
12 | id: 'a2',
13 | label: 'a2',
14 | data: {
15 | title: 'XXX_API_ddddddddddddddddddddddddddddddddddddddddddddddddddddddb1',
16 | logo: 'https://mdn.alipayobjects.com/huamei_ntgeqc/afts/img/A*kgyiRKi04eUAAAAAAAAAAAAADvuvAQ/original',
17 | description: 'XXX_XXX_XXX_API',
18 | },
19 | },
20 | {
21 | id: 'a3',
22 | label: 'a3',
23 | data: {
24 | title: 'XXX_API_ddddddddddddddddddddddddddddddddddddddddddddddddddddddb1',
25 | logo: 'https://mdn.alipayobjects.com/huamei_ntgeqc/afts/img/A*kgyiRKi04eUAAAAAAAAAAAAADvuvAQ/original',
26 | description: 'XXX_XXX_XXX_API',
27 | },
28 | },
29 | ];
30 |
31 | export const getEdges = (className) => [
32 | {
33 | id: 'e1',
34 | source: 'a1',
35 | target: 'a2',
36 | type: 'buttonEdge',
37 | className,
38 | },
39 | {
40 | id: 'e2',
41 | source: 'a1',
42 | target: 'a3',
43 | type: 'buttonEdge',
44 | className,
45 | },
46 | ];
47 |
--------------------------------------------------------------------------------
/docs/guide/demos/customEdge/index.tsx:
--------------------------------------------------------------------------------
1 | /**
2 | * compact: true
3 | * defaultShowCode: true
4 | */
5 | import {
6 | FlowPanel,
7 | FlowView,
8 | FlowViewProvider,
9 | SelectType,
10 | useFlowViewer,
11 | } from '@ant-design/pro-flow';
12 | import { Button } from 'antd';
13 | import { useMemo } from 'react';
14 | import ButtonEdge from './ButtonEdge';
15 | import useStyles from './btn.style';
16 | import { getEdges, nodes } from './data';
17 |
18 | const edgeTypes = {
19 | buttonEdge: ButtonEdge,
20 | };
21 |
22 | function App() {
23 | const { styles } = useStyles();
24 | const edges = useMemo(() => getEdges(styles.customEdge), []);
25 | const { selectEdges } = useFlowViewer();
26 |
27 | return (
28 |
29 |
30 |
31 |
32 |
39 |
46 |
53 |
54 |
55 |
56 |
57 | );
58 | }
59 |
60 | function ProApp() {
61 | return (
62 |
63 |
64 |
65 | );
66 | }
67 |
68 | export default ProApp;
69 |
--------------------------------------------------------------------------------
/docs/guide/demos/customNode/components/CustomNode.tsx:
--------------------------------------------------------------------------------
1 | import { Handle, Position } from '@ant-design/pro-flow';
2 | import { FC } from 'react';
3 | import useStyles from './custom.style';
4 |
5 | interface PipeNodeChild {
6 | title: string;
7 | logo?: string;
8 | id: string;
9 | }
10 |
11 | interface PipeNode {
12 | title: string;
13 | logo: string;
14 | des?: string;
15 | children?: PipeNodeChild[];
16 | }
17 |
18 | const CustomNode: FC<{
19 | data: PipeNode;
20 | }> = ({ data }) => {
21 | const { title, des, logo, children = [] } = data;
22 | const { styles } = useStyles();
23 |
24 | return (
25 |
26 |
27 |
28 |
29 |

30 |
31 |
{title}
32 |
33 |

39 |
40 |
41 | {children.length > 0 && (
42 |
43 | {children.map((item, index) => (
44 | <>
45 |
46 |
54 |
55 | {item.logo && (
56 |
57 |

58 |
59 | )}
60 |
61 |
64 |
72 |
73 | >
74 | ))}
75 |
76 | )}
77 |
78 | {des &&
{des}
}
79 |
80 |
81 | );
82 | };
83 |
84 | export default CustomNode;
85 |
--------------------------------------------------------------------------------
/docs/guide/demos/customNode/components/custom.style.ts:
--------------------------------------------------------------------------------
1 | import { createStyles } from 'antd-style';
2 |
3 | const useStyles = createStyles(() => {
4 | return {
5 | techUIpipeNodeWrap: {
6 | width: '260px',
7 | minHeight: '100px',
8 | backgroundColor: '#f6f8fa',
9 | padding: '12px 6px',
10 | boxSizing: 'border-box',
11 | borderRadius: '8px',
12 | },
13 |
14 | pipeNode: {
15 | width: '100%',
16 | borderRadius: '4px',
17 | backgroundColor: '#fff',
18 | paddingBottom: '12px',
19 | },
20 | mainBox: {
21 | width: '100%',
22 | padding: '12px',
23 | height: '45px',
24 | backgroundColor: 'white',
25 | display: 'flex',
26 | borderBottom: 'none',
27 | borderRadius: '8px',
28 | boxSizing: 'border-box',
29 | position: 'relative',
30 | },
31 | logo: {
32 | display: 'flex',
33 | alignItems: 'center',
34 | img: { width: '16px', height: '16px' },
35 | },
36 | title: {
37 | marginLeft: '8px',
38 | fontSize: '14px',
39 | fontWeight: '500',
40 | color: '#000',
41 | lineHeight: '22px',
42 | whiteSpace: 'nowrap',
43 | },
44 | lineTitle: {
45 | marginLeft: '8px',
46 | fontSize: '14px',
47 | fontWeight: '500',
48 | color: '#000',
49 | lineHeight: '22px',
50 | whiteSpace: 'nowrap',
51 | },
52 | subLogo: {
53 | position: 'absolute',
54 | right: '6px',
55 | top: '8px',
56 | img: { width: '80px', height: '25px' },
57 | },
58 | des: {
59 | marginTop: '16px',
60 | color: '#00000073',
61 | fontSize: '10px',
62 | },
63 | children: {
64 | display: 'flex',
65 | flexDirection: 'column',
66 | alignItems: 'center',
67 | paddingBottom: '8px',
68 | },
69 | childrenBox: {
70 | width: '220px',
71 | height: '30px',
72 | paddingLeft: '5px',
73 | backgroundColor: 'white',
74 | display: 'flex',
75 | alignItems: 'center',
76 | border: '1px solid #f1f1f1',
77 | borderRadius: '3px',
78 | boxSizing: 'border-box',
79 | marginTop: '3px',
80 | position: 'relative',
81 | },
82 | wrap: {
83 | marginLeft: '8px',
84 | display: 'flex',
85 | flexDirection: 'column',
86 | },
87 | container: {
88 | width: '100%',
89 | height: '600px',
90 | },
91 | };
92 | });
93 | export default useStyles;
94 |
--------------------------------------------------------------------------------
/docs/guide/demos/customNode/css/index.style.ts:
--------------------------------------------------------------------------------
1 | import { createStyles } from 'antd-style';
2 |
3 | const useStyles = createStyles(() => {
4 | return {
5 | wrap: {
6 | width: '200px',
7 | height: '83px',
8 | backgroundColor: 'white',
9 | display: 'flex',
10 | alignItems: 'center',
11 | justifyContent: 'center',
12 | },
13 | container: {
14 | width: '100%',
15 | height: '300px',
16 | },
17 | };
18 | });
19 | export default useStyles;
20 |
--------------------------------------------------------------------------------
/docs/guide/demos/customNode/css/multi.style.ts:
--------------------------------------------------------------------------------
1 | import { createStyles } from 'antd-style';
2 |
3 | const useStyles = createStyles(() => {
4 | return {
5 | wrap: {
6 | width: '200px',
7 | height: '83px',
8 | backgroundColor: 'white',
9 | display: 'flex',
10 | alignItems: 'center',
11 | justifyContent: 'center',
12 | },
13 | default: {
14 | border: 'none',
15 | },
16 | select: {
17 | border: '1px solid red',
18 | },
19 | container: {
20 | width: '100%',
21 | height: '300px',
22 | },
23 | };
24 | });
25 | export default useStyles;
26 |
--------------------------------------------------------------------------------
/docs/guide/demos/customNode/data/index.ts:
--------------------------------------------------------------------------------
1 | const nodeWidth = 250;
2 | const nodeHeight = 150;
3 |
4 | export const nodes = [
5 | {
6 | id: 'a2',
7 | type: 'customNode',
8 | width: nodeWidth,
9 | height: nodeHeight,
10 | data: {
11 | title: '项目名称1',
12 | logo: 'https://mdn.alipayobjects.com/huamei_d2ejos/afts/img/A*DR-gSJlc3J4AAAAAAAAAAAAADvl6AQ/original',
13 | des: '累计 10 个任务',
14 | children: [
15 | {
16 | id: 'a2-1',
17 | title: '实验-xxxx',
18 | logo: 'https://mdn.alipayobjects.com/huamei_d2ejos/afts/img/A*AuYvRabu9iAAAAAAAAAAAAAADvl6AQ/original',
19 | },
20 | {
21 | id: 'a2-2',
22 | title: '实验-xxxx',
23 | logo: 'https://mdn.alipayobjects.com/huamei_d2ejos/afts/img/A*AuYvRabu9iAAAAAAAAAAAAAADvl6AQ/original',
24 | },
25 | {
26 | id: 'a2-3',
27 | title: '服务-xxxx',
28 | logo: 'https://mdn.alipayobjects.com/huamei_d2ejos/afts/img/A*AuYvRabu9iAAAAAAAAAAAAAADvl6AQ/original',
29 | },
30 | ],
31 | },
32 | },
33 | ];
34 |
--------------------------------------------------------------------------------
/docs/guide/demos/customNode/index.tsx:
--------------------------------------------------------------------------------
1 | /**
2 | * compact: true
3 | * defaultShowCode: true
4 | */
5 | import { FlowView } from '@ant-design/pro-flow';
6 | import CustomNode from './components/CustomNode';
7 | import useStyles from './css/index.style';
8 | import { nodes } from './data/index';
9 |
10 | const nodeTypes = { customNode: CustomNode };
11 |
12 | function App() {
13 | const { styles } = useStyles();
14 |
15 | return (
16 |
17 |
18 |
19 | );
20 | }
21 |
22 | export default App;
23 |
--------------------------------------------------------------------------------
/docs/guide/demos/customNode/multiHandle.tsx:
--------------------------------------------------------------------------------
1 | /**
2 | * compact: true
3 | * defaultShowCode: true
4 | */
5 | import { FlowView } from '@ant-design/pro-flow';
6 | import CustomNode from './components/CustomNode';
7 | import useStyles from './css/index.style';
8 | import { edges, nodes } from './data/multi';
9 |
10 | const nodeTypes = { customNode: CustomNode };
11 |
12 | function App() {
13 | const { styles } = useStyles();
14 |
15 | return (
16 |
17 |
18 |
19 | );
20 | }
21 |
22 | export default App;
23 |
--------------------------------------------------------------------------------
/docs/guide/demos/flowEditor/EditorNode.tsx:
--------------------------------------------------------------------------------
1 | import { EditNode, Handle, Position } from '@ant-design/pro-flow';
2 | import { FC } from 'react';
3 | import useStyles from './css/index.style';
4 |
5 | export const EditorNode: FC = (node: any) => {
6 | const { handles, id, selected } = node;
7 | const { styles, cx } = useStyles();
8 |
9 | return (
10 |
11 |
16 |
17 |
18 |
19 |
24 |
25 | );
26 | };
27 |
--------------------------------------------------------------------------------
/docs/guide/demos/flowEditor/StringNode.tsx:
--------------------------------------------------------------------------------
1 | import { Handle, Position } from '@ant-design/pro-flow';
2 | import { FC } from 'react';
3 | import useStyles from './css/index.style';
4 |
5 | export const StringRender: FC = (node: any) => {
6 | const { handles, id, selected } = node;
7 | const { styles, cx } = useStyles();
8 |
9 | return (
10 |
11 |
16 | {node.data.title}
17 |
22 |
23 | );
24 | };
25 |
--------------------------------------------------------------------------------
/docs/guide/demos/flowEditor/base.tsx:
--------------------------------------------------------------------------------
1 | /**
2 | * compact: true
3 | * defaultShowCode: true
4 | */
5 | import { FlowEditor, FlowEditorProvider, useFlowEditor } from '@ant-design/pro-flow';
6 | import { useEffect } from 'react';
7 | import { StringRender } from './StringNode';
8 | import useStyles from './css/index.style';
9 |
10 | const ProFlowDemo = () => {
11 | const editor = useFlowEditor();
12 | const { styles } = useStyles();
13 |
14 | useEffect(() => {
15 | editor.addNode({
16 | id: 'a1',
17 | type: 'StringNode',
18 | position: { x: 0, y: 100 },
19 | data: {
20 | title: 'String Node',
21 | handles: {},
22 | },
23 | });
24 | }, [editor]);
25 |
26 | return (
27 |
28 |
29 |
30 | );
31 | };
32 |
33 | const FlowDemo = () => {
34 | return (
35 |
36 |
37 |
38 | );
39 | };
40 |
41 | export default FlowDemo;
42 |
--------------------------------------------------------------------------------
/docs/guide/demos/flowEditor/btnGroup.tsx:
--------------------------------------------------------------------------------
1 | import { useFlowEditor } from '@ant-design/pro-flow';
2 | import { useCallback, useEffect, useState } from 'react';
3 |
4 | export const BtnGroup = () => {
5 | const editor = useFlowEditor();
6 | const [count, setCount] = useState(0);
7 |
8 | const addMockNode = useCallback(() => {
9 | if (editor) {
10 | const id = Math.random();
11 |
12 | editor.addNode({
13 | id: `a${id}`,
14 | type: 'StringNode',
15 | position: { x: count * 200, y: 100 },
16 | data: {
17 | title: 'String Node',
18 | handles: {
19 | source: 'a1-source',
20 | target: 'a1-target',
21 | },
22 | },
23 | });
24 |
25 | setCount((c) => c + 1);
26 | }
27 | }, [editor, count]);
28 |
29 | useEffect(() => {
30 | if (editor) {
31 | addMockNode();
32 | }
33 | }, []);
34 |
35 | return (
36 | <>
37 |
44 |
51 |
52 |
53 |
62 |
63 |
64 |
65 | >
66 | );
67 | };
68 |
--------------------------------------------------------------------------------
/docs/guide/demos/flowEditor/css/dragAddNode.style.ts:
--------------------------------------------------------------------------------
1 | import { createStyles } from 'antd-style';
2 |
3 | const useStyles = createStyles(() => {
4 | return {
5 | container: {
6 | width: '100%',
7 | height: '600px',
8 | display: 'flex',
9 | flexDirection: 'column',
10 | },
11 | aside: {
12 | marginBottom: '10px',
13 | },
14 | description: {
15 | marginBottom: '10px',
16 | },
17 | dndnode: {
18 | height: '20px',
19 | padding: '4px',
20 | border: '1px solid #1a192b',
21 | borderRadius: '2px',
22 | marginBottom: '10px',
23 | display: 'flex',
24 | justifyContent: 'center',
25 | alignItems: 'center',
26 | cursor: 'grab',
27 | },
28 | 'dndnode.input': {
29 | borderColor: '#0041d0',
30 | },
31 | 'dndnode.output': {
32 | borderColor: '#ff0072',
33 | },
34 | 'reactflow-wrapper': {
35 | flexGrow: '1',
36 | height: '100%',
37 | },
38 | selectall: {
39 | marginTop: '10px',
40 | },
41 | };
42 | });
43 | export default useStyles;
44 |
--------------------------------------------------------------------------------
/docs/guide/demos/flowEditor/css/index.style.ts:
--------------------------------------------------------------------------------
1 | import { createStyles } from 'antd-style';
2 |
3 | const useStyles = createStyles(() => {
4 | return {
5 | container: {
6 | width: '100%',
7 | height: '600px',
8 | },
9 | stringNode: {
10 | width: '200px',
11 | height: '50px',
12 | textAlign: 'center',
13 | backgroundColor: 'white',
14 | border: '1px solid aqua',
15 | borderRadius: '4px',
16 | lineHeight: '50px',
17 | },
18 | selected: {
19 | border: '1px solid #007bff',
20 | },
21 | editNode: {
22 | width: '400px',
23 | },
24 | };
25 | });
26 | export default useStyles;
27 |
--------------------------------------------------------------------------------
/docs/guide/demos/flowEditor/css/probase.style.ts:
--------------------------------------------------------------------------------
1 | import { createStyles } from 'antd-style';
2 |
3 | const useStyles = createStyles(() => {
4 | return {
5 | container: {
6 | width: '100%',
7 | height: '600px',
8 | button: {
9 | width: '100px',
10 | height: '30px',
11 | lineHeight: '24px',
12 | boxSizing: 'border-box',
13 | textAlign: 'center',
14 | userSelect: 'none',
15 | marginRight: '10px',
16 | marginTop: '10px',
17 | },
18 | },
19 | };
20 | });
21 | export default useStyles;
22 |
--------------------------------------------------------------------------------
/docs/guide/demos/flowEditor/css/sidebar.style.ts:
--------------------------------------------------------------------------------
1 | import { createStyles } from 'antd-style';
2 |
3 | const useStyles = createStyles(() => {
4 | return {
5 | aside: {
6 | borderRight: '1px solid #eee',
7 | padding: '15px 10px',
8 | fontSize: '12px',
9 | background: '#fcfcfc',
10 | marginBottom: '10px',
11 | },
12 | description: {
13 | marginBottom: '10px',
14 | },
15 | dndnode: {
16 | height: '20px',
17 | padding: '4px',
18 | border: '1px solid #1a192b',
19 | borderRadius: '2px',
20 | marginBottom: '10px',
21 | display: 'flex',
22 | justifyContent: 'center',
23 | alignItems: 'center',
24 | cursor: 'grab',
25 | },
26 | 'dndnode.input': {
27 | borderColor: '#0041d0',
28 | },
29 | 'dndnode.output': {
30 | borderColor: '#ff0072',
31 | },
32 | 'reactflow-wrapper': {
33 | flexGrow: '1',
34 | height: '100%',
35 | },
36 | selectall: {
37 | marginTop: '10px',
38 | },
39 | };
40 | });
41 | export default useStyles;
42 |
--------------------------------------------------------------------------------
/docs/guide/demos/flowEditor/editFlowDemo.tsx:
--------------------------------------------------------------------------------
1 | /**
2 | * compact: true
3 | * defaultShowCode: true
4 | */
5 | import { FlowEditor, FlowEditorProvider, useFlowEditor } from '@ant-design/pro-flow';
6 | import { EditorNode } from './EditorNode';
7 | import useStyles from './css/index.style';
8 |
9 | import { useEffect } from 'react';
10 |
11 | const nodeTypes = { editorNode: EditorNode };
12 |
13 | const ProFlowDemo = () => {
14 | const editor = useFlowEditor();
15 | const { styles } = useStyles();
16 |
17 | useEffect(() => {
18 | editor.addNode({
19 | id: 'a1',
20 | title: '123',
21 | type: 'editorNode',
22 | position: { x: 200, y: 100 },
23 | });
24 | }, [editor]);
25 |
26 | return (
27 |
28 |
29 |
30 | );
31 | };
32 |
33 | const FlowDemo = () => {
34 | return (
35 |
36 |
37 |
38 | );
39 | };
40 |
41 | export default FlowDemo;
42 |
--------------------------------------------------------------------------------
/docs/guide/demos/flowEditor/proBase.tsx:
--------------------------------------------------------------------------------
1 | /**
2 | * compact: true
3 | * defaultShowCode: true
4 | */
5 | import { FlowEditor, FlowEditorProvider, FlowPanel } from '@ant-design/pro-flow';
6 | import { StringRender } from './StringNode';
7 | import { BtnGroup } from './btnGroup';
8 | import useStyles from './css/probase.style';
9 |
10 | const ProFlowDemo = () => {
11 | const { styles } = useStyles();
12 |
13 | return (
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 | );
22 | };
23 |
24 | const FlowDemo = () => {
25 | return (
26 |
27 |
28 |
29 | );
30 | };
31 |
32 | export default FlowDemo;
33 |
--------------------------------------------------------------------------------
/docs/guide/demos/flowEditor/sidebar.tsx:
--------------------------------------------------------------------------------
1 | import useStyles from './css/sidebar.style.ts';
2 |
3 | export default () => {
4 | const { styles, cx } = useStyles();
5 |
6 | const onDragStart = (event, nodeType) => {
7 | event.dataTransfer.setData('application/reactflow', nodeType);
8 | event.dataTransfer.effectAllowed = 'move';
9 | };
10 |
11 | return (
12 |
13 |
您可以将这些节点拖到上面的画布中
14 |
onDragStart(event, 'StringNode')}
17 | draggable
18 | >
19 | String Node
20 |
21 |
onDragStart(event, 'BasicNode')}
24 | draggable
25 | >
26 | BasicNode Node
27 |
28 |
onDragStart(event, 'EditNode')}
31 | draggable
32 | >
33 | EditNode Node
34 |
35 |
36 | );
37 | };
38 |
--------------------------------------------------------------------------------
/docs/guide/demos/flowViewIntro/autoFlow.tsx:
--------------------------------------------------------------------------------
1 | /**
2 | * compact: true
3 | * defaultShowCode: true
4 | */
5 | import { FlowView } from '@ant-design/pro-flow';
6 | import useStyles from './css/index.style';
7 | import { edges, nodes } from './data/data2.tsx';
8 |
9 | function App() {
10 | const { styles } = useStyles();
11 |
12 | return (
13 |
14 | {
18 | console.log(edge);
19 | }}
20 | >
21 |
22 | );
23 | }
24 |
25 | export default App;
26 |
--------------------------------------------------------------------------------
/docs/guide/demos/flowViewIntro/baseMiniMap.tsx:
--------------------------------------------------------------------------------
1 | /**
2 | * compact: true
3 | * defaultShowCode: true
4 | */
5 | import { FlowView } from '@ant-design/pro-flow';
6 | import useStyles from './css/index.style';
7 | import { nodes } from './data/data';
8 |
9 | function App() {
10 | const { styles } = useStyles();
11 |
12 | return (
13 |
14 |
15 |
16 | );
17 | }
18 |
19 | export default App;
20 |
--------------------------------------------------------------------------------
/docs/guide/demos/flowViewIntro/css/index.style.ts:
--------------------------------------------------------------------------------
1 | import { createStyles } from 'antd-style';
2 |
3 | const useStyles = createStyles(() => {
4 | return {
5 | container: {
6 | width: '100%',
7 | height: '400px',
8 | },
9 | };
10 | });
11 | export default useStyles;
12 |
--------------------------------------------------------------------------------
/docs/guide/demos/flowViewIntro/data/data.ts:
--------------------------------------------------------------------------------
1 | export const nodes = [
2 | {
3 | id: 'a1',
4 | data: {
5 | title: 'XXX_API_a1',
6 | logo: 'https://mdn.alipayobjects.com/huamei_ntgeqc/afts/img/A*kgyiRKi04eUAAAAAAAAAAAAADvuvAQ/original',
7 | describe: 'XXX_XXX_XXX_API',
8 | },
9 | },
10 | ];
11 |
--------------------------------------------------------------------------------
/docs/guide/demos/flowViewIntro/data/data2.tsx:
--------------------------------------------------------------------------------
1 | export const nodes = [
2 | {
3 | id: 'a1',
4 | data: {
5 | title: 'XXX_API_a1',
6 | logo: 'https://mdn.alipayobjects.com/huamei_ntgeqc/afts/img/A*kgyiRKi04eUAAAAAAAAAAAAADvuvAQ/original',
7 | describe: 'XXX_XXX_XXX_API',
8 | },
9 | },
10 | {
11 | id: 'a2',
12 | data: {
13 | title: 'XXX_API_a2',
14 | logo: 'https://mdn.alipayobjects.com/huamei_ntgeqc/afts/img/A*kgyiRKi04eUAAAAAAAAAAAAADvuvAQ/original',
15 | describe: 'XXX_XXX_XXX_API',
16 | },
17 | },
18 | {
19 | id: 'a3',
20 | data: {
21 | title: 'XXX_API_a3',
22 | logo: 'https://mdn.alipayobjects.com/huamei_ntgeqc/afts/img/A*kgyiRKi04eUAAAAAAAAAAAAADvuvAQ/original',
23 | describe: 'XXX_XXX_XXX_API',
24 | },
25 | },
26 | ];
27 | export const edges = [
28 | {
29 | id: 'a1-a2',
30 | source: 'a1',
31 | target: 'a2',
32 | label: 1231123
,
33 | },
34 | {
35 | id: 'a1-a3',
36 | source: 'a1',
37 | target: 'a3',
38 | type: 'radius',
39 | label: 1231123
,
40 | },
41 | ];
42 |
--------------------------------------------------------------------------------
/docs/guide/demos/flowViewIntro/data/data3.ts:
--------------------------------------------------------------------------------
1 | export const nodes = [
2 | {
3 | id: 'a1',
4 | position: {
5 | x: 0,
6 | y: 0,
7 | },
8 | data: {
9 | title: 'XXX_API_a1',
10 | logo: 'https://mdn.alipayobjects.com/huamei_ntgeqc/afts/img/A*kgyiRKi04eUAAAAAAAAAAAAADvuvAQ/original',
11 | describe: 'XXX_XXX_XXX_API',
12 | },
13 | },
14 | {
15 | id: 'a2',
16 | position: {
17 | x: 500,
18 | y: -200,
19 | },
20 | data: {
21 | title: 'XXX_API_a2',
22 | logo: 'https://mdn.alipayobjects.com/huamei_ntgeqc/afts/img/A*kgyiRKi04eUAAAAAAAAAAAAADvuvAQ/original',
23 | describe: 'XXX_XXX_XXX_API',
24 | },
25 | },
26 | {
27 | id: 'a3',
28 | position: {
29 | x: 500,
30 | y: 200,
31 | },
32 | data: {
33 | title: 'XXX_API_a3',
34 | logo: 'https://mdn.alipayobjects.com/huamei_ntgeqc/afts/img/A*kgyiRKi04eUAAAAAAAAAAAAADvuvAQ/original',
35 | describe: 'XXX_XXX_XXX_API',
36 | },
37 | },
38 | ];
39 | export const edges = [
40 | {
41 | id: 'a1-a2',
42 | source: 'a1',
43 | type: 'radius',
44 | target: 'a2',
45 | },
46 | {
47 | id: 'a1-a3',
48 | source: 'a1',
49 | target: 'a3',
50 | type: 'radius',
51 | },
52 | ];
53 |
--------------------------------------------------------------------------------
/docs/guide/demos/flowViewIntro/dragableNode.tsx:
--------------------------------------------------------------------------------
1 | /**
2 | * compact: true
3 | * defaultShowCode: true
4 | */
5 | import { FlowView, useNodesState } from '@ant-design/pro-flow';
6 | import useStyles from './css/index.style';
7 | import { nodes as _nodes, edges } from './data/data2';
8 |
9 | function App() {
10 | const { styles } = useStyles();
11 | const [nodes, , onNodesChange] = useNodesState(_nodes);
12 |
13 | return (
14 |
15 |
16 |
17 | );
18 | }
19 |
20 | export default App;
21 |
--------------------------------------------------------------------------------
/docs/guide/demos/flowViewIntro/groupNode.tsx:
--------------------------------------------------------------------------------
1 | /**
2 | * compact: true
3 | * defaultShowCode: true
4 | */
5 | import { FlowView } from '@ant-design/pro-flow';
6 | import useStyles from './css/index.style';
7 | // import { edges, nodes } from './data/data3';
8 |
9 | const GroupNode = () => {
10 | return (
11 |
18 | );
19 | };
20 |
21 | const nodeTypes = { GroupNode };
22 |
23 | const nodes = [
24 | {
25 | id: '1',
26 | type: 'GroupNode',
27 | position: { x: 0, y: 0 },
28 | zIndex: -1,
29 | },
30 | {
31 | id: '2',
32 | position: { x: 50, y: 10 },
33 | parentNode: '1',
34 | extent: 'parent',
35 | },
36 | {
37 | id: '3',
38 | position: { x: 50, y: 190 },
39 | parentNode: '1',
40 | extent: 'parent',
41 | },
42 | ];
43 |
44 | const edges = [
45 | {
46 | id: 'e1',
47 | source: '2',
48 | target: '3',
49 | },
50 | ];
51 |
52 | function App() {
53 | const { styles } = useStyles();
54 |
55 | return (
56 |
57 |
58 |
59 | );
60 | }
61 |
62 | export default App;
63 |
--------------------------------------------------------------------------------
/docs/guide/demos/flowViewIntro/noAutoFlow.tsx:
--------------------------------------------------------------------------------
1 | /**
2 | * compact: true
3 | * defaultShowCode: true
4 | */
5 | import { FlowView } from '@ant-design/pro-flow';
6 | import useStyles from './css/index.style';
7 | import { edges, nodes } from './data/data3';
8 |
9 | function App() {
10 | const { styles } = useStyles();
11 |
12 | return (
13 |
14 |
15 |
16 | );
17 | }
18 |
19 | export default App;
20 |
--------------------------------------------------------------------------------
/docs/guide/demos/flowViewIntro/noMiniMap.tsx:
--------------------------------------------------------------------------------
1 | /**
2 | * compact: true
3 | * defaultShowCode: true
4 | */
5 | import { FlowPanel, FlowView } from '@ant-design/pro-flow';
6 | import { useState } from 'react';
7 | import useStyles from './css/index.style';
8 | import { nodes } from './data/data';
9 |
10 | function App() {
11 | const [open, setOpen] = useState(true);
12 | const { styles } = useStyles();
13 |
14 | return (
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | );
23 | }
24 |
25 | export default App;
26 |
--------------------------------------------------------------------------------
/docs/guide/demos/quickUse/baseFlow.tsx:
--------------------------------------------------------------------------------
1 | /**
2 | * compact: true
3 | * defaultShowCode: true
4 | */
5 | import { FlowView } from '@ant-design/pro-flow';
6 | import useStyles from './css/index.style';
7 | import { edges, nodes } from './data';
8 |
9 | function App() {
10 | const { styles } = useStyles();
11 |
12 | return (
13 |
14 |
15 |
16 | );
17 | }
18 |
19 | export default App;
20 |
--------------------------------------------------------------------------------
/docs/guide/demos/quickUse/css/index.style.ts:
--------------------------------------------------------------------------------
1 | import { createStyles } from 'antd-style';
2 |
3 | const useStyles = createStyles(() => {
4 | return {
5 | container: {
6 | width: '100%',
7 | height: '600px',
8 | },
9 | };
10 | });
11 | export default useStyles;
12 |
--------------------------------------------------------------------------------
/docs/guide/demos/quickUse/data.ts:
--------------------------------------------------------------------------------
1 | export const nodes = [
2 | {
3 | id: 'a1',
4 | data: {
5 | title: 'XXX_API_a1',
6 | logo: 'https://mdn.alipayobjects.com/huamei_ntgeqc/afts/img/A*kgyiRKi04eUAAAAAAAAAAAAADvuvAQ/original',
7 | description: 'XXX_XXX_XXX_API',
8 | },
9 | },
10 | {
11 | id: 'a2',
12 | data: {
13 | title: 'XXX_API_a2',
14 | logo: 'https://mdn.alipayobjects.com/huamei_ntgeqc/afts/img/A*kgyiRKi04eUAAAAAAAAAAAAADvuvAQ/original',
15 | description: 'XXX_XXX_XXX_API',
16 | },
17 | },
18 | {
19 | id: 'a3',
20 | data: {
21 | title: 'XXX_API_a3',
22 | logo: 'https://mdn.alipayobjects.com/huamei_ntgeqc/afts/img/A*kgyiRKi04eUAAAAAAAAAAAAADvuvAQ/original',
23 | description: 'XXX_XXX_XXX_API',
24 | },
25 | },
26 | ];
27 | export const edges = [
28 | {
29 | id: 'a1-a2',
30 | source: 'a1',
31 | target: 'a2',
32 | },
33 | {
34 | id: 'a1-a3',
35 | source: 'a1',
36 | target: 'a3',
37 | type: 'radius',
38 | },
39 | ];
40 |
--------------------------------------------------------------------------------
/docs/guide/demos/quickUse/emptyFLow.tsx:
--------------------------------------------------------------------------------
1 | /**
2 | * compact: true
3 | * defaultShowCode: true
4 | */
5 | import { FlowView } from '@ant-design/pro-flow';
6 | import useStyles from './css/index.style';
7 |
8 | function App() {
9 | const { styles } = useStyles();
10 |
11 | return (
12 |
13 |
14 |
15 | );
16 | }
17 |
18 | export default App;
19 |
--------------------------------------------------------------------------------
/docs/guide/demos/quickUse/selectFlow.tsx:
--------------------------------------------------------------------------------
1 | /**
2 | * compact: true
3 | * defaultShowCode: true
4 | */
5 | import {
6 | FlowPanel,
7 | FlowView,
8 | FlowViewProvider,
9 | SelectType,
10 | useFlowViewer,
11 | } from '@ant-design/pro-flow';
12 | import { useState } from 'react';
13 | import useStyles from './css/index.style';
14 | import { edges, nodes } from './data';
15 |
16 | function App() {
17 | const { selectNode, selectEdges, selectNodes } = useFlowViewer();
18 | const [node, setNode] = useState(null);
19 | const { styles } = useStyles();
20 |
21 | return (
22 |
23 |
{
25 | setNode(node);
26 | selectNodes(['a1', 'a2', 'a3'], SelectType.SUB_SELECT);
27 | selectNode(node.id, SelectType.SELECT);
28 | selectEdges(['a1-a2', 'a1-a3'], SelectType.SUB_SELECT);
29 | }}
30 | onPaneClick={() => {
31 | setNode(null);
32 | selectNodes(['a1', 'a2', 'a3'], SelectType.DEFAULT);
33 | selectEdges(['a1-a2', 'a1-a3'], SelectType.DEFAULT);
34 | }}
35 | nodes={nodes}
36 | edges={edges}
37 | >
38 |
39 |
40 | {node ? `当前选中的是:${node.id}` : `当前没有选中节点`}
41 |
42 |
43 |
44 |
45 | );
46 | }
47 |
48 | function ProApp() {
49 | return (
50 |
51 |
52 |
53 | );
54 | }
55 |
56 | export default ProApp;
57 |
--------------------------------------------------------------------------------
/docs/guide/flowEditor.zh-CN.md:
--------------------------------------------------------------------------------
1 | ---
2 | nav:
3 | title: 快速上手
4 | order: 10
5 | group:
6 | title: 基础使用
7 | order: 3
8 | title: FlowEditor - 图编辑
9 | order: 2
10 | description:
11 | ---
12 |
13 | # FlowEditor - 图编辑组件
14 |
15 | ## 基本用法
16 |
17 | 引入 FlowEditor 组件,即可在页面上获得一块带有小地图能力的的画布。
18 |
19 | ```js
20 | import { FlowEditor } from '@ant-design/pro-flow';
21 |
22 | const editor = useFlowEditor();
23 |
24 | ;
25 | ```
26 |
27 |
28 |
29 | ## 数据受控
30 |
31 |
32 |
33 |
34 |
35 | ## 拖拽插入节点
36 |
37 |
38 |
39 | ## 可编辑节点
40 |
41 |
42 |
--------------------------------------------------------------------------------
/docs/guide/installUse.zh-CN.md:
--------------------------------------------------------------------------------
1 | ---
2 | nav:
3 | title: 快速上手
4 | order: 10
5 | group:
6 | title: 快速上手
7 | order: 2
8 | title: 3分钟快速上手
9 | order: 2
10 | description:
11 | ---
12 |
13 | # 3 分钟快速上手
14 |
15 | ## 快速入门
16 |
17 | 如果您想尽快启动并运行,那么您来对地方了! 此页面将在几分钟内带您从零到一创建一个有效的 ProFlow 应用程序。如果您想深入的了解 ProFlow 的全部内容,请查看示例,或深入了解 API 文档。
18 |
19 | ## 创建第一个 ProFlow
20 |
21 | 引入 FlowView 组件,即可在页面上获得一块带有小地图能力的的画布。
22 |
23 | ```js
24 | import { FlowView } from '@ant-design/pro-flow';
25 | ```
26 |
27 |
28 |
29 | :::warning
30 | 注意:组件必须包裹在具有宽度和高度的元素中。
31 | :::
32 |
33 | ## 添加节点和边缘数据
34 |
35 | 现在我们加入一些 nodes 和 edges 的数据。ProFlow 会提供`计算坐标`和`自动布局`的能力
36 |
37 |
39 |
40 | ## 添加交互性
41 |
42 | 我们需要一些方法来监听用户与画布的交互,比如选中节点、选中边缘以及取消选中。
43 |
44 | - onNodeClick
45 | - onEdgeClick
46 | - onPaneClick
47 |
48 | 可以通过给 node 或 edge 变更不同的 type 来快速实现主要选中,次要选中,以及不同程度的选中。
49 |
50 |
52 |
--------------------------------------------------------------------------------
/docs/guide/quickUse.zh-CN.md:
--------------------------------------------------------------------------------
1 | ---
2 | nav:
3 | title: 快速上手
4 | order: 10
5 | group:
6 | title: 快速上手
7 | order: 2
8 | title: 安装使用
9 | order: 1
10 | description:
11 | ---
12 |
13 | # 安装使用
14 |
15 | ## 创建项目 & 安装
16 |
17 | 若要在本地开始,应具备以下几点:
18 |
19 | - [Nodejs](https://nodejs.org/en) 安装
20 | - npm 或其他包管理工具,比如 [yarn](https://yarnpkg.com/) 或 [pnpm](https://pnpm.io/)
21 | - 以及 [React](https://reactjs.org/) 的基础知识
22 |
23 | 首先启动一个 React 应用,推荐使用 [umi](https://umijs.org/docs/guides/getting-started)。
24 |
25 | ```bash
26 | $ mkdir myapp && cd myapp
27 |
28 | $ pnpm dlx create-umi@latest
29 | ```
30 |
31 | ProFlow 在 npm 上发布为 [@ant-design/pro-flow](https://www.npmjs.com/package/@ant-design/pro-flow) ,推荐使用 pnpm 安装。
32 |
33 | ```bash
34 | pnpm i @ant-design/pro-flow -S
35 | ```
36 |
37 | 最后 React 服务,就可以开始了。
38 |
39 | ```bash
40 | pnpm run dev
41 | ```
42 |
43 | ## 创建第一个 ProFlow
44 |
45 | 引入 FlowView 组件,即可在页面上获得一块带有小地图能力的的画布。
46 |
47 |
48 |
49 | :::warning
50 | 注意:组件必须包裹在具有宽度和高度的元素中。
51 | :::
52 |
--------------------------------------------------------------------------------
/src/Background/components/SwimBg.tsx:
--------------------------------------------------------------------------------
1 | import { createStyles } from 'antd-style';
2 |
3 | const useStyles = createStyles(({ css }) => ({
4 | container: css`
5 | width: 100%;
6 | height: 100%;
7 | display: flex;
8 | /* flex-direction: column; */
9 | `,
10 | label: css`
11 | text-align: center;
12 | line-height: 40px;
13 | height: 40px;
14 | border-bottom: 1px solid #cbcdce;
15 | color: '#A8AAAE';
16 | `,
17 | }));
18 |
19 | export interface SwimlaneBackgroundProps {
20 | lanes: SwimLaneProps[];
21 | className?: string;
22 | style?: React.CSSProperties;
23 | }
24 |
25 | export interface SwimLaneProps {
26 | id: string;
27 | label: string;
28 | labelColor?: string;
29 | backgroundColor?: string;
30 | width?: string;
31 | style?: React.CSSProperties;
32 | }
33 |
34 | const SwimlaneBackground = (props: SwimlaneBackgroundProps) => {
35 | const { lanes, className: clm, style } = props;
36 | const { styles } = useStyles();
37 |
38 | return (
39 |
45 | {lanes.map((lane) => (
46 |
57 |
63 | {lane.label}
64 |
65 |
66 | ))}
67 |
68 | );
69 | };
70 |
71 | export default SwimlaneBackground;
72 |
--------------------------------------------------------------------------------
/src/Background/demos/double.tsx:
--------------------------------------------------------------------------------
1 | /**
2 | * compact: true
3 | */
4 | import { FlowView } from '@/index';
5 | import { createStyles } from 'antd-style';
6 | import { memo } from 'react';
7 | import Background, { BackgroundVariant } from '..';
8 |
9 | const useStyles = createStyles(({ css }) => ({
10 | container: css`
11 | width: 100%;
12 | height: 600px;
13 | `,
14 | }));
15 |
16 | const BackgroundDemo = memo(() => {
17 | const { styles } = useStyles();
18 |
19 | return (
20 |
21 |
22 |
23 |
24 |
25 |
26 | );
27 | });
28 |
29 | export default BackgroundDemo;
30 |
--------------------------------------------------------------------------------
/src/Background/demos/index.tsx:
--------------------------------------------------------------------------------
1 | /**
2 | * compact: true
3 | */
4 | import { Background, BackgroundVariant, FlowPanel, FlowView } from '@ant-design/pro-flow';
5 | import { Button } from 'antd';
6 | import { createStyles } from 'antd-style';
7 | import { memo, useState } from 'react';
8 |
9 | const useStyles = createStyles(({ css }) => ({
10 | container: css`
11 | width: 100%;
12 | height: 600px;
13 | `,
14 | }));
15 |
16 | const BackgroundDemo = memo(() => {
17 | const [variant, setVariant] = useState(BackgroundVariant.Cross);
18 | const { styles } = useStyles();
19 |
20 | return (
21 |
22 |
23 |
24 | variant:
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 | );
33 | });
34 |
35 | export default BackgroundDemo;
36 |
--------------------------------------------------------------------------------
/src/Background/demos/swim.tsx:
--------------------------------------------------------------------------------
1 | /**
2 | * compact: true
3 | */
4 | import { FlowView, SwimLaneProps, SwimlaneBackground } from '@ant-design/pro-flow';
5 | import { createStyles } from 'antd-style';
6 | import { memo } from 'react';
7 |
8 | const useStyles = createStyles(({ css }) => ({
9 | container: css`
10 | width: 100%;
11 | height: 600px;
12 | `,
13 | }));
14 |
15 | const BackgroundDemo = memo(() => {
16 | const { styles } = useStyles();
17 |
18 | return (
19 |
20 |
21 |
63 |
64 |
65 | );
66 | });
67 |
68 | export default BackgroundDemo;
69 |
--------------------------------------------------------------------------------
/src/Background/index.tsx:
--------------------------------------------------------------------------------
1 | import { CSSProperties } from 'react';
2 | import { Background, BackgroundVariant } from 'reactflow';
3 |
4 | interface BackgroundProps {
5 | variant?: BackgroundVariant;
6 | gap?: number | [number, number];
7 | size?: number;
8 | lineWidth?: number;
9 | offset?: number;
10 | color?: string;
11 | style?: CSSProperties;
12 | className?: string;
13 | id?: string;
14 | }
15 |
16 | export default (props: BackgroundProps) => {
17 | const { gap = 10, color = '#bac3d4' } = props;
18 |
19 | return ;
20 | };
21 |
22 | export { BackgroundVariant };
23 |
--------------------------------------------------------------------------------
/src/BasicGroupNode/constants.ts:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ant-design/pro-flow/db1f9ff18297f3aebe848b2627633abf228b43c9/src/BasicGroupNode/constants.ts
--------------------------------------------------------------------------------
/src/BasicGroupNode/demos/index.tsx:
--------------------------------------------------------------------------------
1 | /**
2 | * compact: true
3 | */
4 | import { FlowView, FlowViewEdge, FlowViewNode, SelectType } from '@ant-design/pro-flow';
5 | import { createStyles } from 'antd-style';
6 | import { useState } from 'react';
7 | import { edges, nodes } from './data';
8 |
9 | const useStyles = createStyles(({ css }) => ({
10 | container: css`
11 | width: 100%;
12 | height: 600px;
13 | .ant-progress-text {
14 | text-align: center !important;
15 | }
16 | `,
17 | }));
18 |
19 | const ProFlowDemo = () => {
20 | const [_nodes, setNodes] = useState(nodes);
21 | const [_edges, setEdges] = useState(edges);
22 | const { styles } = useStyles();
23 |
24 | const handleHighlight = (node: FlowViewNode) => {
25 | setNodes(
26 | _nodes.map((_node) => {
27 | if (_node.id === node.id) {
28 | _node.select = SelectType.SELECT;
29 | } else {
30 | _node.select = SelectType.SUB_SELECT;
31 | }
32 | return _node;
33 | }),
34 | );
35 | setEdges(
36 | _edges.map((edge) => {
37 | if (edge.source === node.id || edge.target === node.id) {
38 | edge.select = SelectType.SUB_SELECT;
39 | }
40 | return {
41 | ...edge,
42 | };
43 | }),
44 | );
45 | };
46 |
47 | const handleUnHighlight = () => {
48 | setNodes(
49 | _nodes.map((_node) => {
50 | _node.select = SelectType.DEFAULT;
51 | return _node;
52 | }),
53 | );
54 | setEdges(
55 | _edges.map((edge) => {
56 | edge.select = SelectType.DEFAULT;
57 | return edge;
58 | }),
59 | );
60 | };
61 |
62 | return (
63 |
64 | handleHighlight(node)}
66 | onPaneClick={handleUnHighlight}
67 | nodes={_nodes}
68 | edges={_edges}
69 | >
70 |
71 | );
72 | };
73 |
74 | const FlowDemo = () => {
75 | return ;
76 | };
77 |
78 | export default FlowDemo;
79 |
--------------------------------------------------------------------------------
/src/BasicGroupNode/index.zh-CN.md:
--------------------------------------------------------------------------------
1 | ---
2 | nav:
3 | title: 组件
4 | order: 20
5 | group:
6 | title: 节点
7 | order: 1
8 | title: BasicGroupNode 基础组节点
9 | order: 2
10 | description:
11 | ---
12 |
13 | ## Default
14 |
15 |
16 |
17 | ## API
18 |
19 | | 属性名 | 类型 | 描述 | 默认值 | 必选 |
20 | | ---- | --------------- | ---- | --- | -- |
21 | | id | `string` | 节点标题 | - | - |
22 | | data | `BasicNodeData` | 节点描述 | - | - |
23 |
24 | ### BasicNodeData
25 |
26 | | 属性名 | 类型 | 描述 | 默认值 | 必选 |
27 | | --------- | --------------------------------------------------- | ---------------------------------------------- | --- | -- |
28 | | title | `string` | 节点标题 | - | - |
29 | | describe | `string` | 节点描述 | - | - |
30 | | logo | `string` | 节点 logo 的 url | - | - |
31 | | titleSlot | `{type: 'left' \| 'right', value: React.ReactNode}` | 标题右侧的插槽,left 跟随在 title 文字结尾处,right 在 title 行尾部 | - | - |
32 |
--------------------------------------------------------------------------------
/src/BasicNode/demos/DataViewList.tsx:
--------------------------------------------------------------------------------
1 | import { createStyles } from 'antd-style';
2 | import { memo } from 'react';
3 | import BasicNode from '..';
4 |
5 | const useStyles = createStyles(({ css }) => ({
6 | container: css`
7 | width: 100%;
8 | background-color: #f1f1f1;
9 | padding: 20px 20px 5px 20px;
10 | position: relative;
11 | `,
12 |
13 | node: css`
14 | position: relative;
15 | margin-bottom: 15px;
16 | `,
17 | }));
18 |
19 | const nodeList = [
20 | {
21 | title: 'XXX数据源',
22 | logo: 'https://mdn.alipayobjects.com/huamei_ntgeqc/afts/img/A*jWDsQ5GTmHMAAAAAAAAAAAAADvuvAQ/original',
23 | description: 'cksadjfnf',
24 | },
25 | {
26 | title: 'XXX_API',
27 | logo: 'https://mdn.alipayobjects.com/huamei_ntgeqc/afts/img/A*kgyiRKi04eUAAAAAAAAAAAAADvuvAQ/original',
28 | description: 'XXX_XXX_XXX_API',
29 | },
30 | {
31 | title: 'XXXX产品',
32 | logo: 'https://mdn.alipayobjects.com/huamei_ntgeqc/afts/img/A*ezaYT4wYRBwAAAAAAAAAAAAADvuvAQ/original',
33 | description: '2031030213014',
34 | },
35 | {
36 | title: 'XXXX用户',
37 | logo: 'https://mdn.alipayobjects.com/huamei_ntgeqc/afts/img/A*CsNGRI1dY1wAAAAAAAAAAAAADvuvAQ/original',
38 | description: '23986104595',
39 | },
40 | {
41 | title: 'XXX供应商',
42 | logo: 'https://mdn.alipayobjects.com/huamei_ntgeqc/afts/img/A*wS62Qr4Ez_kAAAAAAAAAAAAADvuvAQ/original',
43 | description: '1283184585',
44 | },
45 | ];
46 |
47 | const NodeList = memo(() => {
48 | const { styles, cx } = useStyles();
49 |
50 | return (
51 |
52 | {nodeList.map((item) => {
53 | return (
54 |
63 | );
64 | })}
65 |
66 | );
67 | });
68 |
69 | export default NodeList;
70 |
--------------------------------------------------------------------------------
/src/BasicNode/demos/default/data.tsx:
--------------------------------------------------------------------------------
1 | import { FlowViewNode } from '@ant-design/pro-flow';
2 | import { Progress } from 'antd';
3 |
4 | const ApiScore: React.FC<{ score: number }> = ({ score }) => {
5 | return (
6 |