CreateParticipantBehavior.js 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255
  1. import inherits from 'inherits';
  2. import CommandInterceptor from 'diagram-js/lib/command/CommandInterceptor';
  3. import { is } from '../../../util/ModelUtil';
  4. import { isLabel } from '../../../util/LabelUtil';
  5. import { getBBox } from 'diagram-js/lib/util/Elements';
  6. import {
  7. assign,
  8. find
  9. } from 'min-dash';
  10. import { asTRBL } from 'diagram-js/lib/layout/LayoutUtil';
  11. var HORIZONTAL_PARTICIPANT_PADDING = 20,
  12. VERTICAL_PARTICIPANT_PADDING = 20;
  13. export var PARTICIPANT_BORDER_WIDTH = 30;
  14. var HIGH_PRIORITY = 2000;
  15. /**
  16. * BPMN-specific behavior for creating participants.
  17. */
  18. export default function CreateParticipantBehavior(canvas, eventBus, modeling) {
  19. CommandInterceptor.call(this, eventBus);
  20. // fit participant
  21. eventBus.on([
  22. 'create.start',
  23. 'shape.move.start'
  24. ], HIGH_PRIORITY, function(event) {
  25. var context = event.context,
  26. shape = context.shape,
  27. rootElement = canvas.getRootElement();
  28. if (!is(shape, 'bpmn:Participant') ||
  29. !is(rootElement, 'bpmn:Process') ||
  30. !rootElement.children.length) {
  31. return;
  32. }
  33. // ignore connections, groups and labels
  34. var children = rootElement.children.filter(function(element) {
  35. return !is(element, 'bpmn:Group') &&
  36. !isLabel(element) &&
  37. !isConnection(element);
  38. });
  39. // ensure for available children to calculate bounds
  40. if (!children.length) {
  41. return;
  42. }
  43. var childrenBBox = getBBox(children);
  44. var participantBounds = getParticipantBounds(shape, childrenBBox);
  45. // assign width and height
  46. assign(shape, participantBounds);
  47. // assign create constraints
  48. context.createConstraints = getParticipantCreateConstraints(shape, childrenBBox);
  49. });
  50. // force hovering process when creating first participant
  51. eventBus.on('create.start', HIGH_PRIORITY, function(event) {
  52. var context = event.context,
  53. shape = context.shape,
  54. rootElement = canvas.getRootElement(),
  55. rootElementGfx = canvas.getGraphics(rootElement);
  56. function ensureHoveringProcess(event) {
  57. event.element = rootElement;
  58. event.gfx = rootElementGfx;
  59. }
  60. if (is(shape, 'bpmn:Participant') && is(rootElement, 'bpmn:Process')) {
  61. eventBus.on('element.hover', HIGH_PRIORITY, ensureHoveringProcess);
  62. eventBus.once('create.cleanup', function() {
  63. eventBus.off('element.hover', ensureHoveringProcess);
  64. });
  65. }
  66. });
  67. function ensureCollaboration(context) {
  68. var parent = context.parent,
  69. collaboration;
  70. var rootElement = canvas.getRootElement();
  71. if (is(rootElement, 'bpmn:Collaboration')) {
  72. collaboration = rootElement;
  73. } else {
  74. // update root element by making collaboration
  75. collaboration = modeling.makeCollaboration();
  76. // re-use process when creating first participant
  77. context.process = parent;
  78. }
  79. context.parent = collaboration;
  80. }
  81. // turn process into collaboration before adding participant
  82. this.preExecute('shape.create', function(context) {
  83. var parent = context.parent,
  84. shape = context.shape;
  85. if (is(shape, 'bpmn:Participant') && is(parent, 'bpmn:Process')) {
  86. ensureCollaboration(context);
  87. }
  88. }, true);
  89. this.execute('shape.create', function(context) {
  90. var process = context.process,
  91. shape = context.shape;
  92. if (process) {
  93. context.oldProcessRef = shape.businessObject.processRef;
  94. // re-use process when creating first participant
  95. shape.businessObject.processRef = process.businessObject;
  96. }
  97. }, true);
  98. this.revert('shape.create', function(context) {
  99. var process = context.process,
  100. shape = context.shape;
  101. if (process) {
  102. // re-use process when creating first participant
  103. shape.businessObject.processRef = context.oldProcessRef;
  104. }
  105. }, true);
  106. this.postExecute('shape.create', function(context) {
  107. var process = context.process,
  108. shape = context.shape;
  109. if (process) {
  110. // move children from process to participant
  111. var processChildren = process.children.slice();
  112. modeling.moveElements(processChildren, { x: 0, y: 0 }, shape);
  113. }
  114. }, true);
  115. // turn process into collaboration when creating participants
  116. this.preExecute('elements.create', HIGH_PRIORITY, function(context) {
  117. var elements = context.elements,
  118. parent = context.parent,
  119. participant;
  120. var hasParticipants = findParticipant(elements);
  121. if (hasParticipants && is(parent, 'bpmn:Process')) {
  122. ensureCollaboration(context);
  123. participant = findParticipant(elements);
  124. context.oldProcessRef = participant.businessObject.processRef;
  125. // re-use process when creating first participant
  126. participant.businessObject.processRef = parent.businessObject;
  127. }
  128. }, true);
  129. this.revert('elements.create', function(context) {
  130. var elements = context.elements,
  131. process = context.process,
  132. participant;
  133. if (process) {
  134. participant = findParticipant(elements);
  135. // re-use process when creating first participant
  136. participant.businessObject.processRef = context.oldProcessRef;
  137. }
  138. }, true);
  139. this.postExecute('elements.create', function(context) {
  140. var elements = context.elements,
  141. process = context.process,
  142. participant;
  143. if (process) {
  144. participant = findParticipant(elements);
  145. // move children from process to first participant
  146. var processChildren = process.children.slice();
  147. modeling.moveElements(processChildren, { x: 0, y: 0 }, participant);
  148. }
  149. }, true);
  150. }
  151. CreateParticipantBehavior.$inject = [
  152. 'canvas',
  153. 'eventBus',
  154. 'modeling'
  155. ];
  156. inherits(CreateParticipantBehavior, CommandInterceptor);
  157. // helpers //////////
  158. function getParticipantBounds(shape, childrenBBox) {
  159. childrenBBox = {
  160. width: childrenBBox.width + HORIZONTAL_PARTICIPANT_PADDING * 2 + PARTICIPANT_BORDER_WIDTH,
  161. height: childrenBBox.height + VERTICAL_PARTICIPANT_PADDING * 2
  162. };
  163. var width = Math.max(shape.width, childrenBBox.width),
  164. height = Math.max(shape.height, childrenBBox.height);
  165. return {
  166. x: -width / 2,
  167. y: -height / 2,
  168. width: width,
  169. height: height
  170. };
  171. }
  172. function getParticipantCreateConstraints(shape, childrenBBox) {
  173. childrenBBox = asTRBL(childrenBBox);
  174. return {
  175. bottom: childrenBBox.top + shape.height / 2 - VERTICAL_PARTICIPANT_PADDING,
  176. left: childrenBBox.right - shape.width / 2 + HORIZONTAL_PARTICIPANT_PADDING,
  177. top: childrenBBox.bottom - shape.height / 2 + VERTICAL_PARTICIPANT_PADDING,
  178. right: childrenBBox.left + shape.width / 2 - HORIZONTAL_PARTICIPANT_PADDING - PARTICIPANT_BORDER_WIDTH
  179. };
  180. }
  181. function isConnection(element) {
  182. return !!element.waypoints;
  183. }
  184. function findParticipant(elements) {
  185. return find(elements, function(element) {
  186. return is(element, 'bpmn:Participant');
  187. });
  188. }