MovePreview.js 5.8 KB

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