label.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510
  1. /**
  2. * DevExtreme (viz/funnel/label.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 _label = require("../series/points/label");
  11. var _label2 = _interopRequireDefault(_label);
  12. var _utils = require("../core/utils");
  13. var _extend = require("../../core/utils/extend");
  14. var _common = require("../../core/utils/common");
  15. function _interopRequireDefault(obj) {
  16. return obj && obj.__esModule ? obj : {
  17. "default": obj
  18. }
  19. }
  20. function _slicedToArray(arr, i) {
  21. return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest()
  22. }
  23. function _nonIterableRest() {
  24. throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")
  25. }
  26. function _iterableToArrayLimit(arr, i) {
  27. var _i = null == arr ? null : "undefined" !== typeof Symbol && arr[Symbol.iterator] || arr["@@iterator"];
  28. if (null == _i) {
  29. return
  30. }
  31. var _arr = [];
  32. var _n = true;
  33. var _d = false;
  34. var _s, _e;
  35. try {
  36. for (_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true) {
  37. _arr.push(_s.value);
  38. if (i && _arr.length === i) {
  39. break
  40. }
  41. }
  42. } catch (err) {
  43. _d = true;
  44. _e = err
  45. } finally {
  46. try {
  47. if (!_n && null != _i.return) {
  48. _i.return()
  49. }
  50. } finally {
  51. if (_d) {
  52. throw _e
  53. }
  54. }
  55. }
  56. return _arr
  57. }
  58. function _arrayWithHoles(arr) {
  59. if (Array.isArray(arr)) {
  60. return arr
  61. }
  62. }
  63. function _toConsumableArray(arr) {
  64. return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread()
  65. }
  66. function _nonIterableSpread() {
  67. throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")
  68. }
  69. function _unsupportedIterableToArray(o, minLen) {
  70. if (!o) {
  71. return
  72. }
  73. if ("string" === typeof o) {
  74. return _arrayLikeToArray(o, minLen)
  75. }
  76. var n = Object.prototype.toString.call(o).slice(8, -1);
  77. if ("Object" === n && o.constructor) {
  78. n = o.constructor.name
  79. }
  80. if ("Map" === n || "Set" === n) {
  81. return Array.from(o)
  82. }
  83. if ("Arguments" === n || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) {
  84. return _arrayLikeToArray(o, minLen)
  85. }
  86. }
  87. function _iterableToArray(iter) {
  88. if ("undefined" !== typeof Symbol && null != iter[Symbol.iterator] || null != iter["@@iterator"]) {
  89. return Array.from(iter)
  90. }
  91. }
  92. function _arrayWithoutHoles(arr) {
  93. if (Array.isArray(arr)) {
  94. return _arrayLikeToArray(arr)
  95. }
  96. }
  97. function _arrayLikeToArray(arr, len) {
  98. if (null == len || len > arr.length) {
  99. len = arr.length
  100. }
  101. for (var i = 0, arr2 = new Array(len); i < len; i++) {
  102. arr2[i] = arr[i]
  103. }
  104. return arr2
  105. }
  106. var OUTSIDE_POSITION = "outside";
  107. var INSIDE_POSITION = "inside";
  108. var OUTSIDE_LABEL_INDENT = 5;
  109. var COLUMNS_LABEL_INDENT = 20;
  110. var CONNECTOR_INDENT = 4;
  111. var PREVENT_EMPTY_PIXEL_OFFSET = 1;
  112. function getLabelIndent(pos) {
  113. pos = (0, _utils.normalizeEnum)(pos);
  114. if (pos === OUTSIDE_POSITION) {
  115. return OUTSIDE_LABEL_INDENT
  116. } else {
  117. if (pos === INSIDE_POSITION) {
  118. return 0
  119. }
  120. }
  121. return COLUMNS_LABEL_INDENT
  122. }
  123. function isOutsidePosition(pos) {
  124. pos = (0, _utils.normalizeEnum)(pos);
  125. return pos === OUTSIDE_POSITION || pos !== INSIDE_POSITION
  126. }
  127. function correctYForInverted(y, bBox, inverted) {
  128. return inverted ? y - bBox.height : y
  129. }
  130. function getOutsideRightLabelPosition(coords, bBox, options, inverted) {
  131. return {
  132. x: coords[2] + options.horizontalOffset + OUTSIDE_LABEL_INDENT,
  133. y: correctYForInverted(coords[3] + options.verticalOffset, bBox, inverted)
  134. }
  135. }
  136. function getOutsideLeftLabelPosition(coords, bBox, options, inverted) {
  137. return {
  138. x: coords[0] - bBox.width - options.horizontalOffset - OUTSIDE_LABEL_INDENT,
  139. y: correctYForInverted(coords[1] + options.verticalOffset, bBox, inverted)
  140. }
  141. }
  142. function getInsideLabelPosition(coords, bBox, options) {
  143. var width = coords[2] - coords[0];
  144. var height = coords[7] - coords[1];
  145. return {
  146. x: coords[0] + width / 2 + options.horizontalOffset - bBox.width / 2,
  147. y: coords[1] + options.verticalOffset + height / 2 - bBox.height / 2
  148. }
  149. }
  150. function getColumnLabelRightPosition(labelRect, rect, textAlignment) {
  151. return function(coords, bBox, options, inverted) {
  152. return {
  153. x: "left" === textAlignment ? rect[2] + options.horizontalOffset + COLUMNS_LABEL_INDENT : labelRect[2] - bBox.width,
  154. y: correctYForInverted(coords[3] + options.verticalOffset, bBox, inverted)
  155. }
  156. }
  157. }
  158. function getColumnLabelLeftPosition(labelRect, rect, textAlignment) {
  159. return function(coords, bBox, options, inverted) {
  160. return {
  161. x: "left" === textAlignment ? labelRect[0] : rect[0] - bBox.width - options.horizontalOffset - COLUMNS_LABEL_INDENT,
  162. y: correctYForInverted(coords[3] + options.verticalOffset, bBox, inverted)
  163. }
  164. }
  165. }
  166. function getConnectorStrategy(options, inverted) {
  167. var isLeftPos = "left" === options.horizontalAlignment;
  168. var connectorIndent = isLeftPos ? CONNECTOR_INDENT : -CONNECTOR_INDENT;
  169. var verticalCorrection = inverted ? -PREVENT_EMPTY_PIXEL_OFFSET : 0;
  170. function getFigureCenter(figure) {
  171. return isLeftPos ? [figure[0] + PREVENT_EMPTY_PIXEL_OFFSET, figure[1] + verticalCorrection] : [figure[2] - PREVENT_EMPTY_PIXEL_OFFSET, figure[3] + verticalCorrection]
  172. }
  173. return {
  174. isLabelInside: function() {
  175. return !isOutsidePosition(options.position)
  176. },
  177. getFigureCenter: getFigureCenter,
  178. prepareLabelPoints: function(bBox) {
  179. var x = bBox.x + connectorIndent;
  180. var y = bBox.y;
  181. var x1 = x + bBox.width;
  182. return _toConsumableArray(Array(bBox.height + 1)).map(function(_, i) {
  183. return [x, y + i]
  184. }).concat(_toConsumableArray(Array(bBox.height + 1)).map(function(_, i) {
  185. return [x1, y + i]
  186. }))
  187. },
  188. isHorizontal: function() {
  189. return true
  190. },
  191. findFigurePoint: function(figure) {
  192. return getFigureCenter(figure)
  193. },
  194. adjustPoints: function(points) {
  195. return points.map(Math.round)
  196. }
  197. }
  198. }
  199. function getLabelOptions(labelOptions, defaultColor, defaultTextAlignment) {
  200. var opt = labelOptions || {};
  201. var labelFont = (0, _extend.extend)({}, opt.font) || {};
  202. var labelBorder = opt.border || {};
  203. var labelConnector = opt.connector || {};
  204. var backgroundAttr = {
  205. fill: opt.backgroundColor || defaultColor,
  206. "stroke-width": labelBorder.visible ? labelBorder.width || 0 : 0,
  207. stroke: labelBorder.visible && labelBorder.width ? labelBorder.color : "none",
  208. dashStyle: labelBorder.dashStyle
  209. };
  210. var connectorAttr = {
  211. stroke: labelConnector.visible && labelConnector.width ? labelConnector.color || defaultColor : "none",
  212. "stroke-width": labelConnector.visible ? labelConnector.width || 0 : 0,
  213. opacity: labelConnector.opacity
  214. };
  215. labelFont.color = "none" === opt.backgroundColor && "#ffffff" === (0, _utils.normalizeEnum)(labelFont.color) && "inside" !== opt.position ? defaultColor : labelFont.color;
  216. return {
  217. format: opt.format,
  218. textAlignment: opt.textAlignment || (isOutsidePosition(opt.position) ? defaultTextAlignment : "center"),
  219. customizeText: opt.customizeText,
  220. attributes: {
  221. font: labelFont
  222. },
  223. visible: 0 !== labelFont.size ? opt.visible : false,
  224. showForZeroValues: opt.showForZeroValues,
  225. horizontalOffset: opt.horizontalOffset,
  226. verticalOffset: opt.verticalOffset,
  227. background: backgroundAttr,
  228. connector: connectorAttr,
  229. wordWrap: labelOptions.wordWrap,
  230. textOverflow: labelOptions.textOverflow
  231. }
  232. }
  233. function correctLabelPosition(pos, bBox, rect) {
  234. if (pos.x < rect[0]) {
  235. pos.x = rect[0]
  236. }
  237. if (pos.x + bBox.width > rect[2]) {
  238. pos.x = rect[2] - bBox.width
  239. }
  240. if (pos.y < rect[1]) {
  241. pos.y = rect[1]
  242. }
  243. if (pos.y + bBox.height > rect[3]) {
  244. pos.y = rect[3] - bBox.height
  245. }
  246. return pos
  247. }
  248. function removeEmptySpace(labels, requiredSpace, startPoint) {
  249. labels.reduce(function(requiredSpace, label, index, labels) {
  250. var prevLabel = labels[index + 1];
  251. if (requiredSpace > 0) {
  252. var bBox = label.getBoundingRect();
  253. var point = prevLabel ? prevLabel.getBoundingRect().y + prevLabel.getBoundingRect().height : startPoint;
  254. var emptySpace = bBox.y - point;
  255. var shift = Math.min(emptySpace, requiredSpace);
  256. labels.slice(0, index + 1).forEach(function(label) {
  257. var bBox = label.getBoundingRect();
  258. label.shift(bBox.x, bBox.y - shift)
  259. });
  260. requiredSpace -= shift
  261. }
  262. return requiredSpace
  263. }, requiredSpace)
  264. }
  265. exports.plugin = {
  266. name: "lables",
  267. init: _common.noop,
  268. dispose: _common.noop,
  269. extenders: {
  270. _initCore: function() {
  271. this._labelsGroup = this._renderer.g().attr({
  272. "class": this._rootClassPrefix + "-labels"
  273. }).append(this._renderer.root);
  274. this._labels = []
  275. },
  276. _applySize: function() {
  277. var options = this._getOption("label");
  278. var adaptiveLayout = this._getOption("adaptiveLayout");
  279. var rect = this._rect;
  280. var labelWidth = 0;
  281. var width = rect[2] - rect[0];
  282. this._labelRect = rect.slice();
  283. if (!this._labels.length || !isOutsidePosition(options.position)) {
  284. if ((0, _utils.normalizeEnum)("none" !== this._getOption("resolveLabelOverlapping", true))) {
  285. this._labels.forEach(function(l) {
  286. return !l.isVisible() && l.draw(true)
  287. })
  288. }
  289. return
  290. }
  291. var groupWidth = this._labels.map(function(label) {
  292. label.resetEllipsis();
  293. return label.getBoundingRect().width
  294. }).reduce(function(max, width) {
  295. return Math.max(max, width)
  296. }, 0);
  297. labelWidth = groupWidth + options.horizontalOffset + getLabelIndent(options.position);
  298. if (!adaptiveLayout.keepLabels && width - labelWidth < adaptiveLayout.width) {
  299. this._labels.forEach(function(label) {
  300. label.draw(false)
  301. });
  302. return
  303. } else {
  304. if (width - labelWidth < adaptiveLayout.width) {
  305. labelWidth = width - adaptiveLayout.width;
  306. labelWidth = labelWidth > 0 ? labelWidth : 0
  307. }
  308. this._labels.forEach(function(label) {
  309. label.draw(true)
  310. })
  311. }
  312. if ("left" === options.horizontalAlignment) {
  313. rect[0] += labelWidth
  314. } else {
  315. rect[2] -= labelWidth
  316. }
  317. },
  318. _buildNodes: function() {
  319. this._createLabels()
  320. },
  321. _change_TILING: function() {
  322. var that = this;
  323. var options = that._getOption("label");
  324. var getCoords = getInsideLabelPosition;
  325. var inverted = that._getOption("inverted", true);
  326. var textAlignment;
  327. if (isOutsidePosition(options.position)) {
  328. if ((0, _utils.normalizeEnum)(options.position) === OUTSIDE_POSITION) {
  329. getCoords = "left" === options.horizontalAlignment ? getOutsideLeftLabelPosition : getOutsideRightLabelPosition
  330. } else {
  331. textAlignment = this._defaultLabelTextAlignment();
  332. getCoords = "left" === options.horizontalAlignment ? getColumnLabelLeftPosition(this._labelRect, this._rect, textAlignment) : getColumnLabelRightPosition(this._labelRect, this._rect, textAlignment)
  333. }
  334. }
  335. that._labels.forEach(function(label, index) {
  336. var item = that._items[index];
  337. var borderWidth = item.getNormalStyle()["stroke-width"];
  338. var halfBorderWidth = inverted ? borderWidth / 2 : -borderWidth / 2;
  339. var coords = halfBorderWidth ? item.coords.map(function(coord, index) {
  340. if (1 === index || 3 === index) {
  341. return coord - halfBorderWidth
  342. } else {
  343. if (2 === index) {
  344. return coord - borderWidth
  345. } else {
  346. if (0 === index) {
  347. return coord + borderWidth
  348. }
  349. }
  350. }
  351. return coord
  352. }) : item.coords;
  353. if (!options.showForZeroValues && 0 === item.value) {
  354. label.draw(false);
  355. return
  356. }
  357. if (isOutsidePosition(options.position)) {
  358. that._correctLabelWidth(label, item.coords, options)
  359. }
  360. var bBox = label.getBoundingRect();
  361. var pos = correctLabelPosition(getCoords(coords, bBox, options, inverted), bBox, that._labelRect);
  362. label.setFigureToDrawConnector(coords);
  363. label.shift(pos.x, pos.y)
  364. });
  365. that._resolveLabelOverlapping()
  366. }
  367. },
  368. members: {
  369. _resolveLabelOverlapping: function() {
  370. var that = this;
  371. var resolveLabelOverlapping = (0, _utils.normalizeEnum)(that._getOption("resolveLabelOverlapping", true));
  372. var labels = this._getOption("inverted", true) ? that._labels.slice().reverse() : that._labels;
  373. if ("hide" === resolveLabelOverlapping) {
  374. labels.reduce(function(height, label) {
  375. if (label.getBoundingRect().y < height) {
  376. label.hide()
  377. } else {
  378. height = label.getBoundingRect().y + label.getBoundingRect().height
  379. }
  380. return height
  381. }, 0)
  382. } else {
  383. if ("shift" === resolveLabelOverlapping) {
  384. var maxHeight = this._labelRect[3];
  385. labels.reduce(function(_ref, label, index, labels) {
  386. var _ref2 = _slicedToArray(_ref, 2),
  387. height = _ref2[0],
  388. emptySpace = _ref2[1];
  389. var bBox = label.getBoundingRect();
  390. var y = bBox.y;
  391. if (bBox.y < height) {
  392. label.shift(bBox.x, height);
  393. y = height
  394. }
  395. if (y - height > 0) {
  396. emptySpace += y - height
  397. }
  398. if (y + bBox.height > maxHeight) {
  399. if (emptySpace && emptySpace > y + bBox.height - maxHeight) {
  400. removeEmptySpace(labels.slice(0, index).reverse(), y + bBox.height - maxHeight, that._labelRect[1]);
  401. emptySpace -= y + bBox.height - maxHeight;
  402. label.shift(bBox.x, y - (y + bBox.height - maxHeight));
  403. height = y - (y + bBox.height - maxHeight) + bBox.height
  404. } else {
  405. label.hide()
  406. }
  407. } else {
  408. height = y + bBox.height
  409. }
  410. return [height, emptySpace]
  411. }, [this._labelRect[1], 0])
  412. }
  413. }
  414. },
  415. _defaultLabelTextAlignment: function() {
  416. return this._getOption("rtlEnabled", true) ? "right" : "left"
  417. },
  418. _correctLabelWidth: function(label, item, options) {
  419. var isLeftPos = "left" === options.horizontalAlignment;
  420. var minX = isLeftPos ? this._labelRect[0] : item[2];
  421. var maxX = isLeftPos ? item[0] : this._labelRect[2];
  422. var maxWidth = maxX - minX;
  423. if (label.getBoundingRect().width > maxWidth) {
  424. label.fit(maxWidth)
  425. }
  426. },
  427. _createLabels: function() {
  428. var that = this;
  429. var labelOptions = that._getOption("label");
  430. var connectorStrategy = getConnectorStrategy(labelOptions, that._getOption("inverted", true));
  431. this._labelsGroup.clear();
  432. if (!labelOptions.visible) {
  433. return
  434. }
  435. this._labels = that._items.map(function(item) {
  436. var label = new _label2.default.Label({
  437. renderer: that._renderer,
  438. labelsGroup: that._labelsGroup,
  439. strategy: connectorStrategy
  440. });
  441. label.setOptions(getLabelOptions(labelOptions, item.color, that._defaultLabelTextAlignment()));
  442. label.setData({
  443. item: item,
  444. value: item.value,
  445. percent: item.percent
  446. });
  447. label.draw(true);
  448. return label
  449. });
  450. if (this._labels.length && isOutsidePosition(labelOptions.position)) {
  451. this._requestChange(["LAYOUT"])
  452. }
  453. }
  454. },
  455. customize: function(constructor) {
  456. constructor.prototype._proxyData.push(function(x, y) {
  457. var that = this;
  458. var data;
  459. that._labels.forEach(function(label, index) {
  460. var rect = label.getBoundingRect();
  461. if (x >= rect.x && x <= rect.x + rect.width && y >= rect.y && y <= rect.y + rect.height) {
  462. var pos = isOutsidePosition(that._getOption("label").position) ? "outside" : "inside";
  463. data = {
  464. id: index,
  465. type: pos + "-label"
  466. };
  467. return true
  468. }
  469. });
  470. return data
  471. });
  472. ["label", "resolveLabelOverlapping"].forEach(function(optionName) {
  473. constructor.addChange({
  474. code: optionName.toUpperCase(),
  475. handler: function() {
  476. this._createLabels();
  477. this._requestChange(["LAYOUT"])
  478. },
  479. isThemeDependent: true,
  480. isOptionChange: true,
  481. option: optionName
  482. })
  483. })
  484. },
  485. fontFields: ["label.font"]
  486. };