1. 1 : /* global Spyral, Highcharts */
  2. 2 :
  3. 3 : import NetworkGraph from './networkgraph';
  4. 4 :
  5. 5 : import Util from './util.js';
  6. 6 :
  7. 7 : /**
  8. 8 : * The Chart class in Spyral.
  9. 9 : * This class provides methods for creating a variety of charts.
  10. 10 : * Charts are created using the [Highcharts Library]{@link https://api.highcharts.com/highcharts/}.
  11. 11 : * Highcharts have many configuration options and Spyral.Chart helps to streamline the process.
  12. 12 : * A simple example:
  13. 13 : *
  14. 14 : * Spyral.Chart.line({ series: [{ data: [0,2,1,3] }] })
  15. 15 : *
  16. 16 : * A more complex example:
  17. 17 : *
  18. 18 : * Spyral.Chart.column({
  19. 19 : * title: 'Wildflowers',
  20. 20 : * series: [{
  21. 21 : * name: 'Ontario',
  22. 22 : * data: [13, 39, 139, 38]
  23. 23 : * },{
  24. 24 : * name: 'Quebec',
  25. 25 : * data: [14, 33, 94, 30]
  26. 26 : * }],
  27. 27 : * xAxis: {
  28. 28 : * title: 'Number of Petals',
  29. 29 : * categories: [3, 4, 5, 6]
  30. 30 : * }
  31. 31 : * })
  32. 32 : *
  33. 33 : * @memberof Spyral
  34. 34 : * @class
  35. 35 : */
  36. 36 : class Chart {
  37. 37 : /**
  38. 38 : * The Highcharts config object
  39. 39 : * @typedef {Object} Spyral.Chart~HighchartsConfig
  40. 40 : * @property {(string|object)} title
  41. 41 : * @property {(string|object)} subtitle
  42. 42 : * @property {Object} credits
  43. 43 : * @property {Object} xAxis
  44. 44 : * @property {Object} yAxis
  45. 45 : * @property {Object} chart
  46. 46 : * @property {Array<Spyral.Chart~HighchartsSeriesConfig>} series
  47. 47 : * @property {Object} plotOptions
  48. 48 : */
  49. 49 :
  50. 50 : /**
  51. 51 : * The series config object
  52. 52 : * @typedef {Object} Spyral.Chart~HighchartsSeriesConfig
  53. 53 : * @property {Array} data
  54. 54 : * @property {string} [name]
  55. 55 : */
  56. 56 :
  57. 57 : /**
  58. 58 : * Construct a new Chart class
  59. 59 : * @constructor
  60. 60 : * @param {(String|Element)} [target] An element or ID to use as the chart's target. If not specified, one will be created.
  61. 61 : * @param {Array} data An array of data to visualize.
  62. 62 : */
  63. 63 : constructor(target, data) {
  64. 64 : if (Util.isNode(target)) {
  65. 65 : if (target.isConnected === false) {
  66. 66 : throw new Error('The target node does not exist within the document.');
  67. 67 : }
  68. 68 : } else if (Util.isString(target) === false) {
  69. 69 : data = target;
  70. 70 : target = undefined;
  71. 71 : }
  72. 72 : this.target = target;
  73. 73 : this.data = data;
  74. 74 : }
  75. 75 :
  76. 76 : /**
  77. 77 : * Create a new chart.
  78. 78 : * See [Highcharts API]{@link https://api.highcharts.com/highcharts/} for full set of config options.
  79. 79 : * @param {(String|Element)} [target] An element or ID to use as the chart's target. If not specified, one will be created.
  80. 80 : * @param {Spyral.Chart~HighchartsConfig} config
  81. 81 : * @returns {Highcharts.Chart}
  82. 82 : */
  83. 83 : create(target, config) {
  84. 84 : [target, config] = Chart._handleTargetAndConfig(target, config);
  85. 85 : return Highcharts.chart(target, config);
  86. 86 : }
  87. 87 :
  88. 88 : /**
  89. 89 : * Create a new chart.
  90. 90 : * See [Highcharts API]{@link https://api.highcharts.com/highcharts/} for full set of config options.
  91. 91 : * @param {(String|Element)} [target] An element or ID to use as the chart's target. If not specified, one will be created.
  92. 92 : * @param {Spyral.Chart~HighchartsConfig} config
  93. 93 : * @returns {Highcharts.Chart}
  94. 94 : * @static
  95. 95 : */
  96. 96 : static create(target, config) {
  97. 97 : [target, config] = Chart._handleTargetAndConfig(target, config);
  98. 98 : return Highcharts.chart(target, config);
  99. 99 : }
  100. 100 :
  101. 101 : static _handleTargetAndConfig(target, config) {
  102. 102 : if (Util.isNode(target) === false && typeof target === 'object') {
  103. 103 : config = target;
  104. 104 : target = undefined;
  105. 105 : }
  106. 106 :
  107. 107 : if (target === undefined) {
  108. 108 : if (typeof Spyral !== 'undefined' && Spyral.Notebook) {
  109. 109 : target = Spyral.Notebook.getTarget();
  110. 110 : if (target.clientHeight <= 40) {
  111. 111 : target.style.height = '400px'; // 400 is the default Highcharts height
  112. 112 : }
  113. 113 : } else {
  114. 114 : target = document.createElement('div');
  115. 115 : document.body.appendChild(target);
  116. 116 : }
  117. 117 : } else {
  118. 118 : if (Util.isNode(target) && target.isConnected === false) {
  119. 119 : throw new Error('The target node does not exist within the document.');
  120. 120 : }
  121. 121 : }
  122. 122 :
  123. 123 : // convert title and suppress if not provided
  124. 124 : if ('title' in config) {
  125. 125 : if (typeof config.title === 'string') {
  126. 126 : config.title = {text: config.title};
  127. 127 : }
  128. 128 : } else {
  129. 129 : config.title = false;
  130. 130 : }
  131. 131 :
  132. 132 : // convert subtitle and convert if not provided
  133. 133 : if ('subtitle' in config) {
  134. 134 : if (typeof config.subtitle === 'string') {
  135. 135 : config.subtitle = {text: config.subtitle};
  136. 136 : }
  137. 137 : } else {
  138. 138 : config.subtitle = false;
  139. 139 : }
  140. 140 :
  141. 141 : // convert credits
  142. 142 : if (!('credits' in config)) {
  143. 143 : config.credits = false;
  144. 144 : }
  145. 145 :
  146. 146 : // suppress xAxis title unless provided
  147. 147 : if (!('xAxis' in config)) {config.xAxis = {};}
  148. 148 : if (!('title' in config.xAxis)) {
  149. 149 : config.xAxis.title = false;
  150. 150 : } else if (typeof config.xAxis.title === 'string') {
  151. 151 : config.xAxis.title = {text: config.xAxis.title};
  152. 152 : }
  153. 153 :
  154. 154 : // suppress xAxis title unless provided
  155. 155 : if (!('yAxis' in config)) {config.yAxis = {};}
  156. 156 : if (!('title' in config.yAxis)) {
  157. 157 : config.yAxis.title = false;
  158. 158 : } else if (typeof config.yAxis.title === 'string') {
  159. 159 : config.yAxis.title = {text: config.yAxis.title};
  160. 160 : }
  161. 161 :
  162. 162 : return [target, config];
  163. 163 : }
  164. 164 :
  165. 165 : static _setDefaultChartType(config, type) {
  166. 166 : if ('type' in config) {
  167. 167 : config.chart.type = config.type;
  168. 168 : delete config.type;
  169. 169 : return;
  170. 170 : }
  171. 171 :
  172. 172 : // TODO: check plot options and series?
  173. 173 :
  174. 174 : if ('chart' in config) {
  175. 175 : if ('type' in config.chart) {return;} // already set
  176. 176 : } else {
  177. 177 : config.chart = {};
  178. 178 : }
  179. 179 :
  180. 180 : config.chart.type = type;
  181. 181 : return config;
  182. 182 : }
  183. 183 :
  184. 184 : /**
  185. 185 : * Add the provided data to the config as a series
  186. 186 : * @param {Spyral.Chart~HighchartsConfig} config
  187. 187 : * @param {Array} data
  188. 188 : * @static
  189. 189 : */
  190. 190 : static setSeriesData(config, data) {
  191. 191 : if (Array.isArray(data)) {
  192. 192 : if (Array.isArray(data[0])) {
  193. 193 : config.series = data.map(subArray => { return {data: subArray}; });
  194. 194 : } else {
  195. 195 : config.series = [{data: data}];
  196. 196 : }
  197. 197 : }
  198. 198 : }
  199. 199 :
  200. 200 : /**
  201. 201 : * Create a bar chart
  202. 202 : * @param {Spyral.Chart~HighchartsConfig} [config]
  203. 203 : * @returns {Highcharts.Chart}
  204. 204 : */
  205. 205 : bar(config={}) {
  206. 206 : Chart.setSeriesData(config, this.data);
  207. 207 : return Chart.bar(this.target, config);
  208. 208 : }
  209. 209 : /**
  210. 210 : * Create a bar chart
  211. 211 : * @param {(String|Element)} [target] An element or ID to use as the chart's target. If not specified, one will be created.
  212. 212 : * @param {Spyral.Chart~HighchartsConfig} config
  213. 213 : * @returns {Highcharts.Chart}
  214. 214 : * @static
  215. 215 : */
  216. 216 : static bar(target, config) {
  217. 217 : [target, config] = Chart._handleTargetAndConfig(target, config);
  218. 218 : Chart._setDefaultChartType(config, 'bar');
  219. 219 : return Highcharts.chart(target, config);
  220. 220 : }
  221. 221 :
  222. 222 : /**
  223. 223 : * Create a column chart
  224. 224 : * @param {Spyral.Chart~HighchartsConfig} [config]
  225. 225 : * @returns {Highcharts.Chart}
  226. 226 : */
  227. 227 : column(config={}) {
  228. 228 : Chart.setSeriesData(config, this.data);
  229. 229 : return Chart.column(this.target, config);
  230. 230 : }
  231. 231 : /**
  232. 232 : * Create a column chart
  233. 233 : * @param {(String|Element)} [target] An element or ID to use as the chart's target. If not specified, one will be created.
  234. 234 : * @param {Spyral.Chart~HighchartsConfig} config
  235. 235 : * @returns {Highcharts.Chart}
  236. 236 : * @static
  237. 237 : */
  238. 238 : static column(target, config) {
  239. 239 : [target, config] = Chart._handleTargetAndConfig(target, config);
  240. 240 : Chart._setDefaultChartType(config, 'column');
  241. 241 : return Highcharts.chart(target, config);
  242. 242 : }
  243. 243 :
  244. 244 : /**
  245. 245 : * Create a line chart
  246. 246 : * @param {Spyral.Chart~HighchartsConfig} [config]
  247. 247 : * @returns {Highcharts.Chart}
  248. 248 : */
  249. 249 : line(config={}) {
  250. 250 : Chart.setSeriesData(config, this.data);
  251. 251 : return Chart.line(this.target, config);
  252. 252 : }
  253. 253 : /**
  254. 254 : * Create a line chart
  255. 255 : * @param {(String|Element)} [target] An element or ID to use as the chart's target. If not specified, one will be created.
  256. 256 : * @param {Spyral.Chart~HighchartsConfig} config
  257. 257 : * @returns {Highcharts.Chart}
  258. 258 : * @static
  259. 259 : */
  260. 260 : static line(target, config) {
  261. 261 : [target, config] = Chart._handleTargetAndConfig(target, config);
  262. 262 : Chart._setDefaultChartType(config, 'line');
  263. 263 : return Highcharts.chart(target, config);
  264. 264 : }
  265. 265 :
  266. 266 : /**
  267. 267 : * Create a scatter plot
  268. 268 : * @param {Spyral.Chart~HighchartsConfig} [config]
  269. 269 : * @returns {Highcharts.Chart}
  270. 270 : */
  271. 271 : scatter(config={}) {
  272. 272 : Chart.setSeriesData(config, this.data);
  273. 273 : return Chart.scatter(this.target, config);
  274. 274 : }
  275. 275 : /**
  276. 276 : * Create a scatter plot
  277. 277 : * @param {(String|Element)} [target] An element or ID to use as the chart's target. If not specified, one will be created.
  278. 278 : * @param {Spyral.Chart~HighchartsConfig} config
  279. 279 : * @returns {Highcharts.Chart}
  280. 280 : * @static
  281. 281 : */
  282. 282 : static scatter(target, config) {
  283. 283 : [target, config] = Chart._handleTargetAndConfig(target, config);
  284. 284 : Chart._setDefaultChartType(config, 'scatter');
  285. 285 : return Highcharts.chart(target, config);
  286. 286 : }
  287. 287 :
  288. 288 : /**
  289. 289 : * Create a network graph
  290. 290 : * @param {NetworkGraph~Config} [config]
  291. 291 : * @returns {NetworkGraph}
  292. 292 : */
  293. 293 : networkgraph(config={}) {
  294. 294 : return Chart.networkgraph(this.target, config);
  295. 295 : }
  296. 296 : /**
  297. 297 : * Create a network graph
  298. 298 : * @param {(String|Element)} [target] An element or ID to use as the chart's target. If not specified, one will be created.
  299. 299 : * @param {NetworkGraph~Config} config
  300. 300 : * @returns {NetworkGraph}
  301. 301 : * @static
  302. 302 : */
  303. 303 : static networkgraph(target, config) {
  304. 304 : [target, config] = Chart._handleTargetAndConfig(target, config);
  305. 305 : return new NetworkGraph(target, config);
  306. 306 : }
  307. 307 : }
  308. 308 :
  309. 309 : export default Chart;