organization.src.js 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550
  1. /**
  2. * @license Highcharts JS v8.1.2 (2020-06-16)
  3. * Organization chart series type
  4. *
  5. * (c) 2019-2019 Torstein Honsi
  6. *
  7. * License: www.highcharts.com/license
  8. */
  9. 'use strict';
  10. (function (factory) {
  11. if (typeof module === 'object' && module.exports) {
  12. factory['default'] = factory;
  13. module.exports = factory;
  14. } else if (typeof define === 'function' && define.amd) {
  15. define('highcharts/modules/organization', ['highcharts', 'highcharts/modules/sankey'], function (Highcharts) {
  16. factory(Highcharts);
  17. factory.Highcharts = Highcharts;
  18. return factory;
  19. });
  20. } else {
  21. factory(typeof Highcharts !== 'undefined' ? Highcharts : undefined);
  22. }
  23. }(function (Highcharts) {
  24. var _modules = Highcharts ? Highcharts._modules : {};
  25. function _registerModule(obj, path, args, fn) {
  26. if (!obj.hasOwnProperty(path)) {
  27. obj[path] = fn.apply(null, args);
  28. }
  29. }
  30. _registerModule(_modules, 'modules/organization.src.js', [_modules['parts/Globals.js'], _modules['parts/Utilities.js']], function (H, U) {
  31. /* *
  32. *
  33. * Organization chart module
  34. *
  35. * (c) 2018-2020 Torstein Honsi
  36. *
  37. * License: www.highcharts.com/license
  38. *
  39. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  40. *
  41. * */
  42. var css = U.css, pick = U.pick, seriesType = U.seriesType, wrap = U.wrap;
  43. /**
  44. * Layout value for the child nodes in an organization chart. If `hanging`, this
  45. * node's children will hang below their parent, allowing a tighter packing of
  46. * nodes in the diagram.
  47. *
  48. * @typedef {"normal"|"hanging"} Highcharts.SeriesOrganizationNodesLayoutValue
  49. */
  50. var base = H.seriesTypes.sankey.prototype;
  51. /**
  52. * @private
  53. * @class
  54. * @name Highcharts.seriesTypes.organization
  55. *
  56. * @augments Highcharts.seriesTypes.sankey
  57. */
  58. seriesType('organization', 'sankey',
  59. /**
  60. * An organization chart is a diagram that shows the structure of an
  61. * organization and the relationships and relative ranks of its parts and
  62. * positions.
  63. *
  64. * @sample highcharts/demo/organization-chart/
  65. * Organization chart
  66. * @sample highcharts/series-organization/horizontal/
  67. * Horizontal organization chart
  68. * @sample highcharts/series-organization/borderless
  69. * Borderless design
  70. * @sample highcharts/series-organization/center-layout
  71. * Centered layout
  72. *
  73. * @extends plotOptions.sankey
  74. * @excluding allowPointSelect, curveFactor, dataSorting
  75. * @since 7.1.0
  76. * @product highcharts
  77. * @requires modules/organization
  78. * @optionparent plotOptions.organization
  79. */
  80. {
  81. /**
  82. * The border color of the node cards.
  83. *
  84. * @type {Highcharts.ColorString}
  85. * @private
  86. */
  87. borderColor: '#666666',
  88. /**
  89. * The border radius of the node cards.
  90. *
  91. * @private
  92. */
  93. borderRadius: 3,
  94. /**
  95. * Radius for the rounded corners of the links between nodes.
  96. *
  97. * @sample highcharts/series-organization/link-options
  98. * Square links
  99. *
  100. * @private
  101. */
  102. linkRadius: 10,
  103. borderWidth: 1,
  104. /**
  105. * @declare Highcharts.SeriesOrganizationDataLabelsOptionsObject
  106. *
  107. * @private
  108. */
  109. dataLabels: {
  110. /* eslint-disable valid-jsdoc */
  111. /**
  112. * A callback for defining the format for _nodes_ in the
  113. * organization chart. The `nodeFormat` option takes precedence
  114. * over `nodeFormatter`.
  115. *
  116. * In an organization chart, the `nodeFormatter` is a quite complex
  117. * function of the available options, striving for a good default
  118. * layout of cards with or without images. In organization chart,
  119. * the data labels come with `useHTML` set to true, meaning they
  120. * will be rendered as true HTML above the SVG.
  121. *
  122. * @sample highcharts/series-organization/datalabels-nodeformatter
  123. * Modify the default label format output
  124. *
  125. * @type {Highcharts.SeriesSankeyDataLabelsFormatterCallbackFunction}
  126. * @since 6.0.2
  127. */
  128. nodeFormatter: function () {
  129. var outerStyle = {
  130. width: '100%',
  131. height: '100%',
  132. display: 'flex',
  133. 'flex-direction': 'row',
  134. 'align-items': 'center',
  135. 'justify-content': 'center'
  136. }, imageStyle = {
  137. 'max-height': '100%',
  138. 'border-radius': '50%'
  139. }, innerStyle = {
  140. width: '100%',
  141. padding: 0,
  142. 'text-align': 'center',
  143. 'white-space': 'normal'
  144. }, nameStyle = {
  145. margin: 0
  146. }, titleStyle = {
  147. margin: 0
  148. }, descriptionStyle = {
  149. opacity: 0.75,
  150. margin: '5px'
  151. };
  152. // eslint-disable-next-line valid-jsdoc
  153. /**
  154. * @private
  155. */
  156. function styleAttr(style) {
  157. return Object.keys(style).reduce(function (str, key) {
  158. return str + key + ':' + style[key] + ';';
  159. }, 'style="') + '"';
  160. }
  161. if (this.point.image) {
  162. imageStyle['max-width'] = '30%';
  163. innerStyle.width = '70%';
  164. }
  165. // PhantomJS doesn't support flex, roll back to absolute
  166. // positioning
  167. if (this.series.chart.renderer.forExport) {
  168. outerStyle.display = 'block';
  169. innerStyle.position = 'absolute';
  170. innerStyle.left = this.point.image ? '30%' : 0;
  171. innerStyle.top = 0;
  172. }
  173. var html = '<div ' + styleAttr(outerStyle) + '>';
  174. if (this.point.image) {
  175. html += '<img src="' + this.point.image + '" ' +
  176. styleAttr(imageStyle) + '>';
  177. }
  178. html += '<div ' + styleAttr(innerStyle) + '>';
  179. if (this.point.name) {
  180. html += '<h4 ' + styleAttr(nameStyle) + '>' +
  181. this.point.name + '</h4>';
  182. }
  183. if (this.point.title) {
  184. html += '<p ' + styleAttr(titleStyle) + '>' +
  185. (this.point.title || '') + '</p>';
  186. }
  187. if (this.point.description) {
  188. html += '<p ' + styleAttr(descriptionStyle) + '>' +
  189. this.point.description + '</p>';
  190. }
  191. html += '</div>' +
  192. '</div>';
  193. return html;
  194. },
  195. /* eslint-enable valid-jsdoc */
  196. style: {
  197. /** @internal */
  198. fontWeight: 'normal',
  199. /** @internal */
  200. fontSize: '13px'
  201. },
  202. useHTML: true
  203. },
  204. /**
  205. * The indentation in pixels of hanging nodes, nodes which parent has
  206. * [layout](#series.organization.nodes.layout) set to `hanging`.
  207. *
  208. * @private
  209. */
  210. hangingIndent: 20,
  211. /**
  212. * The color of the links between nodes.
  213. *
  214. * @type {Highcharts.ColorString}
  215. * @private
  216. */
  217. linkColor: '#666666',
  218. /**
  219. * The line width of the links connecting nodes, in pixels.
  220. *
  221. * @sample highcharts/series-organization/link-options
  222. * Square links
  223. *
  224. * @private
  225. */
  226. linkLineWidth: 1,
  227. /**
  228. * In a horizontal chart, the width of the nodes in pixels. Node that
  229. * most organization charts are vertical, so the name of this option
  230. * is counterintuitive.
  231. *
  232. * @private
  233. */
  234. nodeWidth: 50,
  235. tooltip: {
  236. nodeFormat: '{point.name}<br>{point.title}<br>{point.description}'
  237. }
  238. }, {
  239. pointAttribs: function (point, state) {
  240. var series = this, attribs = base.pointAttribs.call(series, point, state), level = point.isNode ? point.level : point.fromNode.level, levelOptions = series.mapOptionsToLevel[level || 0] || {}, options = point.options, stateOptions = (levelOptions.states && levelOptions.states[state]) || {}, values = ['borderRadius', 'linkColor', 'linkLineWidth']
  241. .reduce(function (obj, key) {
  242. obj[key] = pick(stateOptions[key], options[key], levelOptions[key], series.options[key]);
  243. return obj;
  244. }, {});
  245. if (!point.isNode) {
  246. attribs.stroke = values.linkColor;
  247. attribs['stroke-width'] = values.linkLineWidth;
  248. delete attribs.fill;
  249. }
  250. else {
  251. if (values.borderRadius) {
  252. attribs.r = values.borderRadius;
  253. }
  254. }
  255. return attribs;
  256. },
  257. createNode: function (id) {
  258. var node = base.createNode
  259. .call(this, id);
  260. // All nodes in an org chart are equal width
  261. node.getSum = function () {
  262. return 1;
  263. };
  264. return node;
  265. },
  266. createNodeColumn: function () {
  267. var column = base.createNodeColumn.call(this);
  268. // Wrap the offset function so that the hanging node's children are
  269. // aligned to their parent
  270. wrap(column, 'offset', function (proceed, node, factor) {
  271. var offset = proceed.call(this, node, factor); // eslint-disable-line no-invalid-this
  272. // Modify the default output if the parent's layout is 'hanging'
  273. if (node.hangsFrom) {
  274. return {
  275. absoluteTop: node.hangsFrom.nodeY
  276. };
  277. }
  278. return offset;
  279. });
  280. return column;
  281. },
  282. translateNode: function (node, column) {
  283. base.translateNode.call(this, node, column);
  284. if (node.hangsFrom) {
  285. node.shapeArgs.height -=
  286. this.options.hangingIndent;
  287. if (!this.chart.inverted) {
  288. node.shapeArgs.y += this.options.hangingIndent;
  289. }
  290. }
  291. node.nodeHeight = this.chart.inverted ?
  292. node.shapeArgs.width :
  293. node.shapeArgs.height;
  294. },
  295. // General function to apply corner radius to a path - can be lifted to
  296. // renderer or utilities if we need it elsewhere.
  297. curvedPath: function (path, r) {
  298. var d = [];
  299. for (var i = 0; i < path.length; i++) {
  300. var x = path[i][1];
  301. var y = path[i][2];
  302. if (typeof x === 'number' && typeof y === 'number') {
  303. // moveTo
  304. if (i === 0) {
  305. d.push(['M', x, y]);
  306. }
  307. else if (i === path.length - 1) {
  308. d.push(['L', x, y]);
  309. // curveTo
  310. }
  311. else if (r) {
  312. var prevSeg = path[i - 1];
  313. var nextSeg = path[i + 1];
  314. if (prevSeg && nextSeg) {
  315. var x1 = prevSeg[1], y1 = prevSeg[2], x2 = nextSeg[1], y2 = nextSeg[2];
  316. // Only apply to breaks
  317. if (typeof x1 === 'number' &&
  318. typeof x2 === 'number' &&
  319. typeof y1 === 'number' &&
  320. typeof y2 === 'number' &&
  321. x1 !== x2 &&
  322. y1 !== y2) {
  323. var directionX = x1 < x2 ? 1 : -1, directionY = y1 < y2 ? 1 : -1;
  324. d.push([
  325. 'L',
  326. x - directionX * Math.min(Math.abs(x - x1), r),
  327. y - directionY * Math.min(Math.abs(y - y1), r)
  328. ], [
  329. 'C',
  330. x,
  331. y,
  332. x,
  333. y,
  334. x + directionX * Math.min(Math.abs(x - x2), r),
  335. y + directionY * Math.min(Math.abs(y - y2), r)
  336. ]);
  337. }
  338. }
  339. // lineTo
  340. }
  341. else {
  342. d.push(['L', x, y]);
  343. }
  344. }
  345. }
  346. return d;
  347. },
  348. translateLink: function (point) {
  349. var fromNode = point.fromNode, toNode = point.toNode, crisp = Math.round(this.options.linkLineWidth) % 2 / 2, x1 = Math.floor(fromNode.shapeArgs.x +
  350. fromNode.shapeArgs.width) + crisp, y1 = Math.floor(fromNode.shapeArgs.y +
  351. fromNode.shapeArgs.height / 2) + crisp, x2 = Math.floor(toNode.shapeArgs.x) + crisp, y2 = Math.floor(toNode.shapeArgs.y +
  352. toNode.shapeArgs.height / 2) + crisp, xMiddle, hangingIndent = this.options.hangingIndent, toOffset = toNode.options.offset, percentOffset = /%$/.test(toOffset) && parseInt(toOffset, 10), inverted = this.chart.inverted;
  353. if (inverted) {
  354. x1 -= fromNode.shapeArgs.width;
  355. x2 += toNode.shapeArgs.width;
  356. }
  357. xMiddle = Math.floor(x2 +
  358. (inverted ? 1 : -1) *
  359. (this.colDistance - this.nodeWidth) / 2) + crisp;
  360. // Put the link on the side of the node when an offset is given. HR
  361. // node in the main demo.
  362. if (percentOffset &&
  363. (percentOffset >= 50 || percentOffset <= -50)) {
  364. xMiddle = x2 = Math.floor(x2 + (inverted ? -0.5 : 0.5) *
  365. toNode.shapeArgs.width) + crisp;
  366. y2 = toNode.shapeArgs.y;
  367. if (percentOffset > 0) {
  368. y2 += toNode.shapeArgs.height;
  369. }
  370. }
  371. if (toNode.hangsFrom === fromNode) {
  372. if (this.chart.inverted) {
  373. y1 = Math.floor(fromNode.shapeArgs.y +
  374. fromNode.shapeArgs.height -
  375. hangingIndent / 2) + crisp;
  376. y2 = (toNode.shapeArgs.y +
  377. toNode.shapeArgs.height);
  378. }
  379. else {
  380. y1 = Math.floor(fromNode.shapeArgs.y +
  381. hangingIndent / 2) + crisp;
  382. }
  383. xMiddle = x2 = Math.floor(toNode.shapeArgs.x +
  384. toNode.shapeArgs.width / 2) + crisp;
  385. }
  386. point.plotY = 1;
  387. point.shapeType = 'path';
  388. point.shapeArgs = {
  389. d: this.curvedPath([
  390. ['M', x1, y1],
  391. ['L', xMiddle, y1],
  392. ['L', xMiddle, y2],
  393. ['L', x2, y2]
  394. ], this.options.linkRadius)
  395. };
  396. },
  397. alignDataLabel: function (point, dataLabel, options) {
  398. // Align the data label to the point graphic
  399. if (options.useHTML) {
  400. var width = point.shapeArgs.width, height = point.shapeArgs.height, padjust = (this.options.borderWidth +
  401. 2 * this.options.dataLabels.padding);
  402. if (this.chart.inverted) {
  403. width = height;
  404. height = point.shapeArgs.width;
  405. }
  406. height -= padjust;
  407. width -= padjust;
  408. // Set the size of the surrounding div emulating `g`
  409. var text = dataLabel.text;
  410. if (text) {
  411. css(text.element.parentNode, {
  412. width: width + 'px',
  413. height: height + 'px'
  414. });
  415. // Set properties for the span emulating `text`
  416. css(text.element, {
  417. left: 0,
  418. top: 0,
  419. width: '100%',
  420. height: '100%',
  421. overflow: 'hidden'
  422. });
  423. }
  424. // The getBBox function is used in `alignDataLabel` to align
  425. // inside the box
  426. dataLabel.getBBox = function () {
  427. return {
  428. width: width,
  429. height: height
  430. };
  431. };
  432. // Overwrite dataLabel dimensions (#13100).
  433. dataLabel.width = width;
  434. dataLabel.height = height;
  435. }
  436. H.seriesTypes.column.prototype.alignDataLabel.apply(this, arguments);
  437. }
  438. });
  439. /**
  440. * An `organization` series. If the [type](#series.organization.type) option is
  441. * not specified, it is inherited from [chart.type](#chart.type).
  442. *
  443. * @extends series,plotOptions.organization
  444. * @exclude dataSorting
  445. * @product highcharts
  446. * @requires modules/organization
  447. * @apioption series.organization
  448. */
  449. /**
  450. * @type {Highcharts.SeriesOrganizationDataLabelsOptionsObject|Array<Highcharts.SeriesOrganizationDataLabelsOptionsObject>}
  451. * @product highcharts
  452. * @apioption series.organization.data.dataLabels
  453. */
  454. /**
  455. * A collection of options for the individual nodes. The nodes in an org chart
  456. * are auto-generated instances of `Highcharts.Point`, but options can be
  457. * applied here and linked by the `id`.
  458. *
  459. * @extends series.sankey.nodes
  460. * @type {Array<*>}
  461. * @product highcharts
  462. * @apioption series.organization.nodes
  463. */
  464. /**
  465. * Individual data label for each node. The options are the same as
  466. * the ones for [series.organization.dataLabels](#series.organization.dataLabels).
  467. *
  468. * @type {Highcharts.SeriesOrganizationDataLabelsOptionsObject|Array<Highcharts.SeriesOrganizationDataLabelsOptionsObject>}
  469. *
  470. * @apioption series.organization.nodes.dataLabels
  471. */
  472. /**
  473. * The job description for the node card, will be inserted by the default
  474. * `dataLabel.nodeFormatter`.
  475. *
  476. * @sample highcharts/demo/organization-chart
  477. * Org chart with job descriptions
  478. *
  479. * @type {string}
  480. * @product highcharts
  481. * @apioption series.organization.nodes.description
  482. */
  483. /**
  484. * An image for the node card, will be inserted by the default
  485. * `dataLabel.nodeFormatter`.
  486. *
  487. * @sample highcharts/demo/organization-chart
  488. * Org chart with images
  489. *
  490. * @type {string}
  491. * @product highcharts
  492. * @apioption series.organization.nodes.image
  493. */
  494. /**
  495. * Layout for the node's children. If `hanging`, this node's children will hang
  496. * below their parent, allowing a tighter packing of nodes in the diagram.
  497. *
  498. * @sample highcharts/demo/organization-chart
  499. * Hanging layout
  500. *
  501. * @type {Highcharts.SeriesOrganizationNodesLayoutValue}
  502. * @default normal
  503. * @product highcharts
  504. * @apioption series.organization.nodes.layout
  505. */
  506. /**
  507. * The job title for the node card, will be inserted by the default
  508. * `dataLabel.nodeFormatter`.
  509. *
  510. * @sample highcharts/demo/organization-chart
  511. * Org chart with job titles
  512. *
  513. * @type {string}
  514. * @product highcharts
  515. * @apioption series.organization.nodes.title
  516. */
  517. /**
  518. * An array of data points for the series. For the `organization` series
  519. * type, points can be given in the following way:
  520. *
  521. * An array of objects with named values. The following snippet shows only a
  522. * few settings, see the complete options set below. If the total number of data
  523. * points exceeds the series' [turboThreshold](#series.area.turboThreshold),
  524. * this option is not available.
  525. *
  526. * ```js
  527. * data: [{
  528. * from: 'Category1',
  529. * to: 'Category2',
  530. * weight: 2
  531. * }, {
  532. * from: 'Category1',
  533. * to: 'Category3',
  534. * weight: 5
  535. * }]
  536. * ```
  537. *
  538. * @type {Array<*>}
  539. * @extends series.sankey.data
  540. * @product highcharts
  541. * @apioption series.organization.data
  542. */
  543. ''; // adds doclets above to transpiled file
  544. });
  545. _registerModule(_modules, 'masters/modules/organization.src.js', [], function () {
  546. });
  547. }));