ChartUtilities.js 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276
  1. /* *
  2. *
  3. * (c) 2009-2021 Øystein Moseng
  4. *
  5. * Utils for dealing with charts.
  6. *
  7. * License: www.highcharts.com/license
  8. *
  9. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  10. *
  11. * */
  12. 'use strict';
  13. import HTMLUtilities from './HTMLUtilities.js';
  14. var stripHTMLTags = HTMLUtilities.stripHTMLTagsFromString;
  15. import U from '../../Core/Utilities.js';
  16. var defined = U.defined, find = U.find, fireEvent = U.fireEvent;
  17. /* eslint-disable valid-jsdoc */
  18. /**
  19. * @return {string}
  20. */
  21. function getChartTitle(chart) {
  22. return stripHTMLTags(chart.options.title.text ||
  23. chart.langFormat('accessibility.defaultChartTitle', { chart: chart }));
  24. }
  25. /**
  26. * Return string with the axis name/title.
  27. * @param {Highcharts.Axis} axis
  28. * @return {string}
  29. */
  30. function getAxisDescription(axis) {
  31. return axis && (axis.userOptions && axis.userOptions.accessibility &&
  32. axis.userOptions.accessibility.description ||
  33. axis.axisTitle && axis.axisTitle.textStr ||
  34. axis.options.id ||
  35. axis.categories && 'categories' ||
  36. axis.dateTime && 'Time' ||
  37. 'values');
  38. }
  39. /**
  40. * Return string with text description of the axis range.
  41. * @param {Highcharts.Axis} axis The axis to get range desc of.
  42. * @return {string} A string with the range description for the axis.
  43. */
  44. function getAxisRangeDescription(axis) {
  45. var axisOptions = axis.options || {};
  46. // Handle overridden range description
  47. if (axisOptions.accessibility &&
  48. typeof axisOptions.accessibility.rangeDescription !== 'undefined') {
  49. return axisOptions.accessibility.rangeDescription;
  50. }
  51. // Handle category axes
  52. if (axis.categories) {
  53. return getCategoryAxisRangeDesc(axis);
  54. }
  55. // Use time range, not from-to?
  56. if (axis.dateTime && (axis.min === 0 || axis.dataMin === 0)) {
  57. return getAxisTimeLengthDesc(axis);
  58. }
  59. // Just use from and to.
  60. // We have the range and the unit to use, find the desc format
  61. return getAxisFromToDescription(axis);
  62. }
  63. /**
  64. * Describe the range of a category axis.
  65. * @param {Highcharts.Axis} axis
  66. * @return {string}
  67. */
  68. function getCategoryAxisRangeDesc(axis) {
  69. var chart = axis.chart;
  70. if (axis.dataMax && axis.dataMin) {
  71. return chart.langFormat('accessibility.axis.rangeCategories', {
  72. chart: chart,
  73. axis: axis,
  74. numCategories: axis.dataMax - axis.dataMin + 1
  75. });
  76. }
  77. return '';
  78. }
  79. /**
  80. * Describe the length of the time window shown on an axis.
  81. * @param {Highcharts.Axis} axis
  82. * @return {string}
  83. */
  84. function getAxisTimeLengthDesc(axis) {
  85. var chart = axis.chart;
  86. var range = {};
  87. var rangeUnit = 'Seconds';
  88. range.Seconds = ((axis.max || 0) - (axis.min || 0)) / 1000;
  89. range.Minutes = range.Seconds / 60;
  90. range.Hours = range.Minutes / 60;
  91. range.Days = range.Hours / 24;
  92. ['Minutes', 'Hours', 'Days'].forEach(function (unit) {
  93. if (range[unit] > 2) {
  94. rangeUnit = unit;
  95. }
  96. });
  97. var rangeValue = range[rangeUnit].toFixed(rangeUnit !== 'Seconds' &&
  98. rangeUnit !== 'Minutes' ? 1 : 0 // Use decimals for days/hours
  99. );
  100. // We have the range and the unit to use, find the desc format
  101. return chart.langFormat('accessibility.axis.timeRange' + rangeUnit, {
  102. chart: chart,
  103. axis: axis,
  104. range: rangeValue.replace('.0', '')
  105. });
  106. }
  107. /**
  108. * Describe an axis from-to range.
  109. * @param {Highcharts.Axis} axis
  110. * @return {string}
  111. */
  112. function getAxisFromToDescription(axis) {
  113. var _a, _b;
  114. var chart = axis.chart;
  115. var dateRangeFormat = ((_b = (_a = chart.options) === null || _a === void 0 ? void 0 : _a.accessibility) === null || _b === void 0 ? void 0 : _b.screenReaderSection.axisRangeDateFormat) || '';
  116. var format = function (axisKey) {
  117. return axis.dateTime ? chart.time.dateFormat(dateRangeFormat, axis[axisKey]) : axis[axisKey];
  118. };
  119. return chart.langFormat('accessibility.axis.rangeFromTo', {
  120. chart: chart,
  121. axis: axis,
  122. rangeFrom: format('min'),
  123. rangeTo: format('max')
  124. });
  125. }
  126. /**
  127. * Get the DOM element for the first point in the series.
  128. * @private
  129. * @param {Highcharts.Series} series
  130. * The series to get element for.
  131. * @return {Highcharts.HTMLDOMElement|Highcharts.SVGDOMElement|undefined}
  132. * The DOM element for the point.
  133. */
  134. function getSeriesFirstPointElement(series) {
  135. var _a, _b;
  136. if ((_a = series.points) === null || _a === void 0 ? void 0 : _a.length) {
  137. var firstPointWithGraphic = find(series.points, function (p) { return !!p.graphic; });
  138. return (_b = firstPointWithGraphic === null || firstPointWithGraphic === void 0 ? void 0 : firstPointWithGraphic.graphic) === null || _b === void 0 ? void 0 : _b.element;
  139. }
  140. }
  141. /**
  142. * Get the DOM element for the series that we put accessibility info on.
  143. * @private
  144. * @param {Highcharts.Series} series
  145. * The series to get element for.
  146. * @return {Highcharts.HTMLDOMElement|Highcharts.SVGDOMElement|undefined}
  147. * The DOM element for the series
  148. */
  149. function getSeriesA11yElement(series) {
  150. var firstPointEl = getSeriesFirstPointElement(series);
  151. return (firstPointEl &&
  152. firstPointEl.parentNode || series.graph &&
  153. series.graph.element || series.group &&
  154. series.group.element); // Could be tracker series depending on series type
  155. }
  156. /**
  157. * Remove aria-hidden from element. Also unhides parents of the element, and
  158. * hides siblings that are not explicitly unhidden.
  159. * @private
  160. * @param {Highcharts.Chart} chart
  161. * @param {Highcharts.HTMLDOMElement|Highcharts.SVGDOMElement} element
  162. */
  163. function unhideChartElementFromAT(chart, element) {
  164. element.setAttribute('aria-hidden', false);
  165. if (element === chart.renderTo || !element.parentNode) {
  166. return;
  167. }
  168. // Hide siblings unless their hidden state is already explicitly set
  169. Array.prototype.forEach.call(element.parentNode.childNodes, function (node) {
  170. if (!node.hasAttribute('aria-hidden')) {
  171. node.setAttribute('aria-hidden', true);
  172. }
  173. });
  174. // Repeat for parent
  175. unhideChartElementFromAT(chart, element.parentNode);
  176. }
  177. /**
  178. * Hide series from screen readers.
  179. * @private
  180. * @param {Highcharts.Series} series
  181. * The series to hide
  182. * @return {void}
  183. */
  184. function hideSeriesFromAT(series) {
  185. var seriesEl = getSeriesA11yElement(series);
  186. if (seriesEl) {
  187. seriesEl.setAttribute('aria-hidden', true);
  188. }
  189. }
  190. /**
  191. * Get series objects by series name.
  192. * @private
  193. * @param {Highcharts.Chart} chart
  194. * @param {string} name
  195. * @return {Array<Highcharts.Series>}
  196. */
  197. function getSeriesFromName(chart, name) {
  198. if (!name) {
  199. return chart.series;
  200. }
  201. return (chart.series || []).filter(function (s) {
  202. return s.name === name;
  203. });
  204. }
  205. /**
  206. * Get point in a series from x/y values.
  207. * @private
  208. * @param {Array<Highcharts.Series>} series
  209. * @param {number} x
  210. * @param {number} y
  211. * @return {Highcharts.Point|undefined}
  212. */
  213. function getPointFromXY(series, x, y) {
  214. var i = series.length, res;
  215. while (i--) {
  216. res = find(series[i].points || [], function (p) {
  217. return p.x === x && p.y === y;
  218. });
  219. if (res) {
  220. return res;
  221. }
  222. }
  223. }
  224. /**
  225. * Get relative position of point on an x/y axis from 0 to 1.
  226. * @private
  227. * @param {Highcharts.Axis} axis
  228. * @param {Highcharts.Point} point
  229. * @return {number}
  230. */
  231. function getRelativePointAxisPosition(axis, point) {
  232. if (!defined(axis.dataMin) || !defined(axis.dataMax)) {
  233. return 0;
  234. }
  235. var axisStart = axis.toPixels(axis.dataMin);
  236. var axisEnd = axis.toPixels(axis.dataMax);
  237. // We have to use pixel position because of axis breaks, log axis etc.
  238. var positionProp = axis.coll === 'xAxis' ? 'x' : 'y';
  239. var pointPos = axis.toPixels(point[positionProp] || 0);
  240. return (pointPos - axisStart) / (axisEnd - axisStart);
  241. }
  242. /**
  243. * Get relative position of point on an x/y axis from 0 to 1.
  244. * @private
  245. * @param {Highcharts.Point} point
  246. */
  247. function scrollToPoint(point) {
  248. var xAxis = point.series.xAxis;
  249. var yAxis = point.series.yAxis;
  250. var axis = (xAxis === null || xAxis === void 0 ? void 0 : xAxis.scrollbar) ? xAxis : yAxis;
  251. var scrollbar = axis === null || axis === void 0 ? void 0 : axis.scrollbar;
  252. if (scrollbar && defined(scrollbar.to) && defined(scrollbar.from)) {
  253. var range = scrollbar.to - scrollbar.from;
  254. var pos = getRelativePointAxisPosition(axis, point);
  255. scrollbar.updatePosition(pos - range / 2, pos + range / 2);
  256. fireEvent(scrollbar, 'changed', {
  257. from: scrollbar.from,
  258. to: scrollbar.to,
  259. trigger: 'scrollbar',
  260. DOMEvent: null
  261. });
  262. }
  263. }
  264. var ChartUtilities = {
  265. getChartTitle: getChartTitle,
  266. getAxisDescription: getAxisDescription,
  267. getAxisRangeDescription: getAxisRangeDescription,
  268. getPointFromXY: getPointFromXY,
  269. getSeriesFirstPointElement: getSeriesFirstPointElement,
  270. getSeriesFromName: getSeriesFromName,
  271. getSeriesA11yElement: getSeriesA11yElement,
  272. unhideChartElementFromAT: unhideChartElementFromAT,
  273. hideSeriesFromAT: hideSeriesFromAT,
  274. scrollToPoint: scrollToPoint
  275. };
  276. export default ChartUtilities;