index.ts 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. import inherits from 'inherits-browser';
  2. import CommandInterceptor from 'diagram-js/lib/command/CommandInterceptor';
  3. import { pointsAligned } from 'diagram-js/lib/util/Geometry';
  4. import { assign } from 'min-dash';
  5. import { typeConfluence, typeEnd, typeOutside, typeProcessing, typeStart, typeSubFlow, typeTask, typeTrigger } from '../../../config/variableName';
  6. import { changeTypeByTaskShape, hasGatewayType } from '../../../config';
  7. /**
  8. * @typedef {import('diagram-js/lib/core/EventBus').default} EventBus
  9. * @typedef {import('diagram-js/lib/features/grid-snapping/GridSnapping').default} GridSnapping
  10. * @typedef {import('../../modeling/Modeling').default} Modeling
  11. * @typedef {import('diagram-js/lib/util/Types').Point} Point
  12. */
  13. const HIGH_PRIORITY = 3000;
  14. export default function GridSnappingLayoutConnectionBehavior(this: any, eventBus: any, gridSnapping: any, modeling: any) {
  15. CommandInterceptor.call(this, eventBus);
  16. this._gridSnapping = gridSnapping;
  17. const self = this;
  18. this.postExecuted(['connection.create', 'connection.layout'], HIGH_PRIORITY, function (event: any) {
  19. let context = event.context,
  20. connection = context.connection,
  21. waypoints = connection.waypoints;
  22. let target = connection.target,
  23. source = connection.source;
  24. if (hasGatewayType.has(source.wnType) || hasGatewayType.has(target.wnType)) {
  25. let x = source.x;
  26. if (target.x > source.x) x = source.x + source.width;
  27. waypoints = [
  28. {
  29. x: x,
  30. y: source.y + source.height / 2,
  31. },
  32. {
  33. x: target.x + target.width / 2,
  34. y: source.y + source.height / 2,
  35. },
  36. {
  37. x: target.x + target.width / 2,
  38. y: target.y,
  39. },
  40. ];
  41. if (source.x + source.width / 2 === target.x + target.width / 2) {
  42. waypoints = [
  43. {
  44. x: source.x + source.width / 2,
  45. y: source.y + source.height,
  46. },
  47. {
  48. x: target.x + target.width / 2,
  49. y: target.y,
  50. },
  51. ];
  52. }
  53. }
  54. if (target.wnType === typeConfluence) {
  55. let x = target.x;
  56. if (target.x < source.x) {
  57. x = target.x + target.width;
  58. }
  59. waypoints = [
  60. {
  61. x: source.x + source.width / 2,
  62. y: source.y + source.height,
  63. },
  64. {
  65. x: source.x + source.width / 2,
  66. y: target.y + target.height / 2,
  67. },
  68. {
  69. x: x,
  70. y: target.y + target.height / 2,
  71. },
  72. ];
  73. if (source.x + source.width / 2 === target.x + target.width / 2) {
  74. waypoints = [
  75. {
  76. x: source.x + source.width / 2,
  77. y: source.y + source.height,
  78. },
  79. {
  80. x: target.x + target.width / 2,
  81. y: target.y,
  82. },
  83. ];
  84. }
  85. }
  86. if (([typeStart, typeTask, typeProcessing, typeTrigger, typeOutside].includes(source.wnType) || changeTypeByTaskShape[source.wnType]) &&
  87. (
  88. changeTypeByTaskShape[target.wnType] ||
  89. [typeEnd, typeSubFlow, typeOutside, typeTrigger, typeProcessing, typeTask].includes(target.wnType)
  90. )
  91. ) {
  92. if (target.x + target.width / 2 > source.x + source.width / 2) {
  93. waypoints = [
  94. {
  95. x: source.x + source.width,
  96. y: source.y + source.height / 2,
  97. },
  98. {
  99. x: target.x + target.width / 2,
  100. y: source.y + source.height / 2,
  101. },
  102. {
  103. x: target.x + target.width / 2,
  104. y: target.y,
  105. },
  106. ];
  107. }
  108. if (target.x + target.width / 2 < source.x + source.width / 2) {
  109. waypoints = [
  110. {
  111. x: source.x,
  112. y: source.y + source.height / 2,
  113. },
  114. {
  115. x: target.x + target.width / 2,
  116. y: source.y + source.height / 2,
  117. },
  118. {
  119. x: target.x + target.width / 2,
  120. y: target.y,
  121. },
  122. ];
  123. }
  124. if (target.x + target.width / 2 === source.x + source.width / 2) {
  125. waypoints = [
  126. {
  127. x: source.x + source.width / 2,
  128. y: source.y + source.height,
  129. },
  130. {
  131. x: target.x + target.width / 2,
  132. y: target.y,
  133. },
  134. ];
  135. }
  136. }
  137. if (connection.target?.wnType != typeConfluence) modeling.updateWaypoints(connection, self.snapMiddleSegments(waypoints));
  138. if (connection.target?.wnType === typeConfluence && connection.source.incoming?.length === 1) {
  139. modeling.updateWaypoints(connection, self.snapMiddleSegments(waypoints));
  140. }
  141. if ( connection.label && ([typeTask, typeProcessing, typeTrigger, typeSubFlow, typeOutside, typeEnd].includes(target.wnType) ||
  142. changeTypeByTaskShape[target.wnType])
  143. ) {
  144. connection.label.x = target.x + target.width / 2 - 14;
  145. connection.label.y = connection.label.y;
  146. modeling.updateProperties(connection.label, {});
  147. }
  148. });
  149. }
  150. GridSnappingLayoutConnectionBehavior.$inject = ['eventBus', 'gridSnapping', 'modeling'];
  151. inherits(GridSnappingLayoutConnectionBehavior, CommandInterceptor);
  152. /**
  153. * Snap middle segments of a given connection.
  154. *
  155. * @param {Point[]} waypoints
  156. *
  157. * @return {Point[]}
  158. */
  159. GridSnappingLayoutConnectionBehavior.prototype.snapMiddleSegments = function (this: any, waypoints: any[]) {
  160. const gridSnapping = this._gridSnapping;
  161. waypoints = waypoints.slice();
  162. for (let i = 1; i < waypoints.length - 2; i++) {
  163. const snapped = snapSegment(gridSnapping, waypoints[i], waypoints[i + 1]);
  164. waypoints[i] = snapped[0];
  165. waypoints[i + 1] = snapped[1];
  166. }
  167. return waypoints;
  168. };
  169. /**
  170. * Check whether a connection has middle segments.
  171. *
  172. * @param {Point[]} waypoints
  173. *
  174. * @return {boolean}
  175. */
  176. function hasMiddleSegments(waypoints: any[]) {
  177. return waypoints.length > 3;
  178. }
  179. /**
  180. * Check whether an alignment is horizontal.
  181. *
  182. * @param {string} aligned
  183. *
  184. * @return {boolean}
  185. */
  186. function horizontallyAligned(aligned: string) {
  187. return aligned === 'h';
  188. }
  189. /**
  190. * Check whether an alignment is vertical.
  191. *
  192. * @param {string} aligned
  193. *
  194. * @return {boolean}
  195. */
  196. function verticallyAligned(aligned: string) {
  197. return aligned === 'v';
  198. }
  199. /**
  200. * Get middle segments from a given connection.
  201. *
  202. * @param {Point[]} waypoints
  203. *
  204. * @return {Point[]}
  205. */
  206. function snapSegment(gridSnapping: any, segmentStart: any, segmentEnd: any) {
  207. const aligned: any = pointsAligned(segmentStart, segmentEnd);
  208. const snapped: any = {};
  209. if (horizontallyAligned(aligned)) {
  210. snapped.y = gridSnapping.snapValue(segmentStart.y);
  211. }
  212. if (verticallyAligned(aligned)) {
  213. snapped.x = gridSnapping.snapValue(segmentStart.x);
  214. }
  215. if ('x' in snapped || 'y' in snapped) {
  216. segmentStart = assign({}, segmentStart, snapped);
  217. segmentEnd = assign({}, segmentEnd, snapped);
  218. }
  219. return [segmentStart, segmentEnd];
  220. }