Browse Source

feat: add node alignment into tool bar

master
smallstonesk 4 years ago
parent
commit
d9c90f9638
  1. 10
      packages/core/src/components/xIcon/index.less
  2. 3
      packages/core/src/components/xIcon/index.tsx
  3. 3
      packages/core/src/mods/toolBar/widgets/index.tsx
  4. 162
      packages/core/src/mods/toolBar/widgets/nodeAlign.tsx

10
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;
}
}

3
packages/core/src/components/xIcon/index.tsx

@ -1,5 +1,6 @@
import React from 'react'; import React from 'react';
import { createFromIconfontCN } from '@ant-design/icons'; import { createFromIconfontCN } from '@ant-design/icons';
import './index.less';
interface IconFontProps { interface IconFontProps {
type: string; type: string;
@ -7,7 +8,7 @@ interface IconFontProps {
} }
const XIcon: React.SFC<IconFontProps> = createFromIconfontCN({ const XIcon: React.SFC<IconFontProps> = createFromIconfontCN({
scriptUrl: '//at.alicdn.com/t/font_2024452_02tul6sizoy9.js', scriptUrl: '//at.alicdn.com/t/font_2024452_zgg5gvivois.js',
}); });
export default XIcon; export default XIcon;

3
packages/core/src/mods/toolBar/widgets/index.tsx

@ -13,6 +13,7 @@ import FontSize from './fontSize';
import TextColor from './textColor'; import TextColor from './textColor';
import LineStyle from './lineStyle'; import LineStyle from './lineStyle';
import Underline from './underline'; import Underline from './underline';
import NodeAlign from './nodeAlign';
import FitWindow from './fitWindow'; import FitWindow from './fitWindow';
import BringToTop from './bringToTop'; import BringToTop from './bringToTop';
import BringToBack from './bringToBack'; import BringToBack from './bringToBack';
@ -30,7 +31,7 @@ const tools: React.FC<IProps>[][] = [
[FontSize, Bold, Italic, Underline], [FontSize, Bold, Italic, Underline],
[TextColor, BgColor, BorderColor, LineStyle], [TextColor, BgColor, BorderColor, LineStyle],
[HorizontalAlign, VerticalAlign], [HorizontalAlign, VerticalAlign],
[BringToTop, BringToBack], [NodeAlign, BringToTop, BringToBack],
]; ];
export default tools; export default tools;

162
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: <XIcon type={'icon-align-left'} />,
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: <XIcon type={'icon-align-horizontal-center'} />,
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: <XIcon type={'icon-align-right'} />,
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: <XIcon type={'icon-align-top'} />,
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: <XIcon type={'icon-align-vertical-center'} />,
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: <XIcon type={'icon-align-bottom'} />,
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<IProps> = makeDropdownWidget({
tooltip: '对齐',
getIcon() {
return ALIGN_MAP['left'].icon;
},
getOverlay(flowChart: Graph, onChange: (data: any) => void) {
return (
<Menu onClick={({ key }) => onChange(key)}>
{Object.keys(ALIGN_MAP).map((alignType) => (
<MenuItem key={alignType}>
{ALIGN_MAP[alignType].icon}
<span>{ALIGN_MAP[alignType].text}</span>
</MenuItem>
))}
</Menu>
);
},
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;
Loading…
Cancel
Save