title.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338
  1. /**
  2. * DevExtreme (viz/core/title.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 _Number = Number;
  11. var _isString = require("../../core/utils/type").isString;
  12. var extend = require("../../core/utils/extend").extend;
  13. var _patchFontOptions = require("./utils").patchFontOptions;
  14. var parseHorizontalAlignment = require("./utils").enumParser(["left", "center", "right"]);
  15. var parseVerticalAlignment = require("./utils").enumParser(["top", "bottom"]);
  16. var DEFAULT_MARGIN = 10;
  17. function hasText(text) {
  18. return !!(text && String(text).length > 0)
  19. }
  20. function processTitleLength(elem, text, width, options, placeholderSize) {
  21. if (elem.attr({
  22. text: text
  23. }).setMaxSize(width, placeholderSize, options).textChanged) {
  24. elem.setTitle(text)
  25. }
  26. }
  27. function pickMarginValue(value) {
  28. return value >= 0 ? _Number(value) : DEFAULT_MARGIN
  29. }
  30. function validateMargin(margin) {
  31. var result;
  32. if (margin >= 0) {
  33. result = {
  34. left: _Number(margin),
  35. top: _Number(margin),
  36. right: _Number(margin),
  37. bottom: _Number(margin)
  38. }
  39. } else {
  40. margin = margin || {};
  41. result = {
  42. left: pickMarginValue(margin.left),
  43. top: pickMarginValue(margin.top),
  44. right: pickMarginValue(margin.right),
  45. bottom: pickMarginValue(margin.bottom)
  46. }
  47. }
  48. return result
  49. }
  50. function checkRect(rect, boundingRect) {
  51. return rect[2] - rect[0] < boundingRect.width || rect[3] - rect[1] < boundingRect.height
  52. }
  53. function Title(params) {
  54. this._params = params;
  55. this._group = params.renderer.g().attr({
  56. "class": params.cssClass
  57. }).linkOn(params.root || params.renderer.root, "title");
  58. this._hasText = false
  59. }
  60. extend(Title.prototype, require("./layout_element").LayoutElement.prototype, {
  61. dispose: function() {
  62. var that = this;
  63. that._group.linkRemove();
  64. that._group.linkOff();
  65. if (that._titleElement) {
  66. that._clipRect.dispose();
  67. that._titleElement = that._subtitleElement = that._clipRect = null
  68. }
  69. that._params = that._group = that._options = null
  70. },
  71. _updateOptions: function(options) {
  72. this._options = options;
  73. this._options.horizontalAlignment = parseHorizontalAlignment(options.horizontalAlignment, "center");
  74. this._options.verticalAlignment = parseVerticalAlignment(options.verticalAlignment, "top");
  75. this._options.margin = validateMargin(options.margin)
  76. },
  77. _updateStructure: function() {
  78. var that = this;
  79. var renderer = that._params.renderer;
  80. var group = that._group;
  81. var options = that._options;
  82. var align = options.horizontalAlignment;
  83. if (!that._titleElement) {
  84. that._titleElement = renderer.text().append(group);
  85. that._subtitleElement = renderer.text();
  86. that._clipRect = renderer.clipRect();
  87. group.attr({
  88. "clip-path": that._clipRect.id
  89. })
  90. }
  91. that._titleElement.attr({
  92. align: align,
  93. "class": options.cssClass
  94. });
  95. that._subtitleElement.attr({
  96. align: align,
  97. "class": options.subtitle.cssClass
  98. });
  99. group.linkAppend();
  100. hasText(options.subtitle.text) ? that._subtitleElement.append(group) : that._subtitleElement.remove()
  101. },
  102. _updateTexts: function() {
  103. var that = this;
  104. var options = that._options;
  105. var subtitleOptions = options.subtitle;
  106. var titleElement = that._titleElement;
  107. var subtitleElement = that._subtitleElement;
  108. var testText = "A";
  109. var titleBox;
  110. titleElement.attr({
  111. text: testText,
  112. y: 0
  113. }).css(_patchFontOptions(options.font));
  114. titleBox = titleElement.getBBox();
  115. that._baseLineCorrection = titleBox.height + titleBox.y;
  116. titleElement.attr({
  117. text: options.text
  118. });
  119. titleBox = titleElement.getBBox();
  120. var y = -titleBox.y;
  121. titleElement.attr({
  122. y: y
  123. });
  124. if (hasText(subtitleOptions.text)) {
  125. subtitleElement.attr({
  126. text: subtitleOptions.text,
  127. y: 0
  128. }).css(_patchFontOptions(subtitleOptions.font))
  129. }
  130. },
  131. _shiftSubtitle: function() {
  132. var that = this;
  133. var titleBox = that._titleElement.getBBox();
  134. var element = that._subtitleElement;
  135. var offset = that._options.subtitle.offset;
  136. element.move(0, titleBox.y + titleBox.height - element.getBBox().y - offset)
  137. },
  138. _updateBoundingRectAlignment: function() {
  139. var boundingRect = this._boundingRect;
  140. var options = this._options;
  141. boundingRect.verticalAlignment = options.verticalAlignment;
  142. boundingRect.horizontalAlignment = options.horizontalAlignment;
  143. boundingRect.cutLayoutSide = options.verticalAlignment;
  144. boundingRect.cutSide = "vertical";
  145. boundingRect.position = {
  146. horizontal: options.horizontalAlignment,
  147. vertical: options.verticalAlignment
  148. }
  149. },
  150. hasText: function() {
  151. return this._hasText
  152. },
  153. update: function(themeOptions, userOptions) {
  154. var that = this;
  155. var options = extend(true, {}, themeOptions, processTitleOptions(userOptions));
  156. var _hasText = hasText(options.text);
  157. var isLayoutChanged = _hasText || _hasText !== that._hasText;
  158. that._baseLineCorrection = 0;
  159. that._updateOptions(options);
  160. that._boundingRect = {};
  161. if (_hasText) {
  162. that._updateStructure();
  163. that._updateTexts()
  164. } else {
  165. that._group.linkRemove()
  166. }
  167. that._updateBoundingRect();
  168. that._updateBoundingRectAlignment();
  169. that._hasText = _hasText;
  170. return isLayoutChanged
  171. },
  172. draw: function(width, height) {
  173. var that = this;
  174. if (that._hasText) {
  175. that._group.linkAppend();
  176. that._correctTitleLength(width);
  177. if (that._group.getBBox().height > height) {
  178. this.freeSpace()
  179. }
  180. }
  181. return that
  182. },
  183. probeDraw: function(width, height) {
  184. this.draw(width, height);
  185. return this
  186. },
  187. _correctTitleLength: function(width) {
  188. var that = this;
  189. var options = that._options;
  190. var margin = options.margin;
  191. var maxWidth = width - margin.left - margin.right;
  192. var placeholderSize = options.placeholderSize;
  193. processTitleLength(that._titleElement, options.text, maxWidth, options, placeholderSize);
  194. if (that._subtitleElement) {
  195. if (_Number(placeholderSize) > 0) {
  196. placeholderSize -= that._titleElement.getBBox().height
  197. }
  198. processTitleLength(that._subtitleElement, options.subtitle.text, maxWidth, options.subtitle, placeholderSize);
  199. that._shiftSubtitle()
  200. }
  201. that._updateBoundingRect();
  202. var _this$getCorrectedLay = this.getCorrectedLayoutOptions(),
  203. x = _this$getCorrectedLay.x,
  204. y = _this$getCorrectedLay.y,
  205. height = _this$getCorrectedLay.height;
  206. this._clipRect.attr({
  207. x: x,
  208. y: y,
  209. width: width,
  210. height: height
  211. })
  212. },
  213. getLayoutOptions: function() {
  214. return this._boundingRect || null
  215. },
  216. shift: function(x, y) {
  217. var that = this;
  218. var box = that.getLayoutOptions();
  219. that._group.move(x - box.x, y - box.y);
  220. return that
  221. },
  222. _updateBoundingRect: function() {
  223. var that = this;
  224. var options = that._options;
  225. var margin = options.margin;
  226. var boundingRect = that._boundingRect;
  227. var box = that._hasText ? that._group.getBBox() : {
  228. width: 0,
  229. height: 0,
  230. x: 0,
  231. y: 0,
  232. isEmpty: true
  233. };
  234. if (!box.isEmpty) {
  235. box.height += margin.top + margin.bottom - that._baseLineCorrection;
  236. box.width += margin.left + margin.right;
  237. box.x -= margin.left;
  238. box.y += that._baseLineCorrection - margin.top
  239. }
  240. if (options.placeholderSize > 0) {
  241. box.height = options.placeholderSize
  242. }
  243. boundingRect.height = box.height;
  244. boundingRect.width = box.width;
  245. boundingRect.x = box.x;
  246. boundingRect.y = box.y
  247. },
  248. getCorrectedLayoutOptions: function() {
  249. var srcBox = this.getLayoutOptions();
  250. var correction = this._baseLineCorrection;
  251. return extend({}, srcBox, {
  252. y: srcBox.y - correction,
  253. height: srcBox.height + correction
  254. })
  255. },
  256. layoutOptions: function() {
  257. if (!this._hasText) {
  258. return null
  259. }
  260. return {
  261. horizontalAlignment: this._boundingRect.horizontalAlignment,
  262. verticalAlignment: this._boundingRect.verticalAlignment,
  263. priority: 0
  264. }
  265. },
  266. measure: function(size) {
  267. this.draw(size[0], size[1]);
  268. return [this._boundingRect.width, this._boundingRect.height]
  269. },
  270. move: function(rect, fitRect) {
  271. var boundingRect = this._boundingRect;
  272. if (checkRect(rect, boundingRect)) {
  273. this.shift(fitRect[0], fitRect[1])
  274. } else {
  275. this.shift(Math.round(rect[0]), Math.round(rect[1]))
  276. }
  277. },
  278. freeSpace: function() {
  279. var that = this;
  280. that._params.incidentOccurred("W2103");
  281. that._group.linkRemove();
  282. that._boundingRect.width = that._boundingRect.height = 0
  283. },
  284. getOptions: function() {
  285. return this._options
  286. },
  287. changeLink: function(root) {
  288. this._group.linkRemove();
  289. this._group.linkOn(root, "title")
  290. }
  291. });
  292. exports.Title = Title;
  293. function processTitleOptions(options) {
  294. var newOptions = _isString(options) ? {
  295. text: options
  296. } : options || {};
  297. newOptions.subtitle = _isString(newOptions.subtitle) ? {
  298. text: newOptions.subtitle
  299. } : newOptions.subtitle || {};
  300. return newOptions
  301. }
  302. exports.plugin = {
  303. name: "title",
  304. init: function() {
  305. var that = this;
  306. that._title = new exports.Title({
  307. renderer: that._renderer,
  308. cssClass: that._rootClassPrefix + "-title",
  309. incidentOccurred: that._incidentOccurred
  310. });
  311. that._layout.add(that._title)
  312. },
  313. dispose: function() {
  314. this._title.dispose();
  315. this._title = null
  316. },
  317. customize: function(constructor) {
  318. constructor.addChange({
  319. code: "TITLE",
  320. handler: function() {
  321. if (this._title.update(this._themeManager.theme("title"), this.option("title"))) {
  322. this._change(["LAYOUT"])
  323. }
  324. },
  325. isThemeDependent: true,
  326. option: "title",
  327. isOptionChange: true
  328. })
  329. },
  330. fontFields: ["title.font", "title.subtitle.font"]
  331. };