diff --git a/example/app.js b/example/app.js
index 42e1d79..87d5bba 100644
--- a/example/app.js
+++ b/example/app.js
@@ -30,6 +30,8 @@ const TEMPLATES = [ ...EMAIL_TEMPLATES, ...REST_TEMPLATES ];
const url = new URL(window.location.href);
+const appendAnything = url.searchParams.has('aa');
+
const persistent = url.searchParams.has('p');
const presentationMode = url.searchParams.has('pm');
@@ -71,6 +73,9 @@ const modeler = new BpmnModeler({
keyboard: {
bindTo: document
},
+ connectorsExtension: {
+ appendAnything
+ },
propertiesPanel: {
parent: '#properties-panel'
},
diff --git a/example/style.css b/example/style.css
index 8d40971..34f2879 100644
--- a/example/style.css
+++ b/example/style.css
@@ -8,6 +8,15 @@
--font-family: var(--font-family);
}
+.djs-palette .separator {
+ margin: 5px;
+ padding-top: 10px;
+}
+
+.djs-palette .entry:before {
+ vertical-align: initial;
+}
+
html, body, #canvas {
width: 100%;
height: 100%;
diff --git a/src/ConnectorKeyboardBindings.js b/src/ConnectorKeyboardBindings.js
index 4bdfcc1..4e23bff 100644
--- a/src/ConnectorKeyboardBindings.js
+++ b/src/ConnectorKeyboardBindings.js
@@ -43,10 +43,18 @@ ConnectorKeyboardBindings.prototype.registerKeyboardBindings = function(keyboard
return true;
}
+ if (keyboard.isKey(['n', 'N'], event)) {
+ connectorsExtension.createAnything(mouseEvent);
+
+ return true;
+ }
+
if (keyboard.isKey(['a', 'A'], event)) {
if (selection.get().length === 1) {
connectorsExtension.appendAnything(mouseEvent, selection.get()[0]);
+ } else {
+ connectorsExtension.createAnything(mouseEvent);
}
return true;
diff --git a/src/ConnectorsExtension.js b/src/ConnectorsExtension.js
index 62fff4d..553a338 100644
--- a/src/ConnectorsExtension.js
+++ b/src/ConnectorsExtension.js
@@ -1,11 +1,26 @@
-const appendSvg = `
+const appendConnectorSvg = `
`;
+const appendConnectorImageUrl = 'data:image/svg+xml;utf8,' + encodeURIComponent(appendConnectorSvg);
+
+const appendSvg = `
+
+`;
+
const appendImageUrl = 'data:image/svg+xml;utf8,' + encodeURIComponent(appendSvg);
+const createSvg = `
+
+`;
+
+const createImageUrl = 'data:image/svg+xml;utf8,' + encodeURIComponent(createSvg);
// workaround for
// https://github.com/camunda/camunda-bpmn-js/issues/87
@@ -17,7 +32,8 @@ export default function ConnectorsExtension(
injector, create,
contextPad, translate,
elementTemplatesLoader,
- replaceMenu, appendMenu) {
+ replaceMenu, appendMenu,
+ createMenu, palette) {
this._create = create;
this._contextPad = contextPad;
@@ -25,24 +41,36 @@ export default function ConnectorsExtension(
this._elementTemplatesLoader = elementTemplatesLoader;
this._replaceMenu = replaceMenu;
this._appendMenu = appendMenu;
+ this._createMenu = createMenu;
this._autoPlace = injector.get('autoPlace', false);
+ this._config = config;
+
contextPad.registerProvider(LOWER_PRIORITY, this);
+
+ if (this._isAppendAnything()) {
+ palette.registerProvider(LOWER_PRIORITY, this);
+ }
}
ConnectorsExtension.$inject = [
- 'config.connectorExtension',
+ 'config.connectorsExtension',
'injector', 'create',
'contextPad', 'translate',
'elementTemplatesLoader',
- 'replaceMenu', 'appendMenu'
+ 'replaceMenu', 'appendMenu',
+ 'createMenu', 'palette'
];
ConnectorsExtension.prototype.loadTemplates = function(templates) {
this._elementTemplatesLoader.setTemplates(templates);
};
+ConnectorsExtension.prototype._isAppendAnything = function() {
+ return this._config && this._config.appendAnything;
+};
+
ConnectorsExtension.prototype._getReplaceMenuPosition = function(element) {
var Y_OFFSET = 5;
@@ -139,6 +167,26 @@ ConnectorsExtension.prototype.replaceAnything = function(event, element) {
}
});
};
+
+ConnectorsExtension.prototype.createAnything = function(event) {
+ return this._createMenu.open(event).then(result => {
+
+ const {
+ event: _event,
+ newElement
+ } = result;
+
+ this._create.start(
+ _event instanceof MouseEvent ? _event : event,
+ newElement
+ );
+ }).catch(error => {
+ if (error !== 'user-canceled') {
+ console.error('create-anything :: error', error);
+ }
+ });
+};
+
ConnectorsExtension.prototype.getContextPadEntries = function(element) {
const replaceMenu = this._replaceMenu;
@@ -183,8 +231,8 @@ ConnectorsExtension.prototype.getContextPadEntries = function(element) {
entries['append-anything'] = {
group: 'model',
- imageUrl: appendImageUrl,
- title: translate('Append connector'),
+ imageUrl: this._isAppendAnything() ? appendImageUrl : appendConnectorImageUrl,
+ title: translate(this._isAppendAnything() ? 'Append anything' : 'Append connector'),
action: {
click: (event, element) => {
this.appendAnything(event, element);
@@ -195,4 +243,20 @@ ConnectorsExtension.prototype.getContextPadEntries = function(element) {
return entries;
};
+};
+
+ConnectorsExtension.prototype.getPaletteEntries = function() {
+
+ return {
+ 'create-anything': {
+ group: 'tools',
+ imageUrl: createImageUrl,
+ title: this._translate('Create anything'),
+ action: {
+ click: (event) => {
+ this.createAnything(event);
+ }
+ }
+ }
+ };
};
\ No newline at end of file
diff --git a/src/append-menu/AppendMenu.js b/src/append-menu/AppendMenu.js
index dfe9ee8..9e4d326 100644
--- a/src/append-menu/AppendMenu.js
+++ b/src/append-menu/AppendMenu.js
@@ -12,25 +12,67 @@ import {
import clsx from 'clsx';
+import {
+ CREATE_OPTIONS
+} from '../create-menu/CreateOptions';
+
+
+export default function AppendMenu(
+ config,
+ elementTemplates, elementFactory,
+ injector, changeMenu) {
+
+ this._config = config;
-export default function AppendMenu(elementTemplates, injector, changeMenu) {
this._elementTemplates = elementTemplates;
+ this._elementFactory = elementFactory;
this._injector = injector;
this._changeMenu = changeMenu;
}
AppendMenu.$inject = [
+ 'config.connectorsExtension',
'elementTemplates',
+ 'elementFactory',
'injector',
'changeMenu'
];
+AppendMenu.prototype._isAppendAnything = function() {
+ return this._config && this._config.appendAnything;
+};
+
AppendMenu.prototype._getMatchingTemplates = function() {
return this._elementTemplates.getAll().filter(template => {
return template.appliesTo.includes('bpmn:Task') || template.appliesTo.includes('bpmn:ServiceTask');
});
};
+AppendMenu.prototype._getDefaultEntries = function() {
+
+ return this._isAppendAnything() ? CREATE_OPTIONS.filter(
+ option => ![ 'bpmn:StartEvent', 'bpmn:Participant' ].includes(option.target.type)
+ ).map(option => {
+
+ const {
+ actionName,
+ className,
+ label,
+ target
+ } = option;
+
+ return {
+ label,
+ id: `create-${actionName}`,
+ className,
+ action: () => {
+ return this._elementFactory.create('shape', { ...target });
+ }
+ };
+ }) : [];
+
+};
+
AppendMenu.prototype._getTemplateEntries = function(element) {
if (!('createElement' in this._elementTemplates)) {
@@ -60,14 +102,17 @@ AppendMenu.prototype._getTemplateEntries = function(element) {
AppendMenu.prototype._getContext = function(element) {
- const templateEntries = this._getTemplateEntries(element);
+ const defaultEntries = this._getDefaultEntries();
+
+ const templateEntries = this._getTemplateEntries();
return {
entries: [
+ ...defaultEntries,
...templateEntries
],
empty: !(
- templateEntries.length
+ templateEntries.length + defaultEntries.length
)
};
};
@@ -95,6 +140,7 @@ AppendMenu.prototype.open = function(element, position) {
<${AppendMenuComponent}
entries=${ entries }
onClose=${ onClose }
+ title=${ this._isAppendAnything() ? 'Append element' : 'Append connector' }
/>
`;
@@ -120,7 +166,8 @@ function AppendMenuComponent(props) {
const {
onClose,
- entries
+ entries,
+ title
} = props;
const onSelect = (event, entry, dragstart=false) => {
@@ -248,7 +295,7 @@ function AppendMenuComponent(props) {
return html`
diff --git a/src/create-menu/CreateMenu.js b/src/create-menu/CreateMenu.js
new file mode 100644
index 0000000..05f88a7
--- /dev/null
+++ b/src/create-menu/CreateMenu.js
@@ -0,0 +1,340 @@
+import {
+ html
+} from 'htm/preact';
+
+import {
+ useCallback,
+ useEffect,
+ useLayoutEffect,
+ useRef,
+ useState
+} from 'preact/hooks';
+
+import clsx from 'clsx';
+
+
+import {
+ CREATE_OPTIONS
+} from './CreateOptions';
+
+
+export default function CreateMenu(
+ elementTemplates, elementFactory,
+ injector, changeMenu) {
+
+ this._elementTemplates = elementTemplates;
+ this._elementFactory = elementFactory;
+ this._injector = injector;
+ this._changeMenu = changeMenu;
+}
+
+CreateMenu.$inject = [
+ 'elementTemplates',
+ 'elementFactory',
+ 'injector',
+ 'changeMenu'
+];
+
+CreateMenu.prototype._getMatchingTemplates = function() {
+ return this._elementTemplates.getAll().filter(template => {
+ return template.appliesTo.includes('bpmn:Task') || template.appliesTo.includes('bpmn:ServiceTask');
+ });
+};
+
+CreateMenu.prototype._getDefaultEntries = function() {
+
+ return CREATE_OPTIONS.map(option => {
+
+ const {
+ actionName,
+ className,
+ label,
+ target
+ } = option;
+
+ return {
+ label,
+ id: `create-${actionName}`,
+ className,
+ action: () => {
+ return this._elementFactory.create('shape', { ...target });
+ }
+ };
+ });
+
+};
+
+CreateMenu.prototype._getTemplateEntries = function() {
+
+ if (!('createElement' in this._elementTemplates)) {
+ return [];
+ }
+
+ const templates = this._getMatchingTemplates();
+
+ return templates.map(template => {
+
+ const {
+ id,
+ name,
+ description
+ } = template;
+
+ return {
+ name,
+ description,
+ id: `create-template-${id}`,
+ action: () => {
+ return this._elementTemplates.createElement(template);
+ }
+ };
+ });
+};
+
+CreateMenu.prototype._getContext = function() {
+
+ const defaultEntries = this._getDefaultEntries();
+
+ const templateEntries = this._getTemplateEntries();
+
+ return {
+ entries: [
+ ...defaultEntries,
+ ...templateEntries
+ ],
+ empty: !(
+ templateEntries.length + defaultEntries.length
+ )
+ };
+};
+
+/**
+ * Open create menu and return a promise to signal the result.
+ *
+ * If the user canceles the operation the promise will be
+ * rejected with `user-canceled`.
+ *
+ * @typedef { { event: DOMEvent, newElement: DiagramElement } } CreateMenuResult
+ *
+ * @param { Point } position
+ *
+ * @return { Promise }
+ */
+CreateMenu.prototype.open = function(position) {
+
+ const {
+ entries
+ } = this._getContext();
+
+ const renderFn = (onClose) => html`
+ <${CreateMenuComponent}
+ entries=${ entries }
+ onClose=${ onClose }
+ />
+ `;
+
+ return this._changeMenu.open(renderFn, {
+ position,
+ className: 'cmd-create-menu'
+ }).then((element) => {
+
+ if (!element) {
+ return Promise.reject('user-canceled');
+ }
+
+ return element;
+ });
+};
+
+function CreateMenuComponent(props) {
+
+ const {
+ onClose,
+ entries
+ } = props;
+
+ const onSelect = (event, entry, dragstart=false) => {
+ const newElement = entry.action();
+
+ onClose({
+ event,
+ newElement,
+ dragstart
+ });
+ };
+
+ const inputRef = useRef();
+ const resultsRef = useRef();
+
+ const [ value, setValue ] = useState('');
+
+ const [ templates, setTemplates ] = useState(props.entries);
+ const [ keyboardSelectedTemplate, setKeyboardSelectedTemplate ] = useState(null);
+ const [ mouseSelectedTemplate, setMouseSelectedTemplate ] = useState(null);
+ const [ selectedTemplate, setSelectedTemplate ] = useState(null);
+
+ useEffect(() => {
+
+ const filter = (template) => {
+ if (!value) {
+ return true;
+ }
+
+ const search = [
+ template.name && 'connector' || '',
+ template.name, template.description || '',
+ template.label || ''
+ ].join('---').toLowerCase();
+
+ return value.toLowerCase().split(/\s/g).every(
+ term => search.includes(term)
+ );
+ };
+
+ const templates = entries.filter(filter);
+
+ if (!templates.includes(keyboardSelectedTemplate)) {
+ setKeyboardSelectedTemplate(templates[0]);
+ }
+
+ if (!templates.includes(mouseSelectedTemplate)) {
+ setMouseSelectedTemplate(null);
+ }
+
+ setTemplates(templates);
+ }, [ value, keyboardSelectedTemplate, mouseSelectedTemplate, props.templates ]);
+
+ // focus input on initial mount
+ useLayoutEffect(() => {
+ inputRef.current.focus();
+ }, []);
+
+ // scroll to keyboard selected result
+ useLayoutEffect(() => {
+
+ const containerEl = resultsRef.current;
+
+ const selectedEl = containerEl.querySelector('.selected');
+
+ if (selectedEl) {
+ selectedEl.scrollIntoViewIfNeeded();
+ }
+ }, [ keyboardSelectedTemplate ]);
+
+ useEffect(() => {
+ setSelectedTemplate(mouseSelectedTemplate || keyboardSelectedTemplate);
+ }, [ keyboardSelectedTemplate, mouseSelectedTemplate ]);
+
+
+ const keyboardSelect = useCallback(direction => {
+
+ const idx = templates.indexOf(keyboardSelectedTemplate);
+
+ let nextIdx = idx + direction;
+
+ if (nextIdx < 0) {
+ nextIdx = templates.length - 1;
+ }
+
+ if (nextIdx >= templates.length) {
+ nextIdx = 0;
+ }
+
+ setKeyboardSelectedTemplate(templates[nextIdx]);
+ }, [ templates, keyboardSelectedTemplate ]);
+
+ const handleKeyDown = useCallback(event => {
+
+ if (event.key === 'Enter' && selectedTemplate) {
+ onSelect(event, selectedTemplate);
+
+ return event.preventDefault();
+ }
+
+ if (event.key === 'Escape') {
+ return onClose();
+ }
+
+ // ARROW_UP or SHIFT + TAB navigation
+ if (event.key === 'ArrowUp' || (event.key === 'Tab' && event.shiftKey)) {
+ keyboardSelect(-1);
+
+ return event.preventDefault();
+ }
+
+ // ARROW_DOWN or TAB navigation
+ if (event.key === 'ArrowDown' || event.key === 'Tab') {
+ keyboardSelect(1);
+
+ return event.preventDefault();
+ }
+
+ }, [ selectedTemplate ]);
+
+ const handleKey = useCallback((event) => {
+ setValue(() => event.target.value);
+ }, []);
+
+ return html`
+
+
+
+ `;
+}
\ No newline at end of file
diff --git a/src/create-menu/CreateOptions.js b/src/create-menu/CreateOptions.js
new file mode 100644
index 0000000..bba3033
--- /dev/null
+++ b/src/create-menu/CreateOptions.js
@@ -0,0 +1,576 @@
+export var NONE_EVENTS = [
+ {
+ label: 'Start Event',
+ actionName: 'replace-non-start-event',
+ className: 'bpmn-icon-start-event-none',
+ target: {
+ type: 'bpmn:StartEvent'
+ }
+ },
+ {
+ label: 'Intermediate Throw Event',
+ actionName: 'replace-with-none-intermediate-throwing',
+ className: 'bpmn-icon-intermediate-event-none',
+ target: {
+ type: 'bpmn:IntermediateThrowEvent'
+ }
+ },
+ {
+ label: 'Boundary Event',
+ actionName: 'replace-with-boundary-event',
+ className: 'bpmn-icon-intermediate-event-none',
+ target: {
+ type: 'bpmn:IntermediateThrowEvent'
+ }
+ },
+ {
+ label: 'End Event',
+ actionName: 'replace-with-none-end',
+ className: 'bpmn-icon-end-event-none',
+ target: {
+ type: 'bpmn:EndEvent'
+ }
+ }
+];
+
+export var TYPED_START_EVENTS = [
+ {
+ label: 'Message Start Event',
+ actionName: 'replace-with-message-start',
+ className: 'bpmn-icon-start-event-message',
+ target: {
+ type: 'bpmn:StartEvent',
+ eventDefinitionType: 'bpmn:MessageEventDefinition'
+ }
+ },
+ {
+ label: 'Timer Start Event',
+ actionName: 'replace-with-timer-start',
+ className: 'bpmn-icon-start-event-timer',
+ target: {
+ type: 'bpmn:StartEvent',
+ eventDefinitionType: 'bpmn:TimerEventDefinition'
+ }
+ },
+ {
+ label: 'Conditional Start Event',
+ actionName: 'replace-with-conditional-start',
+ className: 'bpmn-icon-start-event-condition',
+ target: {
+ type: 'bpmn:StartEvent',
+ eventDefinitionType: 'bpmn:ConditionalEventDefinition'
+ }
+ },
+ {
+ label: 'Signal Start Event',
+ actionName: 'replace-with-signal-start',
+ className: 'bpmn-icon-start-event-signal',
+ target: {
+ type: 'bpmn:StartEvent',
+ eventDefinitionType: 'bpmn:SignalEventDefinition'
+ }
+ }
+];
+
+export var TYPED_INTERMEDIATE_EVENT = [
+ {
+ label: 'Message Intermediate Catch Event',
+ actionName: 'replace-with-message-intermediate-catch',
+ className: 'bpmn-icon-intermediate-event-catch-message',
+ target: {
+ type: 'bpmn:IntermediateCatchEvent',
+ eventDefinitionType: 'bpmn:MessageEventDefinition'
+ }
+ },
+ {
+ label: 'Message Intermediate Throw Event',
+ actionName: 'replace-with-message-intermediate-throw',
+ className: 'bpmn-icon-intermediate-event-throw-message',
+ target: {
+ type: 'bpmn:IntermediateThrowEvent',
+ eventDefinitionType: 'bpmn:MessageEventDefinition'
+ }
+ },
+ {
+ label: 'Timer Intermediate Catch Event',
+ actionName: 'replace-with-timer-intermediate-catch',
+ className: 'bpmn-icon-intermediate-event-catch-timer',
+ target: {
+ type: 'bpmn:IntermediateCatchEvent',
+ eventDefinitionType: 'bpmn:TimerEventDefinition'
+ }
+ },
+ {
+ label: 'Escalation Intermediate Throw Event',
+ actionName: 'replace-with-escalation-intermediate-throw',
+ className: 'bpmn-icon-intermediate-event-throw-escalation',
+ target: {
+ type: 'bpmn:IntermediateThrowEvent',
+ eventDefinitionType: 'bpmn:EscalationEventDefinition'
+ }
+ },
+ {
+ label: 'Conditional Intermediate Catch Event',
+ actionName: 'replace-with-conditional-intermediate-catch',
+ className: 'bpmn-icon-intermediate-event-catch-condition',
+ target: {
+ type: 'bpmn:IntermediateCatchEvent',
+ eventDefinitionType: 'bpmn:ConditionalEventDefinition'
+ }
+ },
+ {
+ label: 'Link Intermediate Catch Event',
+ actionName: 'replace-with-link-intermediate-catch',
+ className: 'bpmn-icon-intermediate-event-catch-link',
+ target: {
+ type: 'bpmn:IntermediateCatchEvent',
+ eventDefinitionType: 'bpmn:LinkEventDefinition',
+ eventDefinitionAttrs: {
+ name: ''
+ }
+ }
+ },
+ {
+ label: 'Link Intermediate Throw Event',
+ actionName: 'replace-with-link-intermediate-throw',
+ className: 'bpmn-icon-intermediate-event-throw-link',
+ target: {
+ type: 'bpmn:IntermediateThrowEvent',
+ eventDefinitionType: 'bpmn:LinkEventDefinition',
+ eventDefinitionAttrs: {
+ name: ''
+ }
+ }
+ },
+ {
+ label: 'Compensation Intermediate Throw Event',
+ actionName: 'replace-with-compensation-intermediate-throw',
+ className: 'bpmn-icon-intermediate-event-throw-compensation',
+ target: {
+ type: 'bpmn:IntermediateThrowEvent',
+ eventDefinitionType: 'bpmn:CompensateEventDefinition'
+ }
+ },
+ {
+ label: 'Signal Intermediate Catch Event',
+ actionName: 'replace-with-signal-intermediate-catch',
+ className: 'bpmn-icon-intermediate-event-catch-signal',
+ target: {
+ type: 'bpmn:IntermediateCatchEvent',
+ eventDefinitionType: 'bpmn:SignalEventDefinition'
+ }
+ },
+ {
+ label: 'Signal Intermediate Throw Event',
+ actionName: 'replace-with-signal-intermediate-throw',
+ className: 'bpmn-icon-intermediate-event-throw-signal',
+ target: {
+ type: 'bpmn:IntermediateThrowEvent',
+ eventDefinitionType: 'bpmn:SignalEventDefinition'
+ }
+ }
+];
+
+export var TYPED_BOUNDARY_EVENT = [
+ {
+ label: 'Message Boundary Event',
+ actionName: 'replace-with-message-boundary',
+ className: 'bpmn-icon-intermediate-event-catch-message',
+ target: {
+ type: 'bpmn:BoundaryEvent',
+ eventDefinitionType: 'bpmn:MessageEventDefinition'
+ }
+ },
+ {
+ label: 'Timer Boundary Event',
+ actionName: 'replace-with-timer-boundary',
+ className: 'bpmn-icon-intermediate-event-catch-timer',
+ target: {
+ type: 'bpmn:BoundaryEvent',
+ eventDefinitionType: 'bpmn:TimerEventDefinition'
+ }
+ },
+ {
+ label: 'Escalation Boundary Event',
+ actionName: 'replace-with-escalation-boundary',
+ className: 'bpmn-icon-intermediate-event-catch-escalation',
+ target: {
+ type: 'bpmn:BoundaryEvent',
+ eventDefinitionType: 'bpmn:EscalationEventDefinition'
+ }
+ },
+ {
+ label: 'Conditional Boundary Event',
+ actionName: 'replace-with-conditional-boundary',
+ className: 'bpmn-icon-intermediate-event-catch-condition',
+ target: {
+ type: 'bpmn:BoundaryEvent',
+ eventDefinitionType: 'bpmn:ConditionalEventDefinition'
+ }
+ },
+ {
+ label: 'Error Boundary Event',
+ actionName: 'replace-with-error-boundary',
+ className: 'bpmn-icon-intermediate-event-catch-error',
+ target: {
+ type: 'bpmn:BoundaryEvent',
+ eventDefinitionType: 'bpmn:ErrorEventDefinition'
+ }
+ },
+ {
+ label: 'Cancel Boundary Event',
+ actionName: 'replace-with-cancel-boundary',
+ className: 'bpmn-icon-intermediate-event-catch-cancel',
+ target: {
+ type: 'bpmn:BoundaryEvent',
+ eventDefinitionType: 'bpmn:CancelEventDefinition'
+ }
+ },
+ {
+ label: 'Signal Boundary Event',
+ actionName: 'replace-with-signal-boundary',
+ className: 'bpmn-icon-intermediate-event-catch-signal',
+ target: {
+ type: 'bpmn:BoundaryEvent',
+ eventDefinitionType: 'bpmn:SignalEventDefinition'
+ }
+ },
+ {
+ label: 'Compensation Boundary Event',
+ actionName: 'replace-with-compensation-boundary',
+ className: 'bpmn-icon-intermediate-event-catch-compensation',
+ target: {
+ type: 'bpmn:BoundaryEvent',
+ eventDefinitionType: 'bpmn:CompensateEventDefinition'
+ }
+ },
+ {
+ label: 'Message Boundary Event (non-interrupting)',
+ actionName: 'replace-with-non-interrupting-message-boundary',
+ className: 'bpmn-icon-intermediate-event-catch-non-interrupting-message',
+ target: {
+ type: 'bpmn:BoundaryEvent',
+ eventDefinitionType: 'bpmn:MessageEventDefinition',
+ cancelActivity: false
+ }
+ },
+ {
+ label: 'Timer Boundary Event (non-interrupting)',
+ actionName: 'replace-with-non-interrupting-timer-boundary',
+ className: 'bpmn-icon-intermediate-event-catch-non-interrupting-timer',
+ target: {
+ type: 'bpmn:BoundaryEvent',
+ eventDefinitionType: 'bpmn:TimerEventDefinition',
+ cancelActivity: false
+ }
+ },
+ {
+ label: 'Escalation Boundary Event (non-interrupting)',
+ actionName: 'replace-with-non-interrupting-escalation-boundary',
+ className: 'bpmn-icon-intermediate-event-catch-non-interrupting-escalation',
+ target: {
+ type: 'bpmn:BoundaryEvent',
+ eventDefinitionType: 'bpmn:EscalationEventDefinition',
+ cancelActivity: false
+ }
+ },
+ {
+ label: 'Conditional Boundary Event (non-interrupting)',
+ actionName: 'replace-with-non-interrupting-conditional-boundary',
+ className: 'bpmn-icon-intermediate-event-catch-non-interrupting-condition',
+ target: {
+ type: 'bpmn:BoundaryEvent',
+ eventDefinitionType: 'bpmn:ConditionalEventDefinition',
+ cancelActivity: false
+ }
+ },
+ {
+ label: 'Signal Boundary Event (non-interrupting)',
+ actionName: 'replace-with-non-interrupting-signal-boundary',
+ className: 'bpmn-icon-intermediate-event-catch-non-interrupting-signal',
+ target: {
+ type: 'bpmn:BoundaryEvent',
+ eventDefinitionType: 'bpmn:SignalEventDefinition',
+ cancelActivity: false
+ }
+ }
+];
+
+export var TYPED_END_EVENT = [
+ {
+ label: 'Message End Event',
+ actionName: 'replace-with-message-end',
+ className: 'bpmn-icon-end-event-message',
+ target: {
+ type: 'bpmn:EndEvent',
+ eventDefinitionType: 'bpmn:MessageEventDefinition'
+ }
+ },
+ {
+ label: 'Escalation End Event',
+ actionName: 'replace-with-escalation-end',
+ className: 'bpmn-icon-end-event-escalation',
+ target: {
+ type: 'bpmn:EndEvent',
+ eventDefinitionType: 'bpmn:EscalationEventDefinition'
+ }
+ },
+ {
+ label: 'Error End Event',
+ actionName: 'replace-with-error-end',
+ className: 'bpmn-icon-end-event-error',
+ target: {
+ type: 'bpmn:EndEvent',
+ eventDefinitionType: 'bpmn:ErrorEventDefinition'
+ }
+ },
+ {
+ label: 'Cancel End Event',
+ actionName: 'replace-with-cancel-end',
+ className: 'bpmn-icon-end-event-cancel',
+ target: {
+ type: 'bpmn:EndEvent',
+ eventDefinitionType: 'bpmn:CancelEventDefinition'
+ }
+ },
+ {
+ label: 'Compensation End Event',
+ actionName: 'replace-with-compensation-end',
+ className: 'bpmn-icon-end-event-compensation',
+ target: {
+ type: 'bpmn:EndEvent',
+ eventDefinitionType: 'bpmn:CompensateEventDefinition'
+ }
+ },
+ {
+ label: 'Signal End Event',
+ actionName: 'replace-with-signal-end',
+ className: 'bpmn-icon-end-event-signal',
+ target: {
+ type: 'bpmn:EndEvent',
+ eventDefinitionType: 'bpmn:SignalEventDefinition'
+ }
+ },
+ {
+ label: 'Terminate End Event',
+ actionName: 'replace-with-terminate-end',
+ className: 'bpmn-icon-end-event-terminate',
+ target: {
+ type: 'bpmn:EndEvent',
+ eventDefinitionType: 'bpmn:TerminateEventDefinition'
+ }
+ }
+];
+
+export var GATEWAY = [
+ {
+ label: 'Exclusive Gateway',
+ actionName: 'replace-with-exclusive-gateway',
+ className: 'bpmn-icon-gateway-xor',
+ target: {
+ type: 'bpmn:ExclusiveGateway'
+ }
+ },
+ {
+ label: 'Parallel Gateway',
+ actionName: 'replace-with-parallel-gateway',
+ className: 'bpmn-icon-gateway-parallel',
+ target: {
+ type: 'bpmn:ParallelGateway'
+ }
+ },
+ {
+ label: 'Inclusive Gateway',
+ actionName: 'replace-with-inclusive-gateway',
+ className: 'bpmn-icon-gateway-or',
+ target: {
+ type: 'bpmn:InclusiveGateway'
+ }
+ },
+ {
+ label: 'Complex Gateway',
+ actionName: 'replace-with-complex-gateway',
+ className: 'bpmn-icon-gateway-complex',
+ target: {
+ type: 'bpmn:ComplexGateway'
+ }
+ },
+ {
+ label: 'Event based Gateway',
+ actionName: 'replace-with-event-based-gateway',
+ className: 'bpmn-icon-gateway-eventbased',
+ target: {
+ type: 'bpmn:EventBasedGateway',
+ instantiate: false,
+ eventGatewayType: 'Exclusive'
+ }
+ }
+];
+
+export var SUBPROCESS = [
+ {
+ label: 'Transaction',
+ actionName: 'replace-with-transaction',
+ className: 'bpmn-icon-transaction',
+ target: {
+ type: 'bpmn:Transaction',
+ isExpanded: true
+ }
+ },
+ {
+ label: 'Event Sub Process',
+ actionName: 'replace-with-event-subprocess',
+ className: 'bpmn-icon-event-subprocess-expanded',
+ target: {
+ type: 'bpmn:SubProcess',
+ triggeredByEvent: true,
+ isExpanded: true
+ }
+ },
+ {
+ label: 'Sub Process (collapsed)',
+ actionName: 'replace-with-collapsed-subprocess',
+ className: 'bpmn-icon-subprocess-collapsed',
+ target: {
+ type: 'bpmn:SubProcess',
+ isExpanded: false
+ }
+ },
+ {
+ label: 'Sub Process (expanded)',
+ actionName: 'replace-with-collapsed-subprocess',
+ className: 'bpmn-icon-subprocess-collapsed',
+ target: {
+ type: 'bpmn:SubProcess',
+ isExpanded: true
+ }
+ }
+];
+
+export var TASK = [
+ {
+ label: 'Task',
+ actionName: 'replace-with-task',
+ className: 'bpmn-icon-task',
+ target: {
+ type: 'bpmn:Task'
+ }
+ },
+ {
+ label: 'Send Task',
+ actionName: 'replace-with-send-task',
+ className: 'bpmn-icon-send',
+ target: {
+ type: 'bpmn:SendTask'
+ }
+ },
+ {
+ label: 'Receive Task',
+ actionName: 'replace-with-receive-task',
+ className: 'bpmn-icon-receive',
+ target: {
+ type: 'bpmn:ReceiveTask'
+ }
+ },
+ {
+ label: 'User Task',
+ actionName: 'replace-with-user-task',
+ className: 'bpmn-icon-user',
+ target: {
+ type: 'bpmn:UserTask'
+ }
+ },
+ {
+ label: 'Manual Task',
+ actionName: 'replace-with-manual-task',
+ className: 'bpmn-icon-manual',
+ target: {
+ type: 'bpmn:ManualTask'
+ }
+ },
+ {
+ label: 'Business Rule Task',
+ actionName: 'replace-with-rule-task',
+ className: 'bpmn-icon-business-rule',
+ target: {
+ type: 'bpmn:BusinessRuleTask'
+ }
+ },
+ {
+ label: 'Service Task',
+ actionName: 'replace-with-service-task',
+ className: 'bpmn-icon-service',
+ target: {
+ type: 'bpmn:ServiceTask'
+ }
+ },
+ {
+ label: 'Script Task',
+ actionName: 'replace-with-script-task',
+ className: 'bpmn-icon-script',
+ target: {
+ type: 'bpmn:ScriptTask'
+ }
+ },
+ {
+ label: 'Call Activity',
+ actionName: 'replace-with-call-activity',
+ className: 'bpmn-icon-call-activity',
+ target: {
+ type: 'bpmn:CallActivity'
+ }
+ }
+];
+
+export var DATA_OBJECTS = [
+ {
+ label: 'Data Store Reference',
+ actionName: 'replace-with-data-store-reference',
+ className: 'bpmn-icon-data-store',
+ target: {
+ type: 'bpmn:DataStoreReference'
+ }
+ },
+ {
+ label: 'Data Object Reference',
+ actionName: 'replace-with-data-object-reference',
+ className: 'bpmn-icon-data-object',
+ target: {
+ type: 'bpmn:DataObjectReference'
+ }
+ }
+];
+
+export var PARTICIPANT = [
+ {
+ label: 'Expanded Pool',
+ actionName: 'replace-with-expanded-pool',
+ className: 'bpmn-icon-participant',
+ target: {
+ type: 'bpmn:Participant',
+ isExpanded: true
+ }
+ },
+ {
+ label: 'Empty Pool',
+ actionName: 'replace-with-collapsed-pool',
+ className: 'bpmn-icon-lane',
+ target: {
+ type: 'bpmn:Participant',
+ isExpanded: false
+ }
+ }
+];
+
+export var CREATE_OPTIONS = [
+ ...GATEWAY,
+ ...TASK,
+ ...SUBPROCESS,
+ ...NONE_EVENTS,
+ ...TYPED_START_EVENTS,
+ ...TYPED_INTERMEDIATE_EVENT,
+ ...TYPED_END_EVENT,
+ ...TYPED_BOUNDARY_EVENT,
+ ...DATA_OBJECTS,
+ ...PARTICIPANT
+];
diff --git a/src/create-menu/index.js b/src/create-menu/index.js
new file mode 100644
index 0000000..c59a39f
--- /dev/null
+++ b/src/create-menu/index.js
@@ -0,0 +1,5 @@
+import CreateMenu from './CreateMenu';
+
+export default {
+ createMenu: [ 'type', CreateMenu ]
+};
\ No newline at end of file
diff --git a/src/index.js b/src/index.js
index 9381b0d..2f5fd51 100644
--- a/src/index.js
+++ b/src/index.js
@@ -14,6 +14,7 @@ import ChangeMenuModule from './change-menu';
import ElementTemplateChooserModule from './element-template-chooser';
import ReplaceMenuModule from './replace-menu';
import AppendMenuModule from './append-menu';
+import CreateMenuModule from './create-menu';
export default {
@@ -21,7 +22,8 @@ export default {
ChangeMenuModule,
ElementTemplateChooserModule,
ReplaceMenuModule,
- AppendMenuModule
+ AppendMenuModule,
+ CreateMenuModule
],
__init__: [
'connectorsExtension',