* feat: Add the `GGEditor` demo * style: Update the size of the card * feat: Extract the `MenuItem` and `ToolbarButton` components * feat: Add the shape type of the line * feat: Extract the `DetailForm` components * feat: add PageHeader * fix: add all local laug * fix: Fix the height of the editorpull/3827/head
|
After Width: | Height: | Size: 2.8 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 8.6 KiB |
|
After Width: | Height: | Size: 8.6 KiB |
|
After Width: | Height: | Size: 7.5 KiB |
|
After Width: | Height: | Size: 8.6 KiB |
@ -0,0 +1,11 @@ |
|||
export default { |
|||
'app.editor.flow.title': 'Flowchart Editor', |
|||
'app.editor.flow.description': |
|||
'The flow chart is an excellent way to represent the idea of the algorithm.', |
|||
'app.editor.koni.title': 'Koni Editor', |
|||
'app.editor.koni.description': |
|||
'The topology diagram refers to the network structure diagram composed of network node devices and communication media.', |
|||
'app.editor.mind.title': 'Mind Map Editor', |
|||
'app.editor.mind.description': |
|||
'The brain map is an effective graphical thinking tool for expressing divergent thinking. It is simple but effective and is a practical thinking tool.', |
|||
}; |
|||
@ -0,0 +1,11 @@ |
|||
export default { |
|||
'app.editor.flow.title': 'Editor de diagrama de flujo', |
|||
'app.editor.flow.description': |
|||
'El diagrama de flujo es una excelente manera de representar la idea del algoritmo.', |
|||
'app.editor.koni.title': 'Editor de topologia', |
|||
'app.editor.koni.description': |
|||
'El diagrama de topología se refiere al diagrama de estructura de red compuesto por dispositivos de nodo de red y medios de comunicación.', |
|||
'app.editor.mind.title': 'Editor de mapas cerebrales', |
|||
'app.editor.mind.description': |
|||
'El mapa cerebral es una herramienta de pensamiento gráfico eficaz para expresar el pensamiento divergente. Es simple pero efectivo y es una herramienta de pensamiento práctico.', |
|||
}; |
|||
@ -0,0 +1,9 @@ |
|||
export default { |
|||
'app.editor.flow.title': '流程图编辑器', |
|||
'app.editor.flow.description': '千言万语不如一张图,流程图是表示算法思路的好方法', |
|||
'app.editor.koni.title': '拓扑编辑器', |
|||
'app.editor.koni.description': '拓扑结构图是指由网络节点设备和通信介质构成的网络结构图', |
|||
'app.editor.mind.title': '脑图编辑器', |
|||
'app.editor.mind.description': |
|||
'脑图是表达发散性思维的有效图形思维工具 ,它简单却又很有效,是一种实用性的思维工具。', |
|||
}; |
|||
@ -0,0 +1,9 @@ |
|||
export default { |
|||
'app.editor.flow.title': '流程圖編輯器', |
|||
'app.editor.flow.description': '千言萬語不如一張圖,流程圖是表示算法思路的好方法', |
|||
'app.editor.koni.title': '拓撲編輯器', |
|||
'app.editor.koni.description': '拓撲結構圖是指由網絡節點設備和通信介質構成的網絡結構圖', |
|||
'app.editor.mind.title': '腦圖編輯器', |
|||
'app.editor.mind.description': |
|||
'腦圖是表達發散性思維的有效圖形思維工具 ,它簡單卻又很有效,是一種實用性的思維工具', |
|||
}; |
|||
@ -0,0 +1,43 @@ |
|||
import React from 'react'; |
|||
import { Row, Col } from 'antd'; |
|||
import GGEditor, { Flow } from 'gg-editor'; |
|||
import EditorMinimap from '../components/EditorMinimap'; |
|||
import { FlowContextMenu } from '../components/EditorContextMenu'; |
|||
import { FlowToolbar } from '../components/EditorToolbar'; |
|||
import { FlowItemPanel } from '../components/EditorItemPanel'; |
|||
import { FlowDetailPanel } from '../components/EditorDetailPanel'; |
|||
import styles from './index.less'; |
|||
import { FormattedMessage } from 'umi/locale'; |
|||
import PageHeaderWrapper from '@/components/PageHeaderWrapper'; |
|||
|
|||
const FlowPage = () => { |
|||
return ( |
|||
<PageHeaderWrapper |
|||
title={<FormattedMessage id="app.editor.flow.title" />} |
|||
content={<FormattedMessage id="app.editor.flow.description" />} |
|||
> |
|||
<GGEditor className={styles.editor}> |
|||
<Row type="flex" className={styles.editorHd}> |
|||
<Col span={24}> |
|||
<FlowToolbar /> |
|||
</Col> |
|||
</Row> |
|||
<Row type="flex" className={styles.editorBd}> |
|||
<Col span={4} className={styles.editorSidebar}> |
|||
<FlowItemPanel /> |
|||
</Col> |
|||
<Col span={16} className={styles.editorContent}> |
|||
<Flow className={styles.flow} /> |
|||
</Col> |
|||
<Col span={4} className={styles.editorSidebar}> |
|||
<FlowDetailPanel /> |
|||
<EditorMinimap /> |
|||
</Col> |
|||
</Row> |
|||
<FlowContextMenu /> |
|||
</GGEditor> |
|||
</PageHeaderWrapper> |
|||
); |
|||
}; |
|||
|
|||
export default FlowPage; |
|||
@ -0,0 +1,41 @@ |
|||
.editor { |
|||
display: flex; |
|||
flex: 1; |
|||
flex-direction: column; |
|||
width: 100%; |
|||
height: calc(100vh - 250px); |
|||
background: #fff; |
|||
} |
|||
|
|||
.editorHd { |
|||
padding: 8px; |
|||
border: 1px solid #e6e9ed; |
|||
} |
|||
|
|||
.editorBd { |
|||
flex: 1; |
|||
} |
|||
|
|||
.editorSidebar, |
|||
.editorContent { |
|||
display: flex; |
|||
flex-direction: column; |
|||
} |
|||
|
|||
.editorSidebar { |
|||
background: #fafafa; |
|||
|
|||
&:first-child { |
|||
border-right: 1px solid #e6e9ed; |
|||
} |
|||
|
|||
&:last-child { |
|||
border-left: 1px solid #e6e9ed; |
|||
} |
|||
} |
|||
|
|||
.flow, |
|||
.mind, |
|||
.koni { |
|||
flex: 1; |
|||
} |
|||
@ -0,0 +1,45 @@ |
|||
import React from 'react'; |
|||
import { Row, Col } from 'antd'; |
|||
import GGEditor, { Koni } from 'gg-editor'; |
|||
import EditorMinimap from '../components/EditorMinimap'; |
|||
import { KoniContextMenu } from '../components/EditorContextMenu'; |
|||
import { KoniToolbar } from '../components/EditorToolbar'; |
|||
import { KoniItemPanel } from '../components/EditorItemPanel'; |
|||
import { KoniDetailPanel } from '../components/EditorDetailPanel'; |
|||
import KoniCustomNode from './shape/nodes/KoniCustomNode'; |
|||
import styles from '../Flow/index.less'; |
|||
import { FormattedMessage } from 'umi/locale'; |
|||
import PageHeaderWrapper from '@/components/PageHeaderWrapper'; |
|||
|
|||
const KoniPage = () => { |
|||
return ( |
|||
<PageHeaderWrapper |
|||
title={<FormattedMessage id="app.editor.koni.title" />} |
|||
content={<FormattedMessage id="app.editor.koni.description" />} |
|||
> |
|||
<GGEditor className={styles.editor}> |
|||
<Row type="flex" className={styles.editorHd}> |
|||
<Col span={24}> |
|||
<KoniToolbar /> |
|||
</Col> |
|||
</Row> |
|||
<Row type="flex" className={styles.editorBd}> |
|||
<Col span={4} className={styles.editorSidebar}> |
|||
<KoniItemPanel /> |
|||
</Col> |
|||
<Col span={16} className={styles.editorContent}> |
|||
<Koni className={styles.koni} /> |
|||
</Col> |
|||
<Col span={4} className={styles.editorSidebar}> |
|||
<KoniDetailPanel /> |
|||
<EditorMinimap /> |
|||
</Col> |
|||
</Row> |
|||
<KoniCustomNode /> |
|||
<KoniContextMenu /> |
|||
</GGEditor> |
|||
</PageHeaderWrapper> |
|||
); |
|||
}; |
|||
|
|||
export default KoniPage; |
|||
@ -0,0 +1,33 @@ |
|||
import React from 'react'; |
|||
import { RegisterNode } from 'gg-editor'; |
|||
|
|||
class KoniCustomNode extends React.Component { |
|||
render() { |
|||
const config = { |
|||
draw(item) { |
|||
const keyShape = this.drawKeyShape(item); |
|||
|
|||
// draw label
|
|||
this.drawLabel(item); |
|||
|
|||
// draw image
|
|||
const group = item.getGraphicGroup(); |
|||
const model = item.getModel(); |
|||
|
|||
group.addShape('image', { |
|||
attrs: { |
|||
x: -7, |
|||
y: -7, |
|||
img: model.icon, |
|||
}, |
|||
}); |
|||
|
|||
return keyShape; |
|||
}, |
|||
}; |
|||
|
|||
return <RegisterNode name="koni-custom-node" config={config} />; |
|||
} |
|||
} |
|||
|
|||
export default KoniCustomNode; |
|||
@ -0,0 +1,40 @@ |
|||
import React from 'react'; |
|||
import { Row, Col } from 'antd'; |
|||
import GGEditor, { Mind } from 'gg-editor'; |
|||
import EditorMinimap from '../components/EditorMinimap'; |
|||
import { MindContextMenu } from '../components/EditorContextMenu'; |
|||
import { MindToolbar } from '../components/EditorToolbar'; |
|||
import { MindDetailPanel } from '../components/EditorDetailPanel'; |
|||
import data from '../mock/worldCup2018.json'; |
|||
import styles from '../Flow/index.less'; |
|||
import { FormattedMessage } from 'umi/locale'; |
|||
import PageHeaderWrapper from '@/components/PageHeaderWrapper'; |
|||
|
|||
const MindPage = () => { |
|||
return ( |
|||
<PageHeaderWrapper |
|||
title={<FormattedMessage id="app.editor.mind.title" />} |
|||
content={<FormattedMessage id="app.editor.mind.description" />} |
|||
> |
|||
<GGEditor className={styles.editor}> |
|||
<Row type="flex" className={styles.editorHd}> |
|||
<Col span={24}> |
|||
<MindToolbar /> |
|||
</Col> |
|||
</Row> |
|||
<Row type="flex" className={styles.editorBd}> |
|||
<Col span={20} className={styles.editorContent}> |
|||
<Mind data={data} className={styles.mind} /> |
|||
</Col> |
|||
<Col span={4} className={styles.editorSidebar}> |
|||
<MindDetailPanel /> |
|||
<EditorMinimap /> |
|||
</Col> |
|||
</Row> |
|||
<MindContextMenu /> |
|||
</GGEditor> |
|||
</PageHeaderWrapper> |
|||
); |
|||
}; |
|||
|
|||
export default MindPage; |
|||
@ -0,0 +1,7 @@ |
|||
import { Icon } from 'antd'; |
|||
|
|||
const IconFont = Icon.createFromIconfontCN({ |
|||
scriptUrl: 'https://at.alicdn.com/t/font_1101588_01zniftxm9yp.js', |
|||
}); |
|||
|
|||
export default IconFont; |
|||
@ -0,0 +1,36 @@ |
|||
import React from 'react'; |
|||
import { NodeMenu, EdgeMenu, GroupMenu, MultiMenu, CanvasMenu, ContextMenu } from 'gg-editor'; |
|||
import MenuItem from './MenuItem'; |
|||
import styles from './index.less'; |
|||
|
|||
const FlowContextMenu = () => { |
|||
return ( |
|||
<ContextMenu className={styles.contextMenu}> |
|||
<NodeMenu> |
|||
<MenuItem command="copy" /> |
|||
<MenuItem command="delete" /> |
|||
</NodeMenu> |
|||
<EdgeMenu> |
|||
<MenuItem command="delete" /> |
|||
</EdgeMenu> |
|||
<GroupMenu> |
|||
<MenuItem command="copy" /> |
|||
<MenuItem command="delete" /> |
|||
<MenuItem command="unGroup" icon="ungroup" text="Ungroup" /> |
|||
</GroupMenu> |
|||
<MultiMenu> |
|||
<MenuItem command="copy" /> |
|||
<MenuItem command="paste" /> |
|||
<MenuItem command="addGroup" icon="group" text="Add Group" /> |
|||
<MenuItem command="delete" /> |
|||
</MultiMenu> |
|||
<CanvasMenu> |
|||
<MenuItem command="undo" /> |
|||
<MenuItem command="redo" /> |
|||
<MenuItem command="pasteHere" icon="paste" text="Paste Here" /> |
|||
</CanvasMenu> |
|||
</ContextMenu> |
|||
); |
|||
}; |
|||
|
|||
export default FlowContextMenu; |
|||
@ -0,0 +1,3 @@ |
|||
import FlowContextMenu from './FlowContextMenu'; |
|||
|
|||
export default FlowContextMenu; |
|||
@ -0,0 +1,20 @@ |
|||
import React from 'react'; |
|||
import { Command } from 'gg-editor'; |
|||
import upperFirst from 'lodash/upperFirst'; |
|||
import IconFont from '../../common/IconFont'; |
|||
import styles from './index.less'; |
|||
|
|||
const MenuItem = props => { |
|||
const { command, icon, text } = props; |
|||
|
|||
return ( |
|||
<Command name={command}> |
|||
<div className={styles.item}> |
|||
<IconFont type={`icon-${icon || command}`} /> |
|||
<span>{text || upperFirst(command)}</span> |
|||
</div> |
|||
</Command> |
|||
); |
|||
}; |
|||
|
|||
export default MenuItem; |
|||
@ -0,0 +1,24 @@ |
|||
import React from 'react'; |
|||
import { NodeMenu, CanvasMenu, ContextMenu } from 'gg-editor'; |
|||
import MenuItem from './MenuItem'; |
|||
import styles from './index.less'; |
|||
|
|||
const MindContextMenu = () => { |
|||
return ( |
|||
<ContextMenu className={styles.contextMenu}> |
|||
<NodeMenu> |
|||
<MenuItem command="append" text="Topic" /> |
|||
<MenuItem command="appendChild" icon="append-child" text="Subtopic" /> |
|||
<MenuItem command="collapse" text="Fold" /> |
|||
<MenuItem command="expand" text="Unfold" /> |
|||
<MenuItem command="delete" /> |
|||
</NodeMenu> |
|||
<CanvasMenu> |
|||
<MenuItem command="undo" /> |
|||
<MenuItem command="redo" /> |
|||
</CanvasMenu> |
|||
</ContextMenu> |
|||
); |
|||
}; |
|||
|
|||
export default MindContextMenu; |
|||
@ -0,0 +1,5 @@ |
|||
import FlowContextMenu from './FlowContextMenu'; |
|||
import MindContextMenu from './MindContextMenu'; |
|||
import KoniContextMenu from './KoniContextMenu'; |
|||
|
|||
export { FlowContextMenu, MindContextMenu, KoniContextMenu }; |
|||
@ -0,0 +1,39 @@ |
|||
.contextMenu { |
|||
display: none; |
|||
overflow: hidden; |
|||
background: #fff; |
|||
border-radius: 4px; |
|||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15); |
|||
|
|||
.item { |
|||
display: flex; |
|||
align-items: center; |
|||
padding: 5px 12px; |
|||
cursor: pointer; |
|||
transition: all 0.3s; |
|||
user-select: none; |
|||
|
|||
&:hover { |
|||
background: #e6f7ff; |
|||
} |
|||
|
|||
i { |
|||
margin-right: 8px; |
|||
} |
|||
} |
|||
|
|||
:global { |
|||
.disable { |
|||
:local { |
|||
.item { |
|||
color: rgba(0, 0, 0, 0.25); |
|||
cursor: auto; |
|||
|
|||
&:hover { |
|||
background: #fff; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,129 @@ |
|||
import React, { Fragment } from 'react'; |
|||
import { Card, Form, Input, Select } from 'antd'; |
|||
import { withPropsAPI } from 'gg-editor'; |
|||
import upperFirst from 'lodash/upperFirst'; |
|||
|
|||
const { Item } = Form; |
|||
const { Option } = Select; |
|||
|
|||
const inlineFormItemLayout = { |
|||
labelCol: { |
|||
sm: { span: 8 }, |
|||
}, |
|||
wrapperCol: { |
|||
sm: { span: 16 }, |
|||
}, |
|||
}; |
|||
|
|||
class DetailForm extends React.Component { |
|||
get item() { |
|||
const { propsAPI } = this.props; |
|||
|
|||
return propsAPI.getSelected()[0]; |
|||
} |
|||
|
|||
handleSubmit = e => { |
|||
if (e && e.preventDefault) { |
|||
e.preventDefault(); |
|||
} |
|||
|
|||
const { form, propsAPI } = this.props; |
|||
const { getSelected, executeCommand, update } = propsAPI; |
|||
|
|||
setTimeout(() => { |
|||
form.validateFieldsAndScroll((err, values) => { |
|||
if (err) { |
|||
return; |
|||
} |
|||
|
|||
const item = getSelected()[0]; |
|||
|
|||
if (!item) { |
|||
return; |
|||
} |
|||
|
|||
executeCommand(() => { |
|||
update(item, { |
|||
...values, |
|||
}); |
|||
}); |
|||
}); |
|||
}, 0); |
|||
}; |
|||
|
|||
renderEdgeShapeSelect = () => { |
|||
return ( |
|||
<Select onChange={this.handleSubmit}> |
|||
<Option value="flow-smooth">Smooth</Option> |
|||
<Option value="flow-polyline">Polyline</Option> |
|||
<Option value="flow-polyline-round">Polyline Round</Option> |
|||
</Select> |
|||
); |
|||
}; |
|||
|
|||
renderNodeDetail = () => { |
|||
const { form } = this.props; |
|||
const { label } = this.item.getModel(); |
|||
|
|||
return ( |
|||
<Item label="Label" {...inlineFormItemLayout}> |
|||
{form.getFieldDecorator('label', { |
|||
initialValue: label, |
|||
})(<Input onBlur={this.handleSubmit} />)} |
|||
</Item> |
|||
); |
|||
}; |
|||
|
|||
renderEdgeDetail = () => { |
|||
const { form } = this.props; |
|||
const { label = '', shape = 'flow-smooth' } = this.item.getModel(); |
|||
|
|||
return ( |
|||
<Fragment> |
|||
<Item label="Label" {...inlineFormItemLayout}> |
|||
{form.getFieldDecorator('label', { |
|||
initialValue: label, |
|||
})(<Input onBlur={this.handleSubmit} />)} |
|||
</Item> |
|||
<Item label="Shape" {...inlineFormItemLayout}> |
|||
{form.getFieldDecorator('shape', { |
|||
initialValue: shape, |
|||
})(this.renderEdgeShapeSelect())} |
|||
</Item> |
|||
</Fragment> |
|||
); |
|||
}; |
|||
|
|||
renderGroupDetail = () => { |
|||
const { form } = this.props; |
|||
const { label = '新建分组' } = this.item.getModel(); |
|||
|
|||
return ( |
|||
<Item label="Label" {...inlineFormItemLayout}> |
|||
{form.getFieldDecorator('label', { |
|||
initialValue: label, |
|||
})(<Input onBlur={this.handleSubmit} />)} |
|||
</Item> |
|||
); |
|||
}; |
|||
|
|||
render() { |
|||
const { type } = this.props; |
|||
|
|||
if (!this.item) { |
|||
return null; |
|||
} |
|||
|
|||
return ( |
|||
<Card type="inner" size="small" title={upperFirst(type)} bordered={false}> |
|||
<Form onSubmit={this.handleSubmit}> |
|||
{type === 'node' && this.renderNodeDetail()} |
|||
{type === 'edge' && this.renderEdgeDetail()} |
|||
{type === 'group' && this.renderGroupDetail()} |
|||
</Form> |
|||
</Card> |
|||
); |
|||
} |
|||
} |
|||
|
|||
export default Form.create()(withPropsAPI(DetailForm)); |
|||
@ -0,0 +1,29 @@ |
|||
import React from 'react'; |
|||
import { Card } from 'antd'; |
|||
import { NodePanel, EdgePanel, GroupPanel, MultiPanel, CanvasPanel, DetailPanel } from 'gg-editor'; |
|||
import DetailForm from './DetailForm'; |
|||
import styles from './index.less'; |
|||
|
|||
const FlowDetailPanel = () => { |
|||
return ( |
|||
<DetailPanel className={styles.detailPanel}> |
|||
<NodePanel> |
|||
<DetailForm type="node" /> |
|||
</NodePanel> |
|||
<EdgePanel> |
|||
<DetailForm type="edge" /> |
|||
</EdgePanel> |
|||
<GroupPanel> |
|||
<DetailForm type="group" /> |
|||
</GroupPanel> |
|||
<MultiPanel> |
|||
<Card type="inner" size="small" title="Multi Select" bordered={false} /> |
|||
</MultiPanel> |
|||
<CanvasPanel> |
|||
<Card type="inner" size="small" title="Canvas" bordered={false} /> |
|||
</CanvasPanel> |
|||
</DetailPanel> |
|||
); |
|||
}; |
|||
|
|||
export default FlowDetailPanel; |
|||
@ -0,0 +1,3 @@ |
|||
import FlowDetailPanel from './FlowDetailPanel'; |
|||
|
|||
export default FlowDetailPanel; |
|||
@ -0,0 +1,20 @@ |
|||
import React from 'react'; |
|||
import { Card } from 'antd'; |
|||
import { NodePanel, CanvasPanel, DetailPanel } from 'gg-editor'; |
|||
import DetailForm from './DetailForm'; |
|||
import styles from './index.less'; |
|||
|
|||
const MindDetailPanel = () => { |
|||
return ( |
|||
<DetailPanel className={styles.detailPanel}> |
|||
<NodePanel> |
|||
<DetailForm type="node" /> |
|||
</NodePanel> |
|||
<CanvasPanel> |
|||
<Card type="inner" size="small" title="Canvas" bordered={false} /> |
|||
</CanvasPanel> |
|||
</DetailPanel> |
|||
); |
|||
}; |
|||
|
|||
export default MindDetailPanel; |
|||
@ -0,0 +1,5 @@ |
|||
import FlowDetailPanel from './FlowDetailPanel'; |
|||
import MindDetailPanel from './MindDetailPanel'; |
|||
import KoniDetailPanel from './KoniDetailPanel'; |
|||
|
|||
export { FlowDetailPanel, MindDetailPanel, KoniDetailPanel }; |
|||
@ -0,0 +1,10 @@ |
|||
.detailPanel { |
|||
flex: 1; |
|||
background: #fafafa; |
|||
|
|||
:global { |
|||
.ant-card { |
|||
background: #fafafa; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,55 @@ |
|||
import React from 'react'; |
|||
import { Card } from 'antd'; |
|||
import { ItemPanel, Item } from 'gg-editor'; |
|||
import styles from './index.less'; |
|||
|
|||
const FlowItemPanel = () => { |
|||
return ( |
|||
<ItemPanel className={styles.itemPanel}> |
|||
<Card bordered={false}> |
|||
<Item |
|||
type="node" |
|||
size="72*72" |
|||
shape="flow-circle" |
|||
model={{ |
|||
color: '#FA8C16', |
|||
label: 'Start', |
|||
}} |
|||
src="/ggeditor/flow/start.svg" |
|||
/> |
|||
<Item |
|||
type="node" |
|||
size="80*48" |
|||
shape="flow-rect" |
|||
model={{ |
|||
color: '#1890FF', |
|||
label: 'Normal', |
|||
}} |
|||
src="/ggeditor/flow/normal.svg" |
|||
/> |
|||
<Item |
|||
type="node" |
|||
size="80*72" |
|||
shape="flow-rhombus" |
|||
model={{ |
|||
color: '#13C2C2', |
|||
label: 'Decision', |
|||
}} |
|||
src="/ggeditor/flow/decision.svg" |
|||
/> |
|||
<Item |
|||
type="node" |
|||
size="80*48" |
|||
shape="flow-capsule" |
|||
model={{ |
|||
color: '#722ED1', |
|||
label: 'Model', |
|||
}} |
|||
src="/ggeditor/flow/model.svg" |
|||
/> |
|||
</Card> |
|||
</ItemPanel> |
|||
); |
|||
}; |
|||
|
|||
export default FlowItemPanel; |
|||
@ -0,0 +1,51 @@ |
|||
import React from 'react'; |
|||
import { Card } from 'antd'; |
|||
import { ItemPanel, Item } from 'gg-editor'; |
|||
import styles from './index.less'; |
|||
|
|||
const KoniItemPanel = () => { |
|||
return ( |
|||
<ItemPanel className={styles.itemPanel}> |
|||
<Card bordered={false}> |
|||
<Item |
|||
type="node" |
|||
size="40" |
|||
shape="koni-custom-node" |
|||
model={{ |
|||
color: '#69C0FF', |
|||
label: 'Bank', |
|||
labelOffsetY: 28, |
|||
icon: '/ggeditor/koni/icon.svg', |
|||
}} |
|||
src="/ggeditor/koni/bank.svg" |
|||
/> |
|||
<Item |
|||
type="node" |
|||
size="40" |
|||
shape="koni-custom-node" |
|||
model={{ |
|||
color: '#5CDBD3', |
|||
label: 'Person', |
|||
labelOffsetY: 28, |
|||
icon: '/ggeditor/koni/icon.svg', |
|||
}} |
|||
src="/ggeditor/koni/person.svg" |
|||
/> |
|||
<Item |
|||
type="node" |
|||
size="40" |
|||
shape="koni-custom-node" |
|||
model={{ |
|||
color: '#B37FEB', |
|||
label: 'Country', |
|||
labelOffsetY: 28, |
|||
icon: '/ggeditor/koni/icon.svg', |
|||
}} |
|||
src="/ggeditor/koni/country.svg" |
|||
/> |
|||
</Card> |
|||
</ItemPanel> |
|||
); |
|||
}; |
|||
|
|||
export default KoniItemPanel; |
|||
@ -0,0 +1,4 @@ |
|||
import FlowItemPanel from './FlowItemPanel'; |
|||
import KoniItemPanel from './KoniItemPanel'; |
|||
|
|||
export { FlowItemPanel, KoniItemPanel }; |
|||
@ -0,0 +1,20 @@ |
|||
.itemPanel { |
|||
flex: 1; |
|||
background: #fafafa; |
|||
|
|||
:global { |
|||
.ant-card { |
|||
background: #fafafa; |
|||
} |
|||
|
|||
.ant-card-body { |
|||
display: flex; |
|||
flex-direction: column; |
|||
align-items: center; |
|||
|
|||
> div { |
|||
margin-bottom: 16px; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,13 @@ |
|||
import React from 'react'; |
|||
import { Card } from 'antd'; |
|||
import { Minimap } from 'gg-editor'; |
|||
|
|||
const EditorMinimap = () => { |
|||
return ( |
|||
<Card type="inner" size="small" title="Minimap" bordered={false}> |
|||
<Minimap height={200} /> |
|||
</Card> |
|||
); |
|||
}; |
|||
|
|||
export default EditorMinimap; |
|||
@ -0,0 +1,32 @@ |
|||
import React from 'react'; |
|||
import { Divider } from 'antd'; |
|||
import { Toolbar } from 'gg-editor'; |
|||
import ToolbarButton from './ToolbarButton'; |
|||
import styles from './index.less'; |
|||
|
|||
const FlowToolbar = () => { |
|||
return ( |
|||
<Toolbar className={styles.toolbar}> |
|||
<ToolbarButton command="undo" /> |
|||
<ToolbarButton command="redo" /> |
|||
<Divider type="vertical" /> |
|||
<ToolbarButton command="copy" /> |
|||
<ToolbarButton command="paste" /> |
|||
<ToolbarButton command="delete" /> |
|||
<Divider type="vertical" /> |
|||
<ToolbarButton command="zoomIn" icon="zoom-in" text="Zoom In" /> |
|||
<ToolbarButton command="zoomOut" icon="zoom-out" text="Zoom Out" /> |
|||
<ToolbarButton command="autoZoom" icon="fit-map" text="Fit Map" /> |
|||
<ToolbarButton command="resetZoom" icon="actual-size" text="Actual Size" /> |
|||
<Divider type="vertical" /> |
|||
<ToolbarButton command="toBack" icon="to-back" text="To Back" /> |
|||
<ToolbarButton command="toFront" icon="to-front" text="To Front" /> |
|||
<Divider type="vertical" /> |
|||
<ToolbarButton command="multiSelect" icon="multi-select" text="Multi Select" /> |
|||
<ToolbarButton command="addGroup" icon="group" text="Add Group" /> |
|||
<ToolbarButton command="unGroup" icon="ungroup" text="Ungroup" /> |
|||
</Toolbar> |
|||
); |
|||
}; |
|||
|
|||
export default FlowToolbar; |
|||
@ -0,0 +1,3 @@ |
|||
import FlowToolbar from './FlowToolbar'; |
|||
|
|||
export default FlowToolbar; |
|||
@ -0,0 +1,27 @@ |
|||
import React from 'react'; |
|||
import { Divider } from 'antd'; |
|||
import { Toolbar } from 'gg-editor'; |
|||
import ToolbarButton from './ToolbarButton'; |
|||
import styles from './index.less'; |
|||
|
|||
const FlowToolbar = () => { |
|||
return ( |
|||
<Toolbar className={styles.toolbar}> |
|||
<ToolbarButton command="undo" /> |
|||
<ToolbarButton command="redo" /> |
|||
<Divider type="vertical" /> |
|||
<ToolbarButton command="zoomIn" icon="zoom-in" text="Zoom In" /> |
|||
<ToolbarButton command="zoomOut" icon="zoom-out" text="Zoom Out" /> |
|||
<ToolbarButton command="autoZoom" icon="fit-map" text="Fit Map" /> |
|||
<ToolbarButton command="resetZoom" icon="actual-size" text="Actual Size" /> |
|||
<Divider type="vertical" /> |
|||
<ToolbarButton command="append" text="Topic" /> |
|||
<ToolbarButton command="appendChild" icon="append-child" text="Subtopic" /> |
|||
<Divider type="vertical" /> |
|||
<ToolbarButton command="collapse" text="Fold" /> |
|||
<ToolbarButton command="expand" text="Unfold" /> |
|||
</Toolbar> |
|||
); |
|||
}; |
|||
|
|||
export default FlowToolbar; |
|||
@ -0,0 +1,24 @@ |
|||
import React from 'react'; |
|||
import { Tooltip } from 'antd'; |
|||
import { Command } from 'gg-editor'; |
|||
import upperFirst from 'lodash/upperFirst'; |
|||
import IconFont from '../../common/IconFont'; |
|||
import styles from './index.less'; |
|||
|
|||
const ToolbarButton = props => { |
|||
const { command, icon, text } = props; |
|||
|
|||
return ( |
|||
<Command name={command}> |
|||
<Tooltip |
|||
title={text || upperFirst(command)} |
|||
placement="bottom" |
|||
overlayClassName={styles.tooltip} |
|||
> |
|||
<IconFont type={`icon-${icon || command}`} /> |
|||
</Tooltip> |
|||
</Command> |
|||
); |
|||
}; |
|||
|
|||
export default ToolbarButton; |
|||
@ -0,0 +1,5 @@ |
|||
import FlowToolbar from './FlowToolbar'; |
|||
import MindToolbar from './MindToolbar'; |
|||
import KoniToolbar from './KoniToolbar'; |
|||
|
|||
export { FlowToolbar, MindToolbar, KoniToolbar }; |
|||
@ -0,0 +1,39 @@ |
|||
.toolbar { |
|||
display: flex; |
|||
align-items: center; |
|||
|
|||
:global { |
|||
.command i { |
|||
display: inline-block; |
|||
width: 27px; |
|||
height: 27px; |
|||
margin: 0 6px; |
|||
padding-top: 6px; |
|||
text-align: center; |
|||
border: 1px solid #fff; |
|||
cursor: pointer; |
|||
|
|||
&:hover { |
|||
border: 1px solid #e6e9ed; |
|||
} |
|||
} |
|||
|
|||
.disable i { |
|||
color: rgba(0, 0, 0, 0.25); |
|||
cursor: auto; |
|||
|
|||
&:hover { |
|||
border: 1px solid #fff; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
.tooltip { |
|||
:global { |
|||
.ant-tooltip-inner { |
|||
font-size: 12px; |
|||
border-radius: 0; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,129 @@ |
|||
{ |
|||
"roots": [ |
|||
{ |
|||
"label": "法国", |
|||
"children": [ |
|||
{ |
|||
"label": "克罗地亚", |
|||
"side": "left", |
|||
"children": [ |
|||
{ |
|||
"label": "克罗地亚", |
|||
"children": [ |
|||
{ |
|||
"label": "克罗地亚", |
|||
"children": [ |
|||
{ |
|||
"label": "克罗地亚" |
|||
}, |
|||
{ |
|||
"label": "丹麦" |
|||
} |
|||
] |
|||
}, |
|||
{ |
|||
"label": "俄罗斯", |
|||
"children": [ |
|||
{ |
|||
"label": "俄罗斯" |
|||
}, |
|||
{ |
|||
"label": "西班牙" |
|||
} |
|||
] |
|||
} |
|||
] |
|||
}, |
|||
{ |
|||
"label": "英格兰", |
|||
"children": [ |
|||
{ |
|||
"label": "英格兰", |
|||
"children": [ |
|||
{ |
|||
"label": "英格兰" |
|||
}, |
|||
{ |
|||
"label": "哥伦比亚" |
|||
} |
|||
] |
|||
}, |
|||
{ |
|||
"label": "瑞典", |
|||
"children": [ |
|||
{ |
|||
"label": "瑞士" |
|||
}, |
|||
{ |
|||
"label": "瑞典" |
|||
} |
|||
] |
|||
} |
|||
] |
|||
} |
|||
] |
|||
}, |
|||
{ |
|||
"label": "法国", |
|||
"side": "right", |
|||
"children": [ |
|||
{ |
|||
"label": "法国", |
|||
"children": [ |
|||
{ |
|||
"label": "法国", |
|||
"children": [ |
|||
{ |
|||
"label": "法国" |
|||
}, |
|||
{ |
|||
"label": "阿根廷" |
|||
} |
|||
] |
|||
}, |
|||
{ |
|||
"label": "乌拉圭", |
|||
"children": [ |
|||
{ |
|||
"label": "乌拉圭" |
|||
}, |
|||
{ |
|||
"label": "葡萄牙" |
|||
} |
|||
] |
|||
} |
|||
] |
|||
}, |
|||
{ |
|||
"label": "比利时", |
|||
"children": [ |
|||
{ |
|||
"label": "比利时", |
|||
"children": [ |
|||
{ |
|||
"label": "比利时" |
|||
}, |
|||
{ |
|||
"label": "日本" |
|||
} |
|||
] |
|||
}, |
|||
{ |
|||
"label": "巴西", |
|||
"children": [ |
|||
{ |
|||
"label": "巴西" |
|||
}, |
|||
{ |
|||
"label": "墨西哥" |
|||
} |
|||
] |
|||
} |
|||
] |
|||
} |
|||
] |
|||
} |
|||
] |
|||
} |
|||
] |
|||
} |
|||