item-series.src.js 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544
  1. /**
  2. * @license Highcharts JS v8.2.0 (2020-08-20)
  3. *
  4. * Item series type for Highcharts
  5. *
  6. * (c) 2019 Torstein Honsi
  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/item-series', ['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, 'Series/ItemSeries.js', [_modules['Core/Globals.js'], _modules['Core/Options.js'], _modules['Core/Utilities.js']], function (H, O, U) {
  32. /* *
  33. *
  34. * (c) 2020 Torstein Honsi
  35. *
  36. * Item series type for Highcharts
  37. *
  38. * License: www.highcharts.com/license
  39. *
  40. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  41. *
  42. * */
  43. var defaultOptions = O.defaultOptions;
  44. var defined = U.defined,
  45. extend = U.extend,
  46. fireEvent = U.fireEvent,
  47. isNumber = U.isNumber,
  48. merge = U.merge,
  49. objectEach = U.objectEach,
  50. pick = U.pick,
  51. seriesType = U.seriesType;
  52. var piePoint = H.seriesTypes.pie.prototype.pointClass.prototype;
  53. /**
  54. * The item series type.
  55. *
  56. * @requires module:modules/item-series
  57. *
  58. * @private
  59. * @class
  60. * @name Highcharts.seriesTypes.item
  61. *
  62. * @augments Highcharts.seriesTypes.pie
  63. */
  64. seriesType('item',
  65. // Inherits pie as the most tested non-cartesian series with individual
  66. // point legend, tooltips etc. Only downside is we need to re-enable
  67. // marker options.
  68. 'pie',
  69. /**
  70. * An item chart is an infographic chart where a number of items are laid
  71. * out in either a rectangular or circular pattern. It can be used to
  72. * visualize counts within a group, or for the circular pattern, typically
  73. * a parliament.
  74. *
  75. * The circular layout has much in common with a pie chart. Many of the item
  76. * series options, like `center`, `size` and data label positioning, are
  77. * inherited from the pie series and don't apply for rectangular layouts.
  78. *
  79. * @sample highcharts/demo/parliament-chart
  80. * Parliament chart (circular item chart)
  81. * @sample highcharts/series-item/rectangular
  82. * Rectangular item chart
  83. * @sample highcharts/series-item/symbols
  84. * Infographic with symbols
  85. *
  86. * @extends plotOptions.pie
  87. * @since 7.1.0
  88. * @product highcharts
  89. * @excluding borderColor, borderWidth, depth, linecap, shadow,
  90. * slicedOffset
  91. * @requires modules/item-series
  92. * @optionparent plotOptions.item
  93. */
  94. {
  95. /**
  96. * In circular view, the end angle of the item layout, in degrees where
  97. * 0 is up.
  98. *
  99. * @sample highcharts/demo/parliament-chart
  100. * Parliament chart
  101. * @type {undefined|number}
  102. */
  103. endAngle: void 0,
  104. /**
  105. * In circular view, the size of the inner diameter of the circle. Can
  106. * be a percentage or pixel value. Percentages are relative to the outer
  107. * perimeter. Pixel values are given as integers.
  108. *
  109. * If the `rows` option is set, it overrides the `innerSize` setting.
  110. *
  111. * @sample highcharts/demo/parliament-chart
  112. * Parliament chart
  113. * @type {string|number}
  114. */
  115. innerSize: '40%',
  116. /**
  117. * The padding between the items, given in relative size where the size
  118. * of the item is 1.
  119. * @type {number}
  120. */
  121. itemPadding: 0.1,
  122. /**
  123. * The layout of the items in rectangular view. Can be either
  124. * `horizontal` or `vertical`.
  125. * @sample highcharts/series-item/symbols
  126. * Horizontal layout
  127. * @type {string}
  128. */
  129. layout: 'vertical',
  130. /**
  131. * @extends plotOptions.series.marker
  132. */
  133. marker: merge(defaultOptions.plotOptions.line.marker, {
  134. radius: null
  135. }),
  136. /**
  137. * The number of rows to display in the rectangular or circular view. If
  138. * the `innerSize` is set, it will be overridden by the `rows` setting.
  139. *
  140. * @sample highcharts/series-item/rows-columns
  141. * Fixed row count
  142. * @type {number}
  143. */
  144. rows: void 0,
  145. crisp: false,
  146. showInLegend: true,
  147. /**
  148. * In circular view, the start angle of the item layout, in degrees
  149. * where 0 is up.
  150. *
  151. * @sample highcharts/demo/parliament-chart
  152. * Parliament chart
  153. * @type {undefined|number}
  154. */
  155. startAngle: void 0
  156. },
  157. // Prototype members
  158. {
  159. markerAttribs: void 0,
  160. translate: function (positions) {
  161. // Initialize chart without setting data, #13379.
  162. if (this.total === 0) {
  163. this.center = this.getCenter();
  164. }
  165. if (!this.slots) {
  166. this.slots = [];
  167. }
  168. if (isNumber(this.options.startAngle) &&
  169. isNumber(this.options.endAngle)) {
  170. H.seriesTypes.pie.prototype.translate.apply(this, arguments);
  171. this.slots = this.getSlots();
  172. }
  173. else {
  174. this.generatePoints();
  175. fireEvent(this, 'afterTranslate');
  176. }
  177. },
  178. // Get the semi-circular slots
  179. getSlots: function () {
  180. var center = this.center,
  181. diameter = center[2],
  182. innerSize = center[3],
  183. row,
  184. slots = this.slots,
  185. x,
  186. y,
  187. rowRadius,
  188. rowLength,
  189. colCount,
  190. increment,
  191. angle,
  192. col,
  193. itemSize = 0,
  194. rowCount,
  195. fullAngle = (this.endAngleRad - this.startAngleRad),
  196. itemCount = Number.MAX_VALUE,
  197. finalItemCount,
  198. rows,
  199. testRows,
  200. rowsOption = this.options.rows,
  201. // How many rows (arcs) should be used
  202. rowFraction = (diameter - innerSize) / diameter,
  203. isCircle = fullAngle % (2 * Math.PI) === 0;
  204. // Increase the itemSize until we find the best fit
  205. while (itemCount > this.total + (rows && isCircle ? rows.length : 0)) {
  206. finalItemCount = itemCount;
  207. // Reset
  208. slots.length = 0;
  209. itemCount = 0;
  210. // Now rows is the last successful run
  211. rows = testRows;
  212. testRows = [];
  213. itemSize++;
  214. // Total number of rows (arcs) from the center to the
  215. // perimeter
  216. rowCount = diameter / itemSize / 2;
  217. if (rowsOption) {
  218. innerSize = ((rowCount - rowsOption) / rowCount) * diameter;
  219. if (innerSize >= 0) {
  220. rowCount = rowsOption;
  221. // If innerSize is negative, we are trying to set too
  222. // many rows in the rows option, so fall back to
  223. // treating it as innerSize 0
  224. }
  225. else {
  226. innerSize = 0;
  227. rowFraction = 1;
  228. }
  229. }
  230. else {
  231. rowCount = Math.floor(rowCount * rowFraction);
  232. }
  233. for (row = rowCount; row > 0; row--) {
  234. rowRadius = (innerSize + (row / rowCount) *
  235. (diameter - innerSize - itemSize)) / 2;
  236. rowLength = fullAngle * rowRadius;
  237. colCount = Math.ceil(rowLength / itemSize);
  238. testRows.push({
  239. rowRadius: rowRadius,
  240. rowLength: rowLength,
  241. colCount: colCount
  242. });
  243. itemCount += colCount + 1;
  244. }
  245. }
  246. if (!rows) {
  247. return;
  248. }
  249. // We now have more slots than we have total items. Loop over
  250. // the rows and remove the last slot until the count is correct.
  251. // For each iteration we sort the last slot by the angle, and
  252. // remove those with the highest angles.
  253. var overshoot = finalItemCount - this.total -
  254. (isCircle ? rows.length : 0);
  255. /**
  256. * @private
  257. * @param {Highcharts.ItemRowContainerObject} item
  258. * Wrapped object with angle and row
  259. * @return {void}
  260. */
  261. function cutOffRow(item) {
  262. if (overshoot > 0) {
  263. item.row.colCount--;
  264. overshoot--;
  265. }
  266. }
  267. while (overshoot > 0) {
  268. rows
  269. // Return a simplified representation of the angle of
  270. // the last slot within each row.
  271. .map(function (row) {
  272. return {
  273. angle: row.colCount / row.rowLength,
  274. row: row
  275. };
  276. })
  277. // Sort by the angles...
  278. .sort(function (a, b) {
  279. return b.angle - a.angle;
  280. })
  281. // ...so that we can ignore the items with the lowest
  282. // angles...
  283. .slice(0, Math.min(overshoot, Math.ceil(rows.length / 2)))
  284. // ...and remove the ones with the highest angles
  285. .forEach(cutOffRow);
  286. }
  287. rows.forEach(function (row) {
  288. var rowRadius = row.rowRadius,
  289. colCount = row.colCount;
  290. increment = colCount ? fullAngle / colCount : 0;
  291. for (col = 0; col <= colCount; col += 1) {
  292. angle = this.startAngleRad + col * increment;
  293. x = center[0] + Math.cos(angle) * rowRadius;
  294. y = center[1] + Math.sin(angle) * rowRadius;
  295. slots.push({ x: x, y: y, angle: angle });
  296. }
  297. }, this);
  298. // Sort by angle
  299. slots.sort(function (a, b) {
  300. return a.angle - b.angle;
  301. });
  302. this.itemSize = itemSize;
  303. return slots;
  304. },
  305. getRows: function () {
  306. var rows = this.options.rows,
  307. cols,
  308. ratio;
  309. // Get the row count that gives the most square cells
  310. if (!rows) {
  311. ratio = this.chart.plotWidth / this.chart.plotHeight;
  312. rows = Math.sqrt(this.total);
  313. if (ratio > 1) {
  314. rows = Math.ceil(rows);
  315. while (rows > 0) {
  316. cols = this.total / rows;
  317. if (cols / rows > ratio) {
  318. break;
  319. }
  320. rows--;
  321. }
  322. }
  323. else {
  324. rows = Math.floor(rows);
  325. while (rows < this.total) {
  326. cols = this.total / rows;
  327. if (cols / rows < ratio) {
  328. break;
  329. }
  330. rows++;
  331. }
  332. }
  333. }
  334. return rows;
  335. },
  336. drawPoints: function () {
  337. var series = this,
  338. options = this.options,
  339. renderer = series.chart.renderer,
  340. seriesMarkerOptions = options.marker,
  341. borderWidth = this.borderWidth,
  342. crisp = borderWidth % 2 ? 0.5 : 1,
  343. i = 0,
  344. rows = this.getRows(),
  345. cols = Math.ceil(this.total / rows),
  346. cellWidth = this.chart.plotWidth / cols,
  347. cellHeight = this.chart.plotHeight / rows,
  348. itemSize = this.itemSize || Math.min(cellWidth,
  349. cellHeight);
  350. /*
  351. this.slots.forEach(slot => {
  352. this.chart.renderer.circle(slot.x, slot.y, 6)
  353. .attr({
  354. fill: 'silver'
  355. })
  356. .add(this.group);
  357. });
  358. //*/
  359. this.points.forEach(function (point) {
  360. var attr,
  361. graphics,
  362. pointAttr,
  363. pointMarkerOptions = point.marker || {},
  364. symbol = (pointMarkerOptions.symbol ||
  365. seriesMarkerOptions.symbol),
  366. r = pick(pointMarkerOptions.radius,
  367. seriesMarkerOptions.radius),
  368. size = defined(r) ? 2 * r : itemSize,
  369. padding = size * options.itemPadding,
  370. x,
  371. y,
  372. width,
  373. height;
  374. point.graphics = graphics = point.graphics || {};
  375. if (!series.chart.styledMode) {
  376. pointAttr = series.pointAttribs(point, point.selected && 'select');
  377. }
  378. if (!point.isNull && point.visible) {
  379. if (!point.graphic) {
  380. point.graphic = renderer.g('point')
  381. .add(series.group);
  382. }
  383. for (var val = 0; val < point.y; val++) {
  384. // Semi-circle
  385. if (series.center && series.slots) {
  386. // Fill up the slots from left to right
  387. var slot = series.slots.shift();
  388. x = slot.x - itemSize / 2;
  389. y = slot.y - itemSize / 2;
  390. }
  391. else if (options.layout === 'horizontal') {
  392. x = cellWidth * (i % cols);
  393. y = cellHeight * Math.floor(i / cols);
  394. }
  395. else {
  396. x = cellWidth * Math.floor(i / rows);
  397. y = cellHeight * (i % rows);
  398. }
  399. x += padding;
  400. y += padding;
  401. width = Math.round(size - 2 * padding);
  402. height = width;
  403. if (series.options.crisp) {
  404. x = Math.round(x) - crisp;
  405. y = Math.round(y) + crisp;
  406. }
  407. attr = {
  408. x: x,
  409. y: y,
  410. width: width,
  411. height: height
  412. };
  413. if (typeof r !== 'undefined') {
  414. attr.r = r;
  415. }
  416. if (graphics[val]) {
  417. graphics[val].animate(attr);
  418. }
  419. else {
  420. graphics[val] = renderer
  421. .symbol(symbol, null, null, null, null, {
  422. backgroundSize: 'within'
  423. })
  424. .attr(extend(attr, pointAttr))
  425. .add(point.graphic);
  426. }
  427. graphics[val].isActive = true;
  428. i++;
  429. }
  430. }
  431. objectEach(graphics, function (graphic, key) {
  432. if (!graphic.isActive) {
  433. graphic.destroy();
  434. delete graphics[key];
  435. }
  436. else {
  437. graphic.isActive = false;
  438. }
  439. });
  440. });
  441. },
  442. drawDataLabels: function () {
  443. if (this.center && this.slots) {
  444. H.seriesTypes.pie.prototype.drawDataLabels.call(this);
  445. // else, it's just a dot chart with no natural place to put the
  446. // data labels
  447. }
  448. else {
  449. this.points.forEach(function (point) {
  450. point.destroyElements({ dataLabel: 1 });
  451. });
  452. }
  453. },
  454. // Fade in the whole chart
  455. animate: function (init) {
  456. if (init) {
  457. this.group.attr({
  458. opacity: 0
  459. });
  460. }
  461. else {
  462. this.group.animate({
  463. opacity: 1
  464. }, this.options.animation);
  465. }
  466. }
  467. },
  468. // Point class
  469. {
  470. connectorShapes: piePoint.connectorShapes,
  471. getConnectorPath: piePoint.getConnectorPath,
  472. setVisible: piePoint.setVisible,
  473. getTranslate: piePoint.getTranslate
  474. });
  475. /**
  476. * An `item` series. If the [type](#series.item.type) option is not specified,
  477. * it is inherited from [chart.type](#chart.type).
  478. *
  479. * @extends series,plotOptions.item
  480. * @excluding dataParser, dataURL, stack, xAxis, yAxis, dataSorting,
  481. * boostThreshold, boostBlending
  482. * @product highcharts
  483. * @requires modules/item-series
  484. * @apioption series.item
  485. */
  486. /**
  487. * An array of data points for the series. For the `item` series type,
  488. * points can be given in the following ways:
  489. *
  490. * 1. An array of numerical values. In this case, the numerical values will be
  491. * interpreted as `y` options. Example:
  492. * ```js
  493. * data: [0, 5, 3, 5]
  494. * ```
  495. *
  496. * 2. An array of objects with named values. The following snippet shows only a
  497. * few settings, see the complete options set below. If the total number of
  498. * data points exceeds the series'
  499. * [turboThreshold](#series.item.turboThreshold),
  500. * this option is not available.
  501. * ```js
  502. * data: [{
  503. * y: 1,
  504. * name: "Point2",
  505. * color: "#00FF00"
  506. * }, {
  507. * y: 7,
  508. * name: "Point1",
  509. * color: "#FF00FF"
  510. * }]
  511. * ```
  512. *
  513. * @sample {highcharts} highcharts/chart/reflow-true/
  514. * Numerical values
  515. * @sample {highcharts} highcharts/series/data-array-of-arrays/
  516. * Arrays of numeric x and y
  517. * @sample {highcharts} highcharts/series/data-array-of-arrays-datetime/
  518. * Arrays of datetime x and y
  519. * @sample {highcharts} highcharts/series/data-array-of-name-value/
  520. * Arrays of point.name and y
  521. * @sample {highcharts} highcharts/series/data-array-of-objects/
  522. * Config objects
  523. *
  524. * @type {Array<number|Array<string,(number|null)>|null|*>}
  525. * @extends series.pie.data
  526. * @excludes sliced
  527. * @product highcharts
  528. * @apioption series.item.data
  529. */
  530. /**
  531. * The sequential index of the data point in the legend.
  532. *
  533. * @type {number}
  534. * @product highcharts
  535. * @apioption series.pie.data.legendIndex
  536. */
  537. ''; // adds the doclets above to the transpiled file
  538. });
  539. _registerModule(_modules, 'masters/modules/item-series.src.js', [], function () {
  540. });
  541. }));