diff --git a/README.md b/README.md index 5fb325deb..d00dedd30 100644 --- a/README.md +++ b/README.md @@ -74,6 +74,49 @@ For more information on Cypress, visit [https://cli.vuejs.org/config/#cypress](h For more information on Jest, visit [https://jestjs.io](https://jestjs.io). + +## Extending the Modeler with `modeler-init` + +The `modeler-init` event is the main entry point for plugins and extensions to customize the modeler at runtime. It provides several extension points, including: +- Registering custom nodes +- Registering BPMN extensions +- Registering inspector extensions +- Registering custom dropdown items for nodes + +### Unified Example + +Below is a unified example that demonstrates how to use `modeler-init` to register a custom node and, after the process is loaded, inject a custom dropdown item for a Start Event node. + +```js +import MyCustomNodeConfig from './MyCustomNodeConfig'; + +// Listen for modeler-init to register nodes and extensions +window.ProcessMaker.EventBus.$on('modeler-init', ({ registerNode, registerCustomDropdownData }) => { + // Register a custom node (if needed) + registerNode(MyCustomNodeConfig); + + // Wait for the process to load before injecting dropdown items + window.ProcessMaker.EventBus.$on('modeler-start', ({ modeler }) => { + // Find the first Start Event node (or your custom node) + const startEventNode = modeler.$store.getters.nodes.find( + node => node.type === 'processmaker-modeler-start-event' + ); + if (startEventNode) { + // Inject a custom dropdown item + modeler.registerCustomDropdownData(startEventNode, { + label: 'Custom Start Option', + id: 'custom-start-option', + dataTest: 'switch-to-custom-start-option', + }); + } + }); +}); +``` + +- Use `registerNode` to add custom nodes. +- Use `registerCustomDropdownData` to add dropdown items, but only after the process and nodes are loaded (in `modeler-start`). +- You can also use other extension points provided in the `modeler-init` payload as needed. + ## Architecture The [entry point](https://webpack.js.org/configuration/entry-context/#entry) for the application is `src/main.js`; this is the "starting point" which is used when running `npm run serve` or `npm run build`. diff --git a/src/components/modeler/Modeler.vue b/src/components/modeler/Modeler.vue index f11dcd2fd..18f1784e6 100644 --- a/src/components/modeler/Modeler.vue +++ b/src/components/modeler/Modeler.vue @@ -148,6 +148,7 @@ :is-completed="requestCompletedNodes.includes(node.definition.id)" :is-in-progress="requestInProgressNodes.includes(node.definition.id)" :is-idle="requestIdleNodes.includes(node.definition.id)" + :custom-dropdown-data="customDropdownData" @add-node="addNode" @remove-node="removeNode" @previewNode="[handlePreview($event), setInspectorButtonPosition($event)]" @@ -451,6 +452,7 @@ export default { ], validPreviewElements, centered: false, + customDropdownData: [], }; }, watch: { @@ -855,6 +857,20 @@ export default { registerPreview(config) { this.previewConfigs.push(config); }, + registerCustomDropdownData(node, config) { + console.log('node', node); + console.log('config', config); + node.items.push(config); + + this.customDropdownData.push(this.prepareCustomDropdownData(config)); + }, + prepareCustomDropdownData(config) { + return { + label: config.label, + nodeType: config.id, + dataTest: config.dataTest, + }; + }, handleToolbarAction(action) { if (action.handler instanceof Function) { action.handler(this); @@ -2563,6 +2579,7 @@ export default { registerStatusBar: this.registerStatusBar, registerPmBlock: this.registerPmBlock, registerPreview: this.registerPreview, + registerCustomDropdownData: this.registerCustomDropdownData, }); this.moddle = new BpmnModdle(this.extensions); diff --git a/src/components/nodes/baseStartEvent/baseStartEvent.vue b/src/components/nodes/baseStartEvent/baseStartEvent.vue index e16120ab1..e2404b0a9 100644 --- a/src/components/nodes/baseStartEvent/baseStartEvent.vue +++ b/src/components/nodes/baseStartEvent/baseStartEvent.vue @@ -45,13 +45,14 @@ export default { 'processNode', 'planeElements', 'isRendering', + 'customDropdownData', ], mixins: [highlightConfig, portsConfig, hasMarkers, hideLabelOnDrag, updateIconColor, documentingIcons], data() { return { shape: getEventShape(store.getters.isForDocumenting), definition: null, - dropdownData: [ + defaultDropdownData: [ { label: defaultNames['processmaker-modeler-start-event'], nodeType: 'processmaker-modeler-start-event', @@ -80,6 +81,11 @@ export default { ], }; }, + computed: { + dropdownData() { + return [...this.defaultDropdownData, ...this.customDropdownData]; + }, + }, watch: { 'node.definition.name'(name) { this.shape.attr('label/text', name);