grid-axis.src.js 51 KB

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