grid-axis.src.js 44 KB

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