grid-axis.src.js 47 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055
  1. /**
  2. * @license Highcharts Gantt JS v9.1.0 (2021-05-04)
  3. *
  4. * GridAxis
  5. *
  6. * (c) 2016-2021 Lars A. V. Cabrera
  7. *
  8. * License: www.highcharts.com/license
  9. */
  10. 'use strict';
  11. (function (factory) {
  12. if (typeof module === 'object' && module.exports) {
  13. factory['default'] = factory;
  14. module.exports = factory;
  15. } else if (typeof define === 'function' && define.amd) {
  16. define('highcharts/modules/grid-axis', ['highcharts'], function (Highcharts) {
  17. factory(Highcharts);
  18. factory.Highcharts = Highcharts;
  19. return factory;
  20. });
  21. } else {
  22. factory(typeof Highcharts !== 'undefined' ? Highcharts : undefined);
  23. }
  24. }(function (Highcharts) {
  25. var _modules = Highcharts ? Highcharts._modules : {};
  26. function _registerModule(obj, path, args, fn) {
  27. if (!obj.hasOwnProperty(path)) {
  28. obj[path] = fn.apply(null, args);
  29. }
  30. }
  31. _registerModule(_modules, 'Core/Axis/GridAxis.js', [_modules['Core/Axis/Axis.js'], _modules['Core/Globals.js'], _modules['Core/Axis/Tick.js'], _modules['Core/Utilities.js']], function (Axis, H, Tick, U) {
  32. /* *
  33. *
  34. * (c) 2016 Highsoft AS
  35. * Authors: Lars A. V. Cabrera
  36. *
  37. * License: www.highcharts.com/license
  38. *
  39. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  40. *
  41. * */
  42. var addEvent = U.addEvent,
  43. defined = U.defined,
  44. erase = U.erase,
  45. find = U.find,
  46. isArray = U.isArray,
  47. isNumber = U.isNumber,
  48. merge = U.merge,
  49. pick = U.pick,
  50. timeUnits = U.timeUnits,
  51. wrap = U.wrap;
  52. var argsToArray = function (args) {
  53. return Array.prototype.slice.call(args, 1);
  54. }, isObject = function (x) {
  55. // Always use strict mode
  56. return U.isObject(x, true);
  57. }, Chart = H.Chart;
  58. var applyGridOptions = function applyGridOptions(axis) {
  59. var options = axis.options;
  60. // Center-align by default
  61. /*
  62. if (!options.labels) {
  63. options.labels = {};
  64. }
  65. */
  66. options.labels.align = pick(options.labels.align, 'center');
  67. // @todo: Check against tickLabelPlacement between/on etc
  68. /* Prevents adding the last tick label if the axis is not a category
  69. axis.
  70. Since numeric labels are normally placed at starts and ends of a
  71. range of value, and this module makes the label point at the value,
  72. an "extra" label would appear. */
  73. if (!axis.categories) {
  74. options.showLastLabel = false;
  75. }
  76. // Prevents rotation of labels when squished, as rotating them would not
  77. // help.
  78. axis.labelRotation = 0;
  79. options.labels.rotation = 0;
  80. };
  81. /**
  82. * @productdesc {gantt}
  83. * For grid axes (like in Gantt charts),
  84. * it is possible to declare as a list to provide different
  85. * formats depending on available space.
  86. *
  87. * Defaults to:
  88. * ```js
  89. * {
  90. * hour: { list: ['%H:%M', '%H'] },
  91. * day: { list: ['%A, %e. %B', '%a, %e. %b', '%E'] },
  92. * week: { list: ['Week %W', 'W%W'] },
  93. * month: { list: ['%B', '%b', '%o'] }
  94. * }
  95. * ```
  96. *
  97. * @sample {gantt} gantt/grid-axis/date-time-label-formats
  98. * Gantt chart with custom axis date format.
  99. *
  100. * @apioption xAxis.dateTimeLabelFormats
  101. */
  102. /**
  103. * Set grid options for the axis labels. Requires Highcharts Gantt.
  104. *
  105. * @since 6.2.0
  106. * @product gantt
  107. * @apioption xAxis.grid
  108. */
  109. /**
  110. * Enable grid on the axis labels. Defaults to true for Gantt charts.
  111. *
  112. * @type {boolean}
  113. * @default true
  114. * @since 6.2.0
  115. * @product gantt
  116. * @apioption xAxis.grid.enabled
  117. */
  118. /**
  119. * Set specific options for each column (or row for horizontal axes) in the
  120. * grid. Each extra column/row is its own axis, and the axis options can be set
  121. * here.
  122. *
  123. * @sample gantt/demo/left-axis-table
  124. * Left axis as a table
  125. *
  126. * @type {Array<Highcharts.XAxisOptions>}
  127. * @apioption xAxis.grid.columns
  128. */
  129. /**
  130. * Set border color for the label grid lines.
  131. *
  132. * @type {Highcharts.ColorString}
  133. * @apioption xAxis.grid.borderColor
  134. */
  135. /**
  136. * Set border width of the label grid lines.
  137. *
  138. * @type {number}
  139. * @default 1
  140. * @apioption xAxis.grid.borderWidth
  141. */
  142. /**
  143. * Set cell height for grid axis labels. By default this is calculated from font
  144. * size. This option only applies to horizontal axes.
  145. *
  146. * @sample gantt/grid-axis/cellheight
  147. * Gant chart with custom cell height
  148. * @type {number}
  149. * @apioption xAxis.grid.cellHeight
  150. */
  151. ''; // detach doclets above
  152. /**
  153. * Get the largest label width and height.
  154. *
  155. * @private
  156. * @function Highcharts.Axis#getMaxLabelDimensions
  157. *
  158. * @param {Highcharts.Dictionary<Highcharts.Tick>} ticks
  159. * All the ticks on one axis.
  160. *
  161. * @param {Array<number|string>} tickPositions
  162. * All the tick positions on one axis.
  163. *
  164. * @return {Highcharts.SizeObject}
  165. * Object containing the properties height and width.
  166. *
  167. * @todo Move this to the generic axis implementation, as it is used there.
  168. */
  169. Axis.prototype.getMaxLabelDimensions = function (ticks, tickPositions) {
  170. var dimensions = {
  171. width: 0,
  172. height: 0
  173. };
  174. tickPositions.forEach(function (pos) {
  175. var tick = ticks[pos],
  176. labelHeight = 0,
  177. labelWidth = 0,
  178. label;
  179. if (isObject(tick)) {
  180. label = isObject(tick.label) ? tick.label : {};
  181. // Find width and height of label
  182. labelHeight = label.getBBox ? label.getBBox().height : 0;
  183. if (label.textStr && !isNumber(label.textPxLength)) {
  184. label.textPxLength = label.getBBox().width;
  185. }
  186. labelWidth = isNumber(label.textPxLength) ?
  187. // Math.round ensures crisp lines
  188. Math.round(label.textPxLength) :
  189. 0;
  190. if (label.textStr) {
  191. // Set the tickWidth same as the label width after ellipsis
  192. // applied #10281
  193. labelWidth = Math.round(label.getBBox().width);
  194. }
  195. // Update the result if width and/or height are larger
  196. dimensions.height = Math.max(labelHeight, dimensions.height);
  197. dimensions.width = Math.max(labelWidth, dimensions.width);
  198. }
  199. });
  200. return dimensions;
  201. };
  202. // Adds week date format
  203. H.dateFormats.W = function (timestamp) {
  204. var d = new this.Date(timestamp);
  205. var firstDay = (this.get('Day',
  206. d) + 6) % 7;
  207. var thursday = new this.Date(d.valueOf());
  208. this.set('Date', thursday, this.get('Date', d) - firstDay + 3);
  209. var firstThursday = new this.Date(this.get('FullYear',
  210. thursday), 0, 1);
  211. if (this.get('Day', firstThursday) !== 4) {
  212. this.set('Month', d, 0);
  213. this.set('Date', d, 1 + (11 - this.get('Day', firstThursday)) % 7);
  214. }
  215. return (1 +
  216. Math.floor((thursday.valueOf() - firstThursday.valueOf()) / 604800000)).toString();
  217. };
  218. // First letter of the day of the week, e.g. 'M' for 'Monday'.
  219. H.dateFormats.E = function (timestamp) {
  220. return this.dateFormat('%a', timestamp, true).charAt(0);
  221. };
  222. /* eslint-disable no-invalid-this */
  223. addEvent(Chart, 'afterSetChartSize', function () {
  224. this.axes.forEach(function (axis) {
  225. (axis.grid && axis.grid.columns || []).forEach(function (column) {
  226. column.setAxisSize();
  227. column.setAxisTranslation();
  228. });
  229. });
  230. });
  231. // Center tick labels in cells.
  232. addEvent(Tick, 'afterGetLabelPosition', function (e) {
  233. var tick = this,
  234. label = tick.label,
  235. axis = tick.axis,
  236. reversed = axis.reversed,
  237. chart = axis.chart,
  238. options = axis.options,
  239. gridOptions = options.grid || {},
  240. labelOpts = axis.options.labels,
  241. align = labelOpts.align,
  242. // verticalAlign is currently not supported for axis.labels.
  243. verticalAlign = 'middle', // labelOpts.verticalAlign,
  244. side = GridAxis.Side[axis.side],
  245. tickmarkOffset = e.tickmarkOffset,
  246. tickPositions = axis.tickPositions,
  247. tickPos = tick.pos - tickmarkOffset,
  248. nextTickPos = (isNumber(tickPositions[e.index + 1]) ?
  249. tickPositions[e.index + 1] - tickmarkOffset :
  250. axis.max + tickmarkOffset),
  251. tickSize = axis.tickSize('tick'),
  252. tickWidth = tickSize ? tickSize[0] : 0,
  253. crispCorr = tickSize ? tickSize[1] / 2 : 0,
  254. labelHeight,
  255. lblMetrics,
  256. lines,
  257. bottom,
  258. top,
  259. left,
  260. right;
  261. // Only center tick labels in grid axes
  262. if (gridOptions.enabled === true) {
  263. // Calculate top and bottom positions of the cell.
  264. if (side === 'top') {
  265. bottom = axis.top + axis.offset;
  266. top = bottom - tickWidth;
  267. }
  268. else if (side === 'bottom') {
  269. top = chart.chartHeight - axis.bottom + axis.offset;
  270. bottom = top + tickWidth;
  271. }
  272. else {
  273. bottom = axis.top + axis.len - axis.translate(reversed ? nextTickPos : tickPos);
  274. top = axis.top + axis.len - axis.translate(reversed ? tickPos : nextTickPos);
  275. }
  276. // Calculate left and right positions of the cell.
  277. if (side === 'right') {
  278. left = chart.chartWidth - axis.right + axis.offset;
  279. right = left + tickWidth;
  280. }
  281. else if (side === 'left') {
  282. right = axis.left + axis.offset;
  283. left = right - tickWidth;
  284. }
  285. else {
  286. left = Math.round(axis.left + axis.translate(reversed ? nextTickPos : tickPos)) - crispCorr;
  287. right = Math.round(axis.left + axis.translate(reversed ? tickPos : nextTickPos)) - crispCorr;
  288. }
  289. tick.slotWidth = right - left;
  290. // Calculate the positioning of the label based on
  291. // alignment.
  292. e.pos.x = (align === 'left' ?
  293. left :
  294. align === 'right' ?
  295. right :
  296. left + ((right - left) / 2) // default to center
  297. );
  298. e.pos.y = (verticalAlign === 'top' ?
  299. top :
  300. verticalAlign === 'bottom' ?
  301. bottom :
  302. top + ((bottom - top) / 2) // default to middle
  303. );
  304. lblMetrics = chart.renderer.fontMetrics(labelOpts.style.fontSize, label.element);
  305. labelHeight = label.getBBox().height;
  306. // Adjustment to y position to align the label correctly.
  307. // Would be better to have a setter or similar for this.
  308. if (!labelOpts.useHTML) {
  309. lines = Math.round(labelHeight / lblMetrics.h);
  310. e.pos.y += (
  311. // Center the label
  312. // TODO: why does this actually center the label?
  313. ((lblMetrics.b - (lblMetrics.h - lblMetrics.f)) / 2) +
  314. // Adjust for height of additional lines.
  315. -(((lines - 1) * lblMetrics.h) / 2));
  316. }
  317. else {
  318. e.pos.y += (
  319. // Readjust yCorr in htmlUpdateTransform
  320. lblMetrics.b +
  321. // Adjust for height of html label
  322. -(labelHeight / 2));
  323. }
  324. e.pos.x += (axis.horiz && labelOpts.x || 0);
  325. }
  326. });
  327. addEvent(Tick, 'labelFormat', function (ctx) {
  328. var axis = ctx.axis,
  329. value = ctx.value;
  330. if (axis.options.grid &&
  331. axis.options.grid.enabled) {
  332. var tickPos = axis.tickPositions;
  333. var series = (axis.linkedParent || axis).series[0];
  334. var isFirst = value === tickPos[0];
  335. var isLast = value === tickPos[tickPos.length - 1];
  336. var point = series && find(series.options.data,
  337. function (p) {
  338. return p[axis.isXAxis ? 'x' : 'y'] === value;
  339. });
  340. var pointCopy = void 0;
  341. if (point && series.is('gantt')) {
  342. // For the Gantt set point aliases to the pointCopy
  343. // to do not change the original point
  344. pointCopy = merge(point);
  345. H.seriesTypes.gantt.prototype.pointClass
  346. .setGanttPointAliases(pointCopy);
  347. }
  348. // Make additional properties available for the
  349. // formatter
  350. ctx.isFirst = isFirst;
  351. ctx.isLast = isLast;
  352. ctx.point = pointCopy;
  353. }
  354. });
  355. /* eslint-enable no-invalid-this */
  356. /**
  357. * Additions for grid axes.
  358. * @private
  359. * @class
  360. */
  361. var GridAxisAdditions = /** @class */ (function () {
  362. /* *
  363. *
  364. * Constructors
  365. *
  366. * */
  367. function GridAxisAdditions(axis) {
  368. this.axis = axis;
  369. }
  370. /* *
  371. *
  372. * Functions
  373. *
  374. * */
  375. /**
  376. * Checks if an axis is the outer axis in its dimension. Since
  377. * axes are placed outwards in order, the axis with the highest
  378. * index is the outermost axis.
  379. *
  380. * Example: If there are multiple x-axes at the top of the chart,
  381. * this function returns true if the axis supplied is the last
  382. * of the x-axes.
  383. *
  384. * @private
  385. *
  386. * @return {boolean}
  387. * True if the axis is the outermost axis in its dimension; false if
  388. * not.
  389. */
  390. GridAxisAdditions.prototype.isOuterAxis = function () {
  391. var axis = this.axis;
  392. var chart = axis.chart;
  393. var columnIndex = axis.grid.columnIndex;
  394. var columns = (axis.linkedParent && axis.linkedParent.grid.columns ||
  395. axis.grid.columns);
  396. var parentAxis = columnIndex ? axis.linkedParent : axis;
  397. var thisIndex = -1,
  398. lastIndex = 0;
  399. chart[axis.coll].forEach(function (otherAxis, index) {
  400. if (otherAxis.side === axis.side && !otherAxis.options.isInternal) {
  401. lastIndex = index;
  402. if (otherAxis === parentAxis) {
  403. // Get the index of the axis in question
  404. thisIndex = index;
  405. }
  406. }
  407. });
  408. return (lastIndex === thisIndex &&
  409. (isNumber(columnIndex) ? columns.length === columnIndex : true));
  410. };
  411. /**
  412. * Add extra border based on the provided path.
  413. * *
  414. * @private
  415. *
  416. * @param {SVGPath} path
  417. * The path of the border.
  418. *
  419. * @return {Highcharts.SVGElement}
  420. */
  421. GridAxisAdditions.prototype.renderBorder = function (path) {
  422. var axis = this.axis,
  423. renderer = axis.chart.renderer,
  424. options = axis.options,
  425. extraBorderLine = renderer.path(path)
  426. .addClass('highcharts-axis-line')
  427. .add(axis.axisBorder);
  428. if (!renderer.styledMode) {
  429. extraBorderLine.attr({
  430. stroke: options.lineColor,
  431. 'stroke-width': options.lineWidth,
  432. zIndex: 7
  433. });
  434. }
  435. return extraBorderLine;
  436. };
  437. return GridAxisAdditions;
  438. }());
  439. /**
  440. * Axis with grid support.
  441. * @private
  442. * @class
  443. */
  444. var GridAxis = /** @class */ (function () {
  445. function GridAxis() {
  446. }
  447. /* *
  448. *
  449. * Static Functions
  450. *
  451. * */
  452. /* eslint-disable valid-jsdoc */
  453. /**
  454. * Extends axis class with grid support.
  455. * @private
  456. */
  457. GridAxis.compose = function (AxisClass) {
  458. Axis.keepProps.push('grid');
  459. wrap(AxisClass.prototype, 'unsquish', GridAxis.wrapUnsquish);
  460. // Add event handlers
  461. addEvent(AxisClass, 'init', GridAxis.onInit);
  462. addEvent(AxisClass, 'afterGetOffset', GridAxis.onAfterGetOffset);
  463. addEvent(AxisClass, 'afterGetTitlePosition', GridAxis.onAfterGetTitlePosition);
  464. addEvent(AxisClass, 'afterInit', GridAxis.onAfterInit);
  465. addEvent(AxisClass, 'afterRender', GridAxis.onAfterRender);
  466. addEvent(AxisClass, 'afterSetAxisTranslation', GridAxis.onAfterSetAxisTranslation);
  467. addEvent(AxisClass, 'afterSetOptions', GridAxis.onAfterSetOptions);
  468. addEvent(AxisClass, 'afterSetOptions', GridAxis.onAfterSetOptions2);
  469. addEvent(AxisClass, 'afterSetScale', GridAxis.onAfterSetScale);
  470. addEvent(AxisClass, 'afterTickSize', GridAxis.onAfterTickSize);
  471. addEvent(AxisClass, 'trimTicks', GridAxis.onTrimTicks);
  472. addEvent(AxisClass, 'destroy', GridAxis.onDestroy);
  473. };
  474. /**
  475. * Handle columns and getOffset.
  476. * @private
  477. */
  478. GridAxis.onAfterGetOffset = function () {
  479. var grid = this.grid;
  480. (grid && grid.columns || []).forEach(function (column) {
  481. column.getOffset();
  482. });
  483. };
  484. /**
  485. * @private
  486. */
  487. GridAxis.onAfterGetTitlePosition = function (e) {
  488. var axis = this;
  489. var options = axis.options;
  490. var gridOptions = options.grid || {};
  491. if (gridOptions.enabled === true) {
  492. // compute anchor points for each of the title align options
  493. var axisTitle = axis.axisTitle,
  494. axisHeight = axis.height,
  495. horiz = axis.horiz,
  496. axisLeft = axis.left,
  497. offset = axis.offset,
  498. opposite = axis.opposite,
  499. options_1 = axis.options,
  500. axisTop = axis.top,
  501. axisWidth = axis.width;
  502. var tickSize = axis.tickSize();
  503. var titleWidth = axisTitle && axisTitle.getBBox().width;
  504. var xOption = options_1.title.x;
  505. var yOption = options_1.title.y;
  506. var titleMargin = pick(options_1.title.margin,
  507. horiz ? 5 : 10);
  508. var titleFontSize = axis.chart.renderer.fontMetrics(options_1.title.style.fontSize,
  509. axisTitle).f;
  510. var crispCorr = tickSize ? tickSize[0] / 2 : 0;
  511. // TODO account for alignment
  512. // the position in the perpendicular direction of the axis
  513. var offAxis = ((horiz ? axisTop + axisHeight : axisLeft) +
  514. (horiz ? 1 : -1) * // horizontal axis reverses the margin
  515. (opposite ? -1 : 1) * // so does opposite axes
  516. crispCorr +
  517. (axis.side === GridAxis.Side.bottom ? titleFontSize : 0));
  518. e.titlePosition.x = horiz ?
  519. axisLeft - (titleWidth || 0) / 2 - titleMargin + xOption :
  520. offAxis + (opposite ? axisWidth : 0) + offset + xOption;
  521. e.titlePosition.y = horiz ?
  522. (offAxis -
  523. (opposite ? axisHeight : 0) +
  524. (opposite ? titleFontSize : -titleFontSize) / 2 +
  525. offset +
  526. yOption) :
  527. axisTop - titleMargin + yOption;
  528. }
  529. };
  530. /**
  531. * @private
  532. */
  533. GridAxis.onAfterInit = function () {
  534. var axis = this;
  535. var chart = axis.chart,
  536. _a = axis.options.grid,
  537. gridOptions = _a === void 0 ? {} : _a,
  538. userOptions = axis.userOptions;
  539. if (gridOptions.enabled) {
  540. applyGridOptions(axis);
  541. }
  542. if (gridOptions.columns) {
  543. var columns = axis.grid.columns = [],
  544. columnIndex = axis.grid.columnIndex = 0;
  545. // Handle columns, each column is a grid axis
  546. while (++columnIndex < gridOptions.columns.length) {
  547. var columnOptions = merge(userOptions,
  548. gridOptions.columns[gridOptions.columns.length - columnIndex - 1], {
  549. linkedTo: 0,
  550. // Force to behave like category axis
  551. type: 'category',
  552. // Disable by default the scrollbar on the grid axis
  553. scrollbar: {
  554. enabled: false
  555. }
  556. });
  557. delete columnOptions.grid.columns; // Prevent recursion
  558. var column = new Axis(axis.chart,
  559. columnOptions);
  560. column.grid.isColumn = true;
  561. column.grid.columnIndex = columnIndex;
  562. // Remove column axis from chart axes array, and place it
  563. // in the columns array.
  564. erase(chart.axes, column);
  565. erase(chart[axis.coll], column);
  566. columns.push(column);
  567. }
  568. }
  569. };
  570. /**
  571. * Draw an extra line on the far side of the outermost axis,
  572. * creating floor/roof/wall of a grid. And some padding.
  573. * ```
  574. * Make this:
  575. * (axis.min) __________________________ (axis.max)
  576. * | | | | |
  577. * Into this:
  578. * (axis.min) __________________________ (axis.max)
  579. * ___|____|____|____|____|__
  580. * ```
  581. * @private
  582. */
  583. GridAxis.onAfterRender = function () {
  584. var axis = this,
  585. grid = axis.grid,
  586. options = axis.options,
  587. gridOptions = options.grid || {};
  588. if (gridOptions.enabled === true) {
  589. // @todo acutual label padding (top, bottom, left, right)
  590. axis.maxLabelDimensions = axis.getMaxLabelDimensions(axis.ticks, axis.tickPositions);
  591. // Remove right wall before rendering if updating
  592. if (axis.rightWall) {
  593. axis.rightWall.destroy();
  594. }
  595. /*
  596. Draw an extra axis line on outer axes
  597. >
  598. Make this: |______|______|______|___
  599. > _________________________
  600. Into this: |______|______|______|__|
  601. */
  602. if (axis.grid && axis.grid.isOuterAxis() && axis.axisLine) {
  603. var lineWidth = options.lineWidth;
  604. if (lineWidth) {
  605. var linePath = axis.getLinePath(lineWidth),
  606. startPoint = linePath[0],
  607. endPoint = linePath[1],
  608. // Negate distance if top or left axis
  609. // Subtract 1px to draw the line at the end of the tick
  610. tickLength = (axis.tickSize('tick') || [1])[0],
  611. distance = (tickLength - 1) * ((axis.side === GridAxis.Side.top ||
  612. axis.side === GridAxis.Side.left) ? -1 : 1);
  613. // If axis is horizontal, reposition line path vertically
  614. if (startPoint[0] === 'M' && endPoint[0] === 'L') {
  615. if (axis.horiz) {
  616. startPoint[2] += distance;
  617. endPoint[2] += distance;
  618. }
  619. else {
  620. startPoint[1] += distance;
  621. endPoint[1] += distance;
  622. }
  623. }
  624. // If it doesn't exist, add an upper and lower border
  625. // for the vertical grid axis.
  626. if (!axis.horiz && axis.chart.marginRight) {
  627. var upperBorderStartPoint = startPoint, upperBorderEndPoint = ['L', axis.left, startPoint[2]], upperBorderPath = [upperBorderStartPoint, upperBorderEndPoint], lowerBorderEndPoint = ['L', axis.chart.chartWidth - axis.chart.marginRight, axis.toPixels(axis.max + axis.tickmarkOffset)], lowerBorderStartPoint = ['M', endPoint[1], axis.toPixels(axis.max + axis.tickmarkOffset)], lowerBorderPath = [lowerBorderStartPoint, lowerBorderEndPoint];
  628. if (!axis.grid.upperBorder && axis.min % 1 !== 0) {
  629. axis.grid.upperBorder = axis.grid.renderBorder(upperBorderPath);
  630. }
  631. if (axis.grid.upperBorder) {
  632. axis.grid.upperBorder.animate({
  633. d: upperBorderPath
  634. });
  635. }
  636. if (!axis.grid.lowerBorder && axis.max % 1 !== 0) {
  637. axis.grid.lowerBorder = axis.grid.renderBorder(lowerBorderPath);
  638. }
  639. if (axis.grid.lowerBorder) {
  640. axis.grid.lowerBorder.animate({
  641. d: lowerBorderPath
  642. });
  643. }
  644. }
  645. // Render an extra line parallel to the existing axes,
  646. // to close the grid.
  647. if (!axis.grid.axisLineExtra) {
  648. axis.grid.axisLineExtra = axis.grid.renderBorder(linePath);
  649. }
  650. else {
  651. axis.grid.axisLineExtra.animate({
  652. d: linePath
  653. });
  654. }
  655. // show or hide the line depending on
  656. // options.showEmpty
  657. axis.axisLine[axis.showAxis ? 'show' : 'hide'](true);
  658. }
  659. }
  660. (grid && grid.columns || []).forEach(function (column) {
  661. column.render();
  662. });
  663. // Manipulate the tick mark visibility
  664. // based on the axis.max- allows smooth scrolling.
  665. if (!axis.horiz &&
  666. axis.chart.hasRendered &&
  667. (axis.scrollbar ||
  668. (axis.linkedParent && axis.linkedParent.scrollbar))) {
  669. var max = axis.max,
  670. min = axis.min,
  671. tickmarkOffset = axis.tickmarkOffset,
  672. lastTick = axis.tickPositions[axis.tickPositions.length - 1],
  673. firstTick = axis.tickPositions[0];
  674. // Hide/show firts tick label.
  675. if (min - firstTick > tickmarkOffset) {
  676. axis.ticks[firstTick].label.hide();
  677. }
  678. else {
  679. axis.ticks[firstTick].label.show();
  680. }
  681. // Hide/show last tick mark/label.
  682. if (lastTick - max > tickmarkOffset) {
  683. axis.ticks[lastTick].label.hide();
  684. }
  685. else {
  686. axis.ticks[lastTick].label.show();
  687. }
  688. if (lastTick - max < tickmarkOffset && lastTick - max > 0 && axis.ticks[lastTick].isLast) {
  689. axis.ticks[lastTick].mark.hide();
  690. }
  691. else if (axis.ticks[lastTick - 1]) {
  692. axis.ticks[lastTick - 1].mark.show();
  693. }
  694. }
  695. }
  696. };
  697. /**
  698. * @private
  699. */
  700. GridAxis.onAfterSetAxisTranslation = function () {
  701. var axis = this;
  702. var tickInfo = axis.tickPositions && axis.tickPositions.info;
  703. var options = axis.options;
  704. var gridOptions = options.grid || {};
  705. var userLabels = axis.userOptions.labels || {};
  706. // Fire this only for the Gantt type chart, #14868.
  707. if (gridOptions.enabled) {
  708. if (axis.horiz) {
  709. axis.series.forEach(function (series) {
  710. series.options.pointRange = 0;
  711. });
  712. // Lower level time ticks, like hours or minutes, represent
  713. // points in time and not ranges. These should be aligned
  714. // left in the grid cell by default. The same applies to
  715. // years of higher order.
  716. if (tickInfo &&
  717. options.dateTimeLabelFormats &&
  718. options.labels &&
  719. !defined(userLabels.align) &&
  720. (options.dateTimeLabelFormats[tickInfo.unitName].range === false ||
  721. tickInfo.count > 1 // years
  722. )) {
  723. options.labels.align = 'left';
  724. if (!defined(userLabels.x)) {
  725. options.labels.x = 3;
  726. }
  727. }
  728. }
  729. else {
  730. // Don't trim ticks which not in min/max range but
  731. // they are still in the min/max plus tickInterval.
  732. if (this.options.type !== 'treegrid' &&
  733. axis.grid &&
  734. axis.grid.columns) {
  735. this.minPointOffset = this.tickInterval;
  736. }
  737. }
  738. }
  739. };
  740. /**
  741. * Creates a left and right wall on horizontal axes:
  742. * - Places leftmost tick at the start of the axis, to create a left
  743. * wall
  744. * - Ensures that the rightmost tick is at the end of the axis, to
  745. * create a right wall.
  746. * @private
  747. */
  748. GridAxis.onAfterSetOptions = function (e) {
  749. var options = this.options,
  750. userOptions = e.userOptions,
  751. gridAxisOptions,
  752. gridOptions = ((options && isObject(options.grid)) ? options.grid : {});
  753. if (gridOptions.enabled === true) {
  754. // Merge the user options into default grid axis options so
  755. // that when a user option is set, it takes presedence.
  756. gridAxisOptions = merge(true, {
  757. className: ('highcharts-grid-axis ' + (userOptions.className || '')),
  758. dateTimeLabelFormats: {
  759. hour: {
  760. list: ['%H:%M', '%H']
  761. },
  762. day: {
  763. list: ['%A, %e. %B', '%a, %e. %b', '%E']
  764. },
  765. week: {
  766. list: ['Week %W', 'W%W']
  767. },
  768. month: {
  769. list: ['%B', '%b', '%o']
  770. }
  771. },
  772. grid: {
  773. borderWidth: 1
  774. },
  775. labels: {
  776. padding: 2,
  777. style: {
  778. fontSize: '13px'
  779. }
  780. },
  781. margin: 0,
  782. title: {
  783. text: null,
  784. reserveSpace: false,
  785. rotation: 0
  786. },
  787. // In a grid axis, only allow one unit of certain types,
  788. // for example we shouln't have one grid cell spanning
  789. // two days.
  790. units: [[
  791. 'millisecond',
  792. [1, 10, 100]
  793. ], [
  794. 'second',
  795. [1, 10]
  796. ], [
  797. 'minute',
  798. [1, 5, 15]
  799. ], [
  800. 'hour',
  801. [1, 6]
  802. ], [
  803. 'day',
  804. [1]
  805. ], [
  806. 'week',
  807. [1]
  808. ], [
  809. 'month',
  810. [1]
  811. ], [
  812. 'year',
  813. null
  814. ]]
  815. }, userOptions);
  816. // X-axis specific options
  817. if (this.coll === 'xAxis') {
  818. // For linked axes, tickPixelInterval is used only if
  819. // the tickPositioner below doesn't run or returns
  820. // undefined (like multiple years)
  821. if (defined(userOptions.linkedTo) &&
  822. !defined(userOptions.tickPixelInterval)) {
  823. gridAxisOptions.tickPixelInterval = 350;
  824. }
  825. // For the secondary grid axis, use the primary axis'
  826. // tick intervals and return ticks one level higher.
  827. if (
  828. // Check for tick pixel interval in options
  829. !defined(userOptions.tickPixelInterval) &&
  830. // Only for linked axes
  831. defined(userOptions.linkedTo) &&
  832. !defined(userOptions.tickPositioner) &&
  833. !defined(userOptions.tickInterval)) {
  834. gridAxisOptions.tickPositioner = function (min, max) {
  835. var parentInfo = (this.linkedParent &&
  836. this.linkedParent.tickPositions &&
  837. this.linkedParent.tickPositions.info);
  838. if (parentInfo) {
  839. var unitIdx = void 0,
  840. count = void 0,
  841. unitName = void 0,
  842. i = void 0,
  843. units = gridAxisOptions.units,
  844. unitRange = void 0;
  845. for (i = 0; i < units.length; i++) {
  846. if (units[i][0] ===
  847. parentInfo.unitName) {
  848. unitIdx = i;
  849. break;
  850. }
  851. }
  852. // Get the first allowed count on the next
  853. // unit.
  854. if (units[unitIdx + 1]) {
  855. unitName = units[unitIdx + 1][0];
  856. count =
  857. (units[unitIdx + 1][1] || [1])[0];
  858. // In case the base X axis shows years, make
  859. // the secondary axis show ten times the
  860. // years (#11427)
  861. }
  862. else if (parentInfo.unitName === 'year') {
  863. unitName = 'year';
  864. count = parentInfo.count * 10;
  865. }
  866. unitRange = timeUnits[unitName];
  867. this.tickInterval = unitRange * count;
  868. return this.getTimeTicks({
  869. unitRange: unitRange,
  870. count: count,
  871. unitName: unitName
  872. }, min, max, this.options.startOfWeek);
  873. }
  874. };
  875. }
  876. }
  877. // Now merge the combined options into the axis options
  878. merge(true, this.options, gridAxisOptions);
  879. if (this.horiz) {
  880. /* _________________________
  881. Make this: ___|_____|_____|_____|__|
  882. ^ ^
  883. _________________________
  884. Into this: |_____|_____|_____|_____|
  885. ^ ^ */
  886. options.minPadding = pick(userOptions.minPadding, 0);
  887. options.maxPadding = pick(userOptions.maxPadding, 0);
  888. }
  889. // If borderWidth is set, then use its value for tick and
  890. // line width.
  891. if (isNumber(options.grid.borderWidth)) {
  892. options.tickWidth = options.lineWidth =
  893. gridOptions.borderWidth;
  894. }
  895. }
  896. };
  897. /**
  898. * @private
  899. */
  900. GridAxis.onAfterSetOptions2 = function (e) {
  901. var axis = this;
  902. var userOptions = e.userOptions;
  903. var gridOptions = userOptions && userOptions.grid || {};
  904. var columns = gridOptions.columns;
  905. // Add column options to the parent axis. Children has their column
  906. // options set on init in onGridAxisAfterInit.
  907. if (gridOptions.enabled && columns) {
  908. merge(true, axis.options, columns[columns.length - 1]);
  909. }
  910. };
  911. /**
  912. * Handle columns and setScale.
  913. * @private
  914. */
  915. GridAxis.onAfterSetScale = function () {
  916. var axis = this;
  917. (axis.grid.columns || []).forEach(function (column) {
  918. column.setScale();
  919. });
  920. };
  921. /**
  922. * Draw vertical axis ticks extra long to create cell floors and roofs.
  923. * Overrides the tickLength for vertical axes.
  924. * @private
  925. */
  926. GridAxis.onAfterTickSize = function (e) {
  927. var defaultLeftAxisOptions = Axis.defaultLeftAxisOptions;
  928. var _a = this,
  929. horiz = _a.horiz,
  930. maxLabelDimensions = _a.maxLabelDimensions,
  931. _b = _a.options.grid,
  932. gridOptions = _b === void 0 ? {} : _b;
  933. if (gridOptions.enabled && maxLabelDimensions) {
  934. var labelPadding = (Math.abs(defaultLeftAxisOptions.labels.x) * 2);
  935. var distance = horiz ?
  936. gridOptions.cellHeight || labelPadding + maxLabelDimensions.height :
  937. labelPadding + maxLabelDimensions.width;
  938. if (isArray(e.tickSize)) {
  939. e.tickSize[0] = distance;
  940. }
  941. else {
  942. e.tickSize = [distance, 0];
  943. }
  944. }
  945. };
  946. /**
  947. * @private
  948. */
  949. GridAxis.onDestroy = function (e) {
  950. var grid = this.grid;
  951. (grid.columns || []).forEach(function (column) {
  952. column.destroy(e.keepEvents);
  953. });
  954. grid.columns = void 0;
  955. };
  956. /**
  957. * Wraps axis init to draw cell walls on vertical axes.
  958. * @private
  959. */
  960. GridAxis.onInit = function (e) {
  961. var axis = this;
  962. var userOptions = e.userOptions || {};
  963. var gridOptions = userOptions.grid || {};
  964. if (gridOptions.enabled && defined(gridOptions.borderColor)) {
  965. userOptions.tickColor = userOptions.lineColor = gridOptions.borderColor;
  966. }
  967. if (!axis.grid) {
  968. axis.grid = new GridAxisAdditions(axis);
  969. }
  970. };
  971. /**
  972. * Makes tick labels which are usually ignored in a linked axis
  973. * displayed if they are within range of linkedParent.min.
  974. * ```
  975. * _____________________________
  976. * | | | | |
  977. * Make this: | | 2 | 3 | 4 |
  978. * |___|_______|_______|_______|
  979. * ^
  980. * _____________________________
  981. * | | | | |
  982. * Into this: | 1 | 2 | 3 | 4 |
  983. * |___|_______|_______|_______|
  984. * ^
  985. * ```
  986. * @private
  987. * @todo Does this function do what the drawing says? Seems to affect
  988. * ticks and not the labels directly?
  989. */
  990. GridAxis.onTrimTicks = function () {
  991. var axis = this;
  992. var options = axis.options;
  993. var gridOptions = options.grid || {};
  994. var categoryAxis = axis.categories;
  995. var tickPositions = axis.tickPositions;
  996. var firstPos = tickPositions[0];
  997. var lastPos = tickPositions[tickPositions.length - 1];
  998. var linkedMin = axis.linkedParent && axis.linkedParent.min;
  999. var linkedMax = axis.linkedParent && axis.linkedParent.max;
  1000. var min = linkedMin || axis.min;
  1001. var max = linkedMax || axis.max;
  1002. var tickInterval = axis.tickInterval;
  1003. var endMoreThanMin = (firstPos < min &&
  1004. firstPos + tickInterval > min);
  1005. var startLessThanMax = (lastPos > max &&
  1006. lastPos - tickInterval < max);
  1007. if (gridOptions.enabled === true &&
  1008. !categoryAxis &&
  1009. (axis.horiz || axis.isLinked)) {
  1010. if (endMoreThanMin && !options.startOnTick) {
  1011. tickPositions[0] = min;
  1012. }
  1013. if (startLessThanMax && !options.endOnTick) {
  1014. tickPositions[tickPositions.length - 1] = max;
  1015. }
  1016. }
  1017. };
  1018. /**
  1019. * Avoid altering tickInterval when reserving space.
  1020. * @private
  1021. */
  1022. GridAxis.wrapUnsquish = function (proceed) {
  1023. var axis = this;
  1024. var _a = axis.options.grid,
  1025. gridOptions = _a === void 0 ? {} : _a;
  1026. if (gridOptions.enabled === true && axis.categories) {
  1027. return axis.tickInterval;
  1028. }
  1029. return proceed.apply(axis, argsToArray(arguments));
  1030. };
  1031. return GridAxis;
  1032. }());
  1033. (function (GridAxis) {
  1034. /**
  1035. * Enum for which side the axis is on. Maps to axis.side.
  1036. * @private
  1037. */
  1038. var Side;
  1039. (function (Side) {
  1040. Side[Side["top"] = 0] = "top";
  1041. Side[Side["right"] = 1] = "right";
  1042. Side[Side["bottom"] = 2] = "bottom";
  1043. Side[Side["left"] = 3] = "left";
  1044. })(Side = GridAxis.Side || (GridAxis.Side = {}));
  1045. })(GridAxis || (GridAxis = {}));
  1046. GridAxis.compose(Axis);
  1047. return GridAxis;
  1048. });
  1049. _registerModule(_modules, 'masters/modules/grid-axis.src.js', [], function () {
  1050. });
  1051. }));