AlignElements.js 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. import {
  2. filter,
  3. forEach,
  4. sortBy
  5. } from 'min-dash';
  6. function last(arr) {
  7. return arr && arr[arr.length - 1];
  8. }
  9. function sortTopOrMiddle(element) {
  10. return element.y;
  11. }
  12. function sortLeftOrCenter(element) {
  13. return element.x;
  14. }
  15. /**
  16. * Sorting functions for different types of alignment
  17. *
  18. * @type {Object}
  19. *
  20. * @return {Function}
  21. */
  22. var ALIGNMENT_SORTING = {
  23. left: sortLeftOrCenter,
  24. center: sortLeftOrCenter,
  25. right: function(element) {
  26. return element.x + element.width;
  27. },
  28. top: sortTopOrMiddle,
  29. middle: sortTopOrMiddle,
  30. bottom: function(element) {
  31. return element.y + element.height;
  32. }
  33. };
  34. export default function AlignElements(modeling) {
  35. this._modeling = modeling;
  36. }
  37. AlignElements.$inject = [ 'modeling' ];
  38. /**
  39. * Get the relevant "axis" and "dimension" related to the current type of alignment
  40. *
  41. * @param {String} type left|right|center|top|bottom|middle
  42. *
  43. * @return {Object} { axis, dimension }
  44. */
  45. AlignElements.prototype._getOrientationDetails = function(type) {
  46. var vertical = [ 'top', 'bottom', 'middle' ],
  47. axis = 'x',
  48. dimension = 'width';
  49. if (vertical.indexOf(type) !== -1) {
  50. axis = 'y';
  51. dimension = 'height';
  52. }
  53. return {
  54. axis: axis,
  55. dimension: dimension
  56. };
  57. };
  58. AlignElements.prototype._isType = function(type, types) {
  59. return types.indexOf(type) !== -1;
  60. };
  61. /**
  62. * Get a point on the relevant axis where elements should align to
  63. *
  64. * @param {String} type left|right|center|top|bottom|middle
  65. * @param {Array} sortedElements
  66. *
  67. * @return {Object}
  68. */
  69. AlignElements.prototype._alignmentPosition = function(type, sortedElements) {
  70. var orientation = this._getOrientationDetails(type),
  71. axis = orientation.axis,
  72. dimension = orientation.dimension,
  73. alignment = {},
  74. centers = {},
  75. hasSharedCenters = false,
  76. centeredElements,
  77. firstElement,
  78. lastElement;
  79. function getMiddleOrTop(first, last) {
  80. return Math.round((first[axis] + last[axis] + last[dimension]) / 2);
  81. }
  82. if (this._isType(type, [ 'left', 'top' ])) {
  83. alignment[type] = sortedElements[0][axis];
  84. } else if (this._isType(type, [ 'right', 'bottom' ])) {
  85. lastElement = last(sortedElements);
  86. alignment[type] = lastElement[axis] + lastElement[dimension];
  87. } else if (this._isType(type, [ 'center', 'middle' ])) {
  88. // check if there is a center shared by more than one shape
  89. // if not, just take the middle of the range
  90. forEach(sortedElements, function(element) {
  91. var center = element[axis] + Math.round(element[dimension] / 2);
  92. if (centers[center]) {
  93. centers[center].elements.push(element);
  94. } else {
  95. centers[center] = {
  96. elements: [ element ],
  97. center: center
  98. };
  99. }
  100. });
  101. centeredElements = sortBy(centers, function(center) {
  102. if (center.elements.length > 1) {
  103. hasSharedCenters = true;
  104. }
  105. return center.elements.length;
  106. });
  107. if (hasSharedCenters) {
  108. alignment[type] = last(centeredElements).center;
  109. return alignment;
  110. }
  111. firstElement = sortedElements[0];
  112. sortedElements = sortBy(sortedElements, function(element) {
  113. return element[axis] + element[dimension];
  114. });
  115. lastElement = last(sortedElements);
  116. alignment[type] = getMiddleOrTop(firstElement, lastElement);
  117. }
  118. return alignment;
  119. };
  120. /**
  121. * Executes the alignment of a selection of elements
  122. *
  123. * @param {Array} elements [description]
  124. * @param {String} type left|right|center|top|bottom|middle
  125. */
  126. AlignElements.prototype.trigger = function(elements, type) {
  127. var modeling = this._modeling;
  128. var filteredElements = filter(elements, function(element) {
  129. return !(element.waypoints || element.host || element.labelTarget);
  130. });
  131. var sortFn = ALIGNMENT_SORTING[type];
  132. var sortedElements = sortBy(filteredElements, sortFn);
  133. var alignment = this._alignmentPosition(type, sortedElements);
  134. modeling.alignElements(sortedElements, alignment);
  135. };