GroupBehavior.js 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333
  1. import inherits from 'inherits-browser';
  2. import CommandInterceptor from 'diagram-js/lib/command/CommandInterceptor';
  3. import {
  4. getBusinessObject,
  5. is
  6. } from '../../../util/ModelUtil';
  7. import {
  8. createCategory,
  9. createCategoryValue,
  10. linkCategoryValue,
  11. unlinkCategory,
  12. unlinkCategoryValue
  13. } from './util/CategoryUtil';
  14. /**
  15. * @typedef {import('../BpmnFactory').default} BpmnFactory
  16. * @typedef {import('../../../Modeler').default} Modeler
  17. * @typedef {import('diagram-js/lib/core/ElementRegistry').default} ElementRegistry
  18. * @typedef {import('diagram-js/lib/core/EventBus').default} EventBus
  19. * @typedef {import('didi').Injector} Injector
  20. * @typedef {import('../../copy-paste/ModdleCopy').default} ModdleCopy
  21. *
  22. * @typedef {import('../../../model/Types').Element} Element
  23. * @typedef {import('../../../model/Types').Shape} Shape
  24. *
  25. * @typedef {import('diagram-js/lib/util/Types').DirectionTRBL} DirectionTRBL
  26. */
  27. var LOWER_PRIORITY = 770;
  28. /**
  29. * BPMN specific group behavior.
  30. *
  31. * @param {BpmnFactory} bpmnFactory
  32. * @param {Modeler} bpmnjs
  33. * @param {ElementRegistry} elementRegistry
  34. * @param {EventBus} eventBus
  35. * @param {Injector} injector
  36. * @param {ModdleCopy} moddleCopy
  37. */
  38. export default function GroupBehavior(
  39. bpmnFactory,
  40. bpmnjs,
  41. elementRegistry,
  42. eventBus,
  43. injector,
  44. moddleCopy
  45. ) {
  46. injector.invoke(CommandInterceptor, this);
  47. /**
  48. * Returns all group element in the current registry.
  49. *
  50. * @return {Shape[]}
  51. */
  52. function getGroupElements() {
  53. return elementRegistry.filter(function(e) {
  54. return is(e, 'bpmn:Group');
  55. });
  56. }
  57. /**
  58. * Returns true if given category is referenced in one of the given elements.
  59. *
  60. * @param {Element[]} elements
  61. * @param {ModdleElement} category
  62. *
  63. * @return {boolean}
  64. */
  65. function isReferencedCategory(elements, category) {
  66. return elements.some(function(element) {
  67. var businessObject = getBusinessObject(element);
  68. var _category = businessObject.categoryValueRef && businessObject.categoryValueRef.$parent;
  69. return _category === category;
  70. });
  71. }
  72. /**
  73. * Returns true if given categoryValue is referenced in one of the given elements.
  74. *
  75. * @param {Element[]} elements
  76. * @param {ModdleElement} categoryValue
  77. *
  78. * @return {boolean}
  79. */
  80. function isReferencedCategoryValue(elements, categoryValue) {
  81. return elements.some(function(element) {
  82. var businessObject = getBusinessObject(element);
  83. return businessObject.categoryValueRef === categoryValue;
  84. });
  85. }
  86. /**
  87. * Remove category value unless it is still referenced.
  88. *
  89. * @param {ModdleElement} categoryValue
  90. * @param {ModdleElement} category
  91. * @param {ModdleElement} businessObject
  92. */
  93. function removeCategoryValue(categoryValue, category, businessObject) {
  94. var groups = getGroupElements().filter(function(element) {
  95. return element.businessObject !== businessObject;
  96. });
  97. if (category && !isReferencedCategory(groups, category)) {
  98. unlinkCategory(category);
  99. }
  100. if (categoryValue && !isReferencedCategoryValue(groups, categoryValue)) {
  101. unlinkCategoryValue(categoryValue);
  102. }
  103. }
  104. /**
  105. * Add category value.
  106. *
  107. * @param {ModdleElement} categoryValue
  108. * @param {ModdleElement} category
  109. *
  110. * @return {ModdleElement}
  111. */
  112. function addCategoryValue(categoryValue, category) {
  113. return linkCategoryValue(categoryValue, category, bpmnjs.getDefinitions());
  114. }
  115. function setCategoryValue(element, context) {
  116. var businessObject = getBusinessObject(element),
  117. categoryValue = businessObject.categoryValueRef;
  118. if (!categoryValue) {
  119. categoryValue =
  120. businessObject.categoryValueRef =
  121. context.categoryValue = (
  122. context.categoryValue || createCategoryValue(bpmnFactory)
  123. );
  124. }
  125. var category = categoryValue.$parent;
  126. if (!category) {
  127. category =
  128. categoryValue.$parent =
  129. context.category = (
  130. context.category || createCategory(bpmnFactory)
  131. );
  132. }
  133. addCategoryValue(categoryValue, category, bpmnjs.getDefinitions());
  134. }
  135. function unsetCategoryValue(element, context) {
  136. var category = context.category,
  137. categoryValue = context.categoryValue,
  138. businessObject = getBusinessObject(element);
  139. if (categoryValue) {
  140. businessObject.categoryValueRef = null;
  141. removeCategoryValue(categoryValue, category, businessObject);
  142. } else {
  143. removeCategoryValue(null, businessObject.categoryValueRef.$parent, businessObject);
  144. }
  145. }
  146. // ensure category + value exist before label editing
  147. this.execute('label.create', function(event) {
  148. var context = event.context,
  149. labelTarget = context.labelTarget;
  150. if (!is(labelTarget, 'bpmn:Group')) {
  151. return;
  152. }
  153. setCategoryValue(labelTarget, context);
  154. });
  155. this.revert('label.create', function(event) {
  156. var context = event.context,
  157. labelTarget = context.labelTarget;
  158. if (!is(labelTarget, 'bpmn:Group')) {
  159. return;
  160. }
  161. unsetCategoryValue(labelTarget, context);
  162. });
  163. // remove referenced category + value when group was deleted
  164. this.execute('shape.delete', function(event) {
  165. var context = event.context,
  166. shape = context.shape,
  167. businessObject = getBusinessObject(shape);
  168. if (!is(shape, 'bpmn:Group') || shape.labelTarget) {
  169. return;
  170. }
  171. var categoryValue = context.categoryValue = businessObject.categoryValueRef,
  172. category;
  173. if (categoryValue) {
  174. category = context.category = categoryValue.$parent;
  175. removeCategoryValue(categoryValue, category, businessObject);
  176. businessObject.categoryValueRef = null;
  177. }
  178. });
  179. this.reverted('shape.delete', function(event) {
  180. var context = event.context,
  181. shape = context.shape;
  182. if (!is(shape, 'bpmn:Group') || shape.labelTarget) {
  183. return;
  184. }
  185. var category = context.category,
  186. categoryValue = context.categoryValue,
  187. businessObject = getBusinessObject(shape);
  188. if (categoryValue) {
  189. businessObject.categoryValueRef = categoryValue;
  190. addCategoryValue(categoryValue, category);
  191. }
  192. });
  193. // create new category + value when group was created
  194. this.execute('shape.create', function(event) {
  195. var context = event.context,
  196. shape = context.shape;
  197. if (!is(shape, 'bpmn:Group') || shape.labelTarget) {
  198. return;
  199. }
  200. if (getBusinessObject(shape).categoryValueRef) {
  201. setCategoryValue(shape, context);
  202. }
  203. });
  204. this.reverted('shape.create', function(event) {
  205. var context = event.context,
  206. shape = context.shape;
  207. if (!is(shape, 'bpmn:Group') || shape.labelTarget) {
  208. return;
  209. }
  210. if (getBusinessObject(shape).categoryValueRef) {
  211. unsetCategoryValue(shape, context);
  212. }
  213. });
  214. // copy + paste categoryValueRef with group
  215. function copy(bo, clone) {
  216. var targetBo = bpmnFactory.create(bo.$type);
  217. return moddleCopy.copyElement(bo, targetBo, null, clone);
  218. }
  219. eventBus.on('copyPaste.copyElement', LOWER_PRIORITY, function(context) {
  220. var descriptor = context.descriptor,
  221. element = context.element;
  222. if (!is(element, 'bpmn:Group') || element.labelTarget) {
  223. return;
  224. }
  225. var groupBo = getBusinessObject(element);
  226. if (groupBo.categoryValueRef) {
  227. var categoryValue = groupBo.categoryValueRef;
  228. descriptor.categoryValue = copy(categoryValue, true);
  229. if (categoryValue.$parent) {
  230. descriptor.category = copy(categoryValue.$parent, true);
  231. }
  232. }
  233. });
  234. eventBus.on('copyPaste.pasteElement', LOWER_PRIORITY, function(context) {
  235. var descriptor = context.descriptor,
  236. businessObject = descriptor.businessObject,
  237. categoryValue = descriptor.categoryValue,
  238. category = descriptor.category;
  239. if (categoryValue) {
  240. categoryValue = businessObject.categoryValueRef = copy(categoryValue);
  241. }
  242. if (category) {
  243. categoryValue.$parent = copy(category);
  244. }
  245. delete descriptor.category;
  246. delete descriptor.categoryValue;
  247. });
  248. }
  249. GroupBehavior.$inject = [
  250. 'bpmnFactory',
  251. 'bpmnjs',
  252. 'elementRegistry',
  253. 'eventBus',
  254. 'injector',
  255. 'moddleCopy'
  256. ];
  257. inherits(GroupBehavior, CommandInterceptor);