Resize.js 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  1. import {
  2. pick,
  3. assign
  4. } from 'min-dash';
  5. import {
  6. resizeBounds,
  7. ensureConstraints,
  8. computeChildrenBBox,
  9. getMinResizeBounds
  10. } from './ResizeUtil';
  11. import {
  12. asTRBL,
  13. roundBounds
  14. } from '../../layout/LayoutUtil';
  15. var DEFAULT_MIN_WIDTH = 10;
  16. /**
  17. * A component that provides resizing of shapes on the canvas.
  18. *
  19. * The following components are part of shape resize:
  20. *
  21. * * adding resize handles,
  22. * * creating a visual during resize
  23. * * checking resize rules
  24. * * committing a change once finished
  25. *
  26. *
  27. * ## Customizing
  28. *
  29. * It's possible to customize the resizing behaviour by intercepting 'resize.start'
  30. * and providing the following parameters through the 'context':
  31. *
  32. * * minDimensions ({ width, height }): minimum shape dimensions
  33. *
  34. * * childrenBoxPadding ({ left, top, bottom, right } || number):
  35. * gap between the minimum bounding box and the container
  36. *
  37. * f.ex:
  38. *
  39. * ```javascript
  40. * eventBus.on('resize.start', 1500, function(event) {
  41. * var context = event.context,
  42. *
  43. * context.minDimensions = { width: 140, height: 120 };
  44. *
  45. * // Passing general padding
  46. * context.childrenBoxPadding = 30;
  47. *
  48. * // Passing padding to a specific side
  49. * context.childrenBoxPadding.left = 20;
  50. * });
  51. * ```
  52. */
  53. export default function Resize(eventBus, rules, modeling, dragging) {
  54. this._dragging = dragging;
  55. this._rules = rules;
  56. var self = this;
  57. /**
  58. * Handle resize move by specified delta.
  59. *
  60. * @param {Object} context
  61. * @param {Point} delta
  62. */
  63. function handleMove(context, delta) {
  64. var shape = context.shape,
  65. direction = context.direction,
  66. resizeConstraints = context.resizeConstraints,
  67. newBounds;
  68. context.delta = delta;
  69. newBounds = resizeBounds(shape, direction, delta);
  70. // ensure constraints during resize
  71. context.newBounds = ensureConstraints(newBounds, resizeConstraints);
  72. // update + cache executable state
  73. context.canExecute = self.canResize(context);
  74. }
  75. /**
  76. * Handle resize start.
  77. *
  78. * @param {Object} context
  79. */
  80. function handleStart(context) {
  81. var resizeConstraints = context.resizeConstraints,
  82. // evaluate minBounds for backwards compatibility
  83. minBounds = context.minBounds;
  84. if (resizeConstraints !== undefined) {
  85. return;
  86. }
  87. if (minBounds === undefined) {
  88. minBounds = self.computeMinResizeBox(context);
  89. }
  90. context.resizeConstraints = {
  91. min: asTRBL(minBounds)
  92. };
  93. }
  94. /**
  95. * Handle resize end.
  96. *
  97. * @param {Object} context
  98. */
  99. function handleEnd(context) {
  100. var shape = context.shape,
  101. canExecute = context.canExecute,
  102. newBounds = context.newBounds;
  103. if (canExecute) {
  104. // ensure we have actual pixel values for new bounds
  105. // (important when zoom level was > 1 during move)
  106. newBounds = roundBounds(newBounds);
  107. // perform the actual resize
  108. modeling.resizeShape(shape, newBounds);
  109. }
  110. }
  111. eventBus.on('resize.start', function(event) {
  112. handleStart(event.context);
  113. });
  114. eventBus.on('resize.move', function(event) {
  115. var delta = {
  116. x: event.dx,
  117. y: event.dy
  118. };
  119. handleMove(event.context, delta);
  120. });
  121. eventBus.on('resize.end', function(event) {
  122. handleEnd(event.context);
  123. });
  124. }
  125. Resize.prototype.canResize = function(context) {
  126. var rules = this._rules;
  127. var ctx = pick(context, [ 'newBounds', 'shape', 'delta', 'direction' ]);
  128. return rules.allowed('shape.resize', ctx);
  129. };
  130. /**
  131. * Activate a resize operation.
  132. *
  133. * You may specify additional contextual information and must specify a
  134. * resize direction during activation of the resize event.
  135. *
  136. * @param {MouseEvent} event
  137. * @param {djs.model.Shape} shape
  138. * @param {Object|String} contextOrDirection
  139. */
  140. Resize.prototype.activate = function(event, shape, contextOrDirection) {
  141. var dragging = this._dragging,
  142. context,
  143. direction;
  144. if (typeof contextOrDirection === 'string') {
  145. contextOrDirection = {
  146. direction: contextOrDirection
  147. };
  148. }
  149. context = assign({ shape: shape }, contextOrDirection);
  150. direction = context.direction;
  151. if (!direction) {
  152. throw new Error('must provide a direction (nw|se|ne|sw)');
  153. }
  154. var referencePoint = {
  155. x: /w/.test(direction) ? shape.x : shape.x + shape.width,
  156. y: /n/.test(direction) ? shape.y : shape.y + shape.height
  157. };
  158. dragging.init(event, referencePoint, 'resize', {
  159. autoActivate: true,
  160. cursor: 'resize-' + (/nw|se/.test(direction) ? 'nwse' : 'nesw'),
  161. data: {
  162. shape: shape,
  163. context: context
  164. }
  165. });
  166. };
  167. Resize.prototype.computeMinResizeBox = function(context) {
  168. var shape = context.shape,
  169. direction = context.direction,
  170. minDimensions,
  171. childrenBounds;
  172. minDimensions = context.minDimensions || {
  173. width: DEFAULT_MIN_WIDTH,
  174. height: DEFAULT_MIN_WIDTH
  175. };
  176. // get children bounds
  177. childrenBounds = computeChildrenBBox(shape, context.childrenBoxPadding);
  178. // get correct minimum bounds from given resize direction
  179. // basically ensures that the minBounds is max(childrenBounds, minDimensions)
  180. return getMinResizeBounds(direction, shape, minDimensions, childrenBounds);
  181. };
  182. Resize.$inject = [
  183. 'eventBus',
  184. 'rules',
  185. 'modeling',
  186. 'dragging'
  187. ];