item-series.src.js 21 KB

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