FlagsSeries.js 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648
  1. /* *
  2. *
  3. * (c) 2010-2020 Torstein Honsi
  4. *
  5. * License: www.highcharts.com/license
  6. *
  7. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  8. *
  9. * */
  10. 'use strict';
  11. import H from '../Core/Globals.js';
  12. import SVGElement from '../Core/Renderer/SVG/SVGElement.js';
  13. import SVGRenderer from '../Core/Renderer/SVG/SVGRenderer.js';
  14. import U from '../Core/Utilities.js';
  15. var addEvent = U.addEvent, defined = U.defined, isNumber = U.isNumber, merge = U.merge, objectEach = U.objectEach, seriesType = U.seriesType, wrap = U.wrap;
  16. /**
  17. * @typedef {"circlepin"|"flag"|"squarepin"} Highcharts.FlagsShapeValue
  18. */
  19. import '../Core/Series/Series.js';
  20. import '../Core/Renderer/SVG/SVGRenderer.js';
  21. import onSeriesMixin from '../Mixins/OnSeries.js';
  22. var noop = H.noop, Renderer = H.Renderer, Series = H.Series, TrackerMixin = H.TrackerMixin, VMLRenderer = H.VMLRenderer, symbols = SVGRenderer.prototype.symbols;
  23. /**
  24. * The Flags series.
  25. *
  26. * @private
  27. * @class
  28. * @name Highcharts.seriesTypes.flags
  29. *
  30. * @augments Highcharts.Series
  31. */
  32. seriesType('flags', 'column'
  33. /**
  34. * Flags are used to mark events in stock charts. They can be added on the
  35. * timeline, or attached to a specific series.
  36. *
  37. * @sample stock/demo/flags-general/
  38. * Flags on a line series
  39. *
  40. * @extends plotOptions.column
  41. * @excluding animation, borderColor, borderRadius, borderWidth,
  42. * colorByPoint, dataGrouping, pointPadding, pointWidth,
  43. * turboThreshold
  44. * @product highstock
  45. * @optionparent plotOptions.flags
  46. */
  47. , {
  48. /**
  49. * In case the flag is placed on a series, on what point key to place
  50. * it. Line and columns have one key, `y`. In range or OHLC-type series,
  51. * however, the flag can optionally be placed on the `open`, `high`,
  52. * `low` or `close` key.
  53. *
  54. * @sample {highstock} stock/plotoptions/flags-onkey/
  55. * Range series, flag on high
  56. *
  57. * @type {string}
  58. * @default y
  59. * @since 4.2.2
  60. * @product highstock
  61. * @validvalue ["y", "open", "high", "low", "close"]
  62. * @apioption plotOptions.flags.onKey
  63. */
  64. /**
  65. * The id of the series that the flags should be drawn on. If no id
  66. * is given, the flags are drawn on the x axis.
  67. *
  68. * @sample {highstock} stock/plotoptions/flags/
  69. * Flags on series and on x axis
  70. *
  71. * @type {string}
  72. * @product highstock
  73. * @apioption plotOptions.flags.onSeries
  74. */
  75. pointRange: 0,
  76. /**
  77. * Whether the flags are allowed to overlap sideways. If `false`, the
  78. * flags are moved sideways using an algorithm that seeks to place every
  79. * flag as close as possible to its original position.
  80. *
  81. * @sample {highstock} stock/plotoptions/flags-allowoverlapx
  82. * Allow sideways overlap
  83. *
  84. * @since 6.0.4
  85. */
  86. allowOverlapX: false,
  87. /**
  88. * The shape of the marker. Can be one of "flag", "circlepin",
  89. * "squarepin", or an image of the format `url(/path-to-image.jpg)`.
  90. * Individual shapes can also be set for each point.
  91. *
  92. * @sample {highstock} stock/plotoptions/flags/
  93. * Different shapes
  94. *
  95. * @type {Highcharts.FlagsShapeValue}
  96. * @product highstock
  97. */
  98. shape: 'flag',
  99. /**
  100. * When multiple flags in the same series fall on the same value, this
  101. * number determines the vertical offset between them.
  102. *
  103. * @sample {highstock} stock/plotoptions/flags-stackdistance/
  104. * A greater stack distance
  105. *
  106. * @product highstock
  107. */
  108. stackDistance: 12,
  109. /**
  110. * Text alignment for the text inside the flag.
  111. *
  112. * @since 5.0.0
  113. * @product highstock
  114. * @validvalue ["left", "center", "right"]
  115. */
  116. textAlign: 'center',
  117. /**
  118. * Specific tooltip options for flag series. Flag series tooltips are
  119. * different from most other types in that a flag doesn't have a data
  120. * value, so the tooltip rather displays the `text` option for each
  121. * point.
  122. *
  123. * @extends plotOptions.series.tooltip
  124. * @excluding changeDecimals, valueDecimals, valuePrefix, valueSuffix
  125. * @product highstock
  126. */
  127. tooltip: {
  128. pointFormat: '{point.text}<br/>'
  129. },
  130. threshold: null,
  131. /**
  132. * The text to display on each flag. This can be defined on series
  133. * level, or individually for each point. Defaults to `"A"`.
  134. *
  135. * @type {string}
  136. * @default A
  137. * @product highstock
  138. * @apioption plotOptions.flags.title
  139. */
  140. /**
  141. * The y position of the top left corner of the flag relative to either
  142. * the series (if onSeries is defined), or the x axis. Defaults to
  143. * `-30`.
  144. *
  145. * @product highstock
  146. */
  147. y: -30,
  148. /**
  149. * Whether to use HTML to render the flag texts. Using HTML allows for
  150. * advanced formatting, images and reliable bi-directional text
  151. * rendering. Note that exported images won't respect the HTML, and that
  152. * HTML won't respect Z-index settings.
  153. *
  154. * @type {boolean}
  155. * @default false
  156. * @since 1.3
  157. * @product highstock
  158. * @apioption plotOptions.flags.useHTML
  159. */
  160. /**
  161. * Fixed width of the flag's shape. By default, width is autocalculated
  162. * according to the flag's title.
  163. *
  164. * @sample {highstock} stock/demo/flags-shapes/
  165. * Flags with fixed width
  166. *
  167. * @type {number}
  168. * @product highstock
  169. * @apioption plotOptions.flags.width
  170. */
  171. /**
  172. * Fixed height of the flag's shape. By default, height is
  173. * autocalculated according to the flag's title.
  174. *
  175. * @type {number}
  176. * @product highstock
  177. * @apioption plotOptions.flags.height
  178. */
  179. /**
  180. * The fill color for the flags.
  181. *
  182. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  183. * @product highstock
  184. */
  185. fillColor: '#ffffff',
  186. /**
  187. * The color of the line/border of the flag.
  188. *
  189. * In styled mode, the stroke is set in the
  190. * `.highcharts-flag-series.highcharts-point` rule.
  191. *
  192. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  193. * @default #000000
  194. * @product highstock
  195. * @apioption plotOptions.flags.lineColor
  196. */
  197. /**
  198. * The pixel width of the flag's line/border.
  199. *
  200. * @product highstock
  201. */
  202. lineWidth: 1,
  203. states: {
  204. /**
  205. * @extends plotOptions.column.states.hover
  206. * @product highstock
  207. */
  208. hover: {
  209. /**
  210. * The color of the line/border of the flag.
  211. *
  212. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  213. * @product highstock
  214. */
  215. lineColor: '#000000',
  216. /**
  217. * The fill or background color of the flag.
  218. *
  219. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  220. * @product highstock
  221. */
  222. fillColor: '#ccd6eb'
  223. }
  224. },
  225. /**
  226. * The text styles of the flag.
  227. *
  228. * In styled mode, the styles are set in the
  229. * `.highcharts-flag-series .highcharts-point` rule.
  230. *
  231. * @type {Highcharts.CSSObject}
  232. * @default {"fontSize": "11px", "fontWeight": "bold"}
  233. * @product highstock
  234. */
  235. style: {
  236. /** @ignore-option */
  237. fontSize: '11px',
  238. /** @ignore-option */
  239. fontWeight: 'bold'
  240. }
  241. },
  242. /**
  243. * @lends seriesTypes.flags.prototype
  244. */
  245. {
  246. sorted: false,
  247. noSharedTooltip: true,
  248. allowDG: false,
  249. takeOrdinalPosition: false,
  250. trackerGroups: ['markerGroup'],
  251. forceCrop: true,
  252. /* eslint-disable no-invalid-this, valid-jsdoc */
  253. /**
  254. * Inherit the initialization from base Series.
  255. *
  256. * @private
  257. * @borrows Highcharts.Series#init as Highcharts.seriesTypes.flags#init
  258. */
  259. init: Series.prototype.init,
  260. /**
  261. * Get presentational attributes
  262. *
  263. * @private
  264. * @function Highcharts.seriesTypes.flags#pointAttribs
  265. *
  266. * @param {Highcharts.Point} point
  267. *
  268. * @param {string} [state]
  269. *
  270. * @return {Highcharts.SVGAttributes}
  271. */
  272. pointAttribs: function (point, state) {
  273. var options = this.options, color = (point && point.color) || this.color, lineColor = options.lineColor, lineWidth = (point && point.lineWidth), fill = (point && point.fillColor) || options.fillColor;
  274. if (state) {
  275. fill = options.states[state].fillColor;
  276. lineColor = options.states[state].lineColor;
  277. lineWidth = options.states[state].lineWidth;
  278. }
  279. return {
  280. fill: fill || color,
  281. stroke: lineColor || color,
  282. 'stroke-width': lineWidth || options.lineWidth || 0
  283. };
  284. },
  285. translate: onSeriesMixin.translate,
  286. getPlotBox: onSeriesMixin.getPlotBox,
  287. /**
  288. * Draw the markers.
  289. *
  290. * @private
  291. * @function Highcharts.seriesTypes.flags#drawPoints
  292. * @return {void}
  293. */
  294. drawPoints: function () {
  295. var series = this, points = series.points, chart = series.chart, renderer = chart.renderer, plotX, plotY, inverted = chart.inverted, options = series.options, optionsY = options.y, shape, i, point, graphic, stackIndex, anchorY, attribs, outsideRight, yAxis = series.yAxis, boxesMap = {}, boxes = [], centered;
  296. i = points.length;
  297. while (i--) {
  298. point = points[i];
  299. outsideRight =
  300. (inverted ? point.plotY : point.plotX) >
  301. series.xAxis.len;
  302. plotX = point.plotX;
  303. stackIndex = point.stackIndex;
  304. shape = point.options.shape || options.shape;
  305. plotY = point.plotY;
  306. if (typeof plotY !== 'undefined') {
  307. plotY = point.plotY + optionsY -
  308. (typeof stackIndex !== 'undefined' &&
  309. (stackIndex * options.stackDistance));
  310. }
  311. // skip connectors for higher level stacked points
  312. point.anchorX = stackIndex ? void 0 : point.plotX;
  313. anchorY = stackIndex ? void 0 : point.plotY;
  314. centered = shape !== 'flag';
  315. graphic = point.graphic;
  316. // Only draw the point if y is defined and the flag is within
  317. // the visible area
  318. if (typeof plotY !== 'undefined' &&
  319. plotX >= 0 &&
  320. !outsideRight) {
  321. // Create the flag
  322. if (!graphic) {
  323. graphic = point.graphic = renderer.label('', null, null, shape, null, null, options.useHTML);
  324. if (!chart.styledMode) {
  325. graphic
  326. .attr(series.pointAttribs(point))
  327. .css(merge(options.style, point.style));
  328. }
  329. graphic.attr({
  330. align: centered ? 'center' : 'left',
  331. width: options.width,
  332. height: options.height,
  333. 'text-align': options.textAlign
  334. })
  335. .addClass('highcharts-point')
  336. .add(series.markerGroup);
  337. // Add reference to the point for tracker (#6303)
  338. if (point.graphic.div) {
  339. point.graphic.div.point = point;
  340. }
  341. if (!chart.styledMode) {
  342. graphic.shadow(options.shadow);
  343. }
  344. graphic.isNew = true;
  345. }
  346. if (plotX > 0) { // #3119
  347. plotX -= graphic.strokeWidth() % 2; // #4285
  348. }
  349. // Plant the flag
  350. attribs = {
  351. y: plotY,
  352. anchorY: anchorY
  353. };
  354. if (options.allowOverlapX) {
  355. attribs.x = plotX;
  356. attribs.anchorX = point.anchorX;
  357. }
  358. graphic.attr({
  359. text: point.options.title || options.title || 'A'
  360. })[graphic.isNew ? 'attr' : 'animate'](attribs);
  361. // Rig for the distribute function
  362. if (!options.allowOverlapX) {
  363. if (!boxesMap[point.plotX]) {
  364. boxesMap[point.plotX] = {
  365. align: centered ? 0.5 : 0,
  366. size: graphic.width,
  367. target: plotX,
  368. anchorX: plotX
  369. };
  370. }
  371. else {
  372. boxesMap[point.plotX].size = Math.max(boxesMap[point.plotX].size, graphic.width);
  373. }
  374. }
  375. // Set the tooltip anchor position
  376. point.tooltipPos = [
  377. plotX,
  378. plotY + yAxis.pos - chart.plotTop
  379. ]; // #6327
  380. }
  381. else if (graphic) {
  382. point.graphic = graphic.destroy();
  383. }
  384. }
  385. // Handle X-dimension overlapping
  386. if (!options.allowOverlapX) {
  387. objectEach(boxesMap, function (box) {
  388. box.plotX = box.anchorX;
  389. boxes.push(box);
  390. });
  391. H.distribute(boxes, inverted ? yAxis.len : this.xAxis.len, 100);
  392. points.forEach(function (point) {
  393. var box = point.graphic && boxesMap[point.plotX];
  394. if (box) {
  395. point.graphic[point.graphic.isNew ? 'attr' : 'animate']({
  396. x: box.pos + box.align * box.size,
  397. anchorX: point.anchorX
  398. });
  399. // Hide flag when its box position is not specified
  400. // (#8573, #9299)
  401. if (!defined(box.pos)) {
  402. point.graphic.attr({
  403. x: -9999,
  404. anchorX: -9999
  405. });
  406. point.graphic.isNew = true;
  407. }
  408. else {
  409. point.graphic.isNew = false;
  410. }
  411. }
  412. });
  413. }
  414. // Can be a mix of SVG and HTML and we need events for both (#6303)
  415. if (options.useHTML) {
  416. wrap(series.markerGroup, 'on', function (proceed) {
  417. return SVGElement.prototype.on.apply(
  418. // for HTML
  419. proceed.apply(this, [].slice.call(arguments, 1)),
  420. // and for SVG
  421. [].slice.call(arguments, 1));
  422. });
  423. }
  424. },
  425. /**
  426. * Extend the column trackers with listeners to expand and contract
  427. * stacks.
  428. *
  429. * @private
  430. * @function Highcharts.seriesTypes.flags#drawTracker
  431. * @return {void}
  432. */
  433. drawTracker: function () {
  434. var series = this, points = series.points;
  435. TrackerMixin.drawTrackerPoint.apply(this);
  436. /* *
  437. * Bring each stacked flag up on mouse over, this allows readability
  438. * of vertically stacked elements as well as tight points on the x
  439. * axis. #1924.
  440. */
  441. points.forEach(function (point) {
  442. var graphic = point.graphic;
  443. if (graphic) {
  444. addEvent(graphic.element, 'mouseover', function () {
  445. // Raise this point
  446. if (point.stackIndex > 0 &&
  447. !point.raised) {
  448. point._y = graphic.y;
  449. graphic.attr({
  450. y: point._y - 8
  451. });
  452. point.raised = true;
  453. }
  454. // Revert other raised points
  455. points.forEach(function (otherPoint) {
  456. if (otherPoint !== point &&
  457. otherPoint.raised &&
  458. otherPoint.graphic) {
  459. otherPoint.graphic.attr({
  460. y: otherPoint._y
  461. });
  462. otherPoint.raised = false;
  463. }
  464. });
  465. });
  466. }
  467. });
  468. },
  469. /**
  470. * Disable animation, but keep clipping (#8546).
  471. *
  472. * @private
  473. * @function Highcharts.seriesTypes.flags#animate
  474. * @param {boolean} [init]
  475. * @return {void}
  476. */
  477. animate: function (init) {
  478. if (init) {
  479. this.setClip();
  480. }
  481. },
  482. /**
  483. * @private
  484. * @function Highcharts.seriesTypes.flags#setClip
  485. * @return {void}
  486. */
  487. setClip: function () {
  488. Series.prototype.setClip.apply(this, arguments);
  489. if (this.options.clip !== false && this.sharedClipKey) {
  490. this.markerGroup
  491. .clip(this.chart[this.sharedClipKey]);
  492. }
  493. },
  494. /**
  495. * @private
  496. * @function Highcharts.seriesTypes.flags#buildKDTree
  497. */
  498. buildKDTree: noop,
  499. /**
  500. * Don't invert the flag marker group (#4960).
  501. *
  502. * @private
  503. * @function Highcharts.seriesTypes.flags#invertGroups
  504. */
  505. invertGroups: noop
  506. /* eslint-enable no-invalid-this, valid-jsdoc */
  507. },
  508. /**
  509. * @lends Highcharts.seriesTypes.flag.prototype.pointClass.prototype
  510. */
  511. {
  512. isValid: function () {
  513. // #9233 - Prevent from treating flags as null points (even if
  514. // they have no y values defined).
  515. return isNumber(this.y) || typeof this.y === 'undefined';
  516. }
  517. });
  518. // create the flag icon with anchor
  519. symbols.flag = function (x, y, w, h, options) {
  520. var anchorX = (options && options.anchorX) || x, anchorY = (options && options.anchorY) || y;
  521. // To do: unwanted any cast because symbols.circle has wrong type, it
  522. // actually returns an SVGPathArray
  523. var path = symbols.circle(anchorX - 1, anchorY - 1, 2, 2);
  524. path.push(['M', anchorX, anchorY], ['L', x, y + h], ['L', x, y], ['L', x + w, y], ['L', x + w, y + h], ['L', x, y + h], ['Z']);
  525. return path;
  526. };
  527. /**
  528. * Create the circlepin and squarepin icons with anchor.
  529. * @private
  530. * @param {string} shape - circle or square
  531. * @return {void}
  532. */
  533. function createPinSymbol(shape) {
  534. symbols[shape + 'pin'] = function (x, y, w, h, options) {
  535. var anchorX = options && options.anchorX, anchorY = options && options.anchorY, path;
  536. // For single-letter flags, make sure circular flags are not taller
  537. // than their width
  538. if (shape === 'circle' && h > w) {
  539. x -= Math.round((h - w) / 2);
  540. w = h;
  541. }
  542. path = (symbols[shape])(x, y, w, h);
  543. if (anchorX && anchorY) {
  544. /**
  545. * If the label is below the anchor, draw the connecting line from
  546. * the top edge of the label, otherwise start drawing from the
  547. * bottom edge
  548. */
  549. var labelX = anchorX;
  550. if (shape === 'circle') {
  551. labelX = x + w / 2;
  552. }
  553. else {
  554. var startSeg = path[0];
  555. var endSeg = path[1];
  556. if (startSeg[0] === 'M' && endSeg[0] === 'L') {
  557. labelX = (startSeg[1] + endSeg[1]) / 2;
  558. }
  559. }
  560. var labelY = (y > anchorY) ? y : y + h;
  561. path.push([
  562. 'M',
  563. labelX,
  564. labelY
  565. ], [
  566. 'L',
  567. anchorX,
  568. anchorY
  569. ]);
  570. path = path.concat(symbols.circle(anchorX - 1, anchorY - 1, 2, 2));
  571. }
  572. return path;
  573. };
  574. }
  575. createPinSymbol('circle');
  576. createPinSymbol('square');
  577. /**
  578. * The symbol callbacks are generated on the SVGRenderer object in all browsers.
  579. * Even VML browsers need this in order to generate shapes in export. Now share
  580. * them with the VMLRenderer.
  581. */
  582. if (Renderer === VMLRenderer) {
  583. ['circlepin', 'flag', 'squarepin'].forEach(function (shape) {
  584. VMLRenderer.prototype.symbols[shape] = symbols[shape];
  585. });
  586. }
  587. /**
  588. * A `flags` series. If the [type](#series.flags.type) option is not
  589. * specified, it is inherited from [chart.type](#chart.type).
  590. *
  591. * @extends series,plotOptions.flags
  592. * @excluding animation, borderColor, borderRadius, borderWidth, colorByPoint,
  593. * connectNulls, dashStyle, dataGrouping, dataParser, dataURL,
  594. * gapSize, gapUnit, linecap, lineWidth, marker, pointPadding,
  595. * pointWidth, step, turboThreshold, useOhlcData
  596. * @product highstock
  597. * @apioption series.flags
  598. */
  599. /**
  600. * An array of data points for the series. For the `flags` series type,
  601. * points can be given in the following ways:
  602. *
  603. * 1. An array of objects with named values. The following snippet shows only a
  604. * few settings, see the complete options set below. If the total number of
  605. * data points exceeds the series'
  606. * [turboThreshold](#series.flags.turboThreshold), this option is not
  607. * available.
  608. * ```js
  609. * data: [{
  610. * x: 1,
  611. * title: "A",
  612. * text: "First event"
  613. * }, {
  614. * x: 1,
  615. * title: "B",
  616. * text: "Second event"
  617. * }]
  618. * ```
  619. *
  620. * @type {Array<*>}
  621. * @extends series.line.data
  622. * @excluding dataLabels, marker, name, y
  623. * @product highstock
  624. * @apioption series.flags.data
  625. */
  626. /**
  627. * The fill color of an individual flag. By default it inherits from
  628. * the series color.
  629. *
  630. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  631. * @product highstock
  632. * @apioption series.flags.data.fillColor
  633. */
  634. /**
  635. * The longer text to be shown in the flag's tooltip.
  636. *
  637. * @type {string}
  638. * @product highstock
  639. * @apioption series.flags.data.text
  640. */
  641. /**
  642. * The short text to be shown on the flag.
  643. *
  644. * @type {string}
  645. * @product highstock
  646. * @apioption series.flags.data.title
  647. */
  648. ''; // adds doclets above to transpiled file