From 20e32c9b287c64d0dab703b2eefc3f632bebc35e Mon Sep 17 00:00:00 2001 From: Nico Rehwaldt Date: Fri, 25 Mar 2022 15:36:14 +0100 Subject: [PATCH] feat: create/append anything --- example/app.js | 5 + example/style.css | 9 + src/ConnectorKeyboardBindings.js | 8 + src/ConnectorsExtension.js | 76 +++- src/append-menu/AppendMenu.js | 57 ++- src/create-menu/CreateMenu.js | 340 ++++++++++++++++++ src/create-menu/CreateOptions.js | 576 +++++++++++++++++++++++++++++++ src/create-menu/index.js | 5 + src/index.js | 4 +- 9 files changed, 1068 insertions(+), 12 deletions(-) create mode 100644 src/create-menu/CreateMenu.js create mode 100644 src/create-menu/CreateOptions.js create mode 100644 src/create-menu/index.js 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`

- Append a connector + ${ title }

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` +
+

+ Create element +

+
+ +
+
+ + + + + +
+ +
    + ${templates.map(template => html` +
  • setMouseSelectedTemplate(template) } + onMouseLeave=${ () => setMouseSelectedTemplate(null) } + draggable + onDragStart=${ (event) => { event.stopPropagation(); event.preventDefault(); onSelect(event, template, true); } } + onClick=${ (event) => { event.stopPropagation(); onSelect(event, template); } } + data-entry-id=${ template.id } + > +
    + ${ template.imageUrl && html` + + `} + + + ${template.label || template.name} + + + ${template.description || ''} + +
    + + ${ template.documentationRef && html`
    + + + + + +
    ` } +
  • + `)} + + ${!templates.length && html` +
  • Nothing found
  • + `} +
+
+ `; +} \ 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',