diff --git a/app/spiffworkflow/index.js b/app/spiffworkflow/index.js index 362951c..373dfa5 100644 --- a/app/spiffworkflow/index.js +++ b/app/spiffworkflow/index.js @@ -16,8 +16,8 @@ import SignalPropertiesProvider from './signals/propertiesPanel/SignalProperties import ErrorPropertiesProvider from './errors/propertiesPanel/ErrorPropertiesProvider'; import EscalationPropertiesProvider from './escalations/propertiesPanel/EscalationPropertiesProvider'; import CallActivityPropertiesProvider from './callActivity/propertiesPanel/CallActivityPropertiesProvider'; -import StandardLoopPropertiesProvider from './loops/propertiesPanel/StandardLoopPropertiesProvider'; -import MultiInstancePropertiesProvider from './loops/propertiesPanel/MultiInstancePropertiesProvider'; +import StandardLoopPropertiesProvider from './loops/StandardLoopPropertiesProvider'; +import MultiInstancePropertiesProvider from './loops/MultiInstancePropertiesProvider'; import CallActivityInterceptor from './callActivity/CallActivityInterceptor'; import MessageInterceptor from './messages/MessageInterceptor'; diff --git a/app/spiffworkflow/loops/MultiInstancePropertiesProvider.js b/app/spiffworkflow/loops/MultiInstancePropertiesProvider.js new file mode 100644 index 0000000..a8a0905 --- /dev/null +++ b/app/spiffworkflow/loops/MultiInstancePropertiesProvider.js @@ -0,0 +1,83 @@ +/* eslint-disable prettier/prettier */ +/* eslint-disable no-param-reassign */ +/* eslint-disable import/no-extraneous-dependencies */ +import { is } from 'bpmn-js/lib/util/ModelUtil'; +import { isTextFieldEntryEdited, isCheckboxEntryEdited } from '@bpmn-io/properties-panel'; +import { InputItem } from './propertiesPanel/InputItemEntry'; +import { LoopCardinality } from './propertiesPanel/LoopCardinalityEntry'; +import { InputCollection } from './propertiesPanel/InputCollectionEntry'; +import { OutputItem } from './propertiesPanel/OutputItemEntry'; +import { OutputCollection } from './propertiesPanel/OutputCollectionEntry'; +import { CompletionCondition } from './propertiesPanel/CompletionConditionEntry'; +import { IsOutputElSync } from './propertiesPanel/IsIOSyncEntry'; + +const LOW_PRIORITY = 500; + +export default function MultiInstancePropertiesProvider(propertiesPanel) { + this.getGroups = function getGroupsCallback(element) { + return function pushGroup(groups) { + if ( + is(element, 'bpmn:Task') || + is(element, 'bpmn:CallActivity') || + is(element, 'bpmn:SubProcess') + ) { + const group = groups.filter((g) => g.id === 'multiInstance'); + if (group.length === 1) updateMultiInstanceGroup(element, group[0]); + } + return groups; + }; + }; + propertiesPanel.registerProvider(LOW_PRIORITY, this); +} + +MultiInstancePropertiesProvider.$inject = ['propertiesPanel']; + +function updateMultiInstanceGroup(element, group) { + group.entries = MultiInstanceProps({ element }); + group.shouldOpen = true; +} + +function MultiInstanceProps(props) { + const { element } = props; + const { businessObject } = element; + + return [ + { + id: 'loopCardinality', + component: LoopCardinality, + isEdited: isTextFieldEntryEdited, + }, + { + id: 'loopDataInputRef', + component: InputCollection, + isEdited: isTextFieldEntryEdited, + }, + { + id: 'dataInputItem', + component: InputItem, + isEdited: isTextFieldEntryEdited, + }, + { + id: 'isOutputElSynchronized', + component: IsOutputElSync, + isEdited: isCheckboxEntryEdited, + }, + { + id: 'loopDataOutputRef', + component: OutputCollection, + isEdited: isTextFieldEntryEdited, + }, + !businessObject.get('spiffworkflow:isOutputSynced') + ? { + id: 'dataOutputItem', + component: OutputItem, + isEdited: isTextFieldEntryEdited, + } + : {}, + { + id: 'completionCondition', + component: CompletionCondition, + isEdited: isTextFieldEntryEdited, + }, + ]; +} diff --git a/app/spiffworkflow/loops/propertiesPanel/StandardLoopPropertiesProvider.js b/app/spiffworkflow/loops/StandardLoopPropertiesProvider.js similarity index 94% rename from app/spiffworkflow/loops/propertiesPanel/StandardLoopPropertiesProvider.js rename to app/spiffworkflow/loops/StandardLoopPropertiesProvider.js index 491e199..c56b92f 100644 --- a/app/spiffworkflow/loops/propertiesPanel/StandardLoopPropertiesProvider.js +++ b/app/spiffworkflow/loops/StandardLoopPropertiesProvider.js @@ -1,133 +1,133 @@ -import { is } from 'bpmn-js/lib/util/ModelUtil'; -import { useService } from 'bpmn-js-properties-panel'; -import { - Group, - TextFieldEntry, - isTextFieldEntryEdited, - CheckboxEntry, - isCheckboxEntryEdited, -} from '@bpmn-io/properties-panel'; - -import { getLoopProperty, setLoopProperty } from './LoopProperty'; - -const LOW_PRIORITY = 500; - -export default function StandardLoopPropertiesProvider(propertiesPanel) { - this.getGroups = function getGroupsCallback(element) { - return function pushGroup(groups) { - if ( - (is(element, 'bpmn:Task') || is(element, 'bpmn:CallActivity') || is(element, 'bpmn:SubProcess')) && - typeof(element.businessObject.loopCharacteristics) !== 'undefined' && - element.businessObject.loopCharacteristics.$type === 'bpmn:StandardLoopCharacteristics' - ) { - const group = { - id: 'standardLoopCharacteristics', - component: Group, - label: 'Standard Loop', - entries: StandardLoopProps(element), - shouldOpen: true, - }; - if (groups.length < 3) - groups.push(group); - else - groups.splice(2, 0, group); - } - return groups; - }; - }; - propertiesPanel.registerProvider(LOW_PRIORITY, this); -} - -StandardLoopPropertiesProvider.$inject = ['propertiesPanel']; - -function StandardLoopProps(props) { - const { element } = props; - return [{ - id: 'loopMaximum', - component: LoopMaximum, - isEdited: isTextFieldEntryEdited - }, { - id: 'loopCondition', - component: LoopCondition, - isEdited: isTextFieldEntryEdited - }, { - id: 'testBefore', - component: TestBefore, - isEdited: isCheckboxEntryEdited - }]; -} - -function LoopMaximum(props) { - const { element } = props; - const debounce = useService('debounceInput'); - const translate = useService('translate'); - const commandStack = useService('commandStack'); - const bpmnFactory = useService('bpmnFactory'); - - const getValue = () => { - return getLoopProperty(element, 'loopMaximum'); - }; - - const setValue = value => { - setLoopProperty(element, 'loopMaximum', value, commandStack); - }; - - return TextFieldEntry({ - element, - id: 'loopMaximum', - label: translate('Loop Maximum'), - getValue, - setValue, - debounce - }); -} - -function TestBefore(props) { - const { element } = props; - const debounce = useService('debounceInput'); - const translate = useService('translate'); - const commandStack = useService('commandStack'); - const bpmnFactory = useService('bpmnFactory'); - - const getValue = () => { - return getLoopProperty(element, 'testBefore'); - }; - - const setValue = value => { - setLoopProperty(element, 'testBefore', value, commandStack); - }; - - return CheckboxEntry({ - element, - id: 'testBefore', - label: translate('Test Before'), - getValue, - setValue, - }); -} - -function LoopCondition(props) { - const { element } = props; - const debounce = useService('debounceInput'); - const translate = useService('translate'); - const commandStack = useService('commandStack'); - const bpmnFactory = useService('bpmnFactory'); - - const getValue = () => { - return getLoopProperty(element, 'loopCondition'); - }; - - const setValue = value => { - const loopCondition = bpmnFactory.create('bpmn:FormalExpression', {body: value}) - setLoopProperty(element, 'loopCondition', loopCondition, commandStack); - }; - - return TextFieldEntry({ - element, - id: 'loopCondition', - label: translate('Loop Condition'), - getValue, - setValue, - debounce - }); -} +import { is } from 'bpmn-js/lib/util/ModelUtil'; +import { useService } from 'bpmn-js-properties-panel'; +import { + Group, + TextFieldEntry, + isTextFieldEntryEdited, + CheckboxEntry, + isCheckboxEntryEdited, +} from '@bpmn-io/properties-panel'; + +import { getLoopProperty, setLoopProperty } from './helpers'; + +const LOW_PRIORITY = 500; + +export default function StandardLoopPropertiesProvider(propertiesPanel) { + this.getGroups = function getGroupsCallback(element) { + return function pushGroup(groups) { + if ( + (is(element, 'bpmn:Task') || is(element, 'bpmn:CallActivity') || is(element, 'bpmn:SubProcess')) && + typeof(element.businessObject.loopCharacteristics) !== 'undefined' && + element.businessObject.loopCharacteristics.$type === 'bpmn:StandardLoopCharacteristics' + ) { + const group = { + id: 'standardLoopCharacteristics', + component: Group, + label: 'Standard Loop', + entries: StandardLoopProps(element), + shouldOpen: true, + }; + if (groups.length < 3) + groups.push(group); + else + groups.splice(2, 0, group); + } + return groups; + }; + }; + propertiesPanel.registerProvider(LOW_PRIORITY, this); +} + +StandardLoopPropertiesProvider.$inject = ['propertiesPanel']; + +function StandardLoopProps(props) { + const { element } = props; + return [{ + id: 'loopMaximum', + component: LoopMaximum, + isEdited: isTextFieldEntryEdited + }, { + id: 'loopCondition', + component: LoopCondition, + isEdited: isTextFieldEntryEdited + }, { + id: 'testBefore', + component: TestBefore, + isEdited: isCheckboxEntryEdited + }]; +} + +function LoopMaximum(props) { + const { element } = props; + const debounce = useService('debounceInput'); + const translate = useService('translate'); + const commandStack = useService('commandStack'); + const bpmnFactory = useService('bpmnFactory'); + + const getValue = () => { + return getLoopProperty(element, 'loopMaximum'); + }; + + const setValue = value => { + setLoopProperty(element, 'loopMaximum', value, commandStack); + }; + + return TextFieldEntry({ + element, + id: 'loopMaximum', + label: translate('Loop Maximum'), + getValue, + setValue, + debounce + }); +} + +function TestBefore(props) { + const { element } = props; + const debounce = useService('debounceInput'); + const translate = useService('translate'); + const commandStack = useService('commandStack'); + const bpmnFactory = useService('bpmnFactory'); + + const getValue = () => { + return getLoopProperty(element, 'testBefore'); + }; + + const setValue = value => { + setLoopProperty(element, 'testBefore', value, commandStack); + }; + + return CheckboxEntry({ + element, + id: 'testBefore', + label: translate('Test Before'), + getValue, + setValue, + }); +} + +function LoopCondition(props) { + const { element } = props; + const debounce = useService('debounceInput'); + const translate = useService('translate'); + const commandStack = useService('commandStack'); + const bpmnFactory = useService('bpmnFactory'); + + const getValue = () => { + return getLoopProperty(element, 'loopCondition'); + }; + + const setValue = value => { + const loopCondition = bpmnFactory.create('bpmn:FormalExpression', {body: value}) + setLoopProperty(element, 'loopCondition', loopCondition, commandStack); + }; + + return TextFieldEntry({ + element, + id: 'loopCondition', + label: translate('Loop Condition'), + getValue, + setValue, + debounce + }); +} diff --git a/app/spiffworkflow/loops/helpers.js b/app/spiffworkflow/loops/helpers.js new file mode 100644 index 0000000..e0abff6 --- /dev/null +++ b/app/spiffworkflow/loops/helpers.js @@ -0,0 +1,54 @@ +/* eslint-disable prettier/prettier */ +/* eslint-disable no-param-reassign */ + +export function getLoopProperty(element, propertyName) { + const { loopCharacteristics } = element.businessObject; + const prop = loopCharacteristics.get(propertyName); + let value = ''; + if (typeof (prop) !== 'object') { + value = prop; + } else if (typeof (prop) !== 'undefined') { + if (prop.$type === 'bpmn:FormalExpression') + value = prop.get('body'); + else + value = prop.get('id'); + } + return value; +} + +export function setLoopProperty(element, propertyName, value, commandStack) { + const { loopCharacteristics } = element.businessObject; + + if (typeof (value) === 'object') { + value.$parent = loopCharacteristics; + } + + const properties = { [propertyName]: value }; + if (propertyName === 'loopCardinality') properties.loopDataInputRef = undefined; + if (propertyName === 'loopDataInputRef') properties.loopCardinality = undefined; + + commandStack.execute('element.updateModdleProperties', { + element, + moddleElement: loopCharacteristics, + properties, + }); +} + +export function removeLoopProperty(element, propertyName, commandStack) { + const { loopCharacteristics } = element.businessObject; + const properties = { [propertyName]: undefined }; + commandStack.execute('element.updateModdleProperties', { + element, + moddleElement: loopCharacteristics, + properties, + }); +} + +export function setIsIOValue(element, value, commandStack) { + commandStack.execute('element.updateProperties', { + element, + properties: { + 'spiffworkflow:isOutputSynced': value, + }, + }); +} diff --git a/app/spiffworkflow/loops/propertiesPanel/CompletionConditionEntry.js b/app/spiffworkflow/loops/propertiesPanel/CompletionConditionEntry.js new file mode 100644 index 0000000..6364c7c --- /dev/null +++ b/app/spiffworkflow/loops/propertiesPanel/CompletionConditionEntry.js @@ -0,0 +1,46 @@ +/* eslint-disable prettier/prettier */ +/* eslint-disable import/no-extraneous-dependencies */ +/* eslint-disable import/order */ + +import { useService } from "bpmn-js-properties-panel"; +import { getLoopProperty, removeLoopProperty, setLoopProperty } from "../helpers"; +import { TextFieldEntry } from '@bpmn-io/properties-panel'; + +export function CompletionCondition(props) { + const { element } = props; + const debounce = useService('debounceInput'); + const translate = useService('translate'); + const commandStack = useService('commandStack'); + const bpmnFactory = useService('bpmnFactory'); + + const getValue = () => { + return getLoopProperty(element, 'completionCondition'); + }; + + const setValue = (value) => { + if (!value || value === '') { + // If value is empty, remove completionCondition from XML + removeLoopProperty(element, 'completionCondition', commandStack); + return; + } + const completionCondition = bpmnFactory.create('bpmn:FormalExpression', { + body: value, + }); + setLoopProperty( + element, + 'completionCondition', + completionCondition, + commandStack + ); + }; + + return TextFieldEntry({ + element, + id: 'completionCondition', + label: translate('Completion Condition'), + getValue, + setValue, + debounce, + description: 'Stop executing this task when this condition is met', + }); + } \ No newline at end of file diff --git a/app/spiffworkflow/loops/propertiesPanel/InputCollectionEntry.js b/app/spiffworkflow/loops/propertiesPanel/InputCollectionEntry.js new file mode 100644 index 0000000..5befc57 --- /dev/null +++ b/app/spiffworkflow/loops/propertiesPanel/InputCollectionEntry.js @@ -0,0 +1,41 @@ +/* eslint-disable prettier/prettier */ +/* eslint-disable import/no-extraneous-dependencies */ +/* eslint-disable import/order */ + +import { useService } from "bpmn-js-properties-panel"; +import { getLoopProperty, removeLoopProperty, setLoopProperty } from "../helpers"; +import { TextFieldEntry } from '@bpmn-io/properties-panel'; + +export function InputCollection(props) { + const { element } = props; + const debounce = useService('debounceInput'); + const translate = useService('translate'); + const commandStack = useService('commandStack'); + const bpmnFactory = useService('bpmnFactory'); + + const getValue = () => { + return getLoopProperty(element, 'loopDataInputRef'); + }; + + const setValue = (value) => { + if (!value || value === '') { + // If value is empty or undefined, remove loopDataInputRef from XML + removeLoopProperty(element, 'loopDataInputRef', commandStack); + return; + } + const collection = bpmnFactory.create('bpmn:ItemAwareElement', { + id: value, + }); + setLoopProperty(element, 'loopDataInputRef', collection, commandStack); + }; + + return TextFieldEntry({ + element, + id: 'loopDataInputRef', + label: translate('Input Collection'), + getValue, + setValue, + debounce, + description: 'Create an instance for each item in this collection', + }); +} \ No newline at end of file diff --git a/app/spiffworkflow/loops/propertiesPanel/InputItemEntry.js b/app/spiffworkflow/loops/propertiesPanel/InputItemEntry.js new file mode 100644 index 0000000..6c4778e --- /dev/null +++ b/app/spiffworkflow/loops/propertiesPanel/InputItemEntry.js @@ -0,0 +1,46 @@ +/* eslint-disable prettier/prettier */ +/* eslint-disable import/no-extraneous-dependencies */ +/* eslint-disable import/order */ + +import { useService } from "bpmn-js-properties-panel"; +import { getLoopProperty, setLoopProperty } from "../helpers"; +import { TextFieldEntry } from '@bpmn-io/properties-panel'; + +export function InputItem(props) { + const { element } = props; + const debounce = useService('debounceInput'); + const translate = useService('translate'); + const commandStack = useService('commandStack'); + const bpmnFactory = useService('bpmnFactory'); + + const getValue = () => { + return getLoopProperty(element, 'inputDataItem'); + }; + + const setValue = (value) => { + const item = + typeof value !== 'undefined' && value !== '' + ? bpmnFactory.create('bpmn:DataInput', { id: value, name: value }) + : undefined; + setLoopProperty(element, 'inputDataItem', item, commandStack); + + try { + const { businessObject } = element; + if (businessObject.get('spiffworkflow:isOutputSynced')) { + setLoopProperty(element, 'outputDataItem', item, commandStack); + } + } catch (error) { + console.log('Error caught while set value Input item', error); + } + }; + + return TextFieldEntry({ + element, + id: 'inputDataItem', + label: translate('Input Element'), + getValue, + setValue, + debounce, + description: 'Each item in the collection will be copied to this variable', + }); +} \ No newline at end of file diff --git a/app/spiffworkflow/loops/propertiesPanel/IsIOSyncEntry.js b/app/spiffworkflow/loops/propertiesPanel/IsIOSyncEntry.js new file mode 100644 index 0000000..bd87208 --- /dev/null +++ b/app/spiffworkflow/loops/propertiesPanel/IsIOSyncEntry.js @@ -0,0 +1,48 @@ +/* eslint-disable prettier/prettier */ +/* eslint-disable import/no-extraneous-dependencies */ +/* eslint-disable import/order */ + +import { useService } from "bpmn-js-properties-panel"; +import { getLoopProperty, removeLoopProperty, setIsIOValue, setLoopProperty } from "../helpers"; +import { CheckboxEntry } from '@bpmn-io/properties-panel'; + +export function IsOutputElSync(props) { + const { element } = props; + const translate = useService('translate'); + const commandStack = useService('commandStack'); + const bpmnFactory = useService('bpmnFactory'); + + const getValue = () => { + const { businessObject } = element; + return businessObject.get('spiffworkflow:isOutputSynced') + ? businessObject.get('spiffworkflow:isOutputSynced') + : false; + }; + + const setValue = (value) => { + if (value) { + const valIn = getLoopProperty(element, 'inputDataItem'); + const item = + typeof valIn !== 'undefined' && valIn !== '' + ? bpmnFactory.create('bpmn:DataOutput', { id: valIn, name: valIn }) + : undefined; + if(item){ + // If DataInput Item is found and set, add new DataOut with same value + setLoopProperty(element, 'outputDataItem', item, commandStack); + } + } else { + // Remove DataOutput value when isIoSync is disabled + removeLoopProperty(element, 'outputDataItem', commandStack); + } + + setIsIOValue(element, value, commandStack); + }; + + return CheckboxEntry({ + element, + id: 'testBefore', + label: translate('Output Element is Synchronized with Input Element'), + getValue, + setValue, + }); +} diff --git a/app/spiffworkflow/loops/propertiesPanel/LoopCardinalityEntry.js b/app/spiffworkflow/loops/propertiesPanel/LoopCardinalityEntry.js new file mode 100644 index 0000000..a1b4ed6 --- /dev/null +++ b/app/spiffworkflow/loops/propertiesPanel/LoopCardinalityEntry.js @@ -0,0 +1,41 @@ +/* eslint-disable prettier/prettier */ +/* eslint-disable import/no-extraneous-dependencies */ +/* eslint-disable import/order */ + +import { useService } from "bpmn-js-properties-panel"; +import { getLoopProperty, removeLoopProperty, setLoopProperty } from "../helpers"; +import { TextFieldEntry } from '@bpmn-io/properties-panel'; + +export function LoopCardinality(props) { + const { element } = props; + const debounce = useService('debounceInput'); + const translate = useService('translate'); + const commandStack = useService('commandStack'); + const bpmnFactory = useService('bpmnFactory'); + + const getValue = () => { + return getLoopProperty(element, 'loopCardinality'); + }; + + const setValue = (value) => { + if (!value || value === '') { + // If value is empty or undefined, remove loopCardinality from XML + removeLoopProperty(element, 'loopCardinality', commandStack); + return; + } + const loopCardinality = bpmnFactory.create('bpmn:FormalExpression', { + body: value, + }); + setLoopProperty(element, 'loopCardinality', loopCardinality, commandStack); + }; + + return TextFieldEntry({ + element, + id: 'loopCardinality', + label: translate('Loop Cardinality'), + getValue, + setValue, + debounce, + description: 'Explicitly set the number of instances', + }); +} \ No newline at end of file diff --git a/app/spiffworkflow/loops/propertiesPanel/LoopProperty.js b/app/spiffworkflow/loops/propertiesPanel/LoopProperty.js deleted file mode 100644 index 779d2c9..0000000 --- a/app/spiffworkflow/loops/propertiesPanel/LoopProperty.js +++ /dev/null @@ -1,30 +0,0 @@ -export function getLoopProperty(element, propertyName) { - - const loopCharacteristics = element.businessObject.loopCharacteristics; - const prop = loopCharacteristics.get(propertyName); - - let value = ''; - if (typeof(prop) !== 'object') { - value = prop; - } else if (typeof(prop) !== 'undefined') { - if (prop.$type === 'bpmn:FormalExpression') - value = prop.get('body'); - else - value = prop.get('id'); - } - return value; -} - -export function setLoopProperty(element, propertyName, value, commandStack) { - const loopCharacteristics = element.businessObject.loopCharacteristics; - if (typeof(value) === 'object') - value.$parent = loopCharacteristics; - let properties = { [propertyName]: value }; - if (propertyName === 'loopCardinality') properties['loopDataInputRef'] = undefined; - if (propertyName === 'loopDataInputRef') properties['loopCardinality'] = undefined; - commandStack.execute('element.updateModdleProperties', { - element, - moddleElement: loopCharacteristics, - properties: properties, - }); -} diff --git a/app/spiffworkflow/loops/propertiesPanel/MultiInstancePropertiesProvider.js b/app/spiffworkflow/loops/propertiesPanel/MultiInstancePropertiesProvider.js deleted file mode 100644 index afebae1..0000000 --- a/app/spiffworkflow/loops/propertiesPanel/MultiInstancePropertiesProvider.js +++ /dev/null @@ -1,221 +0,0 @@ -import { is } from 'bpmn-js/lib/util/ModelUtil'; -import { useService } from 'bpmn-js-properties-panel'; -import { TextFieldEntry, isTextFieldEntryEdited } from '@bpmn-io/properties-panel'; -import { getLoopProperty, setLoopProperty } from './LoopProperty'; - -const LOW_PRIORITY = 500; - -export default function MultiInstancePropertiesProvider(propertiesPanel) { - this.getGroups = function getGroupsCallback(element) { - return function pushGroup(groups) { - if (is(element, 'bpmn:Task') || is(element, 'bpmn:CallActivity') || is(element, 'bpmn:SubProcess')) { - let group = groups.filter(g => g.id == 'multiInstance'); - if (group.length == 1) - updateMultiInstanceGroup(element, group[0]); - } - return groups; - }; - }; - propertiesPanel.registerProvider(LOW_PRIORITY, this); -} - -MultiInstancePropertiesProvider.$inject = ['propertiesPanel']; - -function updateMultiInstanceGroup(element, group) { - group.entries = MultiInstanceProps({element}); - group.shouldOpen = true; -} - -function MultiInstanceProps(props) { - const { element } = props; - - const entries = [{ - id: 'loopCardinality', - component: LoopCardinality, - isEdited: isTextFieldEntryEdited - }, { - id: 'loopDataInputRef', - component: InputCollection, - isEdited: isTextFieldEntryEdited - }, { - id: 'dataInputItem', - component: InputItem, - isEdited: isTextFieldEntryEdited - }, { - id: 'loopDataOutputRef', - component: OutputCollection, - isEdited: isTextFieldEntryEdited - }, { - id: 'dataOutputItem', - component: OutputItem, - isEdited: isTextFieldEntryEdited - }, { - id: 'completionCondition', - component: CompletionCondition, - isEdited: isTextFieldEntryEdited - }]; - return entries; -} - -function LoopCardinality(props) { - const { element } = props; - const debounce = useService('debounceInput'); - const translate = useService('translate'); - const commandStack = useService('commandStack'); - const bpmnFactory = useService('bpmnFactory'); - - const getValue = () => { - return getLoopProperty(element, 'loopCardinality'); - }; - - const setValue = value => { - const loopCardinality = bpmnFactory.create('bpmn:FormalExpression', {body: value}) - setLoopProperty(element, 'loopCardinality', loopCardinality, commandStack); - }; - - return TextFieldEntry({ - element, - id: 'loopCardinality', - label: translate('Loop Cardinality'), - getValue, - setValue, - debounce, - description: 'Explicitly set the number of instances' - }); -} - -function InputCollection(props) { - const { element } = props; - const debounce = useService('debounceInput'); - const translate = useService('translate'); - const commandStack = useService('commandStack'); - const bpmnFactory = useService('bpmnFactory'); - - const getValue = () => { - return getLoopProperty(element, 'loopDataInputRef'); - }; - - const setValue = value => { - const collection = bpmnFactory.create('bpmn:ItemAwareElement', {id: value}); - setLoopProperty(element, 'loopDataInputRef', collection, commandStack); - }; - - return TextFieldEntry({ - element, - id: 'loopDataInputRef', - label: translate('Input Collection'), - getValue, - setValue, - debounce, - description: 'Create an instance for each item in this collection' - }); -} - -function InputItem(props) { - const { element } = props; - const debounce = useService('debounceInput'); - const translate = useService('translate'); - const commandStack = useService('commandStack'); - const bpmnFactory = useService('bpmnFactory'); - - const getValue = () => { - return getLoopProperty(element, 'inputDataItem'); - }; - - const setValue = value => { - const item = (typeof(value) !== 'undefined') ? bpmnFactory.create('bpmn:DataInput', {id: value, name: value}) : undefined; - setLoopProperty(element, 'inputDataItem', item, commandStack); - }; - - return TextFieldEntry({ - element, - id: 'inputDataItem', - label: translate('Input Element'), - getValue, - setValue, - debounce, - description: 'Each item in the collection will be copied to this variable' - }); -} - -function OutputCollection(props) { - const { element } = props; - const debounce = useService('debounceInput'); - const translate = useService('translate'); - const commandStack = useService('commandStack'); - const bpmnFactory = useService('bpmnFactory'); - - const getValue = () => { - return getLoopProperty(element, 'loopDataOutputRef'); - }; - - const setValue = value => { - const collection = bpmnFactory.create('bpmn:ItemAwareElement', {id: value}); - setLoopProperty(element, 'loopDataOutputRef', collection, commandStack); - }; - - return TextFieldEntry({ - element, - id: 'loopDataOutputRef', - label: translate('Output Collection'), - getValue, - setValue, - debounce, - description: 'Create or update this collection with the instance results' - }); -} - -function OutputItem(props) { - const { element } = props; - const debounce = useService('debounceInput'); - const translate = useService('translate'); - const commandStack = useService('commandStack'); - const bpmnFactory = useService('bpmnFactory'); - - const getValue = () => { - return getLoopProperty(element, 'outputDataItem'); - }; - - const setValue = value => { - const item = (typeof(value) !== 'undefined') ? bpmnFactory.create('bpmn:DataOutput', {id: value, name: value}) : undefined; - setLoopProperty(element, 'outputDataItem', item, commandStack); - }; - - return TextFieldEntry({ - element, - id: 'outputDataItem', - label: translate('Output Element'), - getValue, - setValue, - debounce, - description: 'The value of this variable will be added to the output collection' - }); -} - -function CompletionCondition(props) { - const { element } = props; - const debounce = useService('debounceInput'); - const translate = useService('translate'); - const commandStack = useService('commandStack'); - const bpmnFactory = useService('bpmnFactory'); - - const getValue = () => { - return getLoopProperty(element, 'completionCondition'); - }; - - const setValue = value => { - const completionCondition = bpmnFactory.create('bpmn:FormalExpression', {body: value}) - setLoopProperty(element, 'completionCondition', completionCondition, commandStack); - }; - - return TextFieldEntry({ - element, - id: 'completionCondition', - label: translate('Completion Condition'), - getValue, - setValue, - debounce, - description: 'Stop executing this task when this condition is met' - }); -} - diff --git a/app/spiffworkflow/loops/propertiesPanel/OutputCollectionEntry.js b/app/spiffworkflow/loops/propertiesPanel/OutputCollectionEntry.js new file mode 100644 index 0000000..dcc5e73 --- /dev/null +++ b/app/spiffworkflow/loops/propertiesPanel/OutputCollectionEntry.js @@ -0,0 +1,41 @@ +/* eslint-disable prettier/prettier */ +/* eslint-disable import/no-extraneous-dependencies */ +/* eslint-disable import/order */ + +import { useService } from "bpmn-js-properties-panel"; +import { getLoopProperty, removeLoopProperty, setLoopProperty } from "../helpers"; +import { TextFieldEntry } from '@bpmn-io/properties-panel'; + +export function OutputCollection(props) { + const { element } = props; + const debounce = useService('debounceInput'); + const translate = useService('translate'); + const commandStack = useService('commandStack'); + const bpmnFactory = useService('bpmnFactory'); + + const getValue = () => { + return getLoopProperty(element, 'loopDataOutputRef'); + }; + + const setValue = (value) => { + if (!value || value === '') { + // If value is empty or undefined, remove loopDataOutputRef from XML + removeLoopProperty(element, 'loopDataOutputRef', commandStack); + return; + } + const collection = bpmnFactory.create('bpmn:ItemAwareElement', { + id: value, + }); + setLoopProperty(element, 'loopDataOutputRef', collection, commandStack); + }; + + return TextFieldEntry({ + element, + id: 'loopDataOutputRef', + label: translate('Output Collection'), + getValue, + setValue, + debounce, + description: 'Create or update this collection with the instance results', + }); +} \ No newline at end of file diff --git a/app/spiffworkflow/loops/propertiesPanel/OutputItemEntry.js b/app/spiffworkflow/loops/propertiesPanel/OutputItemEntry.js new file mode 100644 index 0000000..627fc0c --- /dev/null +++ b/app/spiffworkflow/loops/propertiesPanel/OutputItemEntry.js @@ -0,0 +1,48 @@ +/* eslint-disable prettier/prettier */ +/* eslint-disable import/no-extraneous-dependencies */ +/* eslint-disable import/order */ +import { useService } from "bpmn-js-properties-panel"; +import { getLoopProperty, setLoopProperty } from "../helpers"; +import { TextFieldEntry } from '@bpmn-io/properties-panel'; + + +export function OutputItem(props) { + const { element } = props; + const debounce = useService('debounceInput'); + const translate = useService('translate'); + const commandStack = useService('commandStack'); + const bpmnFactory = useService('bpmnFactory'); + + const getValue = () => { + return getLoopProperty(element, 'outputDataItem'); + }; + + const setValue = (value) => { + try { + const inVal = getLoopProperty(element, 'inputDataItem'); + if (inVal === value) { + alert( 'You have entered the same value for both Input and Output elements without enabling synchronization. Please confirm if this is intended.' ); + return; + } + } catch (error) { + console.log('Error caught while Set Value OutputItem', error); + } + + const item = + typeof value !== 'undefined' && value !== '' + ? bpmnFactory.create('bpmn:DataOutput', { id: value, name: value }) + : undefined; + setLoopProperty(element, 'outputDataItem', item, commandStack); + }; + + return TextFieldEntry({ + element, + id: 'outputDataItem', + label: translate('Output Element'), + getValue, + setValue, + debounce, + description: + 'The value of this variable will be added to the output collection', + }); + } \ No newline at end of file