aboutsummaryrefslogtreecommitdiffstats
path: root/workflow-designer-ui
diff options
context:
space:
mode:
Diffstat (limited to 'workflow-designer-ui')
-rw-r--r--workflow-designer-ui/src/main/frontend/.gitignore2
-rw-r--r--workflow-designer-ui/src/main/frontend/index.html4
-rw-r--r--workflow-designer-ui/src/main/frontend/package.json1
-rw-r--r--workflow-designer-ui/src/main/frontend/resources/scss/components/_notifications.scss1
-rw-r--r--workflow-designer-ui/src/main/frontend/resources/scss/features/_composition.scss41
-rw-r--r--workflow-designer-ui/src/main/frontend/resources/scss/style.scss2
-rw-r--r--workflow-designer-ui/src/main/frontend/src/features/version/composition/Composition.js47
-rw-r--r--workflow-designer-ui/src/main/frontend/src/features/version/composition/CompositionView.js141
-rw-r--r--workflow-designer-ui/src/main/frontend/src/features/version/composition/components/CompositionButton.js33
-rw-r--r--workflow-designer-ui/src/main/frontend/src/features/version/composition/components/CompositionButtonsPanel.js52
-rw-r--r--workflow-designer-ui/src/main/frontend/src/features/version/composition/compositionActions.js21
-rw-r--r--workflow-designer-ui/src/main/frontend/src/features/version/composition/compositionConstants.js16
-rw-r--r--workflow-designer-ui/src/main/frontend/src/features/version/composition/compositionReducer.js27
-rw-r--r--workflow-designer-ui/src/main/frontend/src/features/version/composition/compositionSelectors.js17
-rw-r--r--workflow-designer-ui/src/main/frontend/src/features/version/composition/custom-modeler/custom/CustomContextPadProvider.js43
-rw-r--r--workflow-designer-ui/src/main/frontend/src/features/version/composition/custom-modeler/custom/CustomElementFactory.js101
-rw-r--r--workflow-designer-ui/src/main/frontend/src/features/version/composition/custom-modeler/custom/CustomPalette.js151
-rw-r--r--workflow-designer-ui/src/main/frontend/src/features/version/composition/custom-modeler/custom/CustomRenderer.js176
-rw-r--r--workflow-designer-ui/src/main/frontend/src/features/version/composition/custom-modeler/custom/CustomRules.js136
-rw-r--r--workflow-designer-ui/src/main/frontend/src/features/version/composition/custom-modeler/custom/CustomUpdater.js136
-rw-r--r--workflow-designer-ui/src/main/frontend/src/features/version/composition/custom-modeler/custom/index.js22
-rw-r--r--workflow-designer-ui/src/main/frontend/src/features/version/composition/custom-modeler/index.js99
-rw-r--r--workflow-designer-ui/src/main/frontend/src/features/version/versionApi.js15
-rw-r--r--workflow-designer-ui/src/main/frontend/src/features/version/versionController/versionControllerSelectors.js5
-rw-r--r--workflow-designer-ui/src/main/frontend/src/features/version/versionSaga.js29
-rw-r--r--workflow-designer-ui/src/main/frontend/src/features/workflow/overview/__tests__/overviewReducer-test.js4
-rw-r--r--workflow-designer-ui/src/main/frontend/src/features/workflow/overview/overviewReducer.js2
-rw-r--r--workflow-designer-ui/src/main/frontend/src/i18n/languages.json3
-rw-r--r--workflow-designer-ui/src/main/frontend/src/rootReducers.js5
-rw-r--r--workflow-designer-ui/src/main/frontend/src/routes.js4
-rw-r--r--workflow-designer-ui/src/main/frontend/webpack.config.js9
-rw-r--r--workflow-designer-ui/src/main/frontend/yarn.lock4
32 files changed, 1294 insertions, 55 deletions
diff --git a/workflow-designer-ui/src/main/frontend/.gitignore b/workflow-designer-ui/src/main/frontend/.gitignore
index d2d69816..3491fa20 100644
--- a/workflow-designer-ui/src/main/frontend/.gitignore
+++ b/workflow-designer-ui/src/main/frontend/.gitignore
@@ -4,7 +4,7 @@
/node_modules
/node-install
.idea/
-
+.vscode/
# testing
/coverage
diff --git a/workflow-designer-ui/src/main/frontend/index.html b/workflow-designer-ui/src/main/frontend/index.html
index c3a6f3b6..09d2d0a9 100644
--- a/workflow-designer-ui/src/main/frontend/index.html
+++ b/workflow-designer-ui/src/main/frontend/index.html
@@ -2,9 +2,7 @@
<html>
<head>
<base href="/">
- <meta charset="utf-8">
- <link rel="stylesheet" href="https://unpkg.com/bpmn-js@2.1.0/dist/assets/diagram-js.css" />
- <link rel="stylesheet" href="https://unpkg.com/bpmn-js@2.1.0/dist/assets/bpmn-font/css/bpmn.css" />
+ <meta charset="utf-8">
<title>SDC Workflow App</title>
</head>
<body>
diff --git a/workflow-designer-ui/src/main/frontend/package.json b/workflow-designer-ui/src/main/frontend/package.json
index 529d9096..bd224ba2 100644
--- a/workflow-designer-ui/src/main/frontend/package.json
+++ b/workflow-designer-ui/src/main/frontend/package.json
@@ -23,6 +23,7 @@
"dateformat": "^3.0.3",
"enzyme": "^3.3.0",
"enzyme-adapter-react-16": "^1.1.1",
+ "file-saver": "^1.3.8",
"http-proxy-middleware": "^0.17.4",
"lodash": "^3.0.1",
"md5": "^2.2.1",
diff --git a/workflow-designer-ui/src/main/frontend/resources/scss/components/_notifications.scss b/workflow-designer-ui/src/main/frontend/resources/scss/components/_notifications.scss
index cc44cfbc..855c372e 100644
--- a/workflow-designer-ui/src/main/frontend/resources/scss/components/_notifications.scss
+++ b/workflow-designer-ui/src/main/frontend/resources/scss/components/_notifications.scss
@@ -1,5 +1,6 @@
.workflow-notifications-container {
position: absolute;
+ z-index: 99999;
&.position-top-right {
right: 30px;
top: 50px;
diff --git a/workflow-designer-ui/src/main/frontend/resources/scss/features/_composition.scss b/workflow-designer-ui/src/main/frontend/resources/scss/features/_composition.scss
index 7ab294a2..a159a4b7 100644
--- a/workflow-designer-ui/src/main/frontend/resources/scss/features/_composition.scss
+++ b/workflow-designer-ui/src/main/frontend/resources/scss/features/_composition.scss
@@ -4,12 +4,43 @@
.bpmn-container {
flex-basis: 100%;
- height: 100%;
+ flex-grow: 1
}
-
- .properties-panel {
- &, .bpp-properties-panel {
- height: 100%;
+ .bpmn-sidebar {
+ height: 100%;
+ .properties-panel {
+ &, .bpp-properties-panel {
+ height: 100%;
+ }
+ }
+ .composition-buttons {
+ position: fixed;
+ background-color: #fafafa;
+ left: 265px;
+ bottom: 46px;
+ border: 1px solid lightgray;
+ width: 189px;
+ display: flex;
+ flex-direction: row;
+ justify-content: space-around;
+ height: 57px;
+ align-items: center;
+ padding: 10px;
+ .divider {
+ height: 35px;
+ border: 1px solid $silver;
+ }
+ .diagram-btn {
+
+ &:hover {
+ fill: $blue;
+ cursor: pointer;
+ }
+ .svg-icon {
+ width: 25px;
+ height: 23px;
+ }
+ }
}
}
}
diff --git a/workflow-designer-ui/src/main/frontend/resources/scss/style.scss b/workflow-designer-ui/src/main/frontend/resources/scss/style.scss
index 95828ae2..49278565 100644
--- a/workflow-designer-ui/src/main/frontend/resources/scss/style.scss
+++ b/workflow-designer-ui/src/main/frontend/resources/scss/style.scss
@@ -1,3 +1,5 @@
+@import '../../node_modules/bpmn-js/dist/assets/diagram-js.css';
+@import '../../node_modules/bpmn-js/dist/assets/bpmn-font/css/bpmn.css';
@import 'common';
@import '../../node_modules/sdc-ui/lib/css/style.css';
@import 'components';
diff --git a/workflow-designer-ui/src/main/frontend/src/features/version/composition/Composition.js b/workflow-designer-ui/src/main/frontend/src/features/version/composition/Composition.js
new file mode 100644
index 00000000..d2c273c4
--- /dev/null
+++ b/workflow-designer-ui/src/main/frontend/src/features/version/composition/Composition.js
@@ -0,0 +1,47 @@
+/*
+* Copyright © 2018 European Support Limited
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+import { connect } from 'react-redux';
+import { I18n } from 'react-redux-i18n';
+import { updateComposition } from './compositionActions';
+import CompositionView from './CompositionView';
+import { showErrorModalAction } from '../../../shared/modal/modalWrapperActions';
+import { getComposition } from './compositionSelectors';
+import { getWorkflowName } from '../../workflow/workflowSelectors';
+
+function mapStateToProps(state) {
+ return {
+ composition: getComposition(state),
+ name: getWorkflowName(state)
+ };
+}
+
+function mapDispatchToProps(dispatch) {
+ return {
+ compositionUpdate: composition =>
+ dispatch(updateComposition(composition)),
+ showErrorModal: msg =>
+ dispatch(
+ showErrorModalAction({
+ title: I18n.t('workflow.composition.bpmnError'),
+ body: msg,
+ withButtons: true,
+ closeButtonText: 'Ok'
+ })
+ )
+ };
+}
+
+export default connect(mapStateToProps, mapDispatchToProps)(CompositionView);
diff --git a/workflow-designer-ui/src/main/frontend/src/features/version/composition/CompositionView.js b/workflow-designer-ui/src/main/frontend/src/features/version/composition/CompositionView.js
index ba0351b6..d549456f 100644
--- a/workflow-designer-ui/src/main/frontend/src/features/version/composition/CompositionView.js
+++ b/workflow-designer-ui/src/main/frontend/src/features/version/composition/CompositionView.js
@@ -5,7 +5,7 @@
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
- * http://www.apache.org/licenses/LICENSE-2.0
+*http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
@@ -14,78 +14,151 @@
* limitations under the License.
*/
import React, { Component } from 'react';
-import { connect } from 'react-redux';
-
-import BpmnModeler from 'bpmn-js/lib/Modeler';
-// import propertiesPanelModule from 'bpmn-js-properties-panel';
+import fileSaver from 'file-saver';
+import CustomModeler from './custom-modeler';
+import propertiesPanelModule from 'bpmn-js-properties-panel';
import propertiesProviderModule from 'bpmn-js-properties-panel/lib/provider/camunda';
-import camundaModdleDescriptor from 'camunda-bpmn-moddle/resources/camunda';
-
+import camundaModuleDescriptor from 'camunda-bpmn-moddle/resources/camunda';
import newDiagramXML from './newDiagram.bpmn';
+import PropTypes from 'prop-types';
+import CompositionButtons from './components/CompositionButtonsPanel';
class CompositionView extends Component {
+ static propTypes = {
+ compositionUpdate: PropTypes.func,
+ showErrorModal: PropTypes.func,
+ composition: PropTypes.string,
+ name: PropTypes.string
+ };
constructor() {
super();
this.generatedId = 'bpmn-container' + Date.now();
+ this.fileInput = React.createRef();
+ this.state = {
+ diagram: false
+ };
}
componentDidMount() {
- this.modeler = new BpmnModeler({
+ const { composition } = this.props;
+
+ this.modeler = new CustomModeler({
propertiesPanel: {
- parent: '#js-properties-navigationSideBar'
+ parent: '#js-properties-panel'
},
additionalModules: [
- //TODO:: need to fix
- // propertiesPanelModule,
+ propertiesPanelModule,
propertiesProviderModule
],
moddleExtensions: {
- camunda: camundaModdleDescriptor
+ camunda: camundaModuleDescriptor
}
});
window.modeler = this.modeler;
this.modeler.attachTo('#' + this.generatedId);
- // let diagramXML =
- // '<?xml version="1.0" encoding="UTF-8"?>\r\n<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"\r\n xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI"\r\n xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC"\r\n xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI"\r\n xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"\r\n expressionLanguage="http://www.w3.org/1999/XPath"\r\n typeLanguage="http://www.w3.org/2001/XMLSchema"\r\n targetNamespace=""\r\n xsi:schemaLocation="http://www.omg.org/spec/BPMN/20100524/MODEL http://www.omg.org/spec/BPMN/2.0/20100501/BPMN20.xsd">\r\n<collaboration id="sid-c0e745ff-361e-4afb-8c8d-2a1fc32b1424">\r\n <participant id="sid-87F4C1D6-25E1-4A45-9DA7-AD945993D06F" name="Customer" processRef="sid-C3803939-0872-457F-8336-EAE484DC4A04">\r\n </participant>\r\n</collaboration>\r\n<process id="sid-C3803939-0872-457F-8336-EAE484DC4A04" isClosed="false" isExecutable="false" name="Customer" processType="None">\r\n <extensionElements/>\r\n <laneSet id="sid-b167d0d7-e761-4636-9200-76b7f0e8e83a">\r\n <lane id="sid-57E4FE0D-18E4-478D-BC5D-B15164E93254">\r\n <flowNodeRef>sid-D7F237E8-56D0-4283-A3CE-4F0EFE446138</flowNodeRef>\r\n <flowNodeRef>sid-52EB1772-F36E-433E-8F5B-D5DFD26E6F26</flowNodeRef>\r\n <flowNodeRef>SCAN_OK</flowNodeRef>\r\n <flowNodeRef>sid-E49425CF-8287-4798-B622-D2A7D78EF00B</flowNodeRef>\r\n <flowNodeRef>sid-E433566C-2289-4BEB-A19C-1697048900D2</flowNodeRef>\r\n <flowNodeRef>sid-5134932A-1863-4FFA-BB3C-A4B4078B11A9</flowNodeRef>\r\n </lane>\r\n </laneSet>\r\n <startEvent id="sid-D7F237E8-56D0-4283-A3CE-4F0EFE446138" name="Notices&#10;QR code">\r\n <outgoing>sid-7B791A11-2F2E-4D80-AFB3-91A02CF2B4FD</outgoing>\r\n </startEvent>\r\n <task completionQuantity="1" id="sid-52EB1772-F36E-433E-8F5B-D5DFD26E6F26" isForCompensation="false" name="Scan QR code" startQuantity="1">\r\n <incoming>sid-4DC479E5-5C20-4948-BCFC-9EC5E2F66D8D</incoming>\r\n <outgoing>sid-EE8A7BA0-5D66-4F8B-80E3-CC2751B3856A</outgoing>\r\n </task>\r\n <exclusiveGateway gatewayDirection="Diverging" id="SCAN_OK" name="Scan successful?&#10;">\r\n <incoming>sid-EE8A7BA0-5D66-4F8B-80E3-CC2751B3856A</incoming>\r\n <outgoing>sid-8B820AF5-DC5C-4618-B854-E08B71FB55CB</outgoing>\r\n <outgoing>sid-337A23B9-A923-4CCE-B613-3E247B773CCE</outgoing>\r\n </exclusiveGateway>\r\n <task completionQuantity="1" id="sid-E49425CF-8287-4798-B622-D2A7D78EF00B" isForCompensation="false" name="Open product information in mobile app" startQuantity="1">\r\n <incoming>sid-8B820AF5-DC5C-4618-B854-E08B71FB55CB</incoming>\r\n <outgoing>sid-57EB1F24-BD94-479A-BF1F-57F1EAA19C6C</outgoing>\r\n </task>\r\n <endEvent id="sid-E433566C-2289-4BEB-A19C-1697048900D2" name="Is informed">\r\n <incoming>sid-57EB1F24-BD94-479A-BF1F-57F1EAA19C6C</incoming>\r\n </endEvent>\r\n <exclusiveGateway gatewayDirection="Converging" id="sid-5134932A-1863-4FFA-BB3C-A4B4078B11A9">\r\n <incoming>sid-7B791A11-2F2E-4D80-AFB3-91A02CF2B4FD</incoming>\r\n <incoming>sid-337A23B9-A923-4CCE-B613-3E247B773CCE</incoming>\r\n <outgoing>sid-4DC479E5-5C20-4948-BCFC-9EC5E2F66D8D</outgoing>\r\n </exclusiveGateway>\r\n <sequenceFlow id="sid-7B791A11-2F2E-4D80-AFB3-91A02CF2B4FD" sourceRef="sid-D7F237E8-56D0-4283-A3CE-4F0EFE446138" targetRef="sid-5134932A-1863-4FFA-BB3C-A4B4078B11A9"/>\r\n <sequenceFlow id="sid-EE8A7BA0-5D66-4F8B-80E3-CC2751B3856A" sourceRef="sid-52EB1772-F36E-433E-8F5B-D5DFD26E6F26" targetRef="SCAN_OK"/>\r\n <sequenceFlow id="sid-57EB1F24-BD94-479A-BF1F-57F1EAA19C6C" sourceRef="sid-E49425CF-8287-4798-B622-D2A7D78EF00B" targetRef="sid-E433566C-2289-4BEB-A19C-1697048900D2"/>\r\n <sequenceFlow id="sid-8B820AF5-DC5C-4618-B854-E08B71FB55CB" name="No" sourceRef="SCAN_OK" targetRef="sid-E49425CF-8287-4798-B622-D2A7D78EF00B"/>\r\n <sequenceFlow id="sid-4DC479E5-5C20-4948-BCFC-9EC5E2F66D8D" sourceRef="sid-5134932A-1863-4FFA-BB3C-A4B4078B11A9" targetRef="sid-52EB1772-F36E-433E-8F5B-D5DFD26E6F26"/>\r\n <sequenceFlow id="sid-337A23B9-A923-4CCE-B613-3E247B773CCE" name="Yes" sourceRef="SCAN_OK" targetRef="sid-5134932A-1863-4FFA-BB3C-A4B4078B11A9"/>\r\n</process>\r\n<bpmndi:BPMNDiagram id="sid-74620812-92c4-44e5-949c-aa47393d3830">\r\n <bpmndi:BPMNPlane bpmnElement="sid-c0e745ff-361e-4afb-8c8d-2a1fc32b1424" id="sid-cdcae759-2af7-4a6d-bd02-53f3352a731d">\r\n <bpmndi:BPMNShape bpmnElement="sid-87F4C1D6-25E1-4A45-9DA7-AD945993D06F" id="sid-87F4C1D6-25E1-4A45-9DA7-AD945993D06F_gui" isHorizontal="true">\r\n <omgdc:Bounds height="500.0" width="933.0" x="42.5" y="75.0"/>\r\n <bpmndi:BPMNLabel labelStyle="sid-84cb49fd-2f7c-44fb-8950-83c3fa153d3b">\r\n <omgdc:Bounds height="59.142852783203125" width="12.000000000000014" x="47.49999999999999" y="170.42857360839844"/>\r\n </bpmndi:BPMNLabel>\r\n </bpmndi:BPMNShape>\r\n <bpmndi:BPMNShape bpmnElement="sid-57E4FE0D-18E4-478D-BC5D-B15164E93254" id="sid-57E4FE0D-18E4-478D-BC5D-B15164E93254_gui" isHorizontal="true">\r\n <omgdc:Bounds height="250.0" width="903.0" x="72.5" y="75.0"/>\r\n </bpmndi:BPMNShape>\r\n <bpmndi:BPMNShape bpmnElement="sid-D7F237E8-56D0-4283-A3CE-4F0EFE446138" id="sid-D7F237E8-56D0-4283-A3CE-4F0EFE446138_gui">\r\n <omgdc:Bounds height="30.0" width="30.0" x="150.0" y="165.0"/>\r\n <bpmndi:BPMNLabel labelStyle="sid-e0502d32-f8d1-41cf-9c4a-cbb49fecf581">\r\n <omgdc:Bounds height="22.0" width="46.35714340209961" x="141.8214282989502" y="197.0"/>\r\n </bpmndi:BPMNLabel>\r\n </bpmndi:BPMNShape>\r\n <bpmndi:BPMNShape bpmnElement="sid-52EB1772-F36E-433E-8F5B-D5DFD26E6F26" id="sid-52EB1772-F36E-433E-8F5B-D5DFD26E6F26_gui">\r\n <omgdc:Bounds height="80.0" width="100.0" x="352.5" y="140.0"/>\r\n <bpmndi:BPMNLabel labelStyle="sid-84cb49fd-2f7c-44fb-8950-83c3fa153d3b">\r\n <omgdc:Bounds height="12.0" width="84.0" x="360.5" y="172.0"/>\r\n </bpmndi:BPMNLabel>\r\n </bpmndi:BPMNShape>\r\n <bpmndi:BPMNShape bpmnElement="SCAN_OK" id="SCAN_OK_gui" isMarkerVisible="true">\r\n <omgdc:Bounds height="40.0" width="40.0" x="550.0" y="160.0"/>\r\n <bpmndi:BPMNLabel labelStyle="sid-e0502d32-f8d1-41cf-9c4a-cbb49fecf581">\r\n <omgdc:Bounds height="12.0" width="102.0" x="521.0" y="127.0"/>\r\n </bpmndi:BPMNLabel>\r\n </bpmndi:BPMNShape>\r\n <bpmndi:BPMNShape bpmnElement="sid-E49425CF-8287-4798-B622-D2A7D78EF00B" id="sid-E49425CF-8287-4798-B622-D2A7D78EF00B_gui">\r\n <omgdc:Bounds height="80.0" width="100.0" x="687.5" y="140.0"/>\r\n <bpmndi:BPMNLabel labelStyle="sid-84cb49fd-2f7c-44fb-8950-83c3fa153d3b">\r\n <omgdc:Bounds height="36.0" width="83.14285278320312" x="695.9285736083984" y="162.0"/>\r\n </bpmndi:BPMNLabel>\r\n </bpmndi:BPMNShape>\r\n <bpmndi:BPMNShape bpmnElement="sid-E433566C-2289-4BEB-A19C-1697048900D2" id="sid-E433566C-2289-4BEB-A19C-1697048900D2_gui">\r\n <omgdc:Bounds height="28.0" width="28.0" x="865.0" y="166.0"/>\r\n <bpmndi:BPMNLabel labelStyle="sid-e0502d32-f8d1-41cf-9c4a-cbb49fecf581">\r\n <omgdc:Bounds height="11.0" width="62.857147216796875" x="847.5714263916016" y="196.0"/>\r\n </bpmndi:BPMNLabel>\r\n </bpmndi:BPMNShape>\r\n <bpmndi:BPMNShape bpmnElement="sid-5134932A-1863-4FFA-BB3C-A4B4078B11A9" id="sid-5134932A-1863-4FFA-BB3C-A4B4078B11A9_gui" isMarkerVisible="true">\r\n <omgdc:Bounds height="40.0" width="40.0" x="240.0" y="160.0"/>\r\n </bpmndi:BPMNShape>\r\n <bpmndi:BPMNEdge bpmnElement="sid-EE8A7BA0-5D66-4F8B-80E3-CC2751B3856A" id="sid-EE8A7BA0-5D66-4F8B-80E3-CC2751B3856A_gui">\r\n <omgdi:waypoint x="452.5" y="180"/>\r\n <omgdi:waypoint x="550.0" y="180"/>\r\n </bpmndi:BPMNEdge>\r\n <bpmndi:BPMNEdge bpmnElement="sid-8B820AF5-DC5C-4618-B854-E08B71FB55CB" id="sid-8B820AF5-DC5C-4618-B854-E08B71FB55CB_gui">\r\n <omgdi:waypoint x="590.0" y="180"/>\r\n <omgdi:waypoint x="687.5" y="180"/>\r\n <bpmndi:BPMNLabel labelStyle="sid-e0502d32-f8d1-41cf-9c4a-cbb49fecf581">\r\n <omgdc:Bounds height="12.048704338048935" width="16.32155963195521" x="597.8850936986571" y="155"/>\r\n </bpmndi:BPMNLabel>\r\n </bpmndi:BPMNEdge>\r\n <bpmndi:BPMNEdge bpmnElement="sid-7B791A11-2F2E-4D80-AFB3-91A02CF2B4FD" id="sid-7B791A11-2F2E-4D80-AFB3-91A02CF2B4FD_gui">\r\n <omgdi:waypoint x="180.0" y="180"/>\r\n <omgdi:waypoint x="240.0" y="180"/>\r\n </bpmndi:BPMNEdge>\r\n <bpmndi:BPMNEdge bpmnElement="sid-4DC479E5-5C20-4948-BCFC-9EC5E2F66D8D" id="sid-4DC479E5-5C20-4948-BCFC-9EC5E2F66D8D_gui">\r\n <omgdi:waypoint x="280.0" y="180"/>\r\n <omgdi:waypoint x="352.5" y="180"/>\r\n </bpmndi:BPMNEdge>\r\n <bpmndi:BPMNEdge bpmnElement="sid-57EB1F24-BD94-479A-BF1F-57F1EAA19C6C" id="sid-57EB1F24-BD94-479A-BF1F-57F1EAA19C6C_gui">\r\n <omgdi:waypoint x="787.5" y="180.0"/>\r\n <omgdi:waypoint x="865.0" y="180.0"/>\r\n </bpmndi:BPMNEdge>\r\n <bpmndi:BPMNEdge bpmnElement="sid-337A23B9-A923-4CCE-B613-3E247B773CCE" id="sid-337A23B9-A923-4CCE-B613-3E247B773CCE_gui">\r\n <omgdi:waypoint x="570.5" y="200.0"/>\r\n <omgdi:waypoint x="570.5" y="269.0"/>\r\n <omgdi:waypoint x="260.5" y="269.0"/>\r\n <omgdi:waypoint x="260.5" y="200.0"/>\r\n <bpmndi:BPMNLabel labelStyle="sid-e0502d32-f8d1-41cf-9c4a-cbb49fecf581">\r\n <omgdc:Bounds height="21.4285888671875" width="12.0" x="550" y="205"/>\r\n </bpmndi:BPMNLabel>\r\n </bpmndi:BPMNEdge>\r\n </bpmndi:BPMNPlane>\r\n <bpmndi:BPMNLabelStyle id="sid-e0502d32-f8d1-41cf-9c4a-cbb49fecf581">\r\n <omgdc:Font isBold="false" isItalic="false" isStrikeThrough="false" isUnderline="false" name="Arial" size="11.0"/>\r\n </bpmndi:BPMNLabelStyle>\r\n <bpmndi:BPMNLabelStyle id="sid-84cb49fd-2f7c-44fb-8950-83c3fa153d3b">\r\n <omgdc:Font isBold="false" isItalic="false" isStrikeThrough="false" isUnderline="false" name="Arial" size="12.0"/>\r\n </bpmndi:BPMNLabelStyle>\r\n</bpmndi:BPMNDiagram>\r\n</definitions>\r\n\r\n';
- this.importXML(newDiagramXML);
+ this.setDiagram(composition ? composition : newDiagramXML);
+ var eventBus = this.modeler.get('eventBus');
+ eventBus.on('element.out', () => {
+ this.exportDiagramToStore();
+ });
}
- importXML(xml) {
+ setDiagram = diagram => {
+ this.setState(
+ {
+ diagram
+ },
+ this.importXML
+ );
+ };
+
+ importXML = () => {
+ const { diagram } = this.state;
let modeler = this.modeler;
- this.modeler.importXML(xml, function(err) {
+ this.modeler.importXML(diagram, err => {
if (err) {
- return console.error('could not import BPMN file');
+ //TDOD add i18n
+ return this.props.showErrorModal('could not import diagram');
}
let canvas = modeler.get('canvas');
canvas.zoom('fit-viewport');
});
- }
+ };
- exportDiagram() {
+ exportDiagramToStore = () => {
this.modeler.saveXML({ format: true }, (err, xml) => {
if (err) {
- return console.error('could not save diagram');
+ //TODO add i18n
+ return this.props.showErrorModal('could not save diagram');
}
- console.log('Exported diagram: ', xml);
+ return this.props.compositionUpdate(xml);
});
- }
+ };
+
+ exportDiagram = () => {
+ const { name, showErrorModal } = this.props;
+ this.modeler.saveXML({ format: true }, (err, xml) => {
+ if (err) {
+ //TODO add i18n
+ return showErrorModal('could not save diagram');
+ }
+ const blob = new Blob([xml], { type: 'text/html;charset=utf-8' });
+ fileSaver.saveAs(blob, `${name}-diagram.bpmn`);
+ });
+ };
+
+ loadNewDiagram = () => {
+ this.setDiagram(newDiagramXML);
+ };
+
+ uploadDiagram = () => {
+ this.fileInput.current.click();
+ };
+
+ handleFileInputChange = filesList => {
+ const file = filesList[0];
+ const reader = new FileReader();
+ reader.onloadend = event => {
+ var xml = event.target.result;
+ this.setDiagram(xml);
+ this.fileInput.value = '';
+ };
+ reader.readAsText(file);
+ };
render() {
return (
<div className="composition-view content">
- <div className="bpmn-container" id={this.generatedId} />
- <div className="properties-panel" id="js-properties-panel" />
+ <input
+ ref={this.fileInput}
+ onChange={e => this.handleFileInputChange(e.target.files)}
+ id="file-input"
+ accept=".bpmn, .xml"
+ type="file"
+ name="file-input"
+ style={{ display: 'none' }}
+ />
+ <div
+ onBlur={() => {
+ this.exportDiagramToStore();
+ }}
+ className="bpmn-container"
+ id={this.generatedId}
+ />
+ <div className="bpmn-sidebar">
+ <div
+ className="properties-panel"
+ id="js-properties-panel"
+ />
+ <CompositionButtons
+ onClean={this.loadNewDiagram}
+ onDownload={this.exportDiagram}
+ onUpload={this.uploadDiagram}
+ />
+ </div>
</div>
);
}
}
-function mapStateToProps() {
- return {};
-}
-
-function mapDispatchToProps() {
- return {};
-}
-
-export default connect(mapStateToProps, mapDispatchToProps)(CompositionView);
+export default CompositionView;
diff --git a/workflow-designer-ui/src/main/frontend/src/features/version/composition/components/CompositionButton.js b/workflow-designer-ui/src/main/frontend/src/features/version/composition/components/CompositionButton.js
new file mode 100644
index 00000000..2fc6618c
--- /dev/null
+++ b/workflow-designer-ui/src/main/frontend/src/features/version/composition/components/CompositionButton.js
@@ -0,0 +1,33 @@
+/*
+* Copyright © 2018 European Support Limited
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+import React from 'react';
+import PropTypes from 'prop-types';
+import SVGIcon from 'sdc-ui/lib/react/SVGIcon';
+
+const CompositionButton = ({ onClick, name, title }) => (
+ <div onClick={onClick} className="diagram-btn">
+ <SVGIcon title={title} name={name} />
+ </div>
+);
+
+CompositionButton.propTypes = {
+ onClick: PropTypes.func,
+ className: PropTypes.string,
+ name: PropTypes.string,
+ title: PropTypes.string
+};
+
+export default CompositionButton;
diff --git a/workflow-designer-ui/src/main/frontend/src/features/version/composition/components/CompositionButtonsPanel.js b/workflow-designer-ui/src/main/frontend/src/features/version/composition/components/CompositionButtonsPanel.js
new file mode 100644
index 00000000..add64902
--- /dev/null
+++ b/workflow-designer-ui/src/main/frontend/src/features/version/composition/components/CompositionButtonsPanel.js
@@ -0,0 +1,52 @@
+/*
+* Copyright © 2018 European Support Limited
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+import React from 'react';
+import PropTypes from 'prop-types';
+import CompositionButton from './CompositionButton';
+
+const Divider = () => <div className="divider" />;
+
+const CompositionButtons = ({ onClean, onUpload, onDownload }) => (
+ <div className="composition-buttons">
+ <CompositionButton
+ data-test-id="composition-clear-btn"
+ onClick={onClean}
+ name="trashO"
+ title="clear"
+ />
+ <Divider />
+ <CompositionButton
+ data-test-id="composition-download-btn"
+ onClick={onDownload}
+ name="download"
+ title="download"
+ />
+ <Divider />
+ <CompositionButton
+ data-test-id="composition-download-upload"
+ onClick={onUpload}
+ name="upload"
+ title="upload"
+ />
+ </div>
+);
+
+CompositionButtons.propTypes = {
+ onClean: PropTypes.func,
+ onUpload: PropTypes.func,
+ onDownload: PropTypes.func
+};
+export default CompositionButtons;
diff --git a/workflow-designer-ui/src/main/frontend/src/features/version/composition/compositionActions.js b/workflow-designer-ui/src/main/frontend/src/features/version/composition/compositionActions.js
new file mode 100644
index 00000000..3f1755dd
--- /dev/null
+++ b/workflow-designer-ui/src/main/frontend/src/features/version/composition/compositionActions.js
@@ -0,0 +1,21 @@
+/*
+* Copyright © 2018 European Support Limited
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*http://www.apache.org/licenses/LICENSE-2.0
+*
+ * Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+import { SET_COMPOSITION } from './compositionConstants';
+
+export const updateComposition = payload => ({
+ type: SET_COMPOSITION,
+ payload
+});
diff --git a/workflow-designer-ui/src/main/frontend/src/features/version/composition/compositionConstants.js b/workflow-designer-ui/src/main/frontend/src/features/version/composition/compositionConstants.js
new file mode 100644
index 00000000..74cab0cb
--- /dev/null
+++ b/workflow-designer-ui/src/main/frontend/src/features/version/composition/compositionConstants.js
@@ -0,0 +1,16 @@
+/*
+* Copyright © 2018 European Support Limited
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*http://www.apache.org/licenses/LICENSE-2.0
+*
+ * Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+export const SET_COMPOSITION = 'composition/SET_COMPOSITION';
diff --git a/workflow-designer-ui/src/main/frontend/src/features/version/composition/compositionReducer.js b/workflow-designer-ui/src/main/frontend/src/features/version/composition/compositionReducer.js
new file mode 100644
index 00000000..9c707362
--- /dev/null
+++ b/workflow-designer-ui/src/main/frontend/src/features/version/composition/compositionReducer.js
@@ -0,0 +1,27 @@
+/*
+* Copyright © 2018 European Support Limited
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*http://www.apache.org/licenses/LICENSE-2.0
+*
+ * Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+import { SET_COMPOSITION } from './compositionConstants';
+
+export default (state = {}, action) => {
+ switch (action.type) {
+ case SET_COMPOSITION:
+ return {
+ diagram: action.payload
+ };
+ default:
+ return state;
+ }
+};
diff --git a/workflow-designer-ui/src/main/frontend/src/features/version/composition/compositionSelectors.js b/workflow-designer-ui/src/main/frontend/src/features/version/composition/compositionSelectors.js
new file mode 100644
index 00000000..7e28ca64
--- /dev/null
+++ b/workflow-designer-ui/src/main/frontend/src/features/version/composition/compositionSelectors.js
@@ -0,0 +1,17 @@
+/*
+* Copyright © 2018 European Support Limited
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*http://www.apache.org/licenses/LICENSE-2.0
+*
+ * Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+export const getComposition = state =>
+ state && state.currentVersion && state.currentVersion.composition.diagram;
diff --git a/workflow-designer-ui/src/main/frontend/src/features/version/composition/custom-modeler/custom/CustomContextPadProvider.js b/workflow-designer-ui/src/main/frontend/src/features/version/composition/custom-modeler/custom/CustomContextPadProvider.js
new file mode 100644
index 00000000..0f2ba528
--- /dev/null
+++ b/workflow-designer-ui/src/main/frontend/src/features/version/composition/custom-modeler/custom/CustomContextPadProvider.js
@@ -0,0 +1,43 @@
+import inherits from 'inherits';
+
+import ContextPadProvider from 'bpmn-js/lib/features/context-pad/ContextPadProvider';
+
+import { isAny } from 'bpmn-js/lib/features/modeling/util/ModelingUtil';
+
+import { assign, bind } from 'min-dash';
+
+export default function CustomContextPadProvider(injector, connect, translate) {
+ injector.invoke(ContextPadProvider, this);
+
+ var cached = bind(this.getContextPadEntries, this);
+
+ this.getContextPadEntries = function(element) {
+ var actions = cached(element);
+
+ var businessObject = element.businessObject;
+
+ function startConnect(event, element, autoActivate) {
+ connect.start(event, element, autoActivate);
+ }
+
+ if (isAny(businessObject, ['custom:triangle', 'custom:circle'])) {
+ assign(actions, {
+ connect: {
+ group: 'connect',
+ className: 'bpmn-icon-connection-multi',
+ title: translate('Connect using custom connection'),
+ action: {
+ click: startConnect,
+ dragstart: startConnect
+ }
+ }
+ });
+ }
+
+ return actions;
+ };
+}
+
+inherits(CustomContextPadProvider, ContextPadProvider);
+
+CustomContextPadProvider.$inject = ['injector', 'connect', 'translate'];
diff --git a/workflow-designer-ui/src/main/frontend/src/features/version/composition/custom-modeler/custom/CustomElementFactory.js b/workflow-designer-ui/src/main/frontend/src/features/version/composition/custom-modeler/custom/CustomElementFactory.js
new file mode 100644
index 00000000..01d4d278
--- /dev/null
+++ b/workflow-designer-ui/src/main/frontend/src/features/version/composition/custom-modeler/custom/CustomElementFactory.js
@@ -0,0 +1,101 @@
+import { assign } from 'min-dash';
+
+import inherits from 'inherits';
+
+import BpmnElementFactory from 'bpmn-js/lib/features/modeling/ElementFactory';
+import { DEFAULT_LABEL_SIZE } from 'bpmn-js/lib/util/LabelUtil';
+
+/**
+ * A custom factory that knows how to create BPMN _and_ custom elements.
+ */
+export default function CustomElementFactory(bpmnFactory, moddle) {
+ BpmnElementFactory.call(this, bpmnFactory, moddle);
+
+ var self = this;
+
+ /**
+ * Create a diagram-js element with the given type (any of shape, connection, label).
+ *
+ * @param {String} elementType
+ * @param {Object} attrs
+ *
+ * @return {djs.model.Base}
+ */
+ this.create = function(elementType, attrs) {
+ var type = attrs.type;
+
+ if (elementType === 'label') {
+ return self.baseCreate(
+ elementType,
+ assign({ type: 'label' }, DEFAULT_LABEL_SIZE, attrs)
+ );
+ }
+
+ // add type to businessObject if custom
+ if (/^custom:/.test(type)) {
+ if (!attrs.businessObject) {
+ attrs.businessObject = {
+ type: type
+ };
+
+ if (attrs.id) {
+ assign(attrs.businessObject, {
+ id: attrs.id
+ });
+ }
+ }
+
+ // add width and height if shape
+ if (!/:connection$/.test(type)) {
+ assign(attrs, self._getCustomElementSize(type));
+ }
+
+ if (!('$instanceOf' in attrs.businessObject)) {
+ // ensure we can use ModelUtil#is for type checks
+ Object.defineProperty(attrs.businessObject, '$instanceOf', {
+ value: function(type) {
+ return this.type === type;
+ }
+ });
+ }
+
+ return self.baseCreate(elementType, attrs);
+ }
+
+ return self.createBpmnElement(elementType, attrs);
+ };
+}
+
+inherits(CustomElementFactory, BpmnElementFactory);
+
+CustomElementFactory.$inject = ['bpmnFactory', 'moddle'];
+
+/**
+ * Returns the default size of custom shapes.
+ *
+ * The following example shows an interface on how
+ * to setup the custom shapes's dimensions.
+ *
+ * @example
+ *
+ * var shapes = {
+ * triangle: { width: 40, height: 40 },
+ * rectangle: { width: 100, height: 20 }
+ * };
+ *
+ * return shapes[type];
+ *
+ *
+ * @param {String} type
+ *
+ * @return {Dimensions} a {width, height} object representing the size of the element
+ */
+CustomElementFactory.prototype._getCustomElementSize = function(type) {
+ var shapes = {
+ __default: { width: 100, height: 80 },
+ 'custom:triangle': { width: 40, height: 40 },
+ 'custom:circle': { width: 140, height: 140 }
+ };
+
+ return shapes[type] || shapes.__default;
+};
diff --git a/workflow-designer-ui/src/main/frontend/src/features/version/composition/custom-modeler/custom/CustomPalette.js b/workflow-designer-ui/src/main/frontend/src/features/version/composition/custom-modeler/custom/CustomPalette.js
new file mode 100644
index 00000000..a8adb2fd
--- /dev/null
+++ b/workflow-designer-ui/src/main/frontend/src/features/version/composition/custom-modeler/custom/CustomPalette.js
@@ -0,0 +1,151 @@
+import { assign } from 'min-dash';
+
+/**
+ * A palette that allows you to create BPMN _and_ custom elements.
+ */
+export default function PaletteProvider(
+ palette,
+ create,
+ elementFactory,
+ spaceTool,
+ lassoTool,
+ handTool,
+ globalConnect,
+ translate
+) {
+ this._create = create;
+ this._elementFactory = elementFactory;
+ this._spaceTool = spaceTool;
+ this._lassoTool = lassoTool;
+ this._handTool = handTool;
+ this._globalConnect = globalConnect;
+ this._translate = translate;
+
+ palette.registerProvider(this);
+}
+
+PaletteProvider.$inject = [
+ 'palette',
+ 'create',
+ 'elementFactory',
+ 'spaceTool',
+ 'lassoTool',
+ 'handTool',
+ 'globalConnect',
+ 'translate'
+];
+
+PaletteProvider.prototype.getPaletteEntries = function() {
+ var actions = {},
+ create = this._create,
+ elementFactory = this._elementFactory,
+ spaceTool = this._spaceTool,
+ lassoTool = this._lassoTool,
+ handTool = this._handTool,
+ globalConnect = this._globalConnect,
+ translate = this._translate;
+
+ function createAction(type, group, className, title, options) {
+ function createListener(event) {
+ var shape = elementFactory.createShape(
+ assign({ type: type }, options)
+ );
+
+ if (options) {
+ shape.businessObject.di.isExpanded = options.isExpanded;
+ }
+
+ create.start(event, shape);
+ }
+
+ var shortType = type.replace(/^bpmn:/, '');
+
+ return {
+ group: group,
+ className: className,
+ title: title || 'Create ' + shortType,
+ action: {
+ dragstart: createListener,
+ click: createListener
+ }
+ };
+ }
+
+ assign(actions, {
+ 'hand-tool': {
+ group: 'tools',
+ className: 'bpmn-icon-hand-tool',
+ title: translate('Activate the hand tool'),
+ action: {
+ click: function(event) {
+ handTool.activateHand(event);
+ }
+ }
+ },
+ 'lasso-tool': {
+ group: 'tools',
+ className: 'bpmn-icon-lasso-tool',
+ title: translate('Activate the lasso tool'),
+ action: {
+ click: function(event) {
+ lassoTool.activateSelection(event);
+ }
+ }
+ },
+ 'space-tool': {
+ group: 'tools',
+ className: 'bpmn-icon-space-tool',
+ title: translate('Activate the create/remove space tool'),
+ action: {
+ click: function(event) {
+ spaceTool.activateSelection(event);
+ }
+ }
+ },
+ 'global-connect-tool': {
+ group: 'tools',
+ className: 'bpmn-icon-connection-multi',
+ title: translate('Activate the global connect tool'),
+ action: {
+ click: function(event) {
+ globalConnect.toggle(event);
+ }
+ }
+ },
+ 'tool-separator': {
+ group: 'tools',
+ separator: true
+ },
+ 'create.start-event': createAction(
+ 'bpmn:StartEvent',
+ 'event',
+ 'bpmn-icon-start-event-none'
+ ),
+ 'create.intermediate-event': createAction(
+ 'bpmn:IntermediateThrowEvent',
+ 'event',
+ 'bpmn-icon-intermediate-event-none',
+ translate('Create Intermediate/Boundary Event')
+ ),
+ 'create.end-event': createAction(
+ 'bpmn:EndEvent',
+ 'event',
+ 'bpmn-icon-end-event-none'
+ ),
+ 'create.exclusive-gateway': createAction(
+ 'bpmn:ExclusiveGateway',
+ 'gateway',
+ 'bpmn-icon-gateway-none',
+ translate('Create Gateway')
+ ),
+ 'create.task': createAction('bpmn:Task', 'activity', 'bpmn-icon-task'),
+ 'create.subprocess-expanded': createAction(
+ 'bpmn:SubProcess',
+ 'activity',
+ 'bpmn-icon-subprocess-expanded',
+ translate('Create expanded SubProcess'),
+ { isExpanded: true }
+ )
+ });
+ return actions;
+};
diff --git a/workflow-designer-ui/src/main/frontend/src/features/version/composition/custom-modeler/custom/CustomRenderer.js b/workflow-designer-ui/src/main/frontend/src/features/version/composition/custom-modeler/custom/CustomRenderer.js
new file mode 100644
index 00000000..f397fed9
--- /dev/null
+++ b/workflow-designer-ui/src/main/frontend/src/features/version/composition/custom-modeler/custom/CustomRenderer.js
@@ -0,0 +1,176 @@
+import inherits from 'inherits';
+
+import BaseRenderer from 'diagram-js/lib/draw/BaseRenderer';
+
+import { componentsToPath, createLine } from 'diagram-js/lib/util/RenderUtil';
+
+import {
+ append as svgAppend,
+ attr as svgAttr,
+ create as svgCreate
+} from 'tiny-svg';
+
+/**
+ * A renderer that knows how to render custom elements.
+ */
+export default function CustomRenderer(eventBus, styles) {
+ BaseRenderer.call(this, eventBus, 2000);
+
+ var computeStyle = styles.computeStyle;
+
+ this.drawTriangle = function(p, side) {
+ var halfSide = side / 2,
+ points,
+ attrs;
+
+ points = [halfSide, 0, side, side, 0, side];
+
+ attrs = computeStyle(attrs, {
+ stroke: '#3CAA82',
+ strokeWidth: 2,
+ fill: '#3CAA82'
+ });
+
+ var polygon = svgCreate('polygon');
+
+ svgAttr(polygon, {
+ points: points
+ });
+
+ svgAttr(polygon, attrs);
+
+ svgAppend(p, polygon);
+
+ return polygon;
+ };
+
+ this.getTrianglePath = function(element) {
+ var x = element.x,
+ y = element.y,
+ width = element.width,
+ height = element.height;
+
+ var trianglePath = [
+ ['M', x + width / 2, y],
+ ['l', width / 2, height],
+ ['l', -width, 0],
+ ['z']
+ ];
+
+ return componentsToPath(trianglePath);
+ };
+
+ this.drawCircle = function(p, width, height) {
+ var cx = width / 2,
+ cy = height / 2;
+
+ var attrs = computeStyle(attrs, {
+ stroke: '#4488aa',
+ strokeWidth: 4,
+ fill: 'white'
+ });
+
+ var circle = svgCreate('circle');
+
+ svgAttr(circle, {
+ cx: cx,
+ cy: cy,
+ r: Math.round((width + height) / 4)
+ });
+
+ svgAttr(circle, attrs);
+
+ svgAppend(p, circle);
+
+ return circle;
+ };
+
+ this.getCirclePath = function(shape) {
+ var cx = shape.x + shape.width / 2,
+ cy = shape.y + shape.height / 2,
+ radius = shape.width / 2;
+
+ var circlePath = [
+ ['M', cx, cy],
+ ['m', 0, -radius],
+ ['a', radius, radius, 0, 1, 1, 0, 2 * radius],
+ ['a', radius, radius, 0, 1, 1, 0, -2 * radius],
+ ['z']
+ ];
+
+ return componentsToPath(circlePath);
+ };
+
+ this.drawCustomConnection = function(p, element) {
+ var attrs = computeStyle(attrs, {
+ stroke: '#ff471a',
+ strokeWidth: 2
+ });
+
+ return svgAppend(p, createLine(element.waypoints, attrs));
+ };
+
+ this.getCustomConnectionPath = function(connection) {
+ var waypoints = connection.waypoints.map(function(p) {
+ return p.original || p;
+ });
+
+ var connectionPath = [['M', waypoints[0].x, waypoints[0].y]];
+
+ waypoints.forEach(function(waypoint, index) {
+ if (index !== 0) {
+ connectionPath.push(['L', waypoint.x, waypoint.y]);
+ }
+ });
+
+ return componentsToPath(connectionPath);
+ };
+}
+
+inherits(CustomRenderer, BaseRenderer);
+
+CustomRenderer.$inject = ['eventBus', 'styles'];
+
+CustomRenderer.prototype.canRender = function(element) {
+ return /^custom:/.test(element.type);
+};
+
+CustomRenderer.prototype.drawShape = function(p, element) {
+ var type = element.type;
+
+ if (type === 'custom:triangle') {
+ return this.drawTriangle(p, element.width);
+ }
+
+ if (type === 'custom:circle') {
+ return this.drawCircle(p, element.width, element.height);
+ }
+};
+
+CustomRenderer.prototype.getShapePath = function(shape) {
+ var type = shape.type;
+
+ if (type === 'custom:triangle') {
+ return this.getTrianglePath(shape);
+ }
+
+ if (type === 'custom:circle') {
+ return this.getCirclePath(shape);
+ }
+};
+
+CustomRenderer.prototype.drawConnection = function(p, element) {
+ var type = element.type;
+
+ if (type === 'custom:connection') {
+ return this.drawCustomConnection(p, element);
+ }
+};
+
+CustomRenderer.prototype.getConnectionPath = function(connection) {
+ var type = connection.type;
+
+ if (type === 'custom:connection') {
+ return this.getCustomConnectionPath(connection);
+ }
+};
diff --git a/workflow-designer-ui/src/main/frontend/src/features/version/composition/custom-modeler/custom/CustomRules.js b/workflow-designer-ui/src/main/frontend/src/features/version/composition/custom-modeler/custom/CustomRules.js
new file mode 100644
index 00000000..1dce143d
--- /dev/null
+++ b/workflow-designer-ui/src/main/frontend/src/features/version/composition/custom-modeler/custom/CustomRules.js
@@ -0,0 +1,136 @@
+import { reduce } from 'min-dash';
+
+import inherits from 'inherits';
+
+import { is } from 'bpmn-js/lib/util/ModelUtil';
+
+import RuleProvider from 'diagram-js/lib/features/rules/RuleProvider';
+
+var HIGH_PRIORITY = 1500;
+
+function isCustom(element) {
+ return element && /^custom:/.test(element.type);
+}
+
+/**
+ * Specific rules for custom elements
+ */
+export default function CustomRules(eventBus) {
+ RuleProvider.call(this, eventBus);
+}
+
+inherits(CustomRules, RuleProvider);
+
+CustomRules.$inject = ['eventBus'];
+
+CustomRules.prototype.init = function() {
+ /**
+ * Can shape be created on target container?
+ */
+ function canCreate(shape, target) {
+ // only judge about custom elements
+ if (!isCustom(shape)) {
+ return;
+ }
+
+ // allow creation on processes
+ return (
+ is(target, 'bpmn:Process') ||
+ is(target, 'bpmn:Participant') ||
+ is(target, 'bpmn:Collaboration')
+ );
+ }
+
+ /**
+ * Can source and target be connected?
+ */
+ function canConnect(source, target) {
+ // only judge about custom elements
+ if (!isCustom(source) && !isCustom(target)) {
+ return;
+ }
+
+ // allow connection between custom shape and task
+ if (isCustom(source)) {
+ if (is(target, 'bpmn:Task')) {
+ return { type: 'custom:connection' };
+ } else {
+ return false;
+ }
+ } else if (isCustom(target)) {
+ if (is(source, 'bpmn:Task')) {
+ return { type: 'custom:connection' };
+ } else {
+ return false;
+ }
+ }
+ }
+
+ this.addRule('elements.move', HIGH_PRIORITY, function(context) {
+ var target = context.target,
+ shapes = context.shapes;
+
+ var type;
+
+ // do not allow mixed movements of custom / BPMN shapes
+ // if any shape cannot be moved, the group cannot be moved, too
+ var allowed = reduce(
+ shapes,
+ function(result, s) {
+ if (type === undefined) {
+ type = isCustom(s);
+ }
+
+ if (type !== isCustom(s) || result === false) {
+ return false;
+ }
+
+ return canCreate(s, target);
+ },
+ undefined
+ );
+
+ // reject, if we have at least one
+ // custom element that cannot be moved
+ return allowed;
+ });
+
+ this.addRule('shape.create', HIGH_PRIORITY, function(context) {
+ var target = context.target,
+ shape = context.shape;
+
+ return canCreate(shape, target);
+ });
+
+ this.addRule('shape.resize', HIGH_PRIORITY, function(context) {
+ var shape = context.shape;
+
+ if (isCustom(shape)) {
+ // cannot resize custom elements
+ return false;
+ }
+ });
+
+ this.addRule('connection.create', HIGH_PRIORITY, function(context) {
+ var source = context.source,
+ target = context.target;
+
+ return canConnect(source, target);
+ });
+
+ this.addRule('connection.reconnectStart', HIGH_PRIORITY, function(context) {
+ var connection = context.connection,
+ source = context.hover || context.source,
+ target = connection.target;
+
+ return canConnect(source, target, connection);
+ });
+
+ this.addRule('connection.reconnectEnd', HIGH_PRIORITY, function(context) {
+ var connection = context.connection,
+ source = connection.source,
+ target = context.hover || context.target;
+
+ return canConnect(source, target, connection);
+ });
+};
diff --git a/workflow-designer-ui/src/main/frontend/src/features/version/composition/custom-modeler/custom/CustomUpdater.js b/workflow-designer-ui/src/main/frontend/src/features/version/composition/custom-modeler/custom/CustomUpdater.js
new file mode 100644
index 00000000..532c24f3
--- /dev/null
+++ b/workflow-designer-ui/src/main/frontend/src/features/version/composition/custom-modeler/custom/CustomUpdater.js
@@ -0,0 +1,136 @@
+import inherits from 'inherits';
+
+import { pick, assign } from 'min-dash';
+
+import CommandInterceptor from 'diagram-js/lib/command/CommandInterceptor';
+
+import {
+ add as collectionAdd,
+ remove as collectionRemove
+} from 'diagram-js/lib/util/Collections';
+
+/**
+ * A handler responsible for updating the custom element's businessObject
+ * once changes on the diagram happen.
+ */
+export default function CustomUpdater(eventBus, bpmnjs) {
+ CommandInterceptor.call(this, eventBus);
+
+ function updateCustomElement(e) {
+ var context = e.context,
+ shape = context.shape,
+ businessObject = shape.businessObject;
+
+ if (!isCustom(shape)) {
+ return;
+ }
+
+ var parent = shape.parent;
+
+ var customElements = bpmnjs._customElements;
+
+ // make sure element is added / removed from bpmnjs.customElements
+ if (!parent) {
+ collectionRemove(customElements, businessObject);
+ } else {
+ collectionAdd(customElements, businessObject);
+ }
+
+ // save custom element position
+ assign(businessObject, pick(shape, ['x', 'y']));
+ }
+
+ function updateCustomConnection(e) {
+ var context = e.context,
+ connection = context.connection,
+ source = connection.source,
+ target = connection.target,
+ businessObject = connection.businessObject;
+
+ var parent = connection.parent;
+
+ var customElements = bpmnjs._customElements;
+
+ // make sure element is added / removed from bpmnjs.customElements
+ if (!parent) {
+ collectionRemove(customElements, businessObject);
+ } else {
+ collectionAdd(customElements, businessObject);
+ }
+
+ // update waypoints
+ assign(businessObject, {
+ waypoints: copyWaypoints(connection)
+ });
+
+ if (source && target) {
+ assign(businessObject, {
+ source: source.id,
+ target: target.id
+ });
+ }
+ }
+
+ this.executed(
+ ['shape.create', 'shape.move', 'shape.delete'],
+ ifCustomElement(updateCustomElement)
+ );
+
+ this.reverted(
+ ['shape.create', 'shape.move', 'shape.delete'],
+ ifCustomElement(updateCustomElement)
+ );
+
+ this.executed(
+ [
+ 'connection.create',
+ 'connection.reconnectStart',
+ 'connection.reconnectEnd',
+ 'connection.updateWaypoints',
+ 'connection.delete',
+ 'connection.layout',
+ 'connection.move'
+ ],
+ ifCustomElement(updateCustomConnection)
+ );
+
+ this.reverted(
+ [
+ 'connection.create',
+ 'connection.reconnectStart',
+ 'connection.reconnectEnd',
+ 'connection.updateWaypoints',
+ 'connection.delete',
+ 'connection.layout',
+ 'connection.move'
+ ],
+ ifCustomElement(updateCustomConnection)
+ );
+}
+
+inherits(CustomUpdater, CommandInterceptor);
+
+CustomUpdater.$inject = ['eventBus', 'bpmnjs'];
+
+/////// helpers ///////////////////////////////////
+
+function copyWaypoints(connection) {
+ return connection.waypoints.map(function(p) {
+ return { x: p.x, y: p.y };
+ });
+}
+
+function isCustom(element) {
+ return element && /custom:/.test(element.type);
+}
+
+function ifCustomElement(fn) {
+ return function(event) {
+ var context = event.context,
+ element = context.shape || context.connection;
+
+ if (isCustom(element)) {
+ fn(event);
+ }
+ };
+}
diff --git a/workflow-designer-ui/src/main/frontend/src/features/version/composition/custom-modeler/custom/index.js b/workflow-designer-ui/src/main/frontend/src/features/version/composition/custom-modeler/custom/index.js
new file mode 100644
index 00000000..f1085390
--- /dev/null
+++ b/workflow-designer-ui/src/main/frontend/src/features/version/composition/custom-modeler/custom/index.js
@@ -0,0 +1,22 @@
+import CustomElementFactory from './CustomElementFactory';
+import CustomRenderer from './CustomRenderer';
+import CustomPalette from './CustomPalette';
+import CustomRules from './CustomRules';
+import CustomUpdater from './CustomUpdater';
+import CustomContextPadProvider from './CustomContextPadProvider';
+
+export default {
+ __init__: [
+ 'customRenderer',
+ 'paletteProvider',
+ 'customRules',
+ 'customUpdater',
+ 'contextPadProvider'
+ ],
+ elementFactory: ['type', CustomElementFactory],
+ customRenderer: ['type', CustomRenderer],
+ paletteProvider: ['type', CustomPalette],
+ customRules: ['type', CustomRules],
+ customUpdater: ['type', CustomUpdater],
+ contextPadProvider: ['type', CustomContextPadProvider]
+};
diff --git a/workflow-designer-ui/src/main/frontend/src/features/version/composition/custom-modeler/index.js b/workflow-designer-ui/src/main/frontend/src/features/version/composition/custom-modeler/index.js
new file mode 100644
index 00000000..86fbff6a
--- /dev/null
+++ b/workflow-designer-ui/src/main/frontend/src/features/version/composition/custom-modeler/index.js
@@ -0,0 +1,99 @@
+import Modeler from 'bpmn-js/lib/Modeler';
+
+import { assign, isArray } from 'min-dash';
+
+import inherits from 'inherits';
+
+import CustomModule from './custom';
+
+export default function CustomModeler(options) {
+ Modeler.call(this, options);
+
+ this._customElements = [];
+}
+
+inherits(CustomModeler, Modeler);
+
+CustomModeler.prototype._modules = [].concat(CustomModeler.prototype._modules, [
+ CustomModule
+]);
+
+/**
+ * Add a single custom element to the underlying diagram
+ *
+ * @param {Object} customElement
+ */
+CustomModeler.prototype._addCustomShape = function(customElement) {
+ this._customElements.push(customElement);
+
+ var canvas = this.get('canvas'),
+ elementFactory = this.get('elementFactory');
+
+ var customAttrs = assign({ businessObject: customElement }, customElement);
+
+ var customShape = elementFactory.create('shape', customAttrs);
+
+ return canvas.addShape(customShape);
+};
+
+CustomModeler.prototype._addCustomConnection = function(customElement) {
+ this._customElements.push(customElement);
+
+ var canvas = this.get('canvas'),
+ elementFactory = this.get('elementFactory'),
+ elementRegistry = this.get('elementRegistry');
+
+ var customAttrs = assign({ businessObject: customElement }, customElement);
+
+ var connection = elementFactory.create(
+ 'connection',
+ assign(customAttrs, {
+ source: elementRegistry.get(customElement.source),
+ target: elementRegistry.get(customElement.target)
+ }),
+ elementRegistry.get(customElement.source).parent
+ );
+
+ return canvas.addConnection(connection);
+};
+
+/**
+ * Add a number of custom elements and connections to the underlying diagram.
+ *
+ * @param {Array<Object>} customElements
+ */
+CustomModeler.prototype.addCustomElements = function(customElements) {
+ if (!isArray(customElements)) {
+ throw new Error('argument must be an array');
+ }
+
+ var shapes = [],
+ connections = [];
+
+ customElements.forEach(function(customElement) {
+ if (isCustomConnection(customElement)) {
+ connections.push(customElement);
+ } else {
+ shapes.push(customElement);
+ }
+ });
+
+ // add shapes before connections so that connections
+ // can already rely on the shapes being part of the diagram
+ shapes.forEach(this._addCustomShape, this);
+
+ connections.forEach(this._addCustomConnection, this);
+};
+
+/**
+ * Get custom elements with their current status.
+ *
+ * @return {Array<Object>} custom elements on the diagram
+ */
+CustomModeler.prototype.getCustomElements = function() {
+ return this._customElements;
+};
+
+function isCustomConnection(element) {
+ return element.type === 'custom:connection';
+}
diff --git a/workflow-designer-ui/src/main/frontend/src/features/version/versionApi.js b/workflow-designer-ui/src/main/frontend/src/features/version/versionApi.js
index 7848b1f0..4e591c9b 100644
--- a/workflow-designer-ui/src/main/frontend/src/features/version/versionApi.js
+++ b/workflow-designer-ui/src/main/frontend/src/features/version/versionApi.js
@@ -41,6 +41,21 @@ const Api = {
}
);
},
+ fetchVersionArtifact: ({ workflowId, versionId }) => {
+ return RestfulAPIUtil.fetch(
+ `${baseUrl(workflowId)}/${versionId}/artifact`
+ );
+ },
+ updateVersionArtifact: ({ workflowId, versionId, payload }) => {
+ let formData = new FormData();
+ var blob = new Blob([payload], { type: 'text/xml' });
+ formData.append('fileToUpload', blob);
+
+ return RestfulAPIUtil.put(
+ `${baseUrl(workflowId)}/${versionId}/artifact`,
+ formData
+ );
+ },
certifyVersion: ({ workflowId, versionId }) => {
return RestfulAPIUtil.post(
`${baseUrl(workflowId)}/${versionId}/state`,
diff --git a/workflow-designer-ui/src/main/frontend/src/features/version/versionController/versionControllerSelectors.js b/workflow-designer-ui/src/main/frontend/src/features/version/versionController/versionControllerSelectors.js
index aa2ea8d0..a3ead31f 100644
--- a/workflow-designer-ui/src/main/frontend/src/features/version/versionController/versionControllerSelectors.js
+++ b/workflow-designer-ui/src/main/frontend/src/features/version/versionController/versionControllerSelectors.js
@@ -20,15 +20,18 @@ import {
getOutputs
} from 'features/version/inputOutput/inputOutputSelectors';
import { getVersionInfo } from 'features/version/general/generalSelectors';
+import { getComposition } from 'features/version/composition/compositionSelectors';
export const getVersionsList = state => state && state.workflow.versions;
export const getSavedObjParams = createSelector(
getOutputs,
getInputs,
+ getComposition,
getVersionInfo,
- (outputs, inputs, general) => ({
+ (outputs, inputs, composition, general) => ({
outputs,
inputs,
+ composition,
...general
})
);
diff --git a/workflow-designer-ui/src/main/frontend/src/features/version/versionSaga.js b/workflow-designer-ui/src/main/frontend/src/features/version/versionSaga.js
index 9ef88f9b..78b82ab1 100644
--- a/workflow-designer-ui/src/main/frontend/src/features/version/versionSaga.js
+++ b/workflow-designer-ui/src/main/frontend/src/features/version/versionSaga.js
@@ -32,14 +32,24 @@ import { notificationActions } from 'shared/notifications/notificationsActions';
import { versionState } from 'features/version/versionConstants';
import overviewApi from '../workflow/overview/overviewApi';
import { versionListFetchAction } from '../workflow/overview/overviewConstansts';
+import { updateComposition } from 'features/version/composition/compositionActions';
function* fetchVersion(action) {
try {
const data = yield call(versionApi.fetchVersion, action.payload);
const { inputs, outputs, ...rest } = data;
+ let composition = false;
+
+ if (rest.hasArtifact) {
+ composition = yield call(
+ versionApi.fetchVersionArtifact,
+ action.payload
+ );
+ }
yield all([
put(setWorkflowVersionAction(rest)),
- put(setInputsOutputs({ inputs, outputs }))
+ put(setInputsOutputs({ inputs, outputs })),
+ put(updateComposition(composition))
]);
} catch (error) {
yield put(genericNetworkErrorAction(error));
@@ -62,7 +72,22 @@ function* watchSubmitVersion(action) {
function* watchUpdateVersion(action) {
try {
- yield call(versionApi.updateVersion, action.payload);
+ //const { composition, ...versionData } = action.payload;
+ const {
+ workflowId,
+ params: { composition, ...versionData }
+ } = action.payload;
+ yield call(versionApi.updateVersion, {
+ workflowId,
+ params: versionData
+ });
+ if (composition) {
+ yield call(versionApi.updateVersionArtifact, {
+ workflowId,
+ versionId: versionData.id,
+ payload: composition
+ });
+ }
yield put(
notificationActions.showSuccess({
title: 'Update Workflow Version',
diff --git a/workflow-designer-ui/src/main/frontend/src/features/workflow/overview/__tests__/overviewReducer-test.js b/workflow-designer-ui/src/main/frontend/src/features/workflow/overview/__tests__/overviewReducer-test.js
index 4db8f49d..87600be2 100644
--- a/workflow-designer-ui/src/main/frontend/src/features/workflow/overview/__tests__/overviewReducer-test.js
+++ b/workflow-designer-ui/src/main/frontend/src/features/workflow/overview/__tests__/overviewReducer-test.js
@@ -23,7 +23,7 @@ describe('Overview reducer', () => {
total: 2,
size: 0,
page: 0,
- results: [
+ items: [
{
id: '99adf5bc36764628b8018033d285b591',
name: '1.0',
@@ -91,6 +91,6 @@ describe('Overview reducer', () => {
expect(
overviewReducer([], versionListFetchAction(versionResponse))
- ).toEqual([...versionResponse.results]);
+ ).toEqual([...versionResponse.items]);
});
});
diff --git a/workflow-designer-ui/src/main/frontend/src/features/workflow/overview/overviewReducer.js b/workflow-designer-ui/src/main/frontend/src/features/workflow/overview/overviewReducer.js
index 720d410c..bab482a4 100644
--- a/workflow-designer-ui/src/main/frontend/src/features/workflow/overview/overviewReducer.js
+++ b/workflow-designer-ui/src/main/frontend/src/features/workflow/overview/overviewReducer.js
@@ -19,7 +19,7 @@ import { FETCH_VERSION_LIST } from 'features/workflow/overview/overviewConstanst
export default function overviewReducer(state = [], action) {
switch (action.type) {
case FETCH_VERSION_LIST:
- return [...action.payload.results];
+ return [...action.payload.items];
default:
return state;
}
diff --git a/workflow-designer-ui/src/main/frontend/src/i18n/languages.json b/workflow-designer-ui/src/main/frontend/src/i18n/languages.json
index 986cf128..5fd444a6 100644
--- a/workflow-designer-ui/src/main/frontend/src/i18n/languages.json
+++ b/workflow-designer-ui/src/main/frontend/src/i18n/languages.json
@@ -59,6 +59,9 @@
"confirmDlete": "Are you sure you want to delete \"%{name}\"?",
"alreadyExists": "Already exists",
"invalidCharacters": "Alphanumeric and underscore only"
+ },
+ "composition": {
+ "bpmnError" : "BPMN.IO Error"
}
},
"version": {
diff --git a/workflow-designer-ui/src/main/frontend/src/rootReducers.js b/workflow-designer-ui/src/main/frontend/src/rootReducers.js
index 9dbef266..9fab8140 100644
--- a/workflow-designer-ui/src/main/frontend/src/rootReducers.js
+++ b/workflow-designer-ui/src/main/frontend/src/rootReducers.js
@@ -25,14 +25,15 @@ import loader from 'shared/loader/LoaderReducer';
import modal from 'shared/modal/modalWrapperReducer';
import overviewReducer from 'features/workflow/overview/overviewReducer';
import workflowReducer from 'features/workflow/workflowReducer';
-
+import compositionReducer from 'features/version/composition/compositionReducer';
export default combineReducers({
i18n: i18nReducer,
catalog,
notifications: notificationsReducer,
currentVersion: combineReducers({
general: versionReducer,
- inputOutput
+ inputOutput,
+ composition: compositionReducer
}),
workflow: combineReducers({
data: workflowReducer,
diff --git a/workflow-designer-ui/src/main/frontend/src/routes.js b/workflow-designer-ui/src/main/frontend/src/routes.js
index 79abe303..ea433cca 100644
--- a/workflow-designer-ui/src/main/frontend/src/routes.js
+++ b/workflow-designer-ui/src/main/frontend/src/routes.js
@@ -19,7 +19,7 @@ import Version from 'features/version/Version';
import GeneralView from 'features/version/general/General';
import OverviewView from 'features/workflow/overview/Overview';
import InputOutput from 'features/version/inputOutput/InputOutput';
-import CompositionView from 'features/version/composition/CompositionView';
+import Composition from 'features/version/composition/Composition';
export const routes = [
{
@@ -41,7 +41,7 @@ export const routes = [
},
{
path: '/composition',
- component: CompositionView,
+ component: Composition,
i18nName: 'workflow.sideBar.composition',
id: 'COMPOSITION'
}
diff --git a/workflow-designer-ui/src/main/frontend/webpack.config.js b/workflow-designer-ui/src/main/frontend/webpack.config.js
index 23f53343..fa4e5d83 100644
--- a/workflow-designer-ui/src/main/frontend/webpack.config.js
+++ b/workflow-designer-ui/src/main/frontend/webpack.config.js
@@ -127,8 +127,13 @@ module.exports = (env, argv) => {
include: srcPath
},
{
- test: /\.woff|\.woff2$/,
- loader: 'file-loader'
+ test: /\.(eot|svg|ttf|woff|woff2)(\?.*)?$/,
+ use: [
+ {
+ loader: 'file-loader',
+ options: {}
+ }
+ ]
}
]
},
diff --git a/workflow-designer-ui/src/main/frontend/yarn.lock b/workflow-designer-ui/src/main/frontend/yarn.lock
index 7e31f62b..dbfc8bbe 100644
--- a/workflow-designer-ui/src/main/frontend/yarn.lock
+++ b/workflow-designer-ui/src/main/frontend/yarn.lock
@@ -4450,6 +4450,10 @@ file-loader@^1.1.11:
loader-utils "^1.0.2"
schema-utils "^0.4.5"
+file-saver@^1.3.8:
+ version "1.3.8"
+ resolved "https://registry.yarnpkg.com/file-saver/-/file-saver-1.3.8.tgz#e68a30c7cb044e2fb362b428469feb291c2e09d8"
+
filename-regex@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/filename-regex/-/filename-regex-2.0.1.tgz#c1c4b9bee3e09725ddb106b75c1e301fe2f18b26"