diff --git a/.eslintrc.js b/.eslintrc.js index bfd8cce..28cf5a9 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -37,7 +37,6 @@ module.exports = { 'prettier/@typescript-eslint', ], rules: { - '@typescript-eslint/no-non-null-assertion': 'off', '@typescript-eslint/no-explicit-any': 'off', 'no-param-reassign': 'off', 'import/extensions': [ diff --git a/packages/core/package-lock.json b/packages/core/package-lock.json index 13c0852..f81a506 100644 --- a/packages/core/package-lock.json +++ b/packages/core/package-lock.json @@ -27,9 +27,9 @@ } }, "@ant-design/icons-svg": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@ant-design/icons-svg/-/icons-svg-4.0.0.tgz", - "integrity": "sha512-Nai+cd3XUrv/z50gSk1FI08j6rENZ1e93rhKeLTBGwa5WrmHvhn2vowa5+voZW2qkXJn1btS6tdvTEDB90M0Pw==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@ant-design/icons-svg/-/icons-svg-4.1.0.tgz", + "integrity": "sha512-Fi03PfuUqRs76aI3UWYpP864lkrfPo0hluwGqh7NJdLhvH4iRDc3jbJqZIvRDLHKbXrvAfPPV3+zjUccfFvWOQ==", "dev": true }, "@ant-design/react-slick": { diff --git a/packages/core/package.json b/packages/core/package.json index f2b7c2b..320cd7c 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -36,6 +36,7 @@ "url": "https://github.com/suanmei/iMove/issues" }, "devDependencies": { + "@ant-design/icons": "^4.0.6", "@antv/x6": "^0.3.4", "@antv/x6-react-shape": "^0.2.2", "@emotion/core": "^10.0.28", @@ -47,6 +48,7 @@ "watch": "^1.0.2" }, "peerDependencies": { + "@ant-design/icons": "^4.0.6", "@antv/x6": "^0.3.4", "@antv/x6-react-shape": "^0.2.2", "@emotion/core": "^10.0.28", diff --git a/packages/core/src/components/toolbar.tsx b/packages/core/src/components/toolbar.tsx new file mode 100644 index 0000000..957654a --- /dev/null +++ b/packages/core/src/components/toolbar.tsx @@ -0,0 +1,72 @@ +import * as React from 'react'; +import styled from '@emotion/styled'; +import { Graph, UndoManager } from '@antv/x6'; +import { Tooltip } from 'antd'; +import { bindKey } from '../utils/tool'; +import getCommands from '../data/commands'; +import 'antd/lib/tooltip/style'; + +export interface ToolbarProps { + graph: Graph; +} + +const Group = styled.div` + position: relative; + display: inline-block; + box-sizing: border-box; + padding: 5px; + line-height: 25px; + + :first-of-type { + :before { + display: none; + } + } + + :before { + content: ''; + display: block; + position: absolute; + width: 1px; + height: 12px; + background: #d2d2d2; + top: 50%; + left: 0; + transform: translateY(-50%); + } +`; + +const Item = styled.span` + display: inline-block; + margin: 0 2px; + padding: 0 5px; + border-radius: 3px; + cursor: pointer; + + :hover { + background: #ddd; + } +`; + +const Toolbar = ({ graph }: ToolbarProps): JSX.Element => { + const undoManager = UndoManager.create(graph); + const commands = getCommands(graph, undoManager); + + bindKey(commands, graph); + + return ( +
+ {commands.map((items) => ( + i.name).join('-')}> + {items.map((item) => ( + + {item.icon} + + ))} + + ))} +
+ ); +}; + +export default Toolbar; diff --git a/packages/core/src/data/cells.ts b/packages/core/src/data/cells.ts index 6a6cd34..41e8faa 100644 --- a/packages/core/src/data/cells.ts +++ b/packages/core/src/data/cells.ts @@ -1,4 +1,4 @@ -import { Style, Point } from '@antv/x6'; +import { Style } from '@antv/x6'; import { CellTypes } from '../components/cells'; interface CellStyle extends Style { @@ -8,13 +8,12 @@ interface CellStyle extends Style { export interface DataItem { label?: string; title: string; - data?: any; width: number; height: number; scale: number; + data?: any; style: CellStyle; isEdge?: boolean; - points?: Point[]; } export const generals: DataItem[] = [ diff --git a/packages/core/src/data/commands.tsx b/packages/core/src/data/commands.tsx new file mode 100644 index 0000000..3d6b688 --- /dev/null +++ b/packages/core/src/data/commands.tsx @@ -0,0 +1,88 @@ +import * as React from 'react'; +import { + UndoOutlined, + RedoOutlined, + ZoomInOutlined, + ZoomOutOutlined, + RetweetOutlined, + FullscreenOutlined, + DeleteOutlined, + SaveOutlined, +} from '@ant-design/icons'; +import { Graph, UndoManager } from '@antv/x6'; +import * as Tool from '../utils/tool'; +import { Commands } from '../model'; + +export default function getCommands(graph: Graph, undoManager: UndoManager): Commands { + return [ + [ + { + name: 'undo', + icon: , + tooltip: '撤销', + shortcut: 'Cmd + Z', + handler: (): void => undoManager.undo(), + }, + { + name: 'redo', + icon: , + tooltip: '重做', + shortcut: 'Cmd + Shift + Z', + handler: (): void => undoManager.redo(), + }, + ], + [ + { + name: 'zoomIn', + icon: , + tooltip: '放大', + handler: Tool.zoomIn(graph), + }, + { + name: 'zoomOut', + icon: , + tooltip: '缩小', + handler: Tool.zoomOut(graph), + }, + ], + [ + { + name: 'resetView', + icon: , + tooltip: '重置视口', + handler: (): void => { + graph.zoomTo(1); + graph.center(); + }, + }, + { + name: 'fitWindow', + icon: , + tooltip: '适应窗口', + handler: (): void => { + graph.fit(8); + }, + }, + ], + [ + { + name: 'delete', + icon: , + tooltip: '删除', + shortcut: 'Delete', + handler: (): void => graph.deleteCells(), + }, + ], + [ + { + name: 'save', + icon: , + tooltip: '保存', + shortcut: 'Cmd + S', + handler: (): void => { + // code + }, + }, + ], + ]; +} diff --git a/packages/core/src/index.tsx b/packages/core/src/index.tsx index 41fe77f..7e85c0e 100644 --- a/packages/core/src/index.tsx +++ b/packages/core/src/index.tsx @@ -4,6 +4,7 @@ import { Graph, Shape, DomEvent } from '@antv/x6'; import { ReactShape } from '@antv/x6-react-shape'; import styled from '@emotion/styled'; import Sidebar from './components/sidebar'; +import Toolbar from './components/toolbar'; import createGraph from './utils/create-graph'; import 'antd/lib/layout/style'; @@ -20,14 +21,33 @@ const IMoveLayout = styled(Layout)` `; const IMoveHeader = styled(Header)` + height: 50px; background: #fff; border-bottom: 1px solid #ddd; `; +const IMoveTitle = styled.h2` + line-height: 50px; +`; + const IMoveSider = styled(Sider)` background: #fff; `; +const IMoveContent = styled(Content)` + position: relative; + padding-top: 35px; +`; + +const ToolWrapper = styled.div` + position: absolute; + top: 0; + width: 100%; + height: 35px; + line-height: 35px; + background: #fff; +`; + class Core extends React.Component<{}, CoreState> { private graph: Graph | null = null; @@ -57,13 +77,14 @@ class Core extends React.Component<{}, CoreState> { return ( -

iMove

+ iMove
{this.graph && inited && } - + + {this.graph && inited && }
- + Sider diff --git a/packages/core/src/model/commands.ts b/packages/core/src/model/commands.ts new file mode 100644 index 0000000..19de9b9 --- /dev/null +++ b/packages/core/src/model/commands.ts @@ -0,0 +1,9 @@ +interface Command { + name: string; + icon: JSX.Element; + tooltip: string; + shortcut?: string; + handler: () => void; +} + +export type Commands = Command[][]; diff --git a/packages/core/src/model/index.ts b/packages/core/src/model/index.ts new file mode 100644 index 0000000..30272ff --- /dev/null +++ b/packages/core/src/model/index.ts @@ -0,0 +1 @@ +export * from './commands'; diff --git a/packages/core/src/utils/create-graph.ts b/packages/core/src/utils/create-graph.ts index 4c7fb62..630ca16 100644 --- a/packages/core/src/utils/create-graph.ts +++ b/packages/core/src/utils/create-graph.ts @@ -6,6 +6,7 @@ export default function createGraph(container: HTMLDivElement): Graph { rotate: false, resize: false, infinite: true, + rubberband: true, backgroundColor: '#f8f9fa', grid: { type: 'dot', @@ -55,7 +56,9 @@ export default function createGraph(container: HTMLDivElement): Graph { shouldRedrawOnDataChange(): boolean { return true; }, - getAnchors(cell): [number, number][] { + getAnchors(cell): [number, number][] | null { + if (!cell || !cell.data) return null; + if (cell.data.style.type === 'start') { return [[0.5, 1]]; } diff --git a/packages/core/src/utils/patch-dnd.tsx b/packages/core/src/utils/patch-dnd.tsx index fa1c3b8..1254ce6 100644 --- a/packages/core/src/utils/patch-dnd.tsx +++ b/packages/core/src/utils/patch-dnd.tsx @@ -59,7 +59,7 @@ export default function patchDnd( component: , }); - graph.selectCell(cell); + graph.unSelectCells(graph.getSelectedCells()).selectCell(cell); }); }); } diff --git a/packages/core/src/utils/tool.ts b/packages/core/src/utils/tool.ts new file mode 100644 index 0000000..85a4c0b --- /dev/null +++ b/packages/core/src/utils/tool.ts @@ -0,0 +1,68 @@ +import { Graph } from '@antv/x6'; +import { Commands } from '../model'; + +export function zoomIn(graph: Graph) { + return (): void => { + let { scale } = graph.view; + if (scale >= 8) { + scale += 8; + } else if (scale >= 4) { + scale += 4; + } else if (scale >= 2) { + scale += 1; + } else if (scale >= 1.5) { + scale += 0.5; + } else if (scale >= 1) { + scale += 0.25; + } else if (scale >= 0.7) { + scale += 0.15; + } else if (scale >= 0.4) { + scale += 0.1; + } else if (scale >= 0.15) { + scale += 0.05; + } else if (scale >= 0.01) { + scale += 0.01; + } + graph.zoomTo(scale); + }; +} + +export function zoomOut(graph: Graph) { + return (): void => { + let { scale } = graph.view; + if (scale <= 0.15) { + scale -= 0.01; + } else if (scale <= 0.4) { + scale -= 0.05; + } else if (scale <= 0.7) { + scale -= 0.1; + } else if (scale <= 1) { + scale -= 0.15; + } else if (scale <= 1.5) { + scale -= 0.25; + } else if (scale <= 2) { + scale -= 0.5; + } else if (scale <= 4) { + scale -= 1; + } else if (scale <= 8) { + scale -= 4; + } else if (scale <= 16) { + scale -= 8; + } + graph.zoomTo(scale); + }; +} + +export function bindKey(commands: Commands, graph: Graph): void { + commands.forEach((items) => + items.forEach((item) => { + if (item.shortcut) { + if (item.shortcut === 'Delete') { + graph.bindKey(['del', 'backspace'], item.handler); + } else { + graph.bindKey(item.shortcut, item.handler); + } + } + }) + ); +}