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