This commit is contained in:
Jon Herron 2023-10-31 11:13:07 -04:00
parent 9d46ea3ba8
commit 30b5f1bfae
12 changed files with 271 additions and 10 deletions

View File

@ -22,11 +22,11 @@ export default class DataStoreInterceptor extends CommandInterceptor {
constructor(eventBus, bpmnFactory, commandStack, bpmnUpdater) {
super(eventBus);
/* The default behavior is to move the data object into whatever object the reference is being created in.
* If a data object already has a parent, don't change it.
/* The default behavior is to move the data store into whatever object the reference is being created in.
* If a data store already has a parent, don't change it.
*/
bpmnUpdater.updateSemanticParent = (businessObject, parentBusinessObject) => {
// Special case for participant - which is a valid place to drop a data object, but it needs to be added
// Special case for participant - which is a valid place to drop a data store, but it needs to be added
// to the particpant's Process (which isn't directly accessible in BPMN.io
let realParent = parentBusinessObject;
if (is(realParent, 'bpmn:Participant')) {
@ -34,7 +34,7 @@ export default class DataStoreInterceptor extends CommandInterceptor {
}
if (is(businessObject, 'bpmn:DataStoreReference')) {
// For data object references, always update the flowElements when a parent is provided
// For data store references, always update the flowElements when a parent is provided
// The parent could be null if it's being deleted, and I could probably handle that here instead of
// when the shape is deleted, but not interested in refactoring at the moment.
if (realParent != null) {
@ -45,7 +45,7 @@ export default class DataStoreInterceptor extends CommandInterceptor {
}
}
} else if (is(businessObject, 'bpmn:DataStore')) {
// For data objects, only update the flowElements for new data objects, and set the parent so it doesn't get moved.
// For data stores, only update the flowElements for new data stores, and set the parent so it doesn't get moved.
if (typeof (businessObject.$parent) === 'undefined') {
const flowElements = realParent.get('flowElements');
flowElements.push(businessObject);
@ -60,7 +60,7 @@ export default class DataStoreInterceptor extends CommandInterceptor {
* For DataStoreReferences only ...
* Prevent this from calling the CreateDataStoreBehavior in BPMN-js, as it will
* attempt to crete a dataStore immediately. We can't create the dataStore until
* we know where it is placed - as we want to reuse data objects of the parent when
* we know where it is placed - as we want to reuse data stores of the parent when
* possible */
this.preExecute(['shape.create'], HIGH_PRIORITY, function (event) {
const { context } = event;
@ -71,7 +71,7 @@ export default class DataStoreInterceptor extends CommandInterceptor {
});
/**
* Don't just create a new data object, use the first existing one if it already exists
* Don't just create a new data store, use the first existing one if it already exists
*/
this.executed(['shape.create'], HIGH_PRIORITY, function (event) {
const { context } = event;
@ -98,7 +98,7 @@ export default class DataStoreInterceptor extends CommandInterceptor {
const { context } = event;
const { shape } = context;
// set the reference to the DataStore
// Update the name of the reference to match the data object's id.
// Update the name of the reference to match the data store's id.
if (is(shape, 'bpmn:DataStoreReference') && shape.type !== 'label') {
commandStack.execute('element.updateProperties', {
element: shape,
@ -111,7 +111,7 @@ export default class DataStoreInterceptor extends CommandInterceptor {
});
/**
* Don't remove the associated DataStore, unless all references to that data object
* Don't remove the associated DataStore, unless all references to that data store
* Difficult to do given placement of this logic in the BPMN Updater, so we have
* to manually handle the removal.
*/

View File

@ -0,0 +1,149 @@
import { useService } from 'bpmn-js-properties-panel';
import {
isTextFieldEntryEdited,
TextFieldEntry,
} from '@bpmn-io/properties-panel';
import { without } from 'min-dash';
import { is } from 'bpmn-js/lib/util/ModelUtil';
import {
findDataStores,
findDataStoreReferenceShapes,
idToHumanReadableName,
} from '../DataStoreHelpers';
/**
* Provides a list of data objects, and allows you to add / remove data objects, and change their ids.
* @param props
* @constructor
*/
export function DataStoreArray(props) {
const { moddle } = props;
const { element } = props;
const { commandStack } = props;
const { elementRegistry } = props;
let process;
// This element might be a process, or something that will reference a process.
if (is(element.businessObject, 'bpmn:Process') || is(element.businessObject, 'bpmn:SubProcess')) {
process = element.businessObject;
} else if (element.businessObject.processRef) {
process = element.businessObject.processRef;
}
const dataStores = findDataStores(process);
const items = dataStores.map((dataStore, index) => {
const id = `${process.id}-dataObj-${index}`;
return {
id,
label: dataStore.id,
entries: DataStoreGroup({
idPrefix: id,
element,
dataStore,
}),
autoFocusEntry: `${id}-dataStore`,
remove: removeFactory({
element,
dataStore,
process,
commandStack,
elementRegistry,
}),
};
});
function add(event) {
event.stopPropagation();
const newDataStore = moddle.create('bpmn:DataStore');
const newElements = process.get('flowElements');
newDataStore.id = moddle.ids.nextPrefixed('DataStore_');
newDataStore.$parent = process;
newElements.push(newDataStore);
commandStack.execute('element.updateModdleProperties', {
element,
moddleElement: process,
properties: {
flowElements: newElements,
},
});
}
return { items, add };
}
function removeFactory(props) {
const { element, dataStore, process, commandStack } = props;
return function (event) {
event.stopPropagation();
commandStack.execute('element.updateModdleProperties', {
element,
moddleElement: process,
properties: {
flowElements: without(process.get('flowElements'), dataStore),
},
});
// When a data object is removed, remove all references as well.
const references = findDataStoreReferenceShapes(element.children, dataStore.id);
for (const ref of references) {
commandStack.execute('shape.delete', { shape: ref });
}
};
}
function DataStoreGroup(props) {
const { idPrefix, dataStore } = props;
return [
{
id: `${idPrefix}-dataStore`,
component: DataStoreTextField,
isEdited: isTextFieldEntryEdited,
idPrefix,
dataStore,
},
];
}
function DataStoreTextField(props) {
const { idPrefix, element, parameter, dataStore } = props;
const commandStack = useService('commandStack');
const debounce = useService('debounceInput');
const setValue = (value) => {
commandStack.execute('element.updateModdleProperties', {
element,
moddleElement: dataStore,
properties: {
id: value,
},
});
// Also update the label of all the references
const references = findDataStoreReferenceShapes(element.children, dataStore.id);
for (const ref of references) {
commandStack.execute('element.updateProperties', {
element: ref,
moddleElement: ref.businessObject,
properties: {
name: idToHumanReadableName(value),
},
changed: [ref], // everything is already marked as changed, don't recalculate.
});
}
};
const getValue = () => {
return dataStore.id;
};
return TextFieldEntry({
element: parameter,
id: `${idPrefix}-id`,
label: 'Data Store Id',
getValue,
setValue,
debounce,
});
}

View File

@ -0,0 +1,100 @@
import { is, isAny } from 'bpmn-js/lib/util/ModelUtil';
import { ListGroup, isTextFieldEntryEdited } from '@bpmn-io/properties-panel';
import { DataStoreSelect } from './DataStoreSelect';
import { DataStoreArray } from './DataStoreArray';
const LOW_PRIORITY = 500;
export default function DataStorePropertiesProvider(
propertiesPanel,
translate,
moddle,
commandStack,
elementRegistry
) {
this.getGroups = function (element) {
return function (groups) {
if (is(element, 'bpmn:DataStoreReference')) {
groups.push(
createDataStoreSelector(element, translate, moddle, commandStack)
);
}
if (
isAny(element, ['bpmn:Process', 'bpmn:Participant']) ||
(is(element, 'bpmn:SubProcess') && !element.collapsed)
) {
groups.push(
createDataStoreEditor(
element,
translate,
moddle,
commandStack,
elementRegistry
)
);
}
return groups;
};
};
propertiesPanel.registerProvider(LOW_PRIORITY, this);
}
DataStorePropertiesProvider.$inject = [
'propertiesPanel',
'translate',
'moddle',
'commandStack',
'elementRegistry',
];
/**
* Create a group on the main panel with a select box (for choosing the Data Object to connect)
* @param element
* @param translate
* @param moddle
* @returns entries
*/
function createDataStoreSelector(element, translate, moddle, commandStack) {
return {
id: 'data_object_properties',
label: translate('Data Object Properties'),
entries: [
{
id: 'selectDataStore',
element,
component: DataStoreSelect,
isEdited: isTextFieldEntryEdited,
moddle,
commandStack,
},
],
};
}
/**
* Create a group on the main panel with a select box (for choosing the Data Object to connect) AND a
* full Data Object Array for modifying all the data objects.
* @param element
* @param translate
* @param moddle
* @returns entries
*/
function createDataStoreEditor(
element,
translate,
moddle,
commandStack,
elementRegistry
) {
const dataStoreArray = {
id: 'editDataStores',
element,
label: 'Data Stores',
component: ListGroup,
...DataStoreArray({ element, moddle, commandStack, elementRegistry }),
};
if (dataStoreArray.items) {
return dataStoreArray;
}
}

View File

@ -6,6 +6,10 @@ import DataObjectInterceptor from './DataObject/DataObjectInterceptor';
import DataObjectRules from './DataObject/DataObjectRules';
import DataObjectRenderer from './DataObject/DataObjectRenderer';
import DataObjectPropertiesProvider from './DataObject/propertiesPanel/DataObjectPropertiesProvider';
import DataStoreInterceptor from './DataStore/DataStoreInterceptor';
import DataStoreRules from './DataStore/DataStoreRules';
import DataStoreRenderer from './DataStore/DataStoreRenderer';
import DataStorePropertiesProvider from './DataStore/propertiesPanel/DataStorePropertiesProvider';
import ConditionsPropertiesProvider from './conditions/propertiesPanel/ConditionsPropertiesProvider';
import ExtensionsPropertiesProvider from './extensions/propertiesPanel/ExtensionsPropertiesProvider';
import MessagesPropertiesProvider from './messages/propertiesPanel/MessagesPropertiesProvider';
@ -22,6 +26,11 @@ export default {
'dataObjectInterceptor',
'dataObjectRules',
'dataObjectPropertiesProvider',
'dataObjectRenderer',
'dataStoreInterceptor',
'dataStoreRules',
'dataStorePropertiesProvider',
'dataStoreRenderer',
'conditionsPropertiesProvider',
'extensionsPropertiesProvider',
'messagesPropertiesProvider',
@ -32,7 +41,6 @@ export default {
'ioPalette',
'ioRules',
'ioInterceptor',
'dataObjectRenderer',
'multiInstancePropertiesProvider',
'standardLoopPropertiesProvider',
],
@ -40,6 +48,10 @@ export default {
dataObjectRules: ['type', DataObjectRules],
dataObjectRenderer: ['type', DataObjectRenderer],
dataObjectPropertiesProvider: ['type', DataObjectPropertiesProvider],
dataStoreInterceptor: ['type', DataStoreInterceptor],
dataStoreRules: ['type', DataStoreRules],
dataStoreRenderer: ['type', DataStoreRenderer],
dataStorePropertiesProvider: ['type', DataStorePropertiesProvider],
conditionsPropertiesProvider: ['type', ConditionsPropertiesProvider],
extensionsPropertiesProvider: ['type', ExtensionsPropertiesProvider],
signalPropertiesProvider: ['type', SignalPropertiesProvider],