smallstonesk
4 years ago
4 changed files with 176 additions and 2 deletions
@ -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; |
|||
} |
|||
} |
@ -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…
Reference in new issue