Text.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250
  1. var _excluded = ["x", "y", "lineHeight", "capHeight", "scaleToFit", "textAnchor", "verticalAnchor", "fill"],
  2. _excluded2 = ["dx", "dy", "angle", "className", "breakAll"];
  3. function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
  4. function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }
  5. function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } } return target; }
  6. function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }
  7. function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
  8. function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
  9. function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; }
  10. function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t["return"] && (u = t["return"](), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } }
  11. function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
  12. import React, { useMemo } from 'react';
  13. import isNil from 'lodash/isNil';
  14. import clsx from 'clsx';
  15. import { isNumber, isNumOrStr } from '../util/DataUtils';
  16. import { Global } from '../util/Global';
  17. import { filterProps } from '../util/ReactUtils';
  18. import { getStringSize } from '../util/DOMUtils';
  19. import { reduceCSSCalc } from '../util/ReduceCSSCalc';
  20. var BREAKING_SPACES = /[ \f\n\r\t\v\u2028\u2029]+/;
  21. var calculateWordWidths = function calculateWordWidths(_ref) {
  22. var children = _ref.children,
  23. breakAll = _ref.breakAll,
  24. style = _ref.style;
  25. try {
  26. var words = [];
  27. if (!isNil(children)) {
  28. if (breakAll) {
  29. words = children.toString().split('');
  30. } else {
  31. words = children.toString().split(BREAKING_SPACES);
  32. }
  33. }
  34. var wordsWithComputedWidth = words.map(function (word) {
  35. return {
  36. word: word,
  37. width: getStringSize(word, style).width
  38. };
  39. });
  40. var spaceWidth = breakAll ? 0 : getStringSize("\xA0", style).width;
  41. return {
  42. wordsWithComputedWidth: wordsWithComputedWidth,
  43. spaceWidth: spaceWidth
  44. };
  45. } catch (e) {
  46. return null;
  47. }
  48. };
  49. var calculateWordsByLines = function calculateWordsByLines(_ref2, initialWordsWithComputedWith, spaceWidth, lineWidth, scaleToFit) {
  50. var maxLines = _ref2.maxLines,
  51. children = _ref2.children,
  52. style = _ref2.style,
  53. breakAll = _ref2.breakAll;
  54. var shouldLimitLines = isNumber(maxLines);
  55. var text = children;
  56. var calculate = function calculate() {
  57. var words = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
  58. return words.reduce(function (result, _ref3) {
  59. var word = _ref3.word,
  60. width = _ref3.width;
  61. var currentLine = result[result.length - 1];
  62. if (currentLine && (lineWidth == null || scaleToFit || currentLine.width + width + spaceWidth < Number(lineWidth))) {
  63. // Word can be added to an existing line
  64. currentLine.words.push(word);
  65. currentLine.width += width + spaceWidth;
  66. } else {
  67. // Add first word to line or word is too long to scaleToFit on existing line
  68. var newLine = {
  69. words: [word],
  70. width: width
  71. };
  72. result.push(newLine);
  73. }
  74. return result;
  75. }, []);
  76. };
  77. var originalResult = calculate(initialWordsWithComputedWith);
  78. var findLongestLine = function findLongestLine(words) {
  79. return words.reduce(function (a, b) {
  80. return a.width > b.width ? a : b;
  81. });
  82. };
  83. if (!shouldLimitLines) {
  84. return originalResult;
  85. }
  86. var suffix = '…';
  87. var checkOverflow = function checkOverflow(index) {
  88. var tempText = text.slice(0, index);
  89. var words = calculateWordWidths({
  90. breakAll: breakAll,
  91. style: style,
  92. children: tempText + suffix
  93. }).wordsWithComputedWidth;
  94. var result = calculate(words);
  95. var doesOverflow = result.length > maxLines || findLongestLine(result).width > Number(lineWidth);
  96. return [doesOverflow, result];
  97. };
  98. var start = 0;
  99. var end = text.length - 1;
  100. var iterations = 0;
  101. var trimmedResult;
  102. while (start <= end && iterations <= text.length - 1) {
  103. var middle = Math.floor((start + end) / 2);
  104. var prev = middle - 1;
  105. var _checkOverflow = checkOverflow(prev),
  106. _checkOverflow2 = _slicedToArray(_checkOverflow, 2),
  107. doesPrevOverflow = _checkOverflow2[0],
  108. result = _checkOverflow2[1];
  109. var _checkOverflow3 = checkOverflow(middle),
  110. _checkOverflow4 = _slicedToArray(_checkOverflow3, 1),
  111. doesMiddleOverflow = _checkOverflow4[0];
  112. if (!doesPrevOverflow && !doesMiddleOverflow) {
  113. start = middle + 1;
  114. }
  115. if (doesPrevOverflow && doesMiddleOverflow) {
  116. end = middle - 1;
  117. }
  118. if (!doesPrevOverflow && doesMiddleOverflow) {
  119. trimmedResult = result;
  120. break;
  121. }
  122. iterations++;
  123. }
  124. // Fallback to originalResult (result without trimming) if we cannot find the
  125. // where to trim. This should not happen :tm:
  126. return trimmedResult || originalResult;
  127. };
  128. var getWordsWithoutCalculate = function getWordsWithoutCalculate(children) {
  129. var words = !isNil(children) ? children.toString().split(BREAKING_SPACES) : [];
  130. return [{
  131. words: words
  132. }];
  133. };
  134. var getWordsByLines = function getWordsByLines(_ref4) {
  135. var width = _ref4.width,
  136. scaleToFit = _ref4.scaleToFit,
  137. children = _ref4.children,
  138. style = _ref4.style,
  139. breakAll = _ref4.breakAll,
  140. maxLines = _ref4.maxLines;
  141. // Only perform calculations if using features that require them (multiline, scaleToFit)
  142. if ((width || scaleToFit) && !Global.isSsr) {
  143. var wordsWithComputedWidth, spaceWidth;
  144. var wordWidths = calculateWordWidths({
  145. breakAll: breakAll,
  146. children: children,
  147. style: style
  148. });
  149. if (wordWidths) {
  150. var wcw = wordWidths.wordsWithComputedWidth,
  151. sw = wordWidths.spaceWidth;
  152. wordsWithComputedWidth = wcw;
  153. spaceWidth = sw;
  154. } else {
  155. return getWordsWithoutCalculate(children);
  156. }
  157. return calculateWordsByLines({
  158. breakAll: breakAll,
  159. children: children,
  160. maxLines: maxLines,
  161. style: style
  162. }, wordsWithComputedWidth, spaceWidth, width, scaleToFit);
  163. }
  164. return getWordsWithoutCalculate(children);
  165. };
  166. var DEFAULT_FILL = '#808080';
  167. export var Text = function Text(_ref5) {
  168. var _ref5$x = _ref5.x,
  169. propsX = _ref5$x === void 0 ? 0 : _ref5$x,
  170. _ref5$y = _ref5.y,
  171. propsY = _ref5$y === void 0 ? 0 : _ref5$y,
  172. _ref5$lineHeight = _ref5.lineHeight,
  173. lineHeight = _ref5$lineHeight === void 0 ? '1em' : _ref5$lineHeight,
  174. _ref5$capHeight = _ref5.capHeight,
  175. capHeight = _ref5$capHeight === void 0 ? '0.71em' : _ref5$capHeight,
  176. _ref5$scaleToFit = _ref5.scaleToFit,
  177. scaleToFit = _ref5$scaleToFit === void 0 ? false : _ref5$scaleToFit,
  178. _ref5$textAnchor = _ref5.textAnchor,
  179. textAnchor = _ref5$textAnchor === void 0 ? 'start' : _ref5$textAnchor,
  180. _ref5$verticalAnchor = _ref5.verticalAnchor,
  181. verticalAnchor = _ref5$verticalAnchor === void 0 ? 'end' : _ref5$verticalAnchor,
  182. _ref5$fill = _ref5.fill,
  183. fill = _ref5$fill === void 0 ? DEFAULT_FILL : _ref5$fill,
  184. props = _objectWithoutProperties(_ref5, _excluded);
  185. var wordsByLines = useMemo(function () {
  186. return getWordsByLines({
  187. breakAll: props.breakAll,
  188. children: props.children,
  189. maxLines: props.maxLines,
  190. scaleToFit: scaleToFit,
  191. style: props.style,
  192. width: props.width
  193. });
  194. }, [props.breakAll, props.children, props.maxLines, scaleToFit, props.style, props.width]);
  195. var dx = props.dx,
  196. dy = props.dy,
  197. angle = props.angle,
  198. className = props.className,
  199. breakAll = props.breakAll,
  200. textProps = _objectWithoutProperties(props, _excluded2);
  201. if (!isNumOrStr(propsX) || !isNumOrStr(propsY)) {
  202. return null;
  203. }
  204. var x = propsX + (isNumber(dx) ? dx : 0);
  205. var y = propsY + (isNumber(dy) ? dy : 0);
  206. var startDy;
  207. switch (verticalAnchor) {
  208. case 'start':
  209. startDy = reduceCSSCalc("calc(".concat(capHeight, ")"));
  210. break;
  211. case 'middle':
  212. startDy = reduceCSSCalc("calc(".concat((wordsByLines.length - 1) / 2, " * -").concat(lineHeight, " + (").concat(capHeight, " / 2))"));
  213. break;
  214. default:
  215. startDy = reduceCSSCalc("calc(".concat(wordsByLines.length - 1, " * -").concat(lineHeight, ")"));
  216. break;
  217. }
  218. var transforms = [];
  219. if (scaleToFit) {
  220. var lineWidth = wordsByLines[0].width;
  221. var width = props.width;
  222. transforms.push("scale(".concat((isNumber(width) ? width / lineWidth : 1) / lineWidth, ")"));
  223. }
  224. if (angle) {
  225. transforms.push("rotate(".concat(angle, ", ").concat(x, ", ").concat(y, ")"));
  226. }
  227. if (transforms.length) {
  228. textProps.transform = transforms.join(' ');
  229. }
  230. return /*#__PURE__*/React.createElement("text", _extends({}, filterProps(textProps, true), {
  231. x: x,
  232. y: y,
  233. className: clsx('recharts-text', className),
  234. textAnchor: textAnchor,
  235. fill: fill.includes('url') ? DEFAULT_FILL : fill
  236. }), wordsByLines.map(function (line, index) {
  237. var words = line.words.join(breakAll ? '' : ' ');
  238. return (
  239. /*#__PURE__*/
  240. // duplicate words will cause duplicate keys
  241. // eslint-disable-next-line react/no-array-index-key
  242. React.createElement("tspan", {
  243. x: x,
  244. dy: index === 0 ? startDy : lineHeight,
  245. key: "".concat(words, "-").concat(index)
  246. }, words)
  247. );
  248. }));
  249. };