ReplaceConnectionBehavior.js 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  1. import {
  2. forEach,
  3. find,
  4. matchPattern
  5. } from 'min-dash';
  6. import inherits from 'inherits-browser';
  7. import CommandInterceptor from 'diagram-js/lib/command/CommandInterceptor';
  8. import { is } from '../../../util/ModelUtil';
  9. /**
  10. * @typedef {import('diagram-js/lib/core/EventBus').default} EventBus
  11. * @typedef {import('../Modeling').default} Modeling
  12. * @typedef {import('../../rules/BpmnRules').default} BpmnRules
  13. * @typedef {import('didi').Injector} Injector
  14. */
  15. /**
  16. * @param {EventBus} eventBus
  17. * @param {Modeling} modeling
  18. * @param {BpmnRules} bpmnRules
  19. * @param {Injector} injector
  20. */
  21. export default function ReplaceConnectionBehavior(eventBus, modeling, bpmnRules, injector) {
  22. CommandInterceptor.call(this, eventBus);
  23. var dragging = injector.get('dragging', false);
  24. function fixConnection(connection) {
  25. var source = connection.source,
  26. target = connection.target,
  27. parent = connection.parent;
  28. // do not do anything if connection
  29. // is already deleted (may happen due to other
  30. // behaviors plugged-in before)
  31. if (!parent) {
  32. return;
  33. }
  34. var replacementType,
  35. remove;
  36. /**
  37. * Check if incoming or outgoing connections
  38. * can stay or could be substituted with an
  39. * appropriate replacement.
  40. *
  41. * This holds true for SequenceFlow <> MessageFlow.
  42. */
  43. if (is(connection, 'bpmn:SequenceFlow')) {
  44. if (!bpmnRules.canConnectSequenceFlow(source, target)) {
  45. remove = true;
  46. }
  47. if (bpmnRules.canConnectMessageFlow(source, target)) {
  48. replacementType = 'bpmn:MessageFlow';
  49. }
  50. }
  51. // transform message flows into sequence flows, if possible
  52. if (is(connection, 'bpmn:MessageFlow')) {
  53. if (!bpmnRules.canConnectMessageFlow(source, target)) {
  54. remove = true;
  55. }
  56. if (bpmnRules.canConnectSequenceFlow(source, target)) {
  57. replacementType = 'bpmn:SequenceFlow';
  58. }
  59. }
  60. // remove invalid connection,
  61. // unless it has been removed already
  62. if (remove) {
  63. modeling.removeConnection(connection);
  64. }
  65. // replace SequenceFlow <> MessageFlow
  66. if (replacementType) {
  67. modeling.connect(source, target, {
  68. type: replacementType,
  69. waypoints: connection.waypoints.slice()
  70. });
  71. }
  72. }
  73. function replaceReconnectedConnection(event) {
  74. var context = event.context,
  75. connection = context.connection,
  76. source = context.newSource || connection.source,
  77. target = context.newTarget || connection.target,
  78. allowed,
  79. replacement;
  80. allowed = bpmnRules.canConnect(source, target);
  81. if (!allowed || allowed.type === connection.type) {
  82. return;
  83. }
  84. replacement = modeling.connect(source, target, {
  85. type: allowed.type,
  86. associationDirection: allowed.associationDirection,
  87. waypoints: connection.waypoints.slice()
  88. });
  89. // remove old connection unless it's already removed
  90. if (connection.parent) {
  91. modeling.removeConnection(connection);
  92. }
  93. // replace connection in context to reconnect end/start
  94. context.connection = replacement;
  95. if (dragging) {
  96. cleanDraggingSelection(connection, replacement);
  97. }
  98. }
  99. // monkey-patch selection saved in dragging in order to re-select it when operation is finished
  100. function cleanDraggingSelection(oldConnection, newConnection) {
  101. var context = dragging.context(),
  102. previousSelection = context && context.payload.previousSelection,
  103. index;
  104. // do nothing if not dragging or no selection was present
  105. if (!previousSelection || !previousSelection.length) {
  106. return;
  107. }
  108. index = previousSelection.indexOf(oldConnection);
  109. if (index === -1) {
  110. return;
  111. }
  112. previousSelection.splice(index, 1, newConnection);
  113. }
  114. // lifecycle hooks
  115. this.postExecuted('elements.move', function(context) {
  116. var closure = context.closure,
  117. allConnections = closure.allConnections;
  118. forEach(allConnections, fixConnection);
  119. }, true);
  120. this.preExecute('connection.reconnect', replaceReconnectedConnection);
  121. this.postExecuted('element.updateProperties', function(event) {
  122. var context = event.context,
  123. properties = context.properties,
  124. element = context.element,
  125. businessObject = element.businessObject,
  126. connection;
  127. // remove condition on change to default
  128. if (properties.default) {
  129. connection = find(
  130. element.outgoing,
  131. matchPattern({ id: element.businessObject.default.id })
  132. );
  133. if (connection) {
  134. modeling.updateProperties(connection, { conditionExpression: undefined });
  135. }
  136. }
  137. // remove default from source on change to conditional
  138. if (properties.conditionExpression && businessObject.sourceRef.default === businessObject) {
  139. modeling.updateProperties(element.source, { default: undefined });
  140. }
  141. });
  142. }
  143. inherits(ReplaceConnectionBehavior, CommandInterceptor);
  144. ReplaceConnectionBehavior.$inject = [
  145. 'eventBus',
  146. 'modeling',
  147. 'bpmnRules',
  148. 'injector'
  149. ];