Connect.js 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. import {
  2. getElementLineIntersection
  3. } from '../../layout/LayoutUtil';
  4. import {
  5. getMid
  6. } from '../../layout/LayoutUtil';
  7. var MARKER_OK = 'connect-ok',
  8. MARKER_NOT_OK = 'connect-not-ok';
  9. import {
  10. append as svgAppend,
  11. attr as svgAttr,
  12. create as svgCreate,
  13. remove as svgRemove
  14. } from 'tiny-svg';
  15. export default function Connect(
  16. eventBus, dragging, modeling,
  17. rules, canvas, graphicsFactory) {
  18. // TODO(nre): separate UI and events
  19. // rules
  20. function canConnect(source, target) {
  21. return rules.allowed('connection.create', {
  22. source: source,
  23. target: target
  24. });
  25. }
  26. // layouting
  27. function crop(start, end, source, target) {
  28. var sourcePath = graphicsFactory.getShapePath(source),
  29. targetPath = target && graphicsFactory.getShapePath(target),
  30. connectionPath = graphicsFactory.getConnectionPath({ waypoints: [ start, end ] });
  31. start = getElementLineIntersection(sourcePath, connectionPath, true) || start;
  32. end = (target && getElementLineIntersection(targetPath, connectionPath, false)) || end;
  33. return [ start, end ];
  34. }
  35. // event handlers
  36. eventBus.on('connect.move', function(event) {
  37. var context = event.context,
  38. source = context.source,
  39. target = context.target,
  40. visual = context.visual,
  41. sourcePosition = context.sourcePosition,
  42. endPosition,
  43. waypoints;
  44. // update connection visuals during drag
  45. endPosition = {
  46. x: event.x,
  47. y: event.y
  48. };
  49. waypoints = crop(sourcePosition, endPosition, source, target);
  50. svgAttr(visual, { 'points': [ waypoints[0].x, waypoints[0].y, waypoints[1].x, waypoints[1].y ] });
  51. });
  52. eventBus.on('connect.hover', function(event) {
  53. var context = event.context,
  54. source = context.source,
  55. hover = event.hover,
  56. canExecute;
  57. canExecute = context.canExecute = canConnect(source, hover);
  58. // simply ignore hover
  59. if (canExecute === null) {
  60. return;
  61. }
  62. context.target = hover;
  63. canvas.addMarker(hover, canExecute ? MARKER_OK : MARKER_NOT_OK);
  64. });
  65. eventBus.on([ 'connect.out', 'connect.cleanup' ], function(event) {
  66. var context = event.context;
  67. if (context.target) {
  68. canvas.removeMarker(context.target, context.canExecute ? MARKER_OK : MARKER_NOT_OK);
  69. }
  70. context.target = null;
  71. context.canExecute = false;
  72. });
  73. eventBus.on('connect.cleanup', function(event) {
  74. var context = event.context;
  75. if (context.visual) {
  76. svgRemove(context.visual);
  77. }
  78. });
  79. eventBus.on('connect.start', function(event) {
  80. var context = event.context,
  81. visual;
  82. visual = svgCreate('polyline');
  83. svgAttr(visual, {
  84. 'stroke': '#333',
  85. 'strokeDasharray': [ 1 ],
  86. 'strokeWidth': 2,
  87. 'pointer-events': 'none'
  88. });
  89. svgAppend(canvas.getDefaultLayer(), visual);
  90. context.visual = visual;
  91. });
  92. eventBus.on('connect.end', function(event) {
  93. var context = event.context,
  94. source = context.source,
  95. sourcePosition = context.sourcePosition,
  96. target = context.target,
  97. targetPosition = {
  98. x: event.x,
  99. y: event.y
  100. },
  101. canExecute = context.canExecute || canConnect(source, target);
  102. if (!canExecute) {
  103. return false;
  104. }
  105. var attrs = null,
  106. hints = {
  107. connectionStart: sourcePosition,
  108. connectionEnd: targetPosition
  109. };
  110. if (typeof canExecute === 'object') {
  111. attrs = canExecute;
  112. }
  113. modeling.connect(source, target, attrs, hints);
  114. });
  115. // API
  116. /**
  117. * Start connect operation.
  118. *
  119. * @param {DOMEvent} event
  120. * @param {djs.model.Base} source
  121. * @param {Point} [sourcePosition]
  122. * @param {Boolean} [autoActivate=false]
  123. */
  124. this.start = function(event, source, sourcePosition, autoActivate) {
  125. if (typeof sourcePosition !== 'object') {
  126. autoActivate = sourcePosition;
  127. sourcePosition = getMid(source);
  128. }
  129. dragging.init(event, 'connect', {
  130. autoActivate: autoActivate,
  131. data: {
  132. shape: source,
  133. context: {
  134. source: source,
  135. sourcePosition: sourcePosition
  136. }
  137. }
  138. });
  139. };
  140. }
  141. Connect.$inject = [
  142. 'eventBus',
  143. 'dragging',
  144. 'modeling',
  145. 'rules',
  146. 'canvas',
  147. 'graphicsFactory'
  148. ];