Extending the properties panel changed significantly with
bpmn-js-properties-panel>=1
. For the0.x
version of the library, check out the old version of this example.
This example is based on the properties panel extension example. Its goal is to present how the 1.x series of bpmn-js-properties-panel support (nested) list properties.
- You know how to extend the properties panel. Check out the properties panel extension example for guidance.
Most of the code of this example is a copy of the properties panel extension example. Here, we will only refer to what is added on top of that.
In this example, we extend the properties panel to allow editing a list of magic:parameter
elements on all start events. Furthermore, each parameter contains a list of magic:extension
elements. To achieve that, we will walk through the following steps:
- Add a list group called "Magic parameters" to contain the
magic:parameter
elements - Add nested list entries to contain the respective
magic:extension
elements - Create a new moddle extension
The properties will be persisted as an extension as part of the BPMN 2.0 document:
<bpmn:startEvent id="StartEvent_1">
<bpmn:extensionElements>
<magic:parameters>
<magic:parameter name="Parameter_1" value="magic parameter">
<magic:extensions>
<magic:extension key="magic key" />
</magic:extensions>
</magic:parameter>
</magic:parameters>
</bpmn:extensionElements>
</bpmn:startEvent>
The properties panel entries are provided in the following structure.
├── ParametersGroup
│ ├── ParameterProps
│ ├── ParameterProps
│ ├── ExtensionList
│ ├── ExtensionProps
Let us look into all the important steps in detail.
As part of the properties provider, we define the magic parameters group. We use the ListGroup
component here.
import parameterProps from './parts/ParameterProps';
import { ListGroup } from '@bpmn-io/properties-panel';
function createParametersGroup(element, injector, translate) {
const parametersGroup = {
id: 'parameters',
label: translate('Magic parameters'),
component: ListGroup,
...parametersProps({ element, injector })
};
return parametersGroup;
}
The ParametersProps
component provides all the entries and the handlers to add and remove new list elements.
We can provide the focussed input field after creation via autoFocusEntry
configuration. If not given, the auto-focus behavior is disabled.
function ParametersProps({ element, injector }) {
const parameters = getParameters(element) || [];
const bpmnFactory = injector.get('bpmnFactory'),
commandStack = injector.get('commandStack');
const items = parameters.map((parameter, index) => {
const id = element.id + '-parameter-' + index;
return {
id,
label: parameter.get('name') || '',
entries: ParameterProps({
idPrefix: id,
element,
parameter
}),
autoFocusEntry: id + '-name',
remove: removeFactory({ commandStack, element, parameter })
};
});
return {
items,
add: addFactory({ element, bpmnFactory, commandStack })
};
}
function removeFactory({ commandStack, element, parameter }) {
return function(event) {
// ...
};
}
function addFactory({ element, bpmnFactory, commandStack }) {
return function(event) {
// ...
}
}
A parameter itself can include nested lists as entries. We use the ListEntry
component and define the magic:extension
elements as representative items, the add and remove handlers.
import {
ListEntry
} from '@bpmn-io/properties-panel';
function ExtensionList(props) {
const {
element,
idPrefix,
parameter
} = props;
const id = `${ idPrefix }-extensions`;
const bpmnFactory = useService('bpmnFactory');
const commandStack = useService('commandStack');
const translate = useService('translate');
const businessObject = getBusinessObject(element);
let extensions = parameter.get('extensions');
const extensionsList = (extensions && extensions.get('extensions')) || [];
function addExtension() {
// ...
}
function removeExtension(extension) {
// ...
}
return <ListEntry
element={ element }
autoFocusEntry={ `[data-entry-id="${id}-extension-${extensionsList.length - 1}"] input` }
id={ id }
label={ translate('Extensions') }
items={ extensionsList }
component={ Extension }
onAdd={ addExtension }
onRemove={ removeExtension } />;
}
In comparison to a list group, a list entry is not configurable on runtime. As an example, each item's entries will be rendered directly in the Extension
component and can't be updated via another properties provider.
Similar to the basic extension example, we need to create a moddle extension so that moddle is aware of our new list properties. The extension is a JSON descriptor file magic.json containing the necessary definitions:
{
"name": "Magic",
"prefix": "magic",
"uri": "http://magic",
"xml": {
"tagAlias": "lowerCase"
},
"associations": [],
"types": [
{
"name": "Parameters",
"superClass": [ "Element" ],
"properties": [
{
"name": "values",
"isMany": true,
"type": "Parameter"
}
]
},
{
"name": "Parameter",
"properties": [
{
"name": "name",
"isAttr": true,
"type": "String"
},
{
"name": "value",
"isAttr": true,
"type": "String"
},
{
"name": "extensions",
"type": "Extensions"
}
]
},
{
"name": "Extensions",
"superClass": [ "Element" ],
"properties": [
{
"name": "extensions",
"isMany": true,
"type": "Extension"
}
]
},
{
"name": "Extension",
"properties": [
{
"name": "key",
"isAttr": true,
"type": "String"
}
]
}
]
}
To ship our custom extension with the properties panel we have to wire both the moddle extension and the properties provider when creating the modeler.
import BpmnModeler from 'bpmn-js/lib/Modeler';
import {
BpmnPropertiesPanelModule,
BpmnPropertiesProviderModule
} from 'bpmn-js-properties-panel';
import MagicPropertiesProviderModule from './provider/magic';
import MagicModdleDescriptor from './descriptors/magic';
const bpmnModeler = new BpmnModeler({
container: '#js-canvas',
propertiesPanel: {
parent: '#js-properties-panel'
},
additionalModules: [
BpmnPropertiesPanelModule,
BpmnPropertiesProviderModule,
MagicPropertiesProviderModule
],
moddleExtensions: {
magic: MagicModdleDescriptor
}
});
Install all required dependencies:
npm install
Build and run the project
npm start
MIT