狼叔
4 years ago
committed by
GitHub
22 changed files with 423 additions and 148 deletions
@ -1,18 +0,0 @@ |
|||||
{ |
|
||||
"presets": [ |
|
||||
[ |
|
||||
"@babel/preset-env", |
|
||||
{ |
|
||||
"loose": true, |
|
||||
"modules": false |
|
||||
} |
|
||||
], |
|
||||
"@babel/preset-react", |
|
||||
"@babel/typescript" |
|
||||
], |
|
||||
"plugins": [ |
|
||||
"@babel/proposal-class-properties", |
|
||||
"@babel/proposal-object-rest-spread", |
|
||||
"@babel/plugin-transform-runtime" |
|
||||
] |
|
||||
} |
|
@ -1,3 +0,0 @@ |
|||||
{ |
|
||||
"extends": "../../.babelrc" |
|
||||
} |
|
@ -1,3 +0,0 @@ |
|||||
{ |
|
||||
"extends": "../../.babelrc" |
|
||||
} |
|
@ -0,0 +1,35 @@ |
|||||
|
import React, {useMemo} from 'react'; |
||||
|
|
||||
|
import { |
||||
|
monaco, |
||||
|
ControlledEditor, |
||||
|
ControlledEditorProps |
||||
|
} from '@monaco-editor/react'; |
||||
|
|
||||
|
import monokaiTheme from './theme-monokai'; |
||||
|
|
||||
|
monaco.init().then((monaco) => { |
||||
|
monaco.editor.defineTheme('monokai', monokaiTheme); |
||||
|
}); |
||||
|
|
||||
|
const CODE_EDITOR_OPTIONS = { |
||||
|
fontSize: 14 |
||||
|
}; |
||||
|
|
||||
|
export const CodeEditor: React.FC<ControlledEditorProps> = (props) => { |
||||
|
const {options, ...rest} = props; |
||||
|
const editorOptions = useMemo(() => { |
||||
|
return Object.assign({}, CODE_EDITOR_OPTIONS, options); |
||||
|
}, [options]); |
||||
|
|
||||
|
return ( |
||||
|
<ControlledEditor |
||||
|
theme={'monokai'} |
||||
|
language={'javascript'} |
||||
|
options={editorOptions} |
||||
|
{...rest} |
||||
|
/> |
||||
|
); |
||||
|
}; |
||||
|
|
||||
|
export default CodeEditor; |
@ -0,0 +1,144 @@ |
|||||
|
export default { |
||||
|
"base": "vs-dark", |
||||
|
"inherit": true, |
||||
|
"rules": [ |
||||
|
{ |
||||
|
"background": "272822", |
||||
|
"token": "" |
||||
|
}, |
||||
|
{ |
||||
|
"foreground": "75715e", |
||||
|
"token": "comment" |
||||
|
}, |
||||
|
{ |
||||
|
"foreground": "e6db74", |
||||
|
"token": "string" |
||||
|
}, |
||||
|
{ |
||||
|
"foreground": "ae81ff", |
||||
|
"token": "constant.numeric" |
||||
|
}, |
||||
|
{ |
||||
|
"foreground": "ae81ff", |
||||
|
"token": "constant.language" |
||||
|
}, |
||||
|
{ |
||||
|
"foreground": "ae81ff", |
||||
|
"token": "constant.character" |
||||
|
}, |
||||
|
{ |
||||
|
"foreground": "ae81ff", |
||||
|
"token": "constant.other" |
||||
|
}, |
||||
|
{ |
||||
|
"foreground": "f92672", |
||||
|
"token": "keyword" |
||||
|
}, |
||||
|
{ |
||||
|
"foreground": "f92672", |
||||
|
"token": "storage" |
||||
|
}, |
||||
|
{ |
||||
|
"foreground": "66d9ef", |
||||
|
"fontStyle": "italic", |
||||
|
"token": "storage.type" |
||||
|
}, |
||||
|
{ |
||||
|
"foreground": "a6e22e", |
||||
|
"fontStyle": "underline", |
||||
|
"token": "entity.name.class" |
||||
|
}, |
||||
|
{ |
||||
|
"foreground": "a6e22e", |
||||
|
"fontStyle": "italic underline", |
||||
|
"token": "entity.other.inherited-class" |
||||
|
}, |
||||
|
{ |
||||
|
"foreground": "a6e22e", |
||||
|
"token": "entity.name.function" |
||||
|
}, |
||||
|
{ |
||||
|
"foreground": "fd971f", |
||||
|
"fontStyle": "italic", |
||||
|
"token": "variable.parameter" |
||||
|
}, |
||||
|
{ |
||||
|
"foreground": "f92672", |
||||
|
"token": "entity.name.tag" |
||||
|
}, |
||||
|
{ |
||||
|
"foreground": "a6e22e", |
||||
|
"token": "entity.other.attribute-name" |
||||
|
}, |
||||
|
{ |
||||
|
"foreground": "66d9ef", |
||||
|
"token": "support.function" |
||||
|
}, |
||||
|
{ |
||||
|
"foreground": "66d9ef", |
||||
|
"token": "support.constant" |
||||
|
}, |
||||
|
{ |
||||
|
"foreground": "66d9ef", |
||||
|
"fontStyle": "italic", |
||||
|
"token": "support.type" |
||||
|
}, |
||||
|
{ |
||||
|
"foreground": "66d9ef", |
||||
|
"fontStyle": "italic", |
||||
|
"token": "support.class" |
||||
|
}, |
||||
|
{ |
||||
|
"foreground": "f8f8f0", |
||||
|
"background": "f92672", |
||||
|
"token": "invalid" |
||||
|
}, |
||||
|
{ |
||||
|
"foreground": "f8f8f0", |
||||
|
"background": "ae81ff", |
||||
|
"token": "invalid.deprecated" |
||||
|
}, |
||||
|
{ |
||||
|
"foreground": "cfcfc2", |
||||
|
"token": "meta.structure.dictionary.json string.quoted.double.json" |
||||
|
}, |
||||
|
{ |
||||
|
"foreground": "75715e", |
||||
|
"token": "meta.diff" |
||||
|
}, |
||||
|
{ |
||||
|
"foreground": "75715e", |
||||
|
"token": "meta.diff.header" |
||||
|
}, |
||||
|
{ |
||||
|
"foreground": "f92672", |
||||
|
"token": "markup.deleted" |
||||
|
}, |
||||
|
{ |
||||
|
"foreground": "a6e22e", |
||||
|
"token": "markup.inserted" |
||||
|
}, |
||||
|
{ |
||||
|
"foreground": "e6db74", |
||||
|
"token": "markup.changed" |
||||
|
}, |
||||
|
{ |
||||
|
"foreground": "ae81ffa0", |
||||
|
"token": "constant.numeric.line-number.find-in-files - match" |
||||
|
}, |
||||
|
{ |
||||
|
"foreground": "e6db74", |
||||
|
"token": "entity.name.filename.find-in-files" |
||||
|
} |
||||
|
], |
||||
|
"colors": { |
||||
|
"editor.foreground": "#F8F8F2", |
||||
|
"editor.background": "#272822", |
||||
|
"editor.selectionBackground": "#49483E", |
||||
|
"editor.lineHighlightBackground": "#3E3D32", |
||||
|
"editorCursor.foreground": "#F8F8F0", |
||||
|
"editorWhitespace.foreground": "#3B3A32", |
||||
|
"editorIndentGuide.activeBackground": "#9D550FB0", |
||||
|
"editor.selectionHighlightBorder": "#222218" |
||||
|
} |
||||
|
} |
@ -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; |
@ -1,3 +0,0 @@ |
|||||
{ |
|
||||
"extends": "../../.babelrc" |
|
||||
} |
|
Loading…
Reference in new issue