fixing a bug that was preventing the dropping of components in some situtations.

Adding new data object rules that will prevent you from moving a DataObject from one process to a sub-process.
Fixing the Selection of Data Objects to properly use the command stack.
This commit is contained in:
Dan 2022-07-08 10:30:21 -04:00
parent d20c7e8677
commit ea56f270bc
13 changed files with 134 additions and 54 deletions

View File

@ -39,7 +39,10 @@ import spiffworkflow from 'bpmn-js-spiffworkflow/app/spiffworkflow';
var bpmnJS = new BpmnModeler({
additionalModules: [
spiffworkflow
]
],
moddleExtensions: {
spiffworkflowModdle: spiffModdleExtension
}
});
```

View File

@ -1,13 +1,9 @@
import BpmnModeler from 'bpmn-js/lib/Modeler';
import diagramXML from '../resources/diagram.bpmn';
import { BpmnPropertiesPanelModule, BpmnPropertiesProviderModule } from 'bpmn-js-properties-panel';
import inputOutput from './spiffworkflow/InputOutput';
import SpiffWorkflowPropertiesProvider from './spiffworkflow/PropertiesPanel';
import FileSaver from 'file-saver';
import dataObjectInterceptor from './spiffworkflow/DataObject'; // For file downloads.
import spiffworkflow from './spiffworkflow';
// Examples for extending the xml language can be found at
// https://github.com/camunda/camunda-bpmn-moddle/blob/master/resources/camunda.json
const modelerEl = document.getElementById('modeler');
const panelEl = document.getElementById('panel');
const spiffModdleExtension = require('./spiffworkflow/moddle/spiffworkflow.json');
@ -19,14 +15,12 @@ const bpmnModeler = new BpmnModeler({
parent: panelEl
},
additionalModules: [
inputOutput,
dataObjectInterceptor,
SpiffWorkflowPropertiesProvider,
spiffworkflow,
BpmnPropertiesPanelModule,
BpmnPropertiesProviderModule,
],
moddleExtensions: {
spiffworkflow: spiffModdleExtension
spiffworkflowModdle: spiffModdleExtension
}
});

View File

@ -6,20 +6,28 @@ var HIGH_PRIORITY = 1500;
* This Command Interceptor functions like the BpmnUpdator in BPMN.js - It hooks into events
* from Diagram.js and updates the underlying BPMN model accordingly.
*
* This handles the case where a new DataObjectReference is added to the diagram. In such case
* do NOT just create a new DataObject - rather, check to see if at least one DataObject already
* exists, and if so, use that one.
* This handles some special cases we want to handle for DataObjects and DataObjectReferences,
* for instance:
* 1) Use existing data objects if possible when creating a new reference (don't create new objects each time)
* 2) Don't automatically delete a data object when you delete the reference - unless all references are removed.
* 3) Update the name of the DataObjectReference to match the id of the DataObject.
* 4) Don't allow someone to move a DataObjectReference from one process to another process.
*/
export default class DataObjectInterceptor extends CommandInterceptor {
constructor(eventBus, bpmnFactory, bpmnUpdater) {
super(eventBus);
/**
* Prevent this from calling th CreateDataObjectBehavior in BPMN-js, as it will
* For DataObjectReferences only ...
* Prevent this from calling the CreateDataObjectBehavior in BPMN-js, as it will
* attempt to crete a dataObject immediately. We can't create the dataObject until
* it is placed - as we want to reuse data objects if and when possible. */
* we know where it is placed - as we want to reuse data objects of the parent when
* possible */
this.preExecute([ 'shape.create' ], HIGH_PRIORITY, function(event) {
event.stopPropagation();
const context = event.context, shape = context.shape;
if (is(shape, 'bpmn:DataObjectReference') && shape.type !== 'label') {
event.stopPropagation();
}
});
/**
@ -28,10 +36,8 @@ export default class DataObjectInterceptor extends CommandInterceptor {
this.executed([ 'shape.create' ], HIGH_PRIORITY, function(event) {
const context = event.context, shape = context.shape;
if (is(shape, 'bpmn:DataObjectReference') && shape.type !== 'label') {
console.log("Data Object Ref Exectuted");
let process = shape.parent.businessObject;
let existingDataObjects = findDataObjects(process);
console.log("Existing Data Objects:", existingDataObjects);
let dataObject;
if (existingDataObjects.length > 0) {
dataObject = existingDataObjects[0];
@ -39,20 +45,13 @@ export default class DataObjectInterceptor extends CommandInterceptor {
dataObject = bpmnFactory.create('bpmn:DataObject');
}
// Update the name of the reference to match the data object's id.
shape.businessObject.name = dataObject.id;
// set the reference to the DataObject
shape.businessObject.dataObjectRef = dataObject;
}
});
/**
* Don't remove the associated DataObject, unless all references to that data object were removed.
*/
this.execute([ 'shape.delete' ], HIGH_PRIORITY, function(event) {
let context = event.context;
if ([ 'bpmn:DataObjectReference' ].includes(context.shape.type)) {
}
});
}
}

View File

@ -0,0 +1,38 @@
/**
* Custom Rules for the DataObject - Rules allow you to prevent an
* action from happening in the diagram, such as dropping an element
* where it doesn't belong.
*
* Here we don't allow people to move a data object Reference
* from one parent to another, as we can't move the data objects
* from one parent to another.
*
*/
import RuleProvider from 'diagram-js/lib/features/rules/RuleProvider';
import inherits from 'inherits-browser';
import { is } from 'bpmn-js/lib/util/ModelUtil';
import IoRules from '../InputOutput/IoRules';
export default function DataObjectRules(eventBus) {
RuleProvider.call(this, eventBus);
}
inherits(DataObjectRules, RuleProvider);
const HIGH_PRIORITY = 1500;
DataObjectRules.prototype.init = function() {
this.addRule('elements.move', HIGH_PRIORITY,function(context) {
let elements = context.shapes;
let target = context.target;
return canDrop(elements, target);
});
};
function canDrop(elements, target) {
for (let element of elements) {
if (is(element, 'bpmn:DataObjectReference') && element.parent && target) {
return target === element.parent;
}
}
}
IoRules.prototype.canDrop = canDrop;

View File

@ -1,7 +1,14 @@
import DataObjectInterceptor from './DataObjectInterceptor';
import DataObjectRules from './DataObjectRules';
import RulesModule from 'diagram-js/lib/features/rules';
export default {
__depends__: [
RulesModule
],
__init__: [ 'DataInterceptor' ],
DataInterceptor: [ 'type', DataObjectInterceptor ]
DataInterceptor: [ 'type', DataObjectInterceptor ],
DataObjectRules:[ 'type', DataObjectRules ]
};

View File

@ -5,7 +5,7 @@ import IdGenerator from 'diagram-js/lib/util/IdGenerator';
var HIGH_PRIORITY = 1500;
/**
* This Command Intercetor functions like the BpmnUpdator in BPMN.js - It hooks into events
* This Command Interceptor functions like the BpmnUpdator in BPMN.js - It hooks into events
* from Diagram.js and updates the underlying BPMN model accordingly.
*
* This handles the case where a new DataInput or DataOutput is added to

View File

@ -1,9 +1,8 @@
import scriptGroup, { SCRIPT_TYPE } from './parts/ScriptGroup';
import { is, isAny } from 'bpmn-js/lib/util/ModelUtil';
import dataReferenceGroup from './parts/DataReferenceGroup';
import { DataObjectSelect } from './parts/DataObjectSelect';
import { ListGroup, isTextFieldEntryEdited } from '@bpmn-io/properties-panel';
import {DataObjectArray} from './parts/DataObjectArray';
import { DataObjectArray } from './parts/DataObjectArray';
const LOW_PRIORITY = 500;
export default function SpiffWorkflowPropertiesProvider(propertiesPanel, translate, moddle, commandStack, elementRegistry) {
@ -15,7 +14,7 @@ export default function SpiffWorkflowPropertiesProvider(propertiesPanel, transla
groups.push(preScriptPostScriptGroup(element, translate, moddle));
}
if (is(element, 'bpmn:DataObjectReference')) {
groups.push(createDataObjectSelector(element, translate, moddle));
groups.push(createDataObjectSelector(element, translate, moddle, commandStack));
}
if (is(element, 'bpmn:Process')) {
groups.push(createDataObjectEditor(element, translate, moddle, commandStack, elementRegistry));
@ -79,7 +78,7 @@ function preScriptPostScriptGroup(element, translate, moddle) {
* @param moddle
* @returns entries
*/
function createDataObjectSelector(element, translate, moddle) {
function createDataObjectSelector(element, translate, moddle, commandStack) {
return {
id: 'data_object_properties',
label: translate('Data Object Properties'),
@ -89,7 +88,8 @@ function createDataObjectSelector(element, translate, moddle) {
element,
component: DataObjectSelect,
isEdited: isTextFieldEntryEdited,
moddle: moddle
moddle: moddle,
commandStack: commandStack,
}
]
};

View File

@ -1,6 +1,5 @@
import { useService } from 'bpmn-js-properties-panel';
import { isTextFieldEntryEdited, TextFieldEntry } from '@bpmn-io/properties-panel';
import { remove as collectionRemove } from 'diagram-js/lib/util/Collections';
import { without } from 'min-dash';
/**
@ -58,7 +57,6 @@ function removeFactory(props) {
dataObject,
process,
commandStack,
elementRegistry
} = props;
@ -85,18 +83,9 @@ function findDataObjects(process) {
return dataObjects;
}
export function findDataObject(process, id) {
for (const dataObj of findDataObjects(process)) {
if (dataObj.id == id) {
return dataObj;
}
}
}
function DataObjectGroup(props) {
const {
idPrefix,
element,
dataObject
} = props;
@ -122,7 +111,6 @@ function DataObjectTextField(props) {
} = props;
const commandStack = useService('commandStack');
const translate = useService('translate');
const debounce = useService('debounceInput');
const setValue = (value) => {
@ -138,8 +126,6 @@ function DataObjectTextField(props) {
);
};
const getValue = (parameter) => {
return dataObject.id;
};

View File

@ -19,9 +19,8 @@ import { SelectEntry } from '@bpmn-io/properties-panel';
* @returns {string|null|*}
*/
export function DataObjectSelect(props) {
const moddle = props.moddle;
const id = props.id;
const element = props.element;
const commandStack = props.commandStack;
const debounce = useService('debounceInput');
@ -31,9 +30,22 @@ export function DataObjectSelect(props) {
const setValue = value => {
const businessObject = element.businessObject;
for (const element of businessObject.$parent.flowElements) {
if (element.$type === 'bpmn:DataObject' && element.id === value) {
businessObject.dataObjectRef = element;
for (const flowElem of businessObject.$parent.flowElements) {
if (flowElem.$type === 'bpmn:DataObject' && flowElem.id === value) {
commandStack.execute('element.updateModdleProperties', {
element,
moddleElement: businessObject,
properties: {
dataObjectRef: flowElem
}
});
commandStack.execute('element.updateProperties', {
element,
moddleElement: businessObject,
properties: {
'name': flowElem.id
}
});
}
}
}

View File

@ -0,0 +1,22 @@
import IoPalette from './InputOutput/IoPalette';
import IoRules from './InputOutput/IoRules';
import IoInterceptor from './InputOutput/IoInterceptor';
import SpiffWorkflowPropertiesProvider from './PropertiesPanel/SpiffWorkflowPropertiesProvider';
import DataObjectInterceptor from './DataObject/DataObjectInterceptor';
import DataObjectRules from './DataObject/DataObjectRules';
import RulesModule from 'diagram-js/lib/features/rules';
export default {
__depends__: [ RulesModule ],
__init__: [
'SpiffWorkflowPropertiesProvider',
'DataObjectInterceptor', 'DataObjectRules',
'IoPalette', 'IoRules', 'IoInterceptor' ],
SpiffWorkflowPropertiesProvider: [ 'type', SpiffWorkflowPropertiesProvider ],
DataObjectInterceptor: [ 'type', DataObjectInterceptor ],
DataObjectRules:[ 'type', DataObjectRules ],
IoPalette: [ 'type', IoPalette ],
IoRules: [ 'type', IoRules ],
IoInterceptor: [ 'type', IoInterceptor ],
};

1
package-lock.json generated
View File

@ -13,6 +13,7 @@
"bpmn-js-properties-panel": "^1.1.1",
"diagram-js": "^8.5.0",
"inherits": "^2.0.4",
"inherits-browser": "^0.0.1",
"min-dash": "^3.8.1",
"min-dom": "^3.2.1",
"moddle": "^5.0.3"

View File

@ -68,6 +68,7 @@
"bpmn-js-properties-panel": "^1.1.1",
"diagram-js": "^8.5.0",
"inherits": "^2.0.4",
"inherits-browser": "^0.0.1",
"min-dash": "^3.8.1",
"min-dom": "^3.2.1",
"moddle": "^5.0.3"

View File

@ -49,4 +49,21 @@ describe('DataObject Interceptor', function() {
}));
it('Creating a new Reference will update the name to match the DataObject', inject(function(canvas, modeling) {
// IF - a Data Reference Exists
let rootShape = canvas.getRootElement();
const dataObjectRefShape1 = modeling.createShape({ type: 'bpmn:DataObjectReference' },
{ x: 220, y: 220 }, rootShape);
const dataObjects = findDataObjects(rootShape.businessObject);
expect(dataObjectRefShape1.businessObject.name).to.equal(dataObjects[0].id);
}));
it('will prevent dragging an existing data reference to a different process', inject(function(canvas, modeling) {
}));
});