From 74eaa6eb80827dd5061db205bcf7d5f6f40cb3a3 Mon Sep 17 00:00:00 2001 From: arvinxx Date: Wed, 17 Mar 2021 01:37:31 +0800 Subject: [PATCH] =?UTF-8?q?:sparkles:=20feat:=20=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E5=9F=BA=E7=A1=80=E8=8A=82=E7=82=B9=E5=92=8C=E5=B0=8F=E5=9C=B0?= =?UTF-8?q?=E5=9B=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../biz/examples/Mindflow/index.tsx | 25 ++++++-- docs/components/biz/mindflow.md | 4 +- packages/mindflow/package.json | 2 + packages/mindflow/src/definition/Shape.tsx | 15 ----- packages/mindflow/src/definition/graphOpts.ts | 18 ++++++ packages/mindflow/src/definition/index.ts | 1 - .../mindflow/src/definition/minimapOpts.ts | 23 +++++++ .../src/definition/shapes/MinimaNode.ts | 20 ++++++ .../mindflow/src/definition/shapes/Node.less | 36 +++++++++++ .../mindflow/src/definition/shapes/Node.tsx | 53 +++++++++++++++ .../mindflow/src/definition/shapes/index.ts | 2 + packages/mindflow/src/definition/style.less | 8 --- packages/mindflow/src/hooks/useGraph.ts | 29 ++------- packages/mindflow/src/hooks/useRegister.tsx | 6 +- packages/mindflow/src/index.tsx | 18 +++++- packages/mindflow/src/themes/color.ts | 24 +++++++ packages/mindflow/src/types.ts | 3 + packages/mindflow/src/utils/dataMap.test.ts | 25 ++++++++ packages/mindflow/src/utils/dataMap.ts | 64 +++++++++++++++++++ packages/mindflow/src/utils/index.ts | 2 + 20 files changed, 318 insertions(+), 60 deletions(-) delete mode 100644 packages/mindflow/src/definition/Shape.tsx create mode 100644 packages/mindflow/src/definition/graphOpts.ts delete mode 100644 packages/mindflow/src/definition/index.ts create mode 100644 packages/mindflow/src/definition/minimapOpts.ts create mode 100644 packages/mindflow/src/definition/shapes/MinimaNode.ts create mode 100644 packages/mindflow/src/definition/shapes/Node.less create mode 100644 packages/mindflow/src/definition/shapes/Node.tsx create mode 100644 packages/mindflow/src/definition/shapes/index.ts delete mode 100644 packages/mindflow/src/definition/style.less create mode 100644 packages/mindflow/src/themes/color.ts create mode 100644 packages/mindflow/src/utils/dataMap.test.ts create mode 100644 packages/mindflow/src/utils/dataMap.ts create mode 100644 packages/mindflow/src/utils/index.ts diff --git a/docs/components/biz/examples/Mindflow/index.tsx b/docs/components/biz/examples/Mindflow/index.tsx index b3662377..d1179a68 100644 --- a/docs/components/biz/examples/Mindflow/index.tsx +++ b/docs/components/biz/examples/Mindflow/index.tsx @@ -4,16 +4,31 @@ import Mindflow, { GraphData, MindflowData } from '@arvinxu/mindflow'; const data: GraphData = { nodes: [ { - id: 'node1', // String,可选,节点的唯一标识 - + id: 'node1', + data: { + text: '这是一个 问题 节点', + type: 'question', + }, + }, + { + id: 'node2', + data: { + text: '这是一个 思路 节点', + type: 'idea', + }, + }, + { + id: 'node3', data: { - text: 'hello', + text: '这是一个未定节点', }, }, { - id: 'node2', // String,节点的唯一标识 + id: 'node4', data: { - text: 'world', + text: + '这是一个超长文本节点: 永和九年,岁在癸丑,暮春之初,会于会稽山阴之兰亭,修稧(禊)事也。', + type: 'action', }, }, ], diff --git a/docs/components/biz/mindflow.md b/docs/components/biz/mindflow.md index 2ca17afb..625d2715 100644 --- a/docs/components/biz/mindflow.md +++ b/docs/components/biz/mindflow.md @@ -5,7 +5,9 @@ group: title: 业务组件 --- -## Mindflow +# Mindflow + +## 基础节点 diff --git a/packages/mindflow/package.json b/packages/mindflow/package.json index 2ef917af..a04b601d 100644 --- a/packages/mindflow/package.json +++ b/packages/mindflow/package.json @@ -28,6 +28,7 @@ "@antv/layout": "^0.1.6", "@antv/x6": "^1.13.1", "@antv/x6-react-shape": "^1.3.1", + "chroma-js": "^2.1.1", "dagre": "^0.8.5" }, "peerDependencies": { @@ -36,6 +37,7 @@ "react-dom": "^17.0.1" }, "devDependencies": { + "@types/chroma-js": "^2.1.3", "@types/dagre": "^0.7.44" } } diff --git a/packages/mindflow/src/definition/Shape.tsx b/packages/mindflow/src/definition/Shape.tsx deleted file mode 100644 index 4231affe..00000000 --- a/packages/mindflow/src/definition/Shape.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import type { FC } from 'react'; -import React from 'react'; -import type { ReactShape } from '@antv/x6-react-shape'; -import type { MindflowData } from '../types'; - -import './style.less'; - -interface BaseNodeProps { - node?: ReactShape; -} - -export const Shape: FC = ({ node }) => { - const data = node.getData(); - return
{data.text}
; -}; diff --git a/packages/mindflow/src/definition/graphOpts.ts b/packages/mindflow/src/definition/graphOpts.ts new file mode 100644 index 00000000..9d915746 --- /dev/null +++ b/packages/mindflow/src/definition/graphOpts.ts @@ -0,0 +1,18 @@ +import type { Options } from '@antv/x6/lib/graph/options'; +import { minimapOpts } from './minimapOpts'; + +/** + * 生成图的配置项 + * @param container + * @param minimapCtn + */ +export const graphOpts = (container, minimapCtn): Partial => ({ + container, + background: { + color: '#fafafa', + }, + panning: true, + mousewheel: true, + scroller: true, + minimap: minimapOpts(minimapCtn), +}); diff --git a/packages/mindflow/src/definition/index.ts b/packages/mindflow/src/definition/index.ts deleted file mode 100644 index 4a98ff64..00000000 --- a/packages/mindflow/src/definition/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './Shape'; diff --git a/packages/mindflow/src/definition/minimapOpts.ts b/packages/mindflow/src/definition/minimapOpts.ts new file mode 100644 index 00000000..c196f8d8 --- /dev/null +++ b/packages/mindflow/src/definition/minimapOpts.ts @@ -0,0 +1,23 @@ +import { MinimaNode } from './shapes'; + +export const minimapOpts = (container) => ({ + container, + enabled: true, + maxScale: 1, + scalable: false, + graphOptions: { + async: true, + // 用指定的 View 替换节点默认的 View + getCellView(cell) { + if (cell.isNode()) { + return MinimaNode; + } + }, + // 在小地图中不渲染边 + createCellView(cell) { + if (cell.isEdge()) { + return null; + } + }, + }, +}); diff --git a/packages/mindflow/src/definition/shapes/MinimaNode.ts b/packages/mindflow/src/definition/shapes/MinimaNode.ts new file mode 100644 index 00000000..c9899b9e --- /dev/null +++ b/packages/mindflow/src/definition/shapes/MinimaNode.ts @@ -0,0 +1,20 @@ +import { NodeView } from '@antv/x6'; + +export class MinimaNode extends NodeView { + protected renderMarkup() { + return this.renderJSONMarkup({ + tagName: 'rect', + selector: 'body', + }); + } + + update() { + super.update({ + body: { + refWidth: '100%', + refHeight: '100%', + fill: '#1890ff', + }, + }); + } +} diff --git a/packages/mindflow/src/definition/shapes/Node.less b/packages/mindflow/src/definition/shapes/Node.less new file mode 100644 index 00000000..05f7b7d6 --- /dev/null +++ b/packages/mindflow/src/definition/shapes/Node.less @@ -0,0 +1,36 @@ +.mind-node { + &-container { + position: relative; + padding: 8px; + border: 1px solid transparent; + border-left-width: 4px; + border-radius: 4px; + } + + &-title { + display: -webkit-box; + overflow: hidden; + text-overflow: ellipsis; + -webkit-box-orient: vertical; + -webkit-line-clamp: 2; // 以此类推,3行4行直接该数字就好啦 + } + + &-collapse { + position: absolute; + top: 50%; + right: -10px; + display: flex; + align-items: center; + justify-content: center; + width: 20px; + height: 20px; + background: white; + border: 1px solid transparent; + border-radius: 20px; + transform: translateY(-50%); + + &-icon { + font-size: 13px; + } + } +} diff --git a/packages/mindflow/src/definition/shapes/Node.tsx b/packages/mindflow/src/definition/shapes/Node.tsx new file mode 100644 index 00000000..3c89b980 --- /dev/null +++ b/packages/mindflow/src/definition/shapes/Node.tsx @@ -0,0 +1,53 @@ +import type { FC } from 'react'; +import React from 'react'; +import { PlusOutlined, MinusOutlined } from '@ant-design/icons'; + +import type { ReactShape } from '@antv/x6-react-shape'; +import { mapColorToHex, mapTypeToColor } from '../../utils'; +import chorma from 'chroma-js'; + +import type { MindflowData } from '../../types'; + +import './Node.less'; + +interface BaseNodeProps { + node?: ReactShape; +} + +export const Node: FC = ({ node }) => { + const data = node.getData(); + const { type, collapsed, hasChildren = true } = data; + const baseColor = chorma(mapColorToHex(mapTypeToColor(type))); + + return ( +
+
{data.text}
+ + {hasChildren ? ( +
+ {collapsed ? ( + + ) : ( + + )} +
+ ) : null} +
+ ); +}; diff --git a/packages/mindflow/src/definition/shapes/index.ts b/packages/mindflow/src/definition/shapes/index.ts new file mode 100644 index 00000000..17bbfcf5 --- /dev/null +++ b/packages/mindflow/src/definition/shapes/index.ts @@ -0,0 +1,2 @@ +export * from './Node'; +export * from './MinimaNode'; diff --git a/packages/mindflow/src/definition/style.less b/packages/mindflow/src/definition/style.less deleted file mode 100644 index 073db452..00000000 --- a/packages/mindflow/src/definition/style.less +++ /dev/null @@ -1,8 +0,0 @@ -//@import '~antd/es/style/themes/default'; - -.mind-node { - &-container { - //background: @background-color-base; - border-radius: 4px; - } -} diff --git a/packages/mindflow/src/hooks/useGraph.ts b/packages/mindflow/src/hooks/useGraph.ts index bbd2d9e0..d6bc46e6 100644 --- a/packages/mindflow/src/hooks/useGraph.ts +++ b/packages/mindflow/src/hooks/useGraph.ts @@ -1,27 +1,12 @@ import { useEffect, useRef, useState } from 'react'; import { Graph } from '@antv/x6'; +import { graphOpts } from '../definition/graphOpts'; import { useGraphRegister } from './useRegister'; -import { layout } from '../utils/layout'; - -/** - * 对数据进行预处理 - * @param data - */ -export const preprocessData = (data: any) => { - return { - ...data, - nodes: data.nodes.map((node) => ({ - ...node, - width: 80, - height: 40, - shape: 'react-shape', - component: 'test-shape', - })), - }; -}; +import { layout, preprocessData } from '../utils'; export const useGraph = (data) => { const container = useRef(); + const minimapContainer = useRef(); const [graph, setGraph] = useState(null); useGraphRegister(); @@ -32,12 +17,7 @@ export const useGraph = (data) => { if (!graph) { // 初始化画布 setGraph( - new Graph({ - container: container.current, - background: { - color: '#fafafa', - }, - }), + new Graph(graphOpts(container.current, minimapContainer.current)), ); } }, [container, graph]); @@ -53,5 +33,6 @@ export const useGraph = (data) => { return { container, graph, + minimapContainer, }; }; diff --git a/packages/mindflow/src/hooks/useRegister.tsx b/packages/mindflow/src/hooks/useRegister.tsx index 47adc673..52592a26 100644 --- a/packages/mindflow/src/hooks/useRegister.tsx +++ b/packages/mindflow/src/hooks/useRegister.tsx @@ -1,15 +1,15 @@ import '@antv/x6-react-shape'; import React from 'react'; import { Graph } from '@antv/x6'; -import { Shape } from '../definition'; +import { Node } from '../definition/shapes'; import { useEffect } from 'react'; export const useGraphRegister = () => { useEffect(() => { // 注册返回 React 组件的函数 - Graph.registerReactComponent('test-shape', ); + Graph.registerReactComponent('mind-node', ); return () => { - Graph.unregisterReactComponent('test-shape'); + Graph.unregisterReactComponent('mind-node'); }; }, []); }; diff --git a/packages/mindflow/src/index.tsx b/packages/mindflow/src/index.tsx index 9389eb34..d8625ba9 100644 --- a/packages/mindflow/src/index.tsx +++ b/packages/mindflow/src/index.tsx @@ -8,16 +8,28 @@ export * from './types'; export interface MindflowProps { data: GraphData; + /** + * 宽度 + */ + width?: number | string; + /** + * 高度 + */ + height?: number; } -const Mindflow: FC = ({ data }) => { - const { container, graph } = useGraph(data); +const Mindflow: FC = ({ data, width, height }) => { + const { container, graph, minimapContainer } = useGraph(data); console.log(graph); return ( -
+
+
); }; diff --git a/packages/mindflow/src/themes/color.ts b/packages/mindflow/src/themes/color.ts new file mode 100644 index 00000000..748382d2 --- /dev/null +++ b/packages/mindflow/src/themes/color.ts @@ -0,0 +1,24 @@ +const baseGreen = '#52c41a'; +const baseYellow = '#faad14'; +const baseRed = '#ff4d4f'; +const baseBlue = '#69c0ff'; +const baseCyan = '#5cdbd3'; +const basePurple = '#b37feb'; +const baseGray = '#8f8f8f'; + +const black009 = 'rgba(0,0,0,0.09)'; +const black002 = 'rgba(0,0,0,0.02)'; + +export const mindFlowColors = { + white: '#ffffff', + blue: baseBlue, + cyan: baseCyan, + green: baseGreen, + yellow: baseYellow, + red: baseRed, + purple: basePurple, + gray: baseGray, + black009, + shadowColor: black009, + menuHoverBg: black002, +}; diff --git a/packages/mindflow/src/types.ts b/packages/mindflow/src/types.ts index 2110ac14..f2a441e6 100644 --- a/packages/mindflow/src/types.ts +++ b/packages/mindflow/src/types.ts @@ -15,4 +15,7 @@ export interface Edge { export interface MindflowData { text: string; + type?: string; + collapsed?: boolean; + hasChildren?: boolean; } diff --git a/packages/mindflow/src/utils/dataMap.test.ts b/packages/mindflow/src/utils/dataMap.test.ts new file mode 100644 index 00000000..f8e99f4b --- /dev/null +++ b/packages/mindflow/src/utils/dataMap.test.ts @@ -0,0 +1,25 @@ +import { mapColorToHex } from './dataMap'; + +describe('getHexFromColor', () => { + it('返回红色', () => { + expect(mapColorToHex('red')).toEqual('#ff4d4f'); + expect(mapColorToHex('pink')).toEqual('#ff4d4f'); + }); + + it('返回蓝色', () => { + expect(mapColorToHex('blue')).toEqual('#69c0ff'); + expect(mapColorToHex('cyan')).toEqual('#69c0ff'); + }); + it('返回绿色', () => { + expect(mapColorToHex('green')).toEqual('#52c41a'); + }); + it('返回黄色', () => { + expect(mapColorToHex('yellow')).toEqual('#faad14'); + }); + it('返回青色', () => { + expect(mapColorToHex('teal')).toEqual('#5cdbd3'); + }); + it('返回紫色', () => { + expect(mapColorToHex('purple')).toEqual('#b37feb'); + }); +}); diff --git a/packages/mindflow/src/utils/dataMap.ts b/packages/mindflow/src/utils/dataMap.ts new file mode 100644 index 00000000..b408a102 --- /dev/null +++ b/packages/mindflow/src/utils/dataMap.ts @@ -0,0 +1,64 @@ +import { mindFlowColors } from '../themes/color'; + +/** + * 从文本色值获得 hex + * @param color + */ +export const mapColorToHex = (color: string) => { + switch (color) { + case 'blue': + case 'cyan': + return mindFlowColors.blue; + case 'teal': + return mindFlowColors.cyan; + case 'green': + return mindFlowColors.green; + case 'yellow': + return mindFlowColors.yellow; + case 'purple': + return mindFlowColors.purple; + case 'pink': + case 'red': + case 'orange': + return mindFlowColors.red; + + case 'gray': + return mindFlowColors.gray; + default: + return mindFlowColors.gray; + } +}; + +/** + * 类型 + * @param type + */ +export const mapTypeToColor = (type: string) => { + switch (type) { + case 'question': + return 'red'; + case 'action': + return 'green'; + case 'idea': + return 'yellow'; + default: + return 'grey'; + } +}; + +/** + * 对数据进行预处理 + * @param data + */ +export const preprocessData = (data: any) => { + return { + ...data, + nodes: data.nodes.map((node) => ({ + ...node, + width: 160, + height: 40, + shape: 'react-shape', + component: 'mind-node', + })), + }; +}; diff --git a/packages/mindflow/src/utils/index.ts b/packages/mindflow/src/utils/index.ts new file mode 100644 index 00000000..d766cd23 --- /dev/null +++ b/packages/mindflow/src/utils/index.ts @@ -0,0 +1,2 @@ +export * from './dataMap'; +export * from './layout';