layout.js 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  1. /**
  2. * DevExtreme (viz/core/layout.js)
  3. * Version: 19.1.16
  4. * Build date: Tue Oct 18 2022
  5. *
  6. * Copyright (c) 2012 - 2022 Developer Express Inc. ALL RIGHTS RESERVED
  7. * Read about DevExtreme licensing here: https://js.devexpress.com/Licensing/
  8. */
  9. "use strict";
  10. var _normalizeEnum = require("./utils").normalizeEnum;
  11. var _min = Math.min;
  12. var _max = Math.max;
  13. var _round = Math.round;
  14. var ALIGN_START = 0;
  15. var ALIGN_MIDDLE = 1;
  16. var ALIGN_END = 2;
  17. var horizontalAlignmentMap = {
  18. left: ALIGN_START,
  19. center: ALIGN_MIDDLE,
  20. right: ALIGN_END
  21. };
  22. var verticalAlignmentMap = {
  23. top: ALIGN_START,
  24. center: ALIGN_MIDDLE,
  25. bottom: ALIGN_END
  26. };
  27. var sideMap = {
  28. horizontal: 0,
  29. vertical: 1
  30. };
  31. var slicersMap = {};
  32. var BBOX_CEIL_CORRECTION = 2;
  33. slicersMap[ALIGN_START] = function(a, b, size) {
  34. return [a, _min(b, a + size)]
  35. };
  36. slicersMap[ALIGN_MIDDLE] = function(a, b, size) {
  37. return [_max(a, (a + b - size) / 2), _min(b, (a + b + size) / 2)]
  38. };
  39. slicersMap[ALIGN_END] = function(a, b, size) {
  40. return [_max(a, b - size), b]
  41. };
  42. function pickValue(value, map, defaultValue) {
  43. var val = _normalizeEnum(value);
  44. return val in map ? map[val] : defaultValue
  45. }
  46. function normalizeLayoutOptions(options) {
  47. var side = pickValue(options.side, sideMap, 1);
  48. var alignment = [pickValue(options.horizontalAlignment, horizontalAlignmentMap, ALIGN_MIDDLE), pickValue(options.verticalAlignment, verticalAlignmentMap, ALIGN_START)];
  49. return {
  50. side: side,
  51. primary: bringToEdge(alignment[side]),
  52. secondary: alignment[1 - side],
  53. weak: options.weak,
  54. priority: options.priority || 0,
  55. header: options.header,
  56. position: options.position
  57. }
  58. }
  59. function bringToEdge(primary) {
  60. return primary < 2 ? 0 : 2
  61. }
  62. function getConjugateSide(side) {
  63. return 1 - side
  64. }
  65. function getSlice(alignment, a, b, size) {
  66. return slicersMap[alignment](a, b, size)
  67. }
  68. function getShrink(alignment, size) {
  69. return (alignment > 0 ? -1 : 1) * size
  70. }
  71. function processForward(item, rect, minSize) {
  72. var side = item.side;
  73. var size = item.element.measure([rect[2] - rect[0], rect[3] - rect[1]]);
  74. var minSide = "indside" === item.position ? 0 : minSize[side];
  75. var isValid = size[side] < rect[2 + side] - rect[side] - minSide;
  76. if (isValid) {
  77. if ("inside" !== item.position) {
  78. rect[item.primary + side] += getShrink(item.primary, size[side])
  79. }
  80. item.size = size
  81. }
  82. return isValid
  83. }
  84. function processRectBackward(item, rect, alignmentRect) {
  85. var primarySide = item.side;
  86. var secondarySide = getConjugateSide(primarySide);
  87. var itemRect = [];
  88. var secondary = getSlice(item.secondary, alignmentRect[secondarySide], alignmentRect[2 + secondarySide], item.size[secondarySide]);
  89. itemRect[primarySide] = _round(itemRect[2 + primarySide] = rect[item.primary + primarySide] + ("inside" === item.position ? getShrink(item.primary, item.size[primarySide]) : 0));
  90. itemRect[item.primary + primarySide] = _round(rect[item.primary + primarySide] - getShrink(item.primary, item.size[primarySide]));
  91. if ("inside" !== item.position) {
  92. rect[item.primary + primarySide] = itemRect[item.primary + primarySide]
  93. }
  94. itemRect[secondarySide] = _round(secondary[0]);
  95. itemRect[2 + secondarySide] = _round(secondary[1]);
  96. return itemRect
  97. }
  98. function processBackward(item, rect, alignmentRect, fitRect, size, targetRect) {
  99. var itemRect = processRectBackward(item, rect, alignmentRect);
  100. var itemFitRect = processRectBackward(item, fitRect, fitRect);
  101. if (size[item.side] > 0) {
  102. size[item.side] -= item.size[item.side];
  103. targetRect[item.primary + item.side] = itemRect[item.primary + item.side];
  104. item.element.freeSpace()
  105. } else {
  106. item.element.move(itemRect, itemFitRect)
  107. }
  108. }
  109. function Layout() {
  110. this._targets = []
  111. }
  112. Layout.prototype = {
  113. constructor: Layout,
  114. dispose: function() {
  115. this._targets = null
  116. },
  117. add: function(target) {
  118. this._targets.push(target)
  119. },
  120. forward: function(targetRect, minSize) {
  121. var rect = targetRect.slice();
  122. var targets = createTargets(this._targets);
  123. var i;
  124. var ii = targets.length;
  125. var cache = [];
  126. for (i = 0; i < ii; ++i) {
  127. if (processForward(targets[i], rect, minSize)) {
  128. cache.push(targets[i])
  129. } else {
  130. targets[i].element.freeSpace()
  131. }
  132. }
  133. this._cache = cache.reverse();
  134. return rect
  135. },
  136. backward: function(targetRect, alignmentRect) {
  137. var size = arguments.length > 2 && void 0 !== arguments[2] ? arguments[2] : [0, 0];
  138. var backwardRect = targetRect.slice();
  139. var fitRect = targetRect.slice();
  140. var targets = this._cache;
  141. var targetSide = 0;
  142. var target;
  143. var i;
  144. var ii = targets.length;
  145. for (i = 0; i < ii; ++i) {
  146. target = targets[i];
  147. if (target.side !== targetSide) {
  148. backwardRect = targetRect.slice()
  149. }
  150. processBackward(target, backwardRect, alignmentRect, fitRect, size, targetRect);
  151. targetSide = target.side
  152. }
  153. return size
  154. }
  155. };
  156. function createTargets(targets) {
  157. var i;
  158. var ii = targets.length;
  159. var collection = [];
  160. var layout;
  161. for (i = 0; i < ii; ++i) {
  162. layout = targets[i].layoutOptions();
  163. if (layout) {
  164. layout = normalizeLayoutOptions(layout);
  165. layout.element = targets[i];
  166. collection.push(layout)
  167. }
  168. }
  169. collection.sort(function(a, b) {
  170. return b.side - a.side || a.priority - b.priority
  171. });
  172. collection = processWeakItems(collection);
  173. return collection
  174. }
  175. function processWeakItems(collection) {
  176. var weakItem = collection.filter(function(item) {
  177. return true === item.weak
  178. })[0];
  179. var headerItem;
  180. if (weakItem) {
  181. headerItem = collection.filter(function(item) {
  182. return weakItem.primary === item.primary && item.side === weakItem.side && item !== weakItem
  183. })[0]
  184. }
  185. if (weakItem && headerItem) {
  186. return [makeHeader(headerItem, weakItem)].concat(collection.filter(function(item) {
  187. return !(item === headerItem || item === weakItem)
  188. }))
  189. }
  190. return collection
  191. }
  192. function processBackwardHeaderRect(element, rect) {
  193. var rectCopy = rect.slice();
  194. var itemRect = processRectBackward(element, rectCopy, rectCopy);
  195. itemRect[element.side] = rect[element.side];
  196. itemRect[2 + element.side] = rect[2 + element.side];
  197. return itemRect
  198. }
  199. function makeHeader(header, weakElement) {
  200. var side = header.side;
  201. var primary = header.primary;
  202. var secondary = header.secondary;
  203. return {
  204. side: side,
  205. primary: primary,
  206. secondary: secondary,
  207. priority: 0,
  208. element: {
  209. measure: function(targetSize) {
  210. var result = targetSize.slice();
  211. var weakSize = weakElement.element.measure(targetSize.slice());
  212. targetSize[primary] -= weakSize[primary];
  213. var headerSize = header.element.measure(targetSize.slice());
  214. result[side] = weakSize[side] = headerSize[side] = Math.max(headerSize[side], weakSize[side]);
  215. weakElement.size = weakSize;
  216. header.size = headerSize;
  217. return result
  218. },
  219. move: function(rect, fitRect) {
  220. if (fitRect[2] - fitRect[0] < header.size[0] + weakElement.size[0] - BBOX_CEIL_CORRECTION) {
  221. this.freeSpace();
  222. return
  223. }
  224. var weakRect = processBackwardHeaderRect(weakElement, fitRect, fitRect);
  225. fitRect[2 + weakElement.primary] = weakRect[weakElement.primary];
  226. var headerFitReact = processBackwardHeaderRect(header, fitRect, fitRect);
  227. if (fitRect[2 + weakElement.primary] < rect[2 + weakElement.primary] && header.size[header.primary] > rect[2 + header.primary] - rect[header.primary]) {
  228. rect[2 + weakElement.primary] = fitRect[2 + weakElement.primary]
  229. }
  230. var headerRect = processBackwardHeaderRect(header, rect, rect);
  231. if (headerRect[2 + weakElement.primary] > fitRect[2 + weakElement.primary]) {
  232. rect[2 + weakElement.primary] = fitRect[2 + weakElement.primary];
  233. headerRect = processBackwardHeaderRect(header, rect, rect)
  234. }
  235. weakElement.element.move(weakRect);
  236. header.element.move(headerRect, headerFitReact)
  237. },
  238. freeSpace: function() {
  239. header.element.freeSpace();
  240. weakElement.element.freeSpace()
  241. }
  242. }
  243. }
  244. }
  245. module.exports = Layout;