tooltipMarkup.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378
  1. /*
  2. * Licensed to the Apache Software Foundation (ASF) under one
  3. * or more contributor license agreements. See the NOTICE file
  4. * distributed with this work for additional information
  5. * regarding copyright ownership. The ASF licenses this file
  6. * to you under the Apache License, Version 2.0 (the
  7. * "License"); you may not use this file except in compliance
  8. * with the License. You may obtain a copy of the License at
  9. *
  10. * http://www.apache.org/licenses/LICENSE-2.0
  11. *
  12. * Unless required by applicable law or agreed to in writing,
  13. * software distributed under the License is distributed on an
  14. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  15. * KIND, either express or implied. See the License for the
  16. * specific language governing permissions and limitations
  17. * under the License.
  18. */
  19. /**
  20. * AUTO-GENERATED FILE. DO NOT MODIFY.
  21. */
  22. /*
  23. * Licensed to the Apache Software Foundation (ASF) under one
  24. * or more contributor license agreements. See the NOTICE file
  25. * distributed with this work for additional information
  26. * regarding copyright ownership. The ASF licenses this file
  27. * to you under the Apache License, Version 2.0 (the
  28. * "License"); you may not use this file except in compliance
  29. * with the License. You may obtain a copy of the License at
  30. *
  31. * http://www.apache.org/licenses/LICENSE-2.0
  32. *
  33. * Unless required by applicable law or agreed to in writing,
  34. * software distributed under the License is distributed on an
  35. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  36. * KIND, either express or implied. See the License for the
  37. * specific language governing permissions and limitations
  38. * under the License.
  39. */
  40. import { getTooltipMarker, encodeHTML, makeValueReadable, convertToColorString } from '../../util/format';
  41. import { isString, each, hasOwn, isArray, map, assert, extend } from 'zrender/lib/core/util';
  42. import { SortOrderComparator } from '../../data/helper/dataValueHelper';
  43. import { getRandomIdBase } from '../../util/number';
  44. var TOOLTIP_LINE_HEIGHT_CSS = 'line-height:1'; // TODO: more textStyle option
  45. function getTooltipTextStyle(textStyle, renderMode) {
  46. var nameFontColor = textStyle.color || '#6e7079';
  47. var nameFontSize = textStyle.fontSize || 12;
  48. var nameFontWeight = textStyle.fontWeight || '400';
  49. var valueFontColor = textStyle.color || '#464646';
  50. var valueFontSize = textStyle.fontSize || 14;
  51. var valueFontWeight = textStyle.fontWeight || '900';
  52. if (renderMode === 'html') {
  53. // `textStyle` is probably from user input, should be encoded to reduce security risk.
  54. return {
  55. // eslint-disable-next-line max-len
  56. nameStyle: "font-size:" + encodeHTML(nameFontSize + '') + "px;color:" + encodeHTML(nameFontColor) + ";font-weight:" + encodeHTML(nameFontWeight + ''),
  57. // eslint-disable-next-line max-len
  58. valueStyle: "font-size:" + encodeHTML(valueFontSize + '') + "px;color:" + encodeHTML(valueFontColor) + ";font-weight:" + encodeHTML(valueFontWeight + '')
  59. };
  60. } else {
  61. return {
  62. nameStyle: {
  63. fontSize: nameFontSize,
  64. fill: nameFontColor,
  65. fontWeight: nameFontWeight
  66. },
  67. valueStyle: {
  68. fontSize: valueFontSize,
  69. fill: valueFontColor,
  70. fontWeight: valueFontWeight
  71. }
  72. };
  73. }
  74. } // See `TooltipMarkupLayoutIntent['innerGapLevel']`.
  75. // (value from UI design)
  76. var HTML_GAPS = [0, 10, 20, 30];
  77. var RICH_TEXT_GAPS = ['', '\n', '\n\n', '\n\n\n']; // eslint-disable-next-line max-len
  78. export function createTooltipMarkup(type, option) {
  79. option.type = type;
  80. return option;
  81. }
  82. function getBuilder(fragment) {
  83. return hasOwn(builderMap, fragment.type) && builderMap[fragment.type];
  84. }
  85. var builderMap = {
  86. /**
  87. * A `section` block is like:
  88. * ```
  89. * header
  90. * subBlock
  91. * subBlock
  92. * ...
  93. * ```
  94. */
  95. section: {
  96. planLayout: function (fragment) {
  97. var subBlockLen = fragment.blocks.length;
  98. var thisBlockHasInnerGap = subBlockLen > 1 || subBlockLen > 0 && !fragment.noHeader;
  99. var thisGapLevelBetweenSubBlocks = 0;
  100. each(fragment.blocks, function (subBlock) {
  101. getBuilder(subBlock).planLayout(subBlock);
  102. var subGapLevel = subBlock.__gapLevelBetweenSubBlocks; // If the some of the sub-blocks have some gaps (like 10px) inside, this block
  103. // should use a larger gap (like 20px) to distinguish those sub-blocks.
  104. if (subGapLevel >= thisGapLevelBetweenSubBlocks) {
  105. thisGapLevelBetweenSubBlocks = subGapLevel + (thisBlockHasInnerGap && ( // 0 always can not be readable gap level.
  106. !subGapLevel // If no header, always keep the sub gap level. Otherwise
  107. // look weird in case `multipleSeries`.
  108. || subBlock.type === 'section' && !subBlock.noHeader) ? 1 : 0);
  109. }
  110. });
  111. fragment.__gapLevelBetweenSubBlocks = thisGapLevelBetweenSubBlocks;
  112. },
  113. build: function (ctx, fragment, topMarginForOuterGap, toolTipTextStyle) {
  114. var noHeader = fragment.noHeader;
  115. var gaps = getGap(fragment);
  116. var subMarkupText = buildSubBlocks(ctx, fragment, noHeader ? topMarginForOuterGap : gaps.html, toolTipTextStyle);
  117. if (noHeader) {
  118. return subMarkupText;
  119. }
  120. var displayableHeader = makeValueReadable(fragment.header, 'ordinal', ctx.useUTC);
  121. var nameStyle = getTooltipTextStyle(toolTipTextStyle, ctx.renderMode).nameStyle;
  122. if (ctx.renderMode === 'richText') {
  123. return wrapInlineNameRichText(ctx, displayableHeader, nameStyle) + gaps.richText + subMarkupText;
  124. } else {
  125. return wrapBlockHTML("<div style=\"" + nameStyle + ";" + TOOLTIP_LINE_HEIGHT_CSS + ";\">" + encodeHTML(displayableHeader) + '</div>' + subMarkupText, topMarginForOuterGap);
  126. }
  127. }
  128. },
  129. /**
  130. * A `nameValue` block is like:
  131. * ```
  132. * marker name value
  133. * ```
  134. */
  135. nameValue: {
  136. planLayout: function (fragment) {
  137. fragment.__gapLevelBetweenSubBlocks = 0;
  138. },
  139. build: function (ctx, fragment, topMarginForOuterGap, toolTipTextStyle) {
  140. var renderMode = ctx.renderMode;
  141. var noName = fragment.noName;
  142. var noValue = fragment.noValue;
  143. var noMarker = !fragment.markerType;
  144. var name = fragment.name;
  145. var value = fragment.value;
  146. var useUTC = ctx.useUTC;
  147. if (noName && noValue) {
  148. return;
  149. }
  150. var markerStr = noMarker ? '' : ctx.markupStyleCreator.makeTooltipMarker(fragment.markerType, fragment.markerColor || '#333', renderMode);
  151. var readableName = noName ? '' : makeValueReadable(name, 'ordinal', useUTC);
  152. var valueTypeOption = fragment.valueType;
  153. var readableValueList = noValue ? [] : isArray(value) ? map(value, function (val, idx) {
  154. return makeValueReadable(val, isArray(valueTypeOption) ? valueTypeOption[idx] : valueTypeOption, useUTC);
  155. }) : [makeValueReadable(value, isArray(valueTypeOption) ? valueTypeOption[0] : valueTypeOption, useUTC)];
  156. var valueAlignRight = !noMarker || !noName; // It little weird if only value next to marker but far from marker.
  157. var valueCloseToMarker = !noMarker && noName;
  158. var _a = getTooltipTextStyle(toolTipTextStyle, renderMode),
  159. nameStyle = _a.nameStyle,
  160. valueStyle = _a.valueStyle;
  161. return renderMode === 'richText' ? (noMarker ? '' : markerStr) + (noName ? '' : wrapInlineNameRichText(ctx, readableName, nameStyle)) // Value has commas inside, so use ' ' as delimiter for multiple values.
  162. + (noValue ? '' : wrapInlineValueRichText(ctx, readableValueList, valueAlignRight, valueCloseToMarker, valueStyle)) : wrapBlockHTML((noMarker ? '' : markerStr) + (noName ? '' : wrapInlineNameHTML(readableName, !noMarker, nameStyle)) + (noValue ? '' : wrapInlineValueHTML(readableValueList, valueAlignRight, valueCloseToMarker, valueStyle)), topMarginForOuterGap);
  163. }
  164. }
  165. };
  166. function buildSubBlocks(ctx, fragment, topMarginForOuterGap, tooltipTextStyle) {
  167. var subMarkupTextList = [];
  168. var subBlocks = fragment.blocks || [];
  169. assert(!subBlocks || isArray(subBlocks));
  170. subBlocks = subBlocks || [];
  171. var orderMode = ctx.orderMode;
  172. if (fragment.sortBlocks && orderMode) {
  173. subBlocks = subBlocks.slice();
  174. var orderMap = {
  175. valueAsc: 'asc',
  176. valueDesc: 'desc'
  177. };
  178. if (hasOwn(orderMap, orderMode)) {
  179. var comparator_1 = new SortOrderComparator(orderMap[orderMode], null);
  180. subBlocks.sort(function (a, b) {
  181. return comparator_1.evaluate(a.sortParam, b.sortParam);
  182. });
  183. } // FIXME 'seriesDesc' necessary?
  184. else if (orderMode === 'seriesDesc') {
  185. subBlocks.reverse();
  186. }
  187. }
  188. var gaps = getGap(fragment);
  189. each(subBlocks, function (subBlock, idx) {
  190. var subMarkupText = getBuilder(subBlock).build(ctx, subBlock, idx > 0 ? gaps.html : 0, tooltipTextStyle);
  191. subMarkupText != null && subMarkupTextList.push(subMarkupText);
  192. });
  193. if (!subMarkupTextList.length) {
  194. return;
  195. }
  196. return ctx.renderMode === 'richText' ? subMarkupTextList.join(gaps.richText) : wrapBlockHTML(subMarkupTextList.join(''), topMarginForOuterGap);
  197. }
  198. /**
  199. * @return markupText. null/undefined means no content.
  200. */
  201. export function buildTooltipMarkup(fragment, markupStyleCreator, renderMode, orderMode, useUTC, toolTipTextStyle) {
  202. if (!fragment) {
  203. return;
  204. }
  205. var builder = getBuilder(fragment);
  206. builder.planLayout(fragment);
  207. var ctx = {
  208. useUTC: useUTC,
  209. renderMode: renderMode,
  210. orderMode: orderMode,
  211. markupStyleCreator: markupStyleCreator
  212. };
  213. return builder.build(ctx, fragment, 0, toolTipTextStyle);
  214. }
  215. function getGap(fragment) {
  216. var gapLevelBetweenSubBlocks = fragment.__gapLevelBetweenSubBlocks;
  217. return {
  218. html: HTML_GAPS[gapLevelBetweenSubBlocks],
  219. richText: RICH_TEXT_GAPS[gapLevelBetweenSubBlocks]
  220. };
  221. }
  222. function wrapBlockHTML(encodedContent, topGap) {
  223. var clearfix = '<div style="clear:both"></div>';
  224. var marginCSS = "margin: " + topGap + "px 0 0";
  225. return "<div style=\"" + marginCSS + ";" + TOOLTIP_LINE_HEIGHT_CSS + ";\">" + encodedContent + clearfix + '</div>';
  226. }
  227. function wrapInlineNameHTML(name, leftHasMarker, style) {
  228. var marginCss = leftHasMarker ? 'margin-left:2px' : '';
  229. return "<span style=\"" + style + ";" + marginCss + "\">" + encodeHTML(name) + '</span>';
  230. }
  231. function wrapInlineValueHTML(valueList, alignRight, valueCloseToMarker, style) {
  232. // Do not too close to marker, considering there are multiple values separated by spaces.
  233. var paddingStr = valueCloseToMarker ? '10px' : '20px';
  234. var alignCSS = alignRight ? "float:right;margin-left:" + paddingStr : '';
  235. return "<span style=\"" + alignCSS + ";" + style + "\">" // Value has commas inside, so use ' ' as delimiter for multiple values.
  236. + map(valueList, function (value) {
  237. return encodeHTML(value);
  238. }).join('&nbsp;&nbsp;') + '</span>';
  239. }
  240. function wrapInlineNameRichText(ctx, name, style) {
  241. return ctx.markupStyleCreator.wrapRichTextStyle(name, style);
  242. }
  243. function wrapInlineValueRichText(ctx, valueList, alignRight, valueCloseToMarker, style) {
  244. var styles = [style];
  245. var paddingLeft = valueCloseToMarker ? 10 : 20;
  246. alignRight && styles.push({
  247. padding: [0, 0, 0, paddingLeft],
  248. align: 'right'
  249. }); // Value has commas inside, so use ' ' as delimiter for multiple values.
  250. return ctx.markupStyleCreator.wrapRichTextStyle(valueList.join(' '), styles);
  251. }
  252. export function retrieveVisualColorForTooltipMarker(series, dataIndex) {
  253. var style = series.getData().getItemVisual(dataIndex, 'style');
  254. var color = style[series.visualDrawType];
  255. return convertToColorString(color);
  256. }
  257. export function getPaddingFromTooltipModel(model, renderMode) {
  258. var padding = model.get('padding');
  259. return padding != null ? padding // We give slightly different to look pretty.
  260. : renderMode === 'richText' ? [8, 10] : 10;
  261. }
  262. /**
  263. * The major feature is generate styles for `renderMode: 'richText'`.
  264. * But it also serves `renderMode: 'html'` to provide
  265. * "renderMode-independent" API.
  266. */
  267. var TooltipMarkupStyleCreator =
  268. /** @class */
  269. function () {
  270. function TooltipMarkupStyleCreator() {
  271. this.richTextStyles = {}; // Notice that "generate a style name" usuall happens repeatly when mouse moving and
  272. // displaying a tooltip. So we put the `_nextStyleNameId` as a member of each creator
  273. // rather than static shared by all creators (which will cause it increase to fast).
  274. this._nextStyleNameId = getRandomIdBase();
  275. }
  276. TooltipMarkupStyleCreator.prototype._generateStyleName = function () {
  277. return '__EC_aUTo_' + this._nextStyleNameId++;
  278. };
  279. TooltipMarkupStyleCreator.prototype.makeTooltipMarker = function (markerType, colorStr, renderMode) {
  280. var markerId = renderMode === 'richText' ? this._generateStyleName() : null;
  281. var marker = getTooltipMarker({
  282. color: colorStr,
  283. type: markerType,
  284. renderMode: renderMode,
  285. markerId: markerId
  286. });
  287. if (isString(marker)) {
  288. return marker;
  289. } else {
  290. if (process.env.NODE_ENV !== 'production') {
  291. assert(markerId);
  292. }
  293. this.richTextStyles[markerId] = marker.style;
  294. return marker.content;
  295. }
  296. };
  297. /**
  298. * @usage
  299. * ```ts
  300. * const styledText = markupStyleCreator.wrapRichTextStyle([
  301. * // The styles will be auto merged.
  302. * {
  303. * fontSize: 12,
  304. * color: 'blue'
  305. * },
  306. * {
  307. * padding: 20
  308. * }
  309. * ]);
  310. * ```
  311. */
  312. TooltipMarkupStyleCreator.prototype.wrapRichTextStyle = function (text, styles) {
  313. var finalStl = {};
  314. if (isArray(styles)) {
  315. each(styles, function (stl) {
  316. return extend(finalStl, stl);
  317. });
  318. } else {
  319. extend(finalStl, styles);
  320. }
  321. var styleName = this._generateStyleName();
  322. this.richTextStyles[styleName] = finalStl;
  323. return "{" + styleName + "|" + text + "}";
  324. };
  325. return TooltipMarkupStyleCreator;
  326. }();
  327. export { TooltipMarkupStyleCreator };