MovePreview.js 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  1. import {
  2. flatten,
  3. forEach,
  4. filter,
  5. find,
  6. groupBy,
  7. map,
  8. matchPattern,
  9. size
  10. } from 'min-dash';
  11. import {
  12. selfAndAllChildren
  13. } from '../../util/Elements';
  14. import {
  15. append as svgAppend,
  16. attr as svgAttr,
  17. create as svgCreate,
  18. remove as svgRemove
  19. } from 'tiny-svg';
  20. import { translate } from '../../util/SvgTransformUtil';
  21. var LOW_PRIORITY = 499;
  22. var MARKER_DRAGGING = 'djs-dragging',
  23. MARKER_OK = 'drop-ok',
  24. MARKER_NOT_OK = 'drop-not-ok',
  25. MARKER_NEW_PARENT = 'new-parent',
  26. MARKER_ATTACH = 'attach-ok';
  27. /**
  28. * Provides previews for moving shapes when moving.
  29. *
  30. * @param {EventBus} eventBus
  31. * @param {ElementRegistry} elementRegistry
  32. * @param {Canvas} canvas
  33. * @param {Styles} styles
  34. */
  35. export default function MovePreview(
  36. eventBus, canvas, styles, previewSupport) {
  37. function getVisualDragShapes(shapes) {
  38. var elements = getAllDraggedElements(shapes);
  39. var filteredElements = removeEdges(elements);
  40. return filteredElements;
  41. }
  42. function getAllDraggedElements(shapes) {
  43. var allShapes = selfAndAllChildren(shapes, true);
  44. var allConnections = map(allShapes, function(shape) {
  45. return (shape.incoming || []).concat(shape.outgoing || []);
  46. });
  47. return flatten(allShapes.concat(allConnections));
  48. }
  49. /**
  50. * Sets drop marker on an element.
  51. */
  52. function setMarker(element, marker) {
  53. [ MARKER_ATTACH, MARKER_OK, MARKER_NOT_OK, MARKER_NEW_PARENT ].forEach(function(m) {
  54. if (m === marker) {
  55. canvas.addMarker(element, m);
  56. } else {
  57. canvas.removeMarker(element, m);
  58. }
  59. });
  60. }
  61. /**
  62. * Make an element draggable.
  63. *
  64. * @param {Object} context
  65. * @param {djs.model.Base} element
  66. * @param {boolean} addMarker
  67. */
  68. function makeDraggable(context, element, addMarker) {
  69. previewSupport.addDragger(element, context.dragGroup);
  70. if (addMarker) {
  71. canvas.addMarker(element, MARKER_DRAGGING);
  72. }
  73. if (context.allDraggedElements) {
  74. context.allDraggedElements.push(element);
  75. } else {
  76. context.allDraggedElements = [ element ];
  77. }
  78. }
  79. // assign a low priority to this handler
  80. // to let others modify the move context before
  81. // we draw things
  82. eventBus.on('shape.move.start', LOW_PRIORITY, function(event) {
  83. var context = event.context,
  84. dragShapes = context.shapes,
  85. allDraggedElements = context.allDraggedElements;
  86. var visuallyDraggedShapes = getVisualDragShapes(dragShapes);
  87. if (!context.dragGroup) {
  88. var dragGroup = svgCreate('g');
  89. svgAttr(dragGroup, styles.cls('djs-drag-group', [ 'no-events' ]));
  90. var activeLayer = canvas.getActiveLayer();
  91. svgAppend(activeLayer, dragGroup);
  92. context.dragGroup = dragGroup;
  93. }
  94. // add previews
  95. visuallyDraggedShapes.forEach(function(shape) {
  96. previewSupport.addDragger(shape, context.dragGroup);
  97. });
  98. // cache all dragged elements / gfx
  99. // so that we can quickly undo their state changes later
  100. if (!allDraggedElements) {
  101. allDraggedElements = getAllDraggedElements(dragShapes);
  102. } else {
  103. allDraggedElements = flatten([
  104. allDraggedElements,
  105. getAllDraggedElements(dragShapes)
  106. ]);
  107. }
  108. // add dragging marker
  109. forEach(allDraggedElements, function(e) {
  110. canvas.addMarker(e, MARKER_DRAGGING);
  111. });
  112. context.allDraggedElements = allDraggedElements;
  113. // determine, if any of the dragged elements have different parents
  114. context.differentParents = haveDifferentParents(dragShapes);
  115. });
  116. // update previews
  117. eventBus.on('shape.move.move', LOW_PRIORITY, function(event) {
  118. var context = event.context,
  119. dragGroup = context.dragGroup,
  120. target = context.target,
  121. parent = context.shape.parent,
  122. canExecute = context.canExecute;
  123. if (target) {
  124. if (canExecute === 'attach') {
  125. setMarker(target, MARKER_ATTACH);
  126. } else if (context.canExecute && target && target.id !== parent.id) {
  127. setMarker(target, MARKER_NEW_PARENT);
  128. } else {
  129. setMarker(target, context.canExecute ? MARKER_OK : MARKER_NOT_OK);
  130. }
  131. }
  132. translate(dragGroup, event.dx, event.dy);
  133. });
  134. eventBus.on([ 'shape.move.out', 'shape.move.cleanup' ], function(event) {
  135. var context = event.context,
  136. target = context.target;
  137. if (target) {
  138. setMarker(target, null);
  139. }
  140. });
  141. // remove previews
  142. eventBus.on('shape.move.cleanup', function(event) {
  143. var context = event.context,
  144. allDraggedElements = context.allDraggedElements,
  145. dragGroup = context.dragGroup;
  146. // remove dragging marker
  147. forEach(allDraggedElements, function(e) {
  148. canvas.removeMarker(e, MARKER_DRAGGING);
  149. });
  150. if (dragGroup) {
  151. svgRemove(dragGroup);
  152. }
  153. });
  154. // API //////////////////////
  155. /**
  156. * Make an element draggable.
  157. *
  158. * @param {Object} context
  159. * @param {djs.model.Base} element
  160. * @param {boolean} addMarker
  161. */
  162. this.makeDraggable = makeDraggable;
  163. }
  164. MovePreview.$inject = [
  165. 'eventBus',
  166. 'canvas',
  167. 'styles',
  168. 'previewSupport'
  169. ];
  170. // helpers //////////////////////
  171. /**
  172. * returns elements minus all connections
  173. * where source or target is not elements
  174. */
  175. function removeEdges(elements) {
  176. var filteredElements = filter(elements, function(element) {
  177. if (!isConnection(element)) {
  178. return true;
  179. } else {
  180. return (
  181. find(elements, matchPattern({ id: element.source.id })) &&
  182. find(elements, matchPattern({ id: element.target.id }))
  183. );
  184. }
  185. });
  186. return filteredElements;
  187. }
  188. function haveDifferentParents(elements) {
  189. return size(groupBy(elements, function(e) { return e.parent && e.parent.id; })) !== 1;
  190. }
  191. /**
  192. * Checks if an element is a connection.
  193. */
  194. function isConnection(element) {
  195. return element.waypoints;
  196. }