Merge commit '44e49e6ae6a1f644162489a27618c39194f4628d' into main
This commit is contained in:
commit
9e5e4688b8
File diff suppressed because it is too large
Load Diff
|
@ -20,12 +20,13 @@
|
||||||
"@types/node": "^18.6.5",
|
"@types/node": "^18.6.5",
|
||||||
"@types/react": "^18.0.17",
|
"@types/react": "^18.0.17",
|
||||||
"@types/react-dom": "^18.0.6",
|
"@types/react-dom": "^18.0.6",
|
||||||
|
"@uiw/react-md-editor": "^3.19.5",
|
||||||
"autoprefixer": "10.4.8",
|
"autoprefixer": "10.4.8",
|
||||||
"axios": "^0.27.2",
|
"axios": "^0.27.2",
|
||||||
"bootstrap": "^5.2.0",
|
"bootstrap": "^5.2.0",
|
||||||
"bpmn-js": "^9.3.2",
|
"bpmn-js": "^9.3.2",
|
||||||
"bpmn-js-properties-panel": "^1.10.0",
|
"bpmn-js-properties-panel": "^1.10.0",
|
||||||
"bpmn-js-spiffworkflow": "sartography/bpmn-js-spiffworkflow#main",
|
"bpmn-js-spiffworkflow": "sartography/bpmn-js-spiffworkflow#feature/more_launch_buttons_and_dropdowns",
|
||||||
"craco": "^0.0.3",
|
"craco": "^0.0.3",
|
||||||
"date-fns": "^2.28.0",
|
"date-fns": "^2.28.0",
|
||||||
"diagram-js": "^8.5.0",
|
"diagram-js": "^8.5.0",
|
||||||
|
|
|
@ -69,8 +69,14 @@ type OwnProps = {
|
||||||
diagramXML?: string | null;
|
diagramXML?: string | null;
|
||||||
fileName?: string;
|
fileName?: string;
|
||||||
onLaunchScriptEditor?: (..._args: any[]) => any;
|
onLaunchScriptEditor?: (..._args: any[]) => any;
|
||||||
|
onLaunchMarkdownEditor?: (..._args: any[]) => any;
|
||||||
|
onLaunchBpmnEditor?: (..._args: any[]) => any;
|
||||||
|
onLaunchJsonEditor?: (..._args: any[]) => any;
|
||||||
|
onLaunchDmnEditor?: (..._args: any[]) => any;
|
||||||
onElementClick?: (..._args: any[]) => any;
|
onElementClick?: (..._args: any[]) => any;
|
||||||
onServiceTasksRequested?: (..._args: any[]) => any;
|
onServiceTasksRequested?: (..._args: any[]) => any;
|
||||||
|
onJsonFilesRequested?: (..._args: any[]) => any;
|
||||||
|
onDmnFilesRequested?: (..._args: any[]) => any;
|
||||||
url?: string;
|
url?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -87,8 +93,14 @@ export default function ReactDiagramEditor({
|
||||||
diagramXML,
|
diagramXML,
|
||||||
fileName,
|
fileName,
|
||||||
onLaunchScriptEditor,
|
onLaunchScriptEditor,
|
||||||
|
onLaunchMarkdownEditor,
|
||||||
|
onLaunchBpmnEditor,
|
||||||
|
onLaunchJsonEditor,
|
||||||
|
onLaunchDmnEditor,
|
||||||
onElementClick,
|
onElementClick,
|
||||||
onServiceTasksRequested,
|
onServiceTasksRequested,
|
||||||
|
onJsonFilesRequested,
|
||||||
|
onDmnFilesRequested,
|
||||||
url,
|
url,
|
||||||
}: OwnProps) {
|
}: OwnProps) {
|
||||||
const [diagramXMLString, setDiagramXMLString] = useState('');
|
const [diagramXMLString, setDiagramXMLString] = useState('');
|
||||||
|
@ -191,6 +203,17 @@ export default function ReactDiagramEditor({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleLaunchMarkdownEditor(
|
||||||
|
element: any,
|
||||||
|
value: string,
|
||||||
|
eventBus: any
|
||||||
|
) {
|
||||||
|
if (onLaunchMarkdownEditor) {
|
||||||
|
setPerformingXmlUpdates(true);
|
||||||
|
onLaunchMarkdownEditor(element, value, eventBus);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function handleElementClick(event: any) {
|
function handleElementClick(event: any) {
|
||||||
if (onElementClick) {
|
if (onElementClick) {
|
||||||
onElementClick(event.element);
|
onElementClick(event.element);
|
||||||
|
@ -205,7 +228,7 @@ export default function ReactDiagramEditor({
|
||||||
|
|
||||||
setDiagramModelerState(diagramModeler);
|
setDiagramModelerState(diagramModeler);
|
||||||
|
|
||||||
diagramModeler.on('script.editor.launch', (event: any) => {
|
diagramModeler.on('spiff.script.edit', (event: any) => {
|
||||||
const { error, element, scriptType, script, eventBus } = event;
|
const { error, element, scriptType, script, eventBus } = event;
|
||||||
if (error) {
|
if (error) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
|
@ -213,6 +236,35 @@ export default function ReactDiagramEditor({
|
||||||
handleLaunchScriptEditor(element, script, scriptType, eventBus);
|
handleLaunchScriptEditor(element, script, scriptType, eventBus);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
diagramModeler.on('spiff.markdown.edit', (event: any) => {
|
||||||
|
const { error, element, value, eventBus } = event;
|
||||||
|
if (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
handleLaunchMarkdownEditor(element, value, eventBus);
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* fixme: this is not in use yet, we need the ability to find bpmn files by id.
|
||||||
|
*/
|
||||||
|
diagramModeler.on('spiff.callactivity.edit', (event: any) => {
|
||||||
|
if (onLaunchBpmnEditor) {
|
||||||
|
onLaunchBpmnEditor(event.processId);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
diagramModeler.on('spiff.file.edit', (event: any) => {
|
||||||
|
if (onLaunchJsonEditor) {
|
||||||
|
onLaunchJsonEditor(event.value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
diagramModeler.on('spiff.dmn.edit', (event: any) => {
|
||||||
|
if (onLaunchDmnEditor) {
|
||||||
|
onLaunchDmnEditor(event.value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// 'element.hover',
|
// 'element.hover',
|
||||||
// 'element.out',
|
// 'element.out',
|
||||||
// 'element.click',
|
// 'element.click',
|
||||||
|
@ -226,12 +278,34 @@ export default function ReactDiagramEditor({
|
||||||
diagramModeler.on('spiff.service_tasks.requested', (event: any) => {
|
diagramModeler.on('spiff.service_tasks.requested', (event: any) => {
|
||||||
handleServiceTasksRequested(event);
|
handleServiceTasksRequested(event);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
diagramModeler.on('spiff.json_files.requested', (event: any) => {
|
||||||
|
if (onJsonFilesRequested) {
|
||||||
|
onJsonFilesRequested(event);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
diagramModeler.on('spiff.dmn_files.requested', (event: any) => {
|
||||||
|
if (onDmnFilesRequested) {
|
||||||
|
onDmnFilesRequested(event);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
diagramModeler.on('spiff.json_files.requested', (event: any) => {
|
||||||
|
handleServiceTasksRequested(event);
|
||||||
|
});
|
||||||
}, [
|
}, [
|
||||||
diagramModelerState,
|
diagramModelerState,
|
||||||
diagramType,
|
diagramType,
|
||||||
onLaunchScriptEditor,
|
onLaunchScriptEditor,
|
||||||
|
onLaunchMarkdownEditor,
|
||||||
|
onLaunchBpmnEditor,
|
||||||
|
onLaunchDmnEditor,
|
||||||
|
onLaunchJsonEditor,
|
||||||
onElementClick,
|
onElementClick,
|
||||||
onServiceTasksRequested,
|
onServiceTasksRequested,
|
||||||
|
onJsonFilesRequested,
|
||||||
|
onDmnFilesRequested,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
|
@ -17,11 +17,29 @@ export interface ProcessGroup {
|
||||||
description?: string | null;
|
description?: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ProcessFileReference {
|
||||||
|
id: string; // The unique id of the process or decision table.
|
||||||
|
name: string; // The process or decision table name.
|
||||||
|
type: string; // either "decision" or "process"
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ProcessFile {
|
||||||
|
content_type: string;
|
||||||
|
last_modified: string;
|
||||||
|
name: string;
|
||||||
|
process_group_id: string;
|
||||||
|
process_model_id: string;
|
||||||
|
references: ProcessFileReference[];
|
||||||
|
size: number;
|
||||||
|
type: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface ProcessModel {
|
export interface ProcessModel {
|
||||||
id: string;
|
id: string;
|
||||||
process_group_id: string;
|
process_group_id: string;
|
||||||
display_name: string;
|
display_name: string;
|
||||||
primary_file_name: string;
|
primary_file_name: string;
|
||||||
|
files: ProcessFile[];
|
||||||
}
|
}
|
||||||
|
|
||||||
// tuple of display value and URL
|
// tuple of display value and URL
|
||||||
|
|
|
@ -1,23 +1,29 @@
|
||||||
import { useContext, useEffect, useRef, useState } from 'react';
|
import { useContext, useEffect, useRef, useState } from 'react';
|
||||||
import { useNavigate, useParams, useSearchParams } from 'react-router-dom';
|
import {
|
||||||
|
generatePath,
|
||||||
|
useNavigate,
|
||||||
|
useParams,
|
||||||
|
useSearchParams,
|
||||||
|
} from 'react-router-dom';
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import { Button, Modal, Stack, Content } from '@carbon/react';
|
import { Button, Modal, Stack, Content } from '@carbon/react';
|
||||||
// import Container from 'react-bootstrap/Container';
|
|
||||||
import Row from 'react-bootstrap/Row';
|
import Row from 'react-bootstrap/Row';
|
||||||
import Col from 'react-bootstrap/Col';
|
import Col from 'react-bootstrap/Col';
|
||||||
|
|
||||||
import Editor from '@monaco-editor/react';
|
import Editor from '@monaco-editor/react';
|
||||||
|
|
||||||
|
import MDEditor from '@uiw/react-md-editor';
|
||||||
import ReactDiagramEditor from '../components/ReactDiagramEditor';
|
import ReactDiagramEditor from '../components/ReactDiagramEditor';
|
||||||
import ProcessBreadcrumb from '../components/ProcessBreadcrumb';
|
import ProcessBreadcrumb from '../components/ProcessBreadcrumb';
|
||||||
import HttpService from '../services/HttpService';
|
import HttpService from '../services/HttpService';
|
||||||
import ErrorContext from '../contexts/ErrorContext';
|
import ErrorContext from '../contexts/ErrorContext';
|
||||||
import { makeid } from '../helpers';
|
import { makeid } from '../helpers';
|
||||||
import { ProcessModel } from '../interfaces';
|
import { ProcessFile, ProcessModel } from '../interfaces';
|
||||||
|
|
||||||
export default function ProcessModelEditDiagram() {
|
export default function ProcessModelEditDiagram() {
|
||||||
const [showFileNameEditor, setShowFileNameEditor] = useState(false);
|
const [showFileNameEditor, setShowFileNameEditor] = useState(false);
|
||||||
const handleShowFileNameEditor = () => setShowFileNameEditor(true);
|
const handleShowFileNameEditor = () => setShowFileNameEditor(true);
|
||||||
|
const [processModel, setProcessModel] = useState<ProcessModel | null>(null);
|
||||||
|
|
||||||
const [scriptText, setScriptText] = useState<string>('');
|
const [scriptText, setScriptText] = useState<string>('');
|
||||||
const [scriptType, setScriptType] = useState<string>('');
|
const [scriptType, setScriptType] = useState<string>('');
|
||||||
|
@ -27,6 +33,12 @@ export default function ProcessModelEditDiagram() {
|
||||||
const [showScriptEditor, setShowScriptEditor] = useState(false);
|
const [showScriptEditor, setShowScriptEditor] = useState(false);
|
||||||
const handleShowScriptEditor = () => setShowScriptEditor(true);
|
const handleShowScriptEditor = () => setShowScriptEditor(true);
|
||||||
|
|
||||||
|
const [markdownText, setMarkdownText] = useState<string | undefined>('');
|
||||||
|
const [markdownEventBus, setMarkdownEventBus] = useState<any>(null);
|
||||||
|
const [showMarkdownEditor, setShowMarkdownEditor] = useState(false);
|
||||||
|
|
||||||
|
const handleShowMarkdownEditor = () => setShowMarkdownEditor(true);
|
||||||
|
|
||||||
const editorRef = useRef(null);
|
const editorRef = useRef(null);
|
||||||
const monacoRef = useRef(null);
|
const monacoRef = useRef(null);
|
||||||
|
|
||||||
|
@ -70,8 +82,6 @@ export default function ProcessModelEditDiagram() {
|
||||||
const [bpmnXmlForDiagramRendering, setBpmnXmlForDiagramRendering] =
|
const [bpmnXmlForDiagramRendering, setBpmnXmlForDiagramRendering] =
|
||||||
useState(null);
|
useState(null);
|
||||||
|
|
||||||
const [processModel, setProcessModel] = useState<ProcessModel | null>(null);
|
|
||||||
|
|
||||||
const processModelPath = `process-models/${params.process_group_id}/${params.process_model_id}`;
|
const processModelPath = `process-models/${params.process_group_id}/${params.process_model_id}`;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -85,7 +95,7 @@ export default function ProcessModelEditDiagram() {
|
||||||
}, [processModelPath]);
|
}, [processModelPath]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const processResult = (result: any) => {
|
const fileResult = (result: any) => {
|
||||||
setProcessModelFile(result);
|
setProcessModelFile(result);
|
||||||
setBpmnXmlForDiagramRendering(result.file_contents);
|
setBpmnXmlForDiagramRendering(result.file_contents);
|
||||||
};
|
};
|
||||||
|
@ -93,7 +103,7 @@ export default function ProcessModelEditDiagram() {
|
||||||
if (params.file_name) {
|
if (params.file_name) {
|
||||||
HttpService.makeCallToBackend({
|
HttpService.makeCallToBackend({
|
||||||
path: `/${processModelPath}/files/${params.file_name}`,
|
path: `/${processModelPath}/files/${params.file_name}`,
|
||||||
successCallback: processResult,
|
successCallback: fileResult,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, [processModelPath, params]);
|
}, [processModelPath, params]);
|
||||||
|
@ -246,6 +256,34 @@ export default function ProcessModelEditDiagram() {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const onJsonFilesRequested = (event: any) => {
|
||||||
|
if (processModel) {
|
||||||
|
const jsonFiles = processModel.files.filter((f) => f.type === 'json');
|
||||||
|
const options = jsonFiles.map((f) => {
|
||||||
|
return { label: f.name, value: f.name };
|
||||||
|
});
|
||||||
|
event.eventBus.fire('spiff.json_files.returned', { options });
|
||||||
|
} else {
|
||||||
|
console.log('There is no process Model.');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const onDmnFilesRequested = (event: any) => {
|
||||||
|
if (processModel) {
|
||||||
|
const dmnFiles = processModel.files.filter((f) => f.type === 'dmn');
|
||||||
|
const options: any[] = [];
|
||||||
|
dmnFiles.forEach((file) => {
|
||||||
|
file.references.forEach((ref) => {
|
||||||
|
options.push({ label: ref.name, value: ref.id });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
console.log('Options', options);
|
||||||
|
event.eventBus.fire('spiff.dmn_files.returned', { options });
|
||||||
|
} else {
|
||||||
|
console.log('There is no process model.');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const getScriptUnitTestElements = (element: any) => {
|
const getScriptUnitTestElements = (element: any) => {
|
||||||
const { extensionElements } = element.businessObject;
|
const { extensionElements } = element.businessObject;
|
||||||
if (extensionElements && extensionElements.values.length > 0) {
|
if (extensionElements && extensionElements.values.length > 0) {
|
||||||
|
@ -292,7 +330,7 @@ export default function ProcessModelEditDiagram() {
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleScriptEditorClose = () => {
|
const handleScriptEditorClose = () => {
|
||||||
scriptEventBus.fire('script.editor.update', {
|
scriptEventBus.fire('spiff.script.update', {
|
||||||
scriptType,
|
scriptType,
|
||||||
script: scriptText,
|
script: scriptText,
|
||||||
element: scriptElement,
|
element: scriptElement,
|
||||||
|
@ -580,6 +618,107 @@ export default function ProcessModelEditDiagram() {
|
||||||
</Modal>
|
</Modal>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
const onLaunchMarkdownEditor = (
|
||||||
|
element: any,
|
||||||
|
markdown: string,
|
||||||
|
eventBus: any
|
||||||
|
) => {
|
||||||
|
setMarkdownText(markdown || '');
|
||||||
|
setMarkdownEventBus(eventBus);
|
||||||
|
handleShowMarkdownEditor();
|
||||||
|
};
|
||||||
|
const handleMarkdownEditorClose = () => {
|
||||||
|
markdownEventBus.fire('spiff.markdown.update', {
|
||||||
|
value: markdownText,
|
||||||
|
});
|
||||||
|
setShowMarkdownEditor(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const markdownEditor = () => {
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
size="xl"
|
||||||
|
show={showMarkdownEditor}
|
||||||
|
onHide={handleMarkdownEditorClose}
|
||||||
|
>
|
||||||
|
<Modal.Header closeButton>
|
||||||
|
<Modal.Title>Edit Markdown Content</Modal.Title>
|
||||||
|
</Modal.Header>
|
||||||
|
<Modal.Body>
|
||||||
|
<MDEditor value={markdownText} onChange={setMarkdownText} />
|
||||||
|
</Modal.Body>
|
||||||
|
<Modal.Footer>
|
||||||
|
<Button variant="secondary" onClick={handleMarkdownEditorClose}>
|
||||||
|
Close
|
||||||
|
</Button>
|
||||||
|
</Modal.Footer>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const findFileNameForReferenceId = (
|
||||||
|
id: string,
|
||||||
|
type: string
|
||||||
|
): ProcessFile | null => {
|
||||||
|
// Given a reference id (like a process_id, or decision_id) finds the file
|
||||||
|
// that contains that reference and returns it.
|
||||||
|
let matchFile = null;
|
||||||
|
if (processModel) {
|
||||||
|
const files = processModel.files.filter((f) => f.type === type);
|
||||||
|
files.some((file) => {
|
||||||
|
if (file.references.some((ref) => ref.id === id)) {
|
||||||
|
matchFile = file;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return matchFile;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* fixme: Not currently in use. This would only work for bpmn files within the process model. Which is right for DMN and json, but not right here. Need to merge in work on the nested process groups before tackling this.
|
||||||
|
* @param processId
|
||||||
|
*/
|
||||||
|
const onLaunchBpmnEditor = (processId: string) => {
|
||||||
|
const file = findFileNameForReferenceId(processId, 'bpmn');
|
||||||
|
if (file) {
|
||||||
|
const path = generatePath(
|
||||||
|
'/admin/process-models/:process_group_id/:process_model_id/files/:file_name',
|
||||||
|
{
|
||||||
|
process_group_id: params.process_group_id,
|
||||||
|
process_model_id: params.process_model_id,
|
||||||
|
file_name: file.name,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
window.open(path);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const onLaunchJsonEditor = (fileName: string) => {
|
||||||
|
const path = generatePath(
|
||||||
|
'/admin/process-models/:process_group_id/:process_model_id/form/:file_name',
|
||||||
|
{
|
||||||
|
process_group_id: params.process_group_id,
|
||||||
|
process_model_id: params.process_model_id,
|
||||||
|
file_name: fileName,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
window.open(path);
|
||||||
|
};
|
||||||
|
const onLaunchDmnEditor = (processId: string) => {
|
||||||
|
const file = findFileNameForReferenceId(processId, 'dmn');
|
||||||
|
if (file) {
|
||||||
|
const path = generatePath(
|
||||||
|
'/admin/process-models/:process_group_id/:process_model_id/files/:file_name',
|
||||||
|
{
|
||||||
|
process_group_id: params.process_group_id,
|
||||||
|
process_model_id: params.process_model_id,
|
||||||
|
file_name: file.name,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
window.open(path);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const isDmn = () => {
|
const isDmn = () => {
|
||||||
const fileName = params.file_name || '';
|
const fileName = params.file_name || '';
|
||||||
|
@ -622,12 +761,18 @@ export default function ProcessModelEditDiagram() {
|
||||||
diagramType="bpmn"
|
diagramType="bpmn"
|
||||||
onLaunchScriptEditor={onLaunchScriptEditor}
|
onLaunchScriptEditor={onLaunchScriptEditor}
|
||||||
onServiceTasksRequested={onServiceTasksRequested}
|
onServiceTasksRequested={onServiceTasksRequested}
|
||||||
|
onLaunchMarkdownEditor={onLaunchMarkdownEditor}
|
||||||
|
onLaunchBpmnEditor={onLaunchBpmnEditor}
|
||||||
|
onLaunchJsonEditor={onLaunchJsonEditor}
|
||||||
|
onJsonFilesRequested={onJsonFilesRequested}
|
||||||
|
onLaunchDmnEditor={onLaunchDmnEditor}
|
||||||
|
onDmnFilesRequested={onDmnFilesRequested}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
// if a file name is not given then this is a new model and the ReactDiagramEditor component will handle it
|
// if a file name is not given then this is a new model and the ReactDiagramEditor component will handle it
|
||||||
if (bpmnXmlForDiagramRendering || !params.file_name) {
|
if ((bpmnXmlForDiagramRendering || !params.file_name) && processModel) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ProcessBreadcrumb
|
<ProcessBreadcrumb
|
||||||
|
@ -642,6 +787,7 @@ export default function ProcessModelEditDiagram() {
|
||||||
{appropriateEditor()}
|
{appropriateEditor()}
|
||||||
{newFileNameBox()}
|
{newFileNameBox()}
|
||||||
{scriptEditor()}
|
{scriptEditor()}
|
||||||
|
{markdownEditor()}
|
||||||
|
|
||||||
<div id="diagram-container" />
|
<div id="diagram-container" />
|
||||||
</>
|
</>
|
||||||
|
|
Loading…
Reference in New Issue