From d9c90f9638fac98faa46e6b99bb6fb4fb4bce0f4 Mon Sep 17 00:00:00 2001 From: smallstonesk <575913857@qq.com> Date: Tue, 29 Dec 2020 13:49:08 +0800 Subject: [PATCH] feat: add node alignment into tool bar --- packages/core/src/components/xIcon/index.less | 10 ++ packages/core/src/components/xIcon/index.tsx | 3 +- .../core/src/mods/toolBar/widgets/index.tsx | 3 +- .../src/mods/toolBar/widgets/nodeAlign.tsx | 162 ++++++++++++++++++ 4 files changed, 176 insertions(+), 2 deletions(-) create mode 100644 packages/core/src/components/xIcon/index.less create mode 100644 packages/core/src/mods/toolBar/widgets/nodeAlign.tsx diff --git a/packages/core/src/components/xIcon/index.less b/packages/core/src/components/xIcon/index.less new file mode 100644 index 0000000..68118fc --- /dev/null +++ b/packages/core/src/components/xIcon/index.less @@ -0,0 +1,10 @@ +#icon-align-top, +#icon-align-left, +#icon-align-vertical-center, +#icon-align-horizontal-center, +#icon-align-bottom, +#icon-align-right { + path { + fill: currentColor; + } +} diff --git a/packages/core/src/components/xIcon/index.tsx b/packages/core/src/components/xIcon/index.tsx index 43d33f5..f06bb3d 100644 --- a/packages/core/src/components/xIcon/index.tsx +++ b/packages/core/src/components/xIcon/index.tsx @@ -1,5 +1,6 @@ import React from 'react'; import { createFromIconfontCN } from '@ant-design/icons'; +import './index.less'; interface IconFontProps { type: string; @@ -7,7 +8,7 @@ interface IconFontProps { } const XIcon: React.SFC = createFromIconfontCN({ - scriptUrl: '//at.alicdn.com/t/font_2024452_02tul6sizoy9.js', + scriptUrl: '//at.alicdn.com/t/font_2024452_zgg5gvivois.js', }); export default XIcon; diff --git a/packages/core/src/mods/toolBar/widgets/index.tsx b/packages/core/src/mods/toolBar/widgets/index.tsx index f44bb0e..c7d4c44 100644 --- a/packages/core/src/mods/toolBar/widgets/index.tsx +++ b/packages/core/src/mods/toolBar/widgets/index.tsx @@ -13,6 +13,7 @@ import FontSize from './fontSize'; import TextColor from './textColor'; import LineStyle from './lineStyle'; import Underline from './underline'; +import NodeAlign from './nodeAlign'; import FitWindow from './fitWindow'; import BringToTop from './bringToTop'; import BringToBack from './bringToBack'; @@ -30,7 +31,7 @@ const tools: React.FC[][] = [ [FontSize, Bold, Italic, Underline], [TextColor, BgColor, BorderColor, LineStyle], [HorizontalAlign, VerticalAlign], - [BringToTop, BringToBack], + [NodeAlign, BringToTop, BringToBack], ]; export default tools; diff --git a/packages/core/src/mods/toolBar/widgets/nodeAlign.tsx b/packages/core/src/mods/toolBar/widgets/nodeAlign.tsx new file mode 100644 index 0000000..78a25f8 --- /dev/null +++ b/packages/core/src/mods/toolBar/widgets/nodeAlign.tsx @@ -0,0 +1,162 @@ +import React, { ReactElement } from 'react'; + +import 'antd/es/menu/style'; + +import { Menu } from 'antd'; +import { Cell, Graph } from '@antv/x6'; +import XIcon from '../../../components/xIcon'; +import makeDropdownWidget from './common/makeDropdownWidget'; +import { getSelectedNodes } from '../../../utils/flowChartUtils'; + +interface IProps { + flowChart: Graph; +} + +interface AlignItem { + text: string; + icon: ReactElement; + handler: (selectedNodes: Cell[]) => void; +} + +const MenuItem = Menu.Item; +const ALIGN_MAP: { [key: string]: AlignItem } = { + left: { + text: '左对齐', + icon: , + handler(selectedNodes: Cell[]) { + let minLeftValue = Infinity; + selectedNodes.forEach(node => { + const { x } = node.getProp<{ x: number, y: number }>('position'); + if (x < minLeftValue) { + minLeftValue = x; + } + }); + selectedNodes.forEach(node => node.setProp({ position: { x: minLeftValue } })); + } + }, + horizontalCenter: { + text: '水平居中', + icon: , + handler(selectedNodes: Cell[]) { + let minLeftValue = Infinity; + let maxRightValue = -Infinity; + selectedNodes.forEach(node => { + const { x } = node.getProp<{ x: number, y: number }>('position'); + const { width } = node.getProp<{ width: number, height: number }>('size'); + if (x < minLeftValue) { + minLeftValue = x; + } + if (x + width > maxRightValue) { + maxRightValue = x + width; + } + }); + const centerValue = (minLeftValue + maxRightValue) / 2; + selectedNodes.forEach(node => { + const { width } = node.getProp<{ width: number, height: number }>('size'); + node.setProp({ position: { x: centerValue - width / 2 } }); + }); + } + }, + right: { + text: '右对齐', + icon: , + handler(selectedNodes: Cell[]) { + let maxRightValue = -Infinity; + selectedNodes.forEach(node => { + const { x } = node.getProp<{ x: number, y: number }>('position'); + const { width } = node.getProp<{ width: number, height: number }>('size'); + if (x + width > maxRightValue) { + maxRightValue = x + width; + } + }); + selectedNodes.forEach(node => { + const { width } = node.getProp<{ width: number, height: number }>('size'); + node.setProp({ position: { x: maxRightValue - width } }) + }); + } + }, + top: { + text: '顶部对齐', + icon: , + handler(selectedNodes: Cell[]) { + let minTopValue = Infinity; + selectedNodes.forEach(node => { + const { y } = node.getProp<{ x: number, y: number }>('position'); + if (y < minTopValue) { + minTopValue = y; + } + }); + selectedNodes.forEach(node => node.setProp({ position: { y: minTopValue } })); + } + }, + verticalCenter: { + text: '垂直居中', + icon: , + handler(selectedNodes: Cell[]) { + let minTopValue = Infinity; + let maxBottomValue = -Infinity; + selectedNodes.forEach(node => { + const { y } = node.getProp<{ x: number, y: number }>('position'); + const { height } = node.getProp<{ width: number, height: number }>('size'); + if (y < minTopValue) { + minTopValue = y; + } + if (y + height > maxBottomValue) { + maxBottomValue = y + height; + } + }); + const centerValue = (minTopValue + maxBottomValue) / 2; + selectedNodes.forEach(node => { + const { height } = node.getProp<{ width: number, height: number }>('size'); + node.setProp({ position: { y: centerValue - height / 2 } }); + }); + } + }, + bottom: { + text: '底部对齐', + icon: , + handler(selectedNodes: Cell[]) { + let maxTopValue = -Infinity; + selectedNodes.forEach(node => { + const { y } = node.getProp<{ x: number, y: number }>('position'); + const { height } = node.getProp<{ width: number, height: number }>('size'); + if (y + height > maxTopValue) { + maxTopValue = y + height; + } + }); + selectedNodes.forEach(node => { + const { height } = node.getProp<{ width: number, height: number }>('size'); + node.setProp({ position: { y: maxTopValue - height } }) + }); + } + } +}; + +const NodeAlign: React.FC = makeDropdownWidget({ + tooltip: '对齐', + getIcon() { + return ALIGN_MAP['left'].icon; + }, + getOverlay(flowChart: Graph, onChange: (data: any) => void) { + return ( + onChange(key)}> + {Object.keys(ALIGN_MAP).map((alignType) => ( + + {ALIGN_MAP[alignType].icon} + {ALIGN_MAP[alignType].text} + + ))} + + ); + }, + handler: (flowChart: Graph, value: any) => { + const selectedNodes = getSelectedNodes(flowChart); + ALIGN_MAP[value].handler(selectedNodes); + }, + disabled(flowChart: Graph) { + const selectedNodes = getSelectedNodes(flowChart); + return selectedNodes.length < 2; + }, +}); + +export default NodeAlign;