smallstonesk
4 years ago
15 changed files with 233 additions and 387 deletions
@ -0,0 +1,11 @@ |
|||
.depsInfoModalContent { |
|||
padding-top: 20px; |
|||
} |
|||
|
|||
.modal { |
|||
:global { |
|||
.ant-modal-body { |
|||
padding: 0; |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,116 @@ |
|||
import React, { |
|||
useState, |
|||
useEffect |
|||
} from 'react'; |
|||
|
|||
import 'antd/es/modal/style'; |
|||
import styles from './index.module.less'; |
|||
|
|||
import { Graph } from '@antv/x6'; |
|||
import { Modal, message } from 'antd'; |
|||
import JsonView from 'react-json-view'; |
|||
import { safeParse } from '../../../utils'; |
|||
import analyzeDeps from '../../../utils/analyzeDeps'; |
|||
import CodeEditor from '../../../components/codeEditor'; |
|||
|
|||
interface IProps { |
|||
title?: string; |
|||
flowChart: Graph; |
|||
} |
|||
|
|||
const CodeEditModal: React.FC<IProps> = (props) => { |
|||
|
|||
const { title = '编辑代码', flowChart } = props; |
|||
const [code, setCode] = useState<string>(''); |
|||
const [visible, setVisible] = useState<boolean>(false); |
|||
|
|||
const updateNodeCode = (code: string): void => { |
|||
const cell = flowChart.getSelectedCells()[0]; |
|||
const { dependencies } = cell.getData(); |
|||
cell.setData({ code }); |
|||
message.success('代码保存成功', 1); |
|||
const excludeDeps = safeParse(dependencies); |
|||
analyzeDeps(code, Object.keys(excludeDeps)).then((deps): void => { |
|||
if (Object.keys(deps).length > 0) { |
|||
Modal.info({ |
|||
title: '检测到您的代码有新依赖,已为您自动更新', |
|||
content: ( |
|||
<div className={styles.depsInfoModalContent}> |
|||
<JsonView |
|||
src={deps} |
|||
name={'dependencies'} |
|||
collapsed={false} |
|||
enableClipboard={false} |
|||
displayDataTypes={false} |
|||
displayObjectSize={false} |
|||
/> |
|||
</div> |
|||
), |
|||
onOk() { |
|||
const newDeps = { ...excludeDeps, ...deps }; |
|||
cell.setData({ |
|||
code, |
|||
dependencies: JSON.stringify(newDeps, null, 2) |
|||
}); |
|||
}, |
|||
}); |
|||
} |
|||
}); |
|||
}; |
|||
|
|||
// life
|
|||
useEffect(() => { |
|||
const handler = () => setVisible(true); |
|||
flowChart.on('graph:editCode', handler); |
|||
return () => { |
|||
flowChart.off('graph:editCode', handler); |
|||
}; |
|||
}, []); |
|||
useEffect(() => { |
|||
if (visible) { |
|||
const cell = flowChart.getSelectedCells()[0]; |
|||
const { code } = cell.getData() || {}; |
|||
setCode(code); |
|||
} else { |
|||
setCode(''); |
|||
} |
|||
}, [visible]); |
|||
|
|||
// events
|
|||
const onOk = (): void => { |
|||
setVisible(false); |
|||
updateNodeCode(code); |
|||
}; |
|||
const onCancel = (): void => { |
|||
setVisible(false); |
|||
} |
|||
const onChangeCode = (ev: any, newCode: string | undefined = ''): void => { |
|||
setCode(newCode); |
|||
}; |
|||
const onSaveCode = (newCode: string) => { |
|||
updateNodeCode(newCode); |
|||
}; |
|||
|
|||
return ( |
|||
<Modal |
|||
className={styles.modal} |
|||
width={1000} |
|||
title={title} |
|||
okText={'保存'} |
|||
visible={visible} |
|||
cancelText={'取消'} |
|||
onOk={onOk} |
|||
onCancel={onCancel} |
|||
> |
|||
<CodeEditor |
|||
value={code} |
|||
width={'100%'} |
|||
height={'600px'} |
|||
onChange={onChangeCode} |
|||
onSave={onSaveCode} |
|||
/> |
|||
</Modal> |
|||
); |
|||
}; |
|||
|
|||
export default CodeEditModal; |
@ -0,0 +1,8 @@ |
|||
.modal { |
|||
:global { |
|||
.ant-modal-body { |
|||
padding: 0; |
|||
height: 650px; |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,50 @@ |
|||
import React, { |
|||
useState, |
|||
useEffect, |
|||
useCallback |
|||
} from 'react'; |
|||
|
|||
import styles from './index.module.less'; |
|||
|
|||
import { Modal } from 'antd'; |
|||
import { Graph } from '@antv/x6'; |
|||
import CodeRun from '../../../components/codeRun'; |
|||
|
|||
interface IEditModalProps { |
|||
title?: string; |
|||
flowChart: Graph; |
|||
} |
|||
|
|||
const CodeRunModal: React.FC<IEditModalProps> = (props): JSX.Element => { |
|||
|
|||
const { title = '执行代码', flowChart } = props; |
|||
const [visible, setVisible] = useState(false); |
|||
|
|||
useEffect(() => { |
|||
const handler = () => setVisible(true); |
|||
flowChart.on('graph:runCode', handler); |
|||
return () => { |
|||
flowChart.off('graph:runCode', handler); |
|||
}; |
|||
}, [flowChart]); |
|||
|
|||
// events
|
|||
const onClose = useCallback((): void => { |
|||
setVisible(false); |
|||
}, []); |
|||
|
|||
return ( |
|||
<Modal |
|||
className={styles.modal} |
|||
width={1000} |
|||
title={title} |
|||
visible={visible} |
|||
footer={null} |
|||
onCancel={onClose} |
|||
> |
|||
<CodeRun flowChart={flowChart}/> |
|||
</Modal> |
|||
); |
|||
}; |
|||
|
|||
export default CodeRunModal; |
@ -1,20 +0,0 @@ |
|||
.container { |
|||
margin-top: 20px; |
|||
|
|||
.titleText { |
|||
font-size: 14px; |
|||
margin-bottom: 10px; |
|||
} |
|||
|
|||
.btn { |
|||
margin-top: 5px; |
|||
} |
|||
} |
|||
|
|||
.editModal { |
|||
:global { |
|||
.ant-modal-body { |
|||
padding: 0; |
|||
} |
|||
} |
|||
} |
@ -1,164 +0,0 @@ |
|||
import React, { useState, useEffect } from 'react'; |
|||
|
|||
import 'antd/es/modal/style'; |
|||
import 'antd/es/button/style'; |
|||
import styles from './index.module.less'; |
|||
|
|||
import { Graph } from '@antv/x6'; |
|||
import { Button, Modal, message } from 'antd'; |
|||
import { monaco, EditorDidMount } from '@monaco-editor/react'; |
|||
import CodeEditor from '../../../../components/codeEditor'; |
|||
import CodeRun from '../../../../components/codeRun'; |
|||
interface IProps { |
|||
value: any; |
|||
name: string; |
|||
title: string; |
|||
flowChart: Graph; |
|||
onValueChange: (value: string) => void; |
|||
} |
|||
|
|||
// NOTE: avoid monaco init many times
|
|||
let KeyMod: any = {}; |
|||
let KeyCode: any = {}; |
|||
monaco.init().then(monaco => { |
|||
KeyMod = monaco.KeyMod; |
|||
KeyCode = monaco.KeyCode; |
|||
}); |
|||
|
|||
const Code: React.FC<IProps> = (props) => { |
|||
const { title, value, onValueChange, flowChart } = props; |
|||
const [visible, setVisible] = useState<boolean>(false); |
|||
|
|||
useEffect(() => { |
|||
flowChart.on('settingBar:clickEditCode', onClickEdit); |
|||
return () => { |
|||
flowChart.off('settingBar:clickEditCode', onClickEdit); |
|||
}; |
|||
}, []); |
|||
|
|||
// events
|
|||
const onClickEdit = (): void => { |
|||
setVisible(true); |
|||
}; |
|||
const onClickCancel = (): void => { |
|||
setVisible(false); |
|||
}; |
|||
const onClickOk = (newCode: string): void => { |
|||
setVisible(false); |
|||
onValueChange(newCode); |
|||
}; |
|||
const onSave = (newCode: string): void => { |
|||
onValueChange(newCode); |
|||
}; |
|||
|
|||
return ( |
|||
<div className={styles.container}> |
|||
<p className={styles.titleText}>{title}</p> |
|||
<Button block={true} className={styles.btn} onClick={onClickEdit}> |
|||
编辑 |
|||
</Button> |
|||
<EditModal |
|||
title={title} |
|||
value={value} |
|||
visible={visible} |
|||
flowChart={flowChart} |
|||
onSave={onSave} |
|||
onOk={onClickOk} |
|||
onCancel={onClickCancel} |
|||
/> |
|||
</div> |
|||
); |
|||
}; |
|||
|
|||
interface IEditorModalProps { |
|||
visible: boolean; |
|||
title: string; |
|||
value: string; |
|||
flowChart: Graph; |
|||
onSave: (val: string) => void; |
|||
onOk: (val: string) => void; |
|||
onCancel: () => void; |
|||
} |
|||
|
|||
const EditModal: React.FC<IEditorModalProps> = (props) => { |
|||
const { visible, title, value, flowChart, onSave, onOk, onCancel } = props; |
|||
const [code, setCode] = useState<string>(value); |
|||
const [editorInst, setEditorInst] = useState<any>(); |
|||
const [codeRunVisible, setCodeRunVisible] = useState<boolean>(false); |
|||
|
|||
// life
|
|||
useEffect(() => { |
|||
// set value when opening modal
|
|||
// clear content when closing modal
|
|||
if (visible) { |
|||
setCode(value); |
|||
} else { |
|||
setCode(''); |
|||
} |
|||
}, [visible]); |
|||
useEffect(() => { |
|||
if (editorInst) { |
|||
// NOTE: how to add command(https://microsoft.github.io/monaco-editor/api/interfaces/monaco.editor.istandalonecodeeditor.html#addcommand)
|
|||
editorInst.addCommand(KeyMod.CtrlCmd | KeyCode.KEY_S, () => { |
|||
onSave(editorInst.getValue()); |
|||
message.success('保存成功', 1); |
|||
}); |
|||
} |
|||
}, [editorInst, onSave]); |
|||
|
|||
// events
|
|||
const onEditOk = (): void => { |
|||
// onOk(code);
|
|||
setCodeRunVisible(true) |
|||
}; |
|||
|
|||
const onRunOk = (): void => { |
|||
// setCodeRunVisible(false)
|
|||
} |
|||
const onRunCancel = (): void => { |
|||
setCodeRunVisible(false) |
|||
} |
|||
const onChangeCode = (ev: any, newCode: string | undefined = ''): void => { |
|||
if (newCode !== code) { |
|||
setCode(newCode); |
|||
} |
|||
}; |
|||
const onEditorDidMount: EditorDidMount = (getEditorValue, editor) => { |
|||
setEditorInst(editor); |
|||
}; |
|||
|
|||
return ( |
|||
<Modal |
|||
className={styles.editModal} |
|||
width={1000} |
|||
title={title} |
|||
okText={'执行代码'} |
|||
visible={visible} |
|||
cancelText={'取消'} |
|||
onOk={onEditOk} |
|||
onCancel={onCancel} |
|||
> |
|||
<CodeEditor |
|||
value={code} |
|||
width={'100%'} |
|||
height={'600px'} |
|||
onChange={onChangeCode} |
|||
editorDidMount={onEditorDidMount} |
|||
/> |
|||
<Modal |
|||
className={styles.runModal} |
|||
width={1000} |
|||
title={'执行代码'} |
|||
okText={'运行'} |
|||
visible={codeRunVisible} |
|||
cancelText={'取消'} |
|||
onOk={onRunOk} |
|||
onCancel={onRunCancel} |
|||
> |
|||
<CodeRun flowChart={flowChart}/> |
|||
</Modal> |
|||
</Modal> |
|||
); |
|||
}; |
|||
|
|||
export default Code; |
@ -1,23 +0,0 @@ |
|||
.container { |
|||
|
|||
display: flex; |
|||
flex-direction: column; |
|||
padding: 0 20px; |
|||
height: 100%; |
|||
|
|||
.empty { |
|||
margin-top: 100px; |
|||
} |
|||
|
|||
.footContainer { |
|||
|
|||
display: flex; |
|||
flex-direction: row; |
|||
justify-content: flex-end; |
|||
margin-top: 20px; |
|||
|
|||
.saveBtn { |
|||
margin-left: 20px; |
|||
} |
|||
} |
|||
} |
@ -1,123 +0,0 @@ |
|||
import React, { useState, useEffect } from 'react'; |
|||
|
|||
import styles from './index.module.less'; |
|||
|
|||
import { Cell } from '@antv/x6'; |
|||
import { Button, Empty } from 'antd'; |
|||
import Input from '../../components/input'; |
|||
import Checkbox from '../../components/checkbox'; |
|||
import { safeParse } from '../../../../utils'; |
|||
|
|||
// FIXME
|
|||
// save original config in tempConfig when change to edit mode
|
|||
// and recover original config when click cancel button
|
|||
let tempConfig: any = null; |
|||
|
|||
enum Mode { |
|||
edit = 'edit', |
|||
readOnly = 'readOnly', |
|||
} |
|||
|
|||
interface IConfig { |
|||
[key: string]: any; |
|||
} |
|||
|
|||
interface IProps { |
|||
selectedCell: Cell; |
|||
} |
|||
|
|||
const Config: React.FC<IProps> = (props) => { |
|||
const { selectedCell } = props; |
|||
const [mode, setMode] = useState<Mode>(Mode.readOnly); |
|||
const [configData, setConfigData] = useState<IConfig>({}); |
|||
const [configSchema, setConfigSchema] = useState<IConfig>({}); |
|||
|
|||
// life
|
|||
useEffect(() => { |
|||
const { configSchema, configData = {} } = selectedCell.getData() || {}; |
|||
setConfigData(configData); |
|||
setConfigSchema(safeParse(configSchema)); |
|||
selectedCell.on('change:configSchema', (data: { configSchema: string }) => { |
|||
setConfigSchema(safeParse(data.configSchema)); |
|||
}); |
|||
return () => { |
|||
selectedCell.off('change:configSchema'); |
|||
}; |
|||
}, [selectedCell]); |
|||
|
|||
// events
|
|||
const onFieldValueChange = (key: string, value: any) => { |
|||
setConfigData(Object.assign({}, configData, { [key]: value })); |
|||
}; |
|||
const onClickEdit = (): void => { |
|||
setMode(Mode.edit); |
|||
tempConfig = configData; |
|||
}; |
|||
const onClickCancel = (): void => { |
|||
setMode(Mode.readOnly); |
|||
setConfigData(tempConfig); |
|||
}; |
|||
const onClickSave = (): void => { |
|||
setMode(Mode.readOnly); |
|||
selectedCell.setData({ configData }); |
|||
}; |
|||
|
|||
// no config schema
|
|||
if (!configSchema || Object.keys(configSchema).length === 0) { |
|||
return ( |
|||
<div className={styles.container}> |
|||
<Empty |
|||
className={styles.empty} |
|||
description={'请编辑投放配置schema'} |
|||
image={Empty.PRESENTED_IMAGE_SIMPLE} |
|||
/> |
|||
</div> |
|||
); |
|||
} else { |
|||
return ( |
|||
<div className={styles.container}> |
|||
{Object.keys(configSchema).map((key, idx) => { |
|||
const { title, description, type } = configSchema[key]; |
|||
const FieldComponent = Helper.getFieldComponent(type); |
|||
return ( |
|||
<FieldComponent |
|||
key={idx} |
|||
name={key} |
|||
title={title} |
|||
value={configData[key]} |
|||
description={description} |
|||
disabled={mode === Mode.readOnly} |
|||
onValueChange={(value: any) => onFieldValueChange(key, value)} |
|||
/> |
|||
); |
|||
})} |
|||
{mode === Mode.readOnly ? ( |
|||
<div className={styles.footContainer}> |
|||
<Button block onClick={onClickEdit}> |
|||
编辑 |
|||
</Button> |
|||
</div> |
|||
) : ( |
|||
<div className={styles.footContainer}> |
|||
<Button onClick={onClickCancel}>取消</Button> |
|||
<Button className={styles.saveBtn} type={'primary'} onClick={onClickSave}> |
|||
保存 |
|||
</Button> |
|||
</div> |
|||
)} |
|||
</div> |
|||
); |
|||
} |
|||
}; |
|||
|
|||
const Helper = { |
|||
getFieldComponent(type: string) { |
|||
if (type === 'boolean') { |
|||
return Checkbox; |
|||
} else { |
|||
return Input; |
|||
} |
|||
}, |
|||
}; |
|||
|
|||
export default Config; |
Loading…
Reference in new issue