diff --git a/package-lock.json b/package-lock.json index dc0c3c5..1792581 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,7 @@ "@testing-library/jest-dom": "^5.16.4", "@testing-library/react": "^13.3.0", "@testing-library/user-event": "^13.5.0", + "axios": "^0.27.2", "bpmn-js": "^9.1.0", "bpmn-js-properties-panel": "^1.1.1", "react": "^18.1.0", @@ -4691,6 +4692,28 @@ "node": ">=12" } }, + "node_modules/axios": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz", + "integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==", + "dependencies": { + "follow-redirects": "^1.14.9", + "form-data": "^4.0.0" + } + }, + "node_modules/axios/node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/axobject-query": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.2.0.tgz", @@ -19921,6 +19944,27 @@ "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.4.2.tgz", "integrity": "sha512-LVAaGp/wkkgYJcjmHsoKx4juT1aQvJyPcW09MLCjVTh3V2cc6PnyempiLMNH5iMdfIX/zdbjUx2KDjMLCTdPeA==" }, + "axios": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz", + "integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==", + "requires": { + "follow-redirects": "^1.14.9", + "form-data": "^4.0.0" + }, + "dependencies": { + "form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + } + } + }, "axobject-query": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.2.0.tgz", diff --git a/package.json b/package.json index f7d6a99..9a5ab7a 100644 --- a/package.json +++ b/package.json @@ -6,6 +6,7 @@ "@testing-library/jest-dom": "^5.16.4", "@testing-library/react": "^13.3.0", "@testing-library/user-event": "^13.5.0", + "axios": "^0.27.2", "bpmn-js": "^9.1.0", "bpmn-js-properties-panel": "^1.1.1", "react": "^18.1.0", diff --git a/src/App.js b/src/App.js index 7cb686e..fe426bc 100644 --- a/src/App.js +++ b/src/App.js @@ -1,12 +1,19 @@ // import logo from './logo.svg'; -import './App.css'; -import BpmnModeler from 'bpmn-js'; -import { - BpmnPropertiesPanelModule, - BpmnPropertiesProviderModule, -} from 'bpmn-js-properties-panel'; +// import './App.css'; +// import BpmnModeler from 'bpmn-js'; +// import { +// BpmnPropertiesPanelModule, +// BpmnPropertiesProviderModule, +// } from 'bpmn-js-properties-panel'; +// +import React, { useEffect, useState } from "react"; +import Modeler from "bpmn-js/lib/Modeler"; +import "bpmn-js/dist/assets/diagram-js.css"; +import "bpmn-js/dist/assets/bpmn-font/css/bpmn-embedded.css"; +import axios from "axios"; +import ReactEditor from "./react_editor" -function App(props) { +function App() { // return ( //
//
@@ -25,6 +32,146 @@ function App(props) { //
//
// ); + + // from spiff backend + // const bpmnModeler = new BpmnModeler({ + // container: '#canvas', + // propertiesPanel: { + // parent: '#properties' + // }, + // additionalModules: [ + // BpmnPropertiesPanelModule, + // BpmnPropertiesProviderModule + // ] + // }); + // + // async function exportDiagram() { + // try { + // var data = await bpmnModeler.saveXML({ format: true }); + // //POST request with body equal on data in JSON format + // fetch("/admin/process-models/{{ process_model.id }}/save/{{ file_name }}", { + // method: "POST", + // headers: { + // "Content-Type": "text/xml", + // }, + // body: data.xml, + // }) + // .then((response) => response.json()) + // //Then with the data from the response in JSON... + // .then((data) => { + // console.log("Success:", data); + // }) + // //Then with the error genereted... + // .catch((error) => { + // console.error("Error:", error); + // }); + // + // alert("Diagram exported. Check the developer tools!"); + // } catch (err) { + // console.error("could not save BPMN 2.0 diagram", err); + // } + // } + // + // #<{(|* + // * Open diagram in our modeler instance. + // * + // * @param {String} bpmnXML diagram to display + // |)}># + // async function openDiagram(bpmnXML) { + // // import diagram + // try { + // await bpmnModeler.importXML(bpmnXML); + // + // // access modeler components + // var canvas = bpmnModeler.get("canvas"); + // var overlays = bpmnModeler.get("overlays"); + // + // // zoom to fit full viewport + // canvas.zoom("fit-viewport"); + // + // // attach an overlay to a node + // overlays.add("SCAN_OK", "note", { + // position: { + // bottom: 0, + // right: 0, + // }, + // html: '
Mixed up the labels?
', + // }); + // + // // add marker + // canvas.addMarker("SCAN_OK", "needs-discussion"); + // } catch (err) { + // console.error("could not import BPMN 2.0 diagram", err); + // } + // } + // + // // trying to use the python variable bpmn_xml directly causes the xml to have escape sequences + // // and using the meta tag seems to help with that + // // var bpmn_xml = $("#bpmn_xml").data(); + // openDiagram({process.env.PUBLIC_URL + '/sample.bpmn'}); + // + // // wire save button + // $("#save-button").click(exportDiagram); + + + // const [diagram, diagramSet] = useState(""); + // const container = document.getElementById("container"); + // + // useEffect(() => { + // if (diagram.length === 0) { + // axios + // .get( + // "https://cdn.staticaly.com/gh/bpmn-io/bpmn-js-examples/master/colors/resources/pizza-collaboration.bpmn" + // ) + // .then((r) => { + // diagramSet(r.data); + // }) + // .catch((e) => { + // console.log(e); + // }); + // } + // }, [diagram]); + // + // if (diagram.length > 0) { + // const modeler = new Modeler({ + // container, + // keyboard: { + // bindTo: document + // } + // }); + // modeler + // .importXML(diagram) + // .then(({ warnings }) => { + // if (warnings.length) { + // console.log("Warnings", warnings); + // } + // + // const canvas = modeler.get("modeling"); + // canvas.setColor("CalmCustomerTask", { + // stroke: "green", + // fill: "yellow" + // }); + // }) + // .catch((err) => { + // console.log("error", err); + // }); + // } + // + // return ( + //
+ //
+ //
+ // ); + + function onShown() { console.log('diagram shown'); } @@ -37,95 +184,14 @@ function App(props) { console.log('failed to show diagram'); } - // return ( - // - // ); - // - - const bpmnModeler = new BpmnModeler({ - container: '#canvas', - propertiesPanel: { - parent: '#properties' - }, - additionalModules: [ - BpmnPropertiesPanelModule, - BpmnPropertiesProviderModule - ] - }); - - async function exportDiagram() { - try { - var data = await bpmnModeler.saveXML({ format: true }); - //POST request with body equal on data in JSON format - fetch("/admin/process-models/{{ process_model.id }}/save/{{ file_name }}", { - method: "POST", - headers: { - "Content-Type": "text/xml", - }, - body: data.xml, - }) - .then((response) => response.json()) - //Then with the data from the response in JSON... - .then((data) => { - console.log("Success:", data); - }) - //Then with the error genereted... - .catch((error) => { - console.error("Error:", error); - }); - - alert("Diagram exported. Check the developer tools!"); - } catch (err) { - console.error("could not save BPMN 2.0 diagram", err); - } - } - - /** - * Open diagram in our modeler instance. - * - * @param {String} bpmnXML diagram to display - */ - async function openDiagram(bpmnXML) { - // import diagram - try { - await bpmnModeler.importXML(bpmnXML); - - // access modeler components - var canvas = bpmnModeler.get("canvas"); - var overlays = bpmnModeler.get("overlays"); - - // zoom to fit full viewport - canvas.zoom("fit-viewport"); - - // attach an overlay to a node - overlays.add("SCAN_OK", "note", { - position: { - bottom: 0, - right: 0, - }, - html: '
Mixed up the labels?
', - }); - - // add marker - canvas.addMarker("SCAN_OK", "needs-discussion"); - } catch (err) { - console.error("could not import BPMN 2.0 diagram", err); - } - } - - // trying to use the python variable bpmn_xml directly causes the xml to have escape sequences - // and using the meta tag seems to help with that - // var bpmn_xml = $("#bpmn_xml").data(); - openDiagram({process.env.PUBLIC_URL + '/sample.bpmn'}); - - // wire save button - $("#save-button").click(exportDiagram); - + return ( + + ); } export default App; diff --git a/src/bpmn-js-properties-panel.css b/src/bpmn-js-properties-panel.css new file mode 100644 index 0000000..e2866ce --- /dev/null +++ b/src/bpmn-js-properties-panel.css @@ -0,0 +1,101 @@ +* { + box-sizing: border-box; +} +body, +html { + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 12px; + height: 100%; + max-height: 100%; + padding: 0; + margin: 0; +} +#js-properties-panel { + width: 400px; +} +a:link { + text-decoration: none; +} +.content { + position: relative; + width: 100%; + height: 100%; + display: flex; +} +.content > .message { + width: 100%; + height: 100%; + text-align: center; + display: table; + font-size: 16px; + color: #111; +} +.content > .message .note { + vertical-align: middle; + text-align: center; + display: table-cell; +} +.content > .message.error .details { + max-width: 500px; + font-size: 12px; + margin: 20px auto; + text-align: left; + color: #BD2828; +} +.content > .message.error pre { + border: solid 1px #BD2828; + background: #fefafa; + padding: 10px; + color: #BD2828; +} +.content:not(.with-error) .error, +.content.with-error .intro, +.content.with-diagram .intro { + display: none; +} +.content .canvas { + width: 100%; +} +.content .canvas, +.content .properties-panel-parent { + display: none; +} +.content.with-diagram .canvas, +.content.with-diagram .properties-panel-parent { + display: block; +} +.buttons { + position: fixed; + bottom: 20px; + left: 20px; + padding: 0; + margin: 0; + list-style: none; +} +.buttons > li { + display: inline-block; + margin-right: 10px; +} +.buttons > li > a { + background: #DDD; + border: solid 1px #666; + display: inline-block; + padding: 5px; +} +.buttons a { + opacity: 0.3; +} +.buttons a.active { + opacity: 1; +} +.properties-panel-parent { + border-left: 1px solid #ccc; + overflow: auto; +} +.properties-panel-parent:empty { + display: none; +} +.properties-panel-parent > .djs-properties-panel { + padding-bottom: 70px; + min-height: 100%; +} diff --git a/src/react_editor.js b/src/react_editor.js new file mode 100644 index 0000000..4c1e1bc --- /dev/null +++ b/src/react_editor.js @@ -0,0 +1,143 @@ +import BpmnModeler from 'bpmn-js/lib/Modeler'; +import { + BpmnPropertiesPanelModule, + BpmnPropertiesProviderModule, +} from 'bpmn-js-properties-panel'; +import React from "react"; + +import "bpmn-js-properties-panel/dist/assets/properties-panel.css" +import './bpmn-js-properties-panel.css'; + +export default class ReactEditor extends React.Component { + constructor(props) { + super(props); + + this.state = { }; + + this.containerRef = React.createRef(); + } + + componentDidMount() { + + const { + url, + diagramXML + } = this.props; + + const container = this.containerRef.current; + + this.bpmnViewer = new BpmnModeler({ + container: container, + keyboard: { + bindTo: document + }, + propertiesPanel: { + parent: '#js-properties-panel' + }, + additionalModules: [ + BpmnPropertiesPanelModule, + BpmnPropertiesProviderModule + ] + }); + + this.bpmnViewer.on('import.done', (event) => { + const { + error, + warnings + } = event; + + if (error) { + return this.handleError(error); + } + + this.bpmnViewer.get('canvas').zoom('fit-viewport'); + + return this.handleShown(warnings); + }); + + if (url) { + return this.fetchDiagram(url); + } + + if (diagramXML) { + return this.displayDiagram(diagramXML); + } + } + + componentWillUnmount() { + this.bpmnViewer.destroy(); + } + + componentDidUpdate(prevProps, prevState) { + const { + props, + state + } = this; + + if (props.url !== prevProps.url) { + return this.fetchDiagram(props.url); + } + + const currentXML = props.diagramXML || state.diagramXML; + + const previousXML = prevProps.diagramXML || prevState.diagramXML; + + if (currentXML && currentXML !== previousXML) { + return this.displayDiagram(currentXML); + } + } + + displayDiagram(diagramXML) { + this.bpmnViewer.importXML(diagramXML); + } + + fetchDiagram(url) { + + this.handleLoading(); + + fetch(url) + .then(response => response.text()) + .then(text => this.setState({ diagramXML: text })) + .catch(err => this.handleError(err)); + } + + handleLoading() { + const { onLoading } = this.props; + + if (onLoading) { + onLoading(); + } + } + + handleError(err) { + const { onError } = this.props; + + if (onError) { + onError(err); + } + } + + handleShown(warnings) { + const { onShown } = this.props; + + if (onShown) { + onShown(warnings); + } + } + + render() { + return ( +
+
+
+
+ ); + } +}