├── .babelrc ├── .eslintignore ├── .eslintrc.yml ├── .gitignore ├── .npmignore ├── LICENSE.md ├── README.md ├── __tests__ ├── ReactFC.spec.js ├── __snapshots__ │ └── ReactFC.spec.js.snap ├── data.json └── utils │ ├── options.spec.js │ └── utils.spec.js ├── components └── DrillDown.js ├── dist ├── drill-down.js ├── drill-down.min.js ├── react-fusioncharts.js └── react-fusioncharts.min.js ├── example ├── ChartViewer.js ├── index.html └── index.js ├── index.js ├── lib ├── ReactFC.d.ts ├── ReactFC.js └── utils │ ├── options.js │ └── utils.js ├── package.json ├── src ├── DrillDown.js ├── ReactFC.js └── utils │ ├── options.js │ └── utils.js ├── umd-src └── DrillDown.js ├── webpack.config.example.js └── webpack.config.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "@babel/preset-react", 4 | "@babel/preset-env" 5 | ] 6 | } -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | lib/ 3 | node_modules/ 4 | -------------------------------------------------------------------------------- /.eslintrc.yml: -------------------------------------------------------------------------------- 1 | extends: 2 | - airbnb 3 | - prettier 4 | env: 5 | browser: true 6 | node: true 7 | rules: 8 | import/no-extraneous-dependencies: 0 9 | react/jsx-filename-extension: 0 10 | react/prop-types: 0 11 | class-methods-use-this: 0 12 | no-plusplus: 0 13 | no-prototype-builtins: 0 14 | react/sort-comp: 0 15 | no-param-reassign: 0 16 | no-return-assign: 0 17 | no-sequences: 0 18 | global-require: 0 19 | import/prefer-default-export: 0 20 | react/forbid-prop-types: 0 21 | no-unused-expressions: 0 22 | no-useless-return: 0 23 | no-continue: 0 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Coverage tools 11 | lib-cov 12 | coverage 13 | coverage.html 14 | .cover* 15 | 16 | # Dependency directory 17 | node_modules 18 | bower_components 19 | 20 | # Example build directory 21 | example/dist 22 | .publish 23 | 24 | # Editor and other tmp files 25 | *.swp 26 | *.un~ 27 | *.iml 28 | *.ipr 29 | *.iws 30 | *.sublime-* 31 | .idea/ 32 | *.DS_Store 33 | 34 | package-lock.json -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # PLATFORM 2 | # ======== 3 | # All exclusions that are specific to the NPM, GIT, IDE and Operating Systems. 4 | 5 | # - Do not allow installed node modules to be committed. Doing `npm install -d` will bring them in root or other places. 6 | node_modules 7 | 8 | # - Do not commit any log file from anywhere 9 | *.log 10 | 11 | # - Prevent addition of OS specific file explorer files 12 | Thumbs.db 13 | .DS_Store 14 | 15 | 16 | # PROJECT 17 | # ======= 18 | # Configuration pertaining to project specific repository structure. 19 | 20 | # - Prevent Sublime text IDE files from being commited to repository 21 | *.sublime-* 22 | 23 | # - Prevent NetBeans IDE files from being commited to repository 24 | nbproject/ 25 | 26 | 27 | # - Prevent extra `jshint` configuration from being committed from anywhere. Only `package.json` will be used as 28 | # accepted source of config. 29 | .jshintrc 30 | 31 | # - Prevent CI output files from being Added 32 | /out/ 33 | 34 | # - Prevent diff backups from SourceTree from showing as commit. 35 | *.BACKUP.* 36 | *.BASE.* 37 | *.LOCAL.* 38 | *.REMOTE.* 39 | *.orig 40 | 41 | # ---------------------------------------------------------------------------------------------------------------------- 42 | # NPMIGNORE ADDITIONS 43 | # =================== 44 | 45 | 46 | # - Do not distribute submodule sources 47 | /.gitmodules 48 | 49 | __tests__/ 50 | 51 | example/ 52 | 53 | webpack.config.js 54 | 55 | webpack.config.example.js 56 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 FusionCharts, Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | A simple and lightweight official React component for FusionCharts JavaScript charting library. `react-fusioncharts` enables you to add JavaScript charts in your React application or project without any hassle. 2 | 3 | ## [Demo](https://fusioncharts.github.io/react-fusioncharts-component/) 4 | 5 | - Github Repo: [https://github.com/fusioncharts/react-fusioncharts-component](https://github.com/fusioncharts/react-fusioncharts-component) 6 | - Documentation: [https://www.fusioncharts.com/dev/getting-started/react/your-first-chart-using-react](https://www.fusioncharts.com/dev/getting-started/react/your-first-chart-using-react) 7 | - Support: [https://www.fusioncharts.com/contact-support](https://www.fusioncharts.com/contact-support) 8 | - FusionCharts 9 | - Official Website: [https://www.fusioncharts.com/](https://www.fusioncharts.com/) 10 | - Official NPM Package: [https://www.npmjs.com/package/fusioncharts](https://www.npmjs.com/package/fusioncharts) 11 | - Issues: [https://github.com/fusioncharts/react-fusioncharts-component/issues](https://github.com/fusioncharts/react-fusioncharts-component/issues) 12 | 13 | --- 14 | 15 | ## Validation and Verification 16 | 17 | The FusionCharts React integration component has been verified and validated with different versions of Fusioncharts (3.19 / 3.18 / 3.17) with React versions 16/17/18 18 | 19 | ## Table of Contents 20 | 21 | - [Getting Started](#getting-started) 22 | - [Requirements](#requirements) 23 | - [Installation](#installation) 24 | - [Usage](#usage) 25 | - [Working with chart API](#working-with-apis) 26 | - [Working with events](#working-with-events) 27 | - [Quick Start](#quick-start) 28 | - [Custom Components](#custom-components) 29 | - [Drill Down](#drill-down-component) 30 | - [Going Beyond Charts](#going-beyond-charts) 31 | - [Usage and Integration of FusionTime](#usage-and-integration-of-fusionTime) 32 | - [For Contributors](#for-contributors) 33 | - [Licensing](#licensing) 34 | 35 | ## Getting Started 36 | 37 | ### Requirements 38 | 39 | - **Node.js**, **NPM/Yarn** installed globally in your OS. 40 | - **FusionCharts** and **React** installed in your project, as detailed below: 41 | 42 | ### Installation 43 | 44 | There are multiple ways to install `react-fusioncharts` component. 45 | 46 | **Direct Download** 47 | All binaries are located on our [github repository](https://github.com/fusioncharts/react-fusioncharts-component). 48 | 49 | **Install from NPM** 50 | 51 | ``` 52 | npm install --save react-fusioncharts 53 | ``` 54 | 55 | See [npm documentation](https://docs.npmjs.com/) to know more about npm usage. 56 | 57 | **Install from Yarn** 58 | 59 | ``` 60 | yarn add react-fusioncharts 61 | ``` 62 | 63 | See [yarn documentation](https://yarnpkg.com/en/docs) to know more about yarn usage. 64 | 65 | For general instructions, refer to this [developer docs page](https://www.fusioncharts.com/dev/getting-started/react/your-first-chart-using-react#installation-2). 66 | 67 | ### Usage 68 | 69 | #### If you have created your app using `create-react-app` 70 | 71 | Import React, `react-fusioncharts` and FusionCharts in your app: 72 | 73 | ``` 74 | import React from 'react'; 75 | import ReactDOM from 'react-dom'; 76 | import FusionCharts from 'fusioncharts'; 77 | import Charts from 'fusioncharts/fusioncharts.charts'; 78 | import ReactFC from 'react-fusioncharts'; 79 | 80 | ReactFC.fcRoot(FusionCharts, Charts); 81 | ``` 82 | 83 | #### If you have created your app using tools like `webpack` or `parcel` 84 | 85 | Import React, `react-fusioncharts` and FusionCharts in your app: 86 | 87 | ``` 88 | import React from 'react'; 89 | import ReactDOM from 'react-dom'; 90 | import FusionCharts from 'fusioncharts/core'; 91 | import Column2d from 'fusioncharts/viz/column2d'; 92 | import ReactFC from 'react-fusioncharts'; 93 | 94 | ReactFC.fcRoot(FusionCharts, Column2d); 95 | ``` 96 | 97 | Note: This way of import will not work in IE11 and below. 98 | 99 | ## Quick Start 100 | 101 | Here is a basic sample that shows how to create a chart using `react-fusioncharts`: 102 | 103 | ```javascript 104 | import React from 'react'; 105 | import ReactDOM from 'react-dom'; 106 | import FusionCharts from 'fusioncharts'; 107 | import Charts from 'fusioncharts/fusioncharts.charts'; 108 | import FusionTheme from 'fusioncharts/themes/fusioncharts.theme.fusion'; 109 | import ReactFC from 'react-fusioncharts'; 110 | 111 | ReactFC.fcRoot(FusionCharts, Charts, FusionTheme); 112 | 113 | const dataSource = { 114 | chart: { 115 | caption: 'Countries With Most Oil Reserves [2017-18]', 116 | subCaption: 'In MMbbl = One Million barrels', 117 | xAxisName: 'Country', 118 | yAxisName: 'Reserves (MMbbl)', 119 | numberSuffix: 'K', 120 | theme: 'fusion' 121 | }, 122 | data: [ 123 | { label: 'Venezuela', value: '290' }, 124 | { label: 'Saudi', value: '260' }, 125 | { label: 'Canada', value: '180' }, 126 | { label: 'Iran', value: '140' }, 127 | { label: 'Russia', value: '115' }, 128 | { label: 'UAE', value: '100' }, 129 | { label: 'US', value: '30' }, 130 | { label: 'China', value: '30' } 131 | ] 132 | }; 133 | 134 | const chartConfigs = { 135 | type: 'column2d', 136 | width: 600, 137 | height: 400, 138 | dataFormat: 'json', 139 | dataSource: dataSource 140 | }; 141 | 142 | ReactDOM.render(, document.getElementById('root')); 143 | ``` 144 | 145 | ## Render FusionMaps 146 | 147 | To render a map, import the FusionMaps module along with the map definition. 148 | 149 | ```javascript 150 | import React from 'react'; 151 | import ReactDOM from 'react-dom'; 152 | import FusionCharts from 'fusioncharts'; 153 | import Maps from 'fusioncharts/fusioncharts.maps'; 154 | import World from 'fusioncharts/maps/fusioncharts.world'; 155 | import FusionTheme from 'fusioncharts/themes/fusioncharts.theme.fusion'; 156 | import ReactFC from 'react-fusioncharts'; 157 | 158 | ReactFC.fcRoot(FusionCharts, Maps, World, FusionTheme); 159 | // Data Source B 160 | const dataSource = { 161 | chart: { 162 | caption: 'Average Annual Population Growth', 163 | subcaption: ' 1955-2015', 164 | numbersuffix: '%', 165 | includevalueinlabels: '1', 166 | labelsepchar: ': ', 167 | entityFillHoverColor: '#FFF9C4', 168 | theme: 'fusion' 169 | }, 170 | colorrange: { 171 | minvalue: '0', 172 | code: '#FFE0B2', 173 | gradient: '1', 174 | color: [ 175 | { minvalue: '0.5', maxvalue: '1.0', color: '#FFD74D' }, 176 | { minvalue: '1.0', maxvalue: '2.0', color: '#FB8C00' }, 177 | { minvalue: '2.0', maxvalue: '3.0', color: '#E65100' } 178 | ] 179 | }, 180 | data: [ 181 | { id: 'NA', value: '.82', showLabel: '1' }, 182 | { id: 'SA', value: '2.04', showLabel: '1' }, 183 | { id: 'AS', value: '1.78', showLabel: '1' }, 184 | { id: 'EU', value: '.40', showLabel: '1' }, 185 | { id: 'AF', value: '2.58', showLabel: '1' }, 186 | { id: 'AU', value: '1.30', showLabel: '1' } 187 | ] 188 | }; 189 | 190 | const chartConfigs = { 191 | type: 'world', 192 | width: 600, 193 | height: 400, 194 | dataFormat: 'json', 195 | dataSource: dataSource 196 | }; 197 | 198 | ReactDOM.render(, document.getElementById('root')); 199 | ``` 200 | 201 | ## Working with Events 202 | 203 | To attach event callbacks to a FusionCharts component, follow the pattern below. 204 | 205 | Write the callback: 206 | 207 | As a separate function: 208 | 209 | ```javascript 210 | var chartEventCallback = function (eventObj, dataObj) { 211 | [Code goes here] 212 | } 213 | ``` 214 | 215 | Or, as a component class method: 216 | 217 | ```javascript 218 | chartEventCallback (eventObj, dataObj) { 219 | [Code goes here] 220 | } 221 | ``` 222 | 223 | Attach the callback to an event through the React-FC component: 224 | 225 | ```javascript 226 | 227 | ``` 228 | 229 | Where, EVENTNAME is to be replaced by the event you want to track. 230 | 231 | ##### Consider the example below that tracks hover events on a data plot. 232 | 233 | ```javascript 234 | import React, { Component } from 'react'; 235 | import ReactDOM from 'react-dom'; 236 | import FusionCharts from 'fusioncharts'; 237 | import Charts from 'fusioncharts/fusioncharts.charts'; 238 | import ReactFC from 'react-fusioncharts'; 239 | import FusionTheme from 'fusioncharts/Charts/fusioncharts.theme.fusion'; 240 | 241 | ReactFC.fcRoot(FusionCharts, Charts, FusionTheme); 242 | 243 | const dataSource = /* Data Source A , given above */; 244 | 245 | const chartConfigs = { 246 | type: 'column2d', 247 | width: 600, 248 | height: 400, 249 | dataFormat: 'json', 250 | dataSource: dataSource 251 | }; 252 | 253 | class Chart extends Component { 254 | constructor(props) { 255 | super(props); 256 | this.state = { 257 | actualValue: 'Hover on the plot to see the value along with the label' 258 | }; 259 | this.showPlotValue = this.showPlotValue.bind(this); 260 | } 261 | 262 | // Event callback handler for 'dataplotRollOver'. 263 | // Shows the value of the hovered plot on the page. 264 | showPlotValue(eventObj, dataObj) { 265 | this.setState({ 266 | actualValue: `You’re are currently hovering over ${ 267 | dataObj.categoryLabel 268 | } whose value is ${dataObj.displayValue}` 269 | }); 270 | } 271 | 272 | render() { 273 | return ( 274 |
275 | 279 |

280 | {this.state.actualValue} 281 |

282 |
283 | ); 284 | } 285 | } 286 | 287 | ReactDOM.render(, document.getElementById('root')); 288 | ``` 289 | 290 | ## Usage and integration of FusionTime 291 | 292 | From `fusioncharts@3.13.3-sr.1` and `react-fusioncharts@3.0.0`, You can visualize timeseries data easily on react. 293 | 294 | Learn more about FusionTime [here](https://www.fusioncharts.com/fusiontime). 295 | 296 | ### Consider the example below for integration of FusionTime 297 | 298 | ```javascript 299 | import React from 'react'; 300 | import FusionCharts from 'fusioncharts'; 301 | import TimeSeries from 'fusioncharts/fusioncharts.timeseries'; 302 | import ReactFC from 'react-fusioncharts'; 303 | 304 | ReactFC.fcRoot(FusionCharts, TimeSeries); 305 | 306 | const jsonify = res => res.json(); 307 | const dataFetch = fetch( 308 | 'https://raw.githubusercontent.com/fusioncharts/dev_centre_docs/fusiontime-beta-release/charts-resources/fusiontime/online-sales-single-series/data.json' 309 | ).then(jsonify); 310 | const schemaFetch = fetch( 311 | 'https://raw.githubusercontent.com/fusioncharts/dev_centre_docs/fusiontime-beta-release/charts-resources/fusiontime/online-sales-single-series/schema.json' 312 | ).then(jsonify); 313 | 314 | class ChartViewer extends React.Component { 315 | constructor(props) { 316 | super(props); 317 | this.onFetchData = this.onFetchData.bind(this); 318 | this.state = { 319 | timeseriesDs: { 320 | type: 'timeseries', 321 | renderAt: 'container', 322 | width: '600', 323 | height: '400', 324 | dataSource: { 325 | caption: { text: 'Online Sales of a SuperStore in the US' }, 326 | data: null, 327 | yAxis: [ 328 | { 329 | plot: [ 330 | { 331 | value: 'Sales ($)' 332 | } 333 | ] 334 | } 335 | ] 336 | } 337 | } 338 | }; 339 | } 340 | 341 | componentDidMount() { 342 | this.onFetchData(); 343 | } 344 | 345 | onFetchData() { 346 | Promise.all([dataFetch, schemaFetch]).then(res => { 347 | const data = res[0]; 348 | const schema = res[1]; 349 | const fusionTable = new FusionCharts.DataStore().createDataTable( 350 | data, 351 | schema 352 | ); 353 | const timeseriesDs = Object.assign({}, this.state.timeseriesDs); 354 | timeseriesDs.dataSource.data = fusionTable; 355 | this.setState({ 356 | timeseriesDs 357 | }); 358 | }); 359 | } 360 | 361 | render() { 362 | return ( 363 |
364 | {this.state.timeseriesDs.dataSource.data ? ( 365 | 366 | ) : ( 367 | 'loading' 368 | )} 369 |
370 | ); 371 | } 372 | } 373 | ``` 374 | 375 | Useful links for FusionTime 376 | 377 | - [How FusionTime works](https://www.fusioncharts.com/dev/fusiontime/getting-started/how-fusion-time-works) 378 | - [Create your first chart](https://www.fusioncharts.com/dev/fusiontime/getting-started/create-your-first-chart-in-fusiontime) 379 | 380 | ## Drill Down Component 381 | 382 | A custom component to easily add drill down to your react application. 383 | 384 | ### Syntax 385 | 386 | ```javascript 387 | import FusionCharts from 'fusioncharts'; 388 | import Charts from 'fusioncharts/fusioncharts.charts'; 389 | import ReactFC from 'react-fusioncharts'; 390 | import DrillDown from 'react-fusioncharts/components/DrillDown'; 391 | DrillDown.fcRoot(FusionCharts, Charts); 392 | ``` 393 | 394 | ```jsx 395 | class MyComponent extends React.Component{ 396 | constructor(props){ 397 | super(props); 398 | this.plotChildMap = [ 0, 2, 1 ]; 399 | this.dataSource = /*Data Source A : Given above */ ; 400 | this.btnConfig = {text : 'Back'}; 401 | this.type= 'column2d'; 402 | this.height = 400; 403 | this.width = 400; 404 | } 405 | render(){ 406 | return ( 407 | 418 | /* ReactFC as a child */ 419 | 420 | ... 421 | /* DrillDown as a child for next level drill down*/ 422 | 423 | ) 424 | } 425 | } 426 | ``` 427 | 428 | #### Attribute Description 429 | 430 | - plotChildMap[Array(Number)| Array[Object]] 431 | 432 | - Array ( Number ) - Representation of child mapped to the plot of the parent data source passed to the `DrillDown` 433 | component . Array location are considered plot index of parent, the value corresponding to it are considered which child chart to render. 434 | `Eg. [0,2,1]` 435 | `0(location) -> 0 (value)` means clicking the first (zero indexed) data plot , render the 0th child , 436 | `1(location) -> 2(value)` means clicking the second data plot, render the 1st Child (Note: It is 0-indexed ) 437 | **Null case** : You can pass `null` for a data plot for which you dont want a drill down. 438 | - Array ( Object ) - Representation of child mapped with plot in form of an object of shape 439 | `{ "plotPosition": Number, "childPosition": Number }` 440 | This object holds information about which child render on a data plot is clicked. 441 | `Eg. [{ plotPosition: 1 , childPosition: 0}, { plotPosition: 0, childPosition: 1}]` 442 | Note: plotChildMap does not honour heterogeneous data , eg. Number and Object 443 | both. 444 | `[ 0 , { plotPosition:0, childPosition: 1 } ]` 445 | 446 | - btnConfig [Object]- Basic configuration without overriding the default button styles 447 | - `text`: PropTypes.string - Button Text 448 | - `color`: PropTypes.string 449 | - `backgroundColor`: PropTypes.string 450 | - `borderColor`: PropTypes.string 451 | - `fontSize`: PropTypes.string 452 | - `fontWeight`: PropTypes.string 453 | - `padding`: PropTypes.string 454 | - `fontFamily`: PropTypes.string 455 | - btnStyle [Object] - CSS styles which override the styles in default btnConfig except `text`. 456 | 457 | ## Working with APIs 458 | 459 | To call APIs we will need the chart object. To get the chart object for an React-FC component, pass a callback through the attribute `onRender`. 460 | 461 | Write the callback: 462 | 463 | As a separate function: 464 | 465 | ```javascript 466 | var chartRenderCallback = function (chart) { 467 | [Code goes here] 468 | } 469 | ``` 470 | 471 | Or, as a component class method: 472 | 473 | ```javascript 474 | chartRenderCallback (chart) { 475 | [Code goes here] 476 | } 477 | ``` 478 | 479 | Pass the callback as a prop, to which the chart object will be returned on rendering 480 | 481 | ``` 482 | 483 | ``` 484 | 485 | ##### Consider the example below that conerts a Column 2D chart to a Pie 2D chart after 5 seconds. 486 | 487 | ```javascript 488 | import React, { Component } from 'react'; 489 | import ReactDOM from 'react-dom'; 490 | import FusionCharts from 'fusioncharts'; 491 | import Charts from 'fusioncharts/fusioncharts.charts'; 492 | import ReactFC from 'react-fusioncharts'; 493 | import FusionTheme from 'fusioncharts/themes/fusioncharts.theme.fusion'; 494 | 495 | ReactFC.fcRoot(FusionCharts, Charts, FusionTheme); 496 | 497 | const dataSource = /* Data source A given above */; 498 | 499 | const chartConfigs = { 500 | type: 'column2d', 501 | width: 600, 502 | height: 400, 503 | dataFormat: 'json', 504 | dataSource: dataSource 505 | }; 506 | 507 | class Chart extends Component { 508 | // Convert the chart to a 2D Pie chart after 5 secs. 509 | alterChart(chart) { 510 | setTimeout(() => { 511 | chart.chartType('pie2d'); 512 | }, 5000); 513 | } 514 | 515 | render() { 516 | return ( 517 |
518 | 519 |
520 | ); 521 | } 522 | } 523 | 524 | ReactDOM.render(, document.getElementById('root')); 525 | ``` 526 | 527 | links to help you get started: 528 | 529 | - [Live samples with code](https://fusioncharts.github.io/react-fusioncharts-component/) 530 | - [Documentation](https://www.fusioncharts.com/dev/getting-started/react/your-first-chart-using-react) 531 | - [Use Chart API events & methods in React](https://www.fusioncharts.com/dev/getting-started/react/configure-your-chart-using-react) 532 | - [Chart gallery](https://www.fusioncharts.com/explore/chart-gallery) 533 | - [FusionCharts API](https://www.fusioncharts.com/dev/api/fusioncharts) 534 | 535 | ## Going Beyond Charts 536 | 537 | - Explore 20+ pre-built business specific dashboards for different industries like energy and manufacturing to business functions like sales, marketing and operations [here](https://www.fusioncharts.com/explore/dashboards). 538 | - See [Data Stories](https://www.fusioncharts.com/explore/data-stories) built using FusionCharts’ interactive JavaScript visualizations and learn how to communicate real-world narratives through underlying data to tell compelling stories. 539 | 540 | ## For Contributors 541 | 542 | - Clone the repository and install dependencies 543 | 544 | ``` 545 | $ git clone https://github.com/fusioncharts/react-fusioncharts-component.git 546 | $ cd react-fusioncharts-component 547 | $ npm i 548 | $ npm start 549 | ``` 550 | 551 | - Run `npm run build` to start the dev server and point your browser at [http://localhost:3000/](http://localhost:3000/). 552 | 553 | ## Licensing 554 | 555 | The FusionCharts React integration component is open-source and distributed under the terms of the MIT/X11 License. However, you will need to download and include FusionCharts library in your page separately, which has a [separate license](https://www.fusioncharts.com/buy). 556 | -------------------------------------------------------------------------------- /__tests__/ReactFC.spec.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import renderer from 'react-test-renderer'; 3 | import ReactFC from '../src/ReactFC'; 4 | import fusionChartsOptions from '../src/utils/options'; 5 | import sampleData from './data.json'; 6 | 7 | /* global describe it expect jest */ 8 | 9 | jest.mock('fusioncharts', () => { 10 | const FusionCharts = function FusionCharts() {}; 11 | FusionCharts.prototype.render = () => {}; 12 | FusionCharts.prototype.dispose = () => {}; 13 | FusionCharts.prototype.resizeTo = () => {}; 14 | FusionCharts.prototype.chartType = () => {}; 15 | FusionCharts.prototype.setChartData = () => {}; 16 | FusionCharts.prototype.addEventListener = () => {}; 17 | FusionCharts.prototype.removeEventListener = () => {}; 18 | return FusionCharts; 19 | }); 20 | 21 | jest.mock('uuid/v4', () => () => 'CONSTANT_UUID'); 22 | 23 | const chartOptions = { 24 | type: 'column2d', 25 | width: 400, 26 | height: 300, 27 | dataFormat: 'json', 28 | dataSource: sampleData, 29 | }; 30 | 31 | function changeComponentProps(component, changes) { 32 | const mockedChangedProps = Object.assign({}, component.props, changes); 33 | component.componentWillReceiveProps(mockedChangedProps); 34 | component.props = mockedChangedProps; 35 | } 36 | 37 | describe('ReactFC', () => { 38 | it('should render as expected', () => { 39 | const tree = renderer.create().toJSON(); 42 | expect(tree).toMatchSnapshot(); 43 | }); 44 | 45 | it('should resolve the chart options passed through properties', () => { 46 | const mockedChartOptions = Object.assign({}, chartOptions, { 47 | chartConfig: { containerBackgroundColor: 'transparent' }, 48 | events: { dataPlotClick: () => { } }, 49 | link: {}, 50 | }); 51 | const instance = renderer.create().getInstance(); 54 | 55 | const resolvedChartOptions = instance.resolveChartOptions(instance.props); 56 | const expected = { 57 | ...fusionChartsOptions.reduce((options, optionName) => (options[optionName] = undefined, options), {}), 58 | ...chartOptions, 59 | events: Object.assign({}, mockedChartOptions.events), 60 | link: Object.assign({}, mockedChartOptions.link), 61 | ...mockedChartOptions.chartConfig, 62 | }; 63 | expect(resolvedChartOptions).toEqual(expected); 64 | }); 65 | 66 | it('should update the chart when chart data changes', () => { 67 | const instance = renderer.create().getInstance(); 70 | 71 | changeComponentProps(instance, { dataFormat: 'jsonurl', dataSource: 'data.json' }); 72 | expect(instance.oldOptions.dataFormat).toBe('jsonurl'); 73 | expect(instance.oldOptions.dataSource).toBe('data.json'); 74 | 75 | changeComponentProps(instance, { dataFormat: undefined }); 76 | expect(instance.oldOptions.dataFormat).toBeUndefined(); 77 | expect(instance.oldOptions.dataSource).toEqual('data.json'); 78 | 79 | changeComponentProps(instance, { dataSource: 'data2.json' }); 80 | expect(instance.oldOptions.dataFormat).toBeUndefined(); 81 | expect(instance.oldOptions.dataSource).toBe('data2.json'); 82 | 83 | changeComponentProps(instance, { dataFormat: 'json', dataSource: require('./data.json') }); 84 | expect(instance.oldOptions.dataFormat).toBe('json'); 85 | expect(instance.oldOptions.dataSource).toEqual(require('./data.json')); 86 | 87 | const newData = require('./data.json'); 88 | newData.chart.caption = 'New caption'; 89 | changeComponentProps(instance, { dataSource: newData }); 90 | expect(instance.oldOptions.dataFormat).toBe('json'); 91 | expect(instance.oldOptions.dataSource).toEqual(newData); 92 | 93 | changeComponentProps(instance, {}); 94 | expect(instance.oldOptions.dataFormat).toBe('json'); 95 | expect(instance.oldOptions.dataSource).toEqual(newData); 96 | 97 | changeComponentProps(instance, { dataSource: undefined }); 98 | expect(instance.oldOptions.dataFormat).toBe('json'); 99 | expect(instance.oldOptions.dataSource).toBeUndefined(); 100 | }); 101 | 102 | it('should update the chart when chart event changes', () => { 103 | const mockedChartOptions = Object.assign({}, chartOptions, { 104 | events: { 105 | event1() { }, 106 | event2() { }, 107 | }, 108 | }); 109 | const instance = renderer.create().getInstance(); 112 | 113 | changeComponentProps(instance, { events: { event1: () => { }, event2: () => { }, event3: () => { } } }); 114 | expect(Object.keys(instance.oldOptions.events).sort()).toEqual(['event1', 'event2', 'event3'].sort()); 115 | 116 | changeComponentProps(instance, { events: undefined }); 117 | expect(instance.oldOptions.events).toBeUndefined(); 118 | 119 | changeComponentProps(instance, { events: undefined }); 120 | expect(instance.oldOptions.events).toBeUndefined(); 121 | 122 | changeComponentProps(instance, { events: mockedChartOptions.events }); 123 | expect(Object.keys(instance.oldOptions.events).sort()).toEqual(['event1', 'event2'].sort()); 124 | 125 | changeComponentProps(instance, { events: { event1: mockedChartOptions.events.event1 } }); 126 | expect(Object.keys(instance.oldOptions.events).sort()).toEqual(['event1'].sort()); 127 | 128 | changeComponentProps(instance, { events: { event1: mockedChartOptions.events.event1 } }); 129 | expect(Object.keys(instance.oldOptions.events).sort()).toEqual(['event1'].sort()); 130 | 131 | changeComponentProps(instance, { events: { event1: mockedChartOptions.events.event2 } }); 132 | expect(Object.keys(instance.oldOptions.events).sort()).toEqual(['event1'].sort()); 133 | }); 134 | 135 | it('should update the chart when other chart configurations change', () => { 136 | const mockedChartOptions = Object.assign({}, chartOptions, { 137 | containerBackgroundColor: 'transparent', 138 | link: {}, 139 | }); 140 | const instance = renderer.create().getInstance(); 143 | 144 | changeComponentProps(instance, { containerBackgroundColor: '#000000' }); 145 | expect(instance.oldOptions.containerBackgroundColor).toBe('#000000'); 146 | expect(instance.oldOptions.link).toEqual({}); 147 | 148 | changeComponentProps(instance, { link: { link1: 'mocked_value' } }); 149 | expect(instance.oldOptions.containerBackgroundColor).toBe('#000000'); 150 | expect(instance.oldOptions.link).toEqual({ link1: 'mocked_value' }); 151 | 152 | changeComponentProps(instance, { containerBackgroundColor: undefined }); 153 | expect(instance.oldOptions.containerBackgroundColor).toBeUndefined(); 154 | expect(instance.oldOptions.link).toEqual({ link1: 'mocked_value' }); 155 | }); 156 | 157 | it('should ignore updating chart if there is no old options', () => { 158 | const instance = renderer.create().getInstance(); 161 | 162 | instance.oldOptions = null; 163 | changeComponentProps(instance, { width: 10 }); 164 | 165 | expect(instance.oldOptions).toBeNull(); 166 | }); 167 | 168 | it('should dispose the chart when chart container is unmounted', () => { 169 | const instance = renderer.create().getInstance(); 172 | 173 | const dispose = jest.fn(); 174 | instance.chartObj.dispose = dispose; 175 | instance.componentWillUnmount(); 176 | expect(dispose).toHaveBeenCalledTimes(1); 177 | }); 178 | 179 | it('should update the chart when chart dimensions are changed', () => { 180 | const instance = renderer.create().getInstance(); 183 | 184 | const resizeTo = jest.fn(); 185 | instance.chartObj.resizeTo = resizeTo; 186 | 187 | changeComponentProps(instance, { width: 100, height: 500 }); 188 | expect(resizeTo).toHaveBeenCalledWith(100, 500); 189 | 190 | changeComponentProps(instance, { width: 10, height: undefined }); 191 | expect(resizeTo).toHaveBeenCalledWith({ w: 10 }); 192 | 193 | changeComponentProps(instance, { width: undefined, height: 400 }); 194 | expect(resizeTo).toHaveBeenCalledWith({ h: 400 }); 195 | }); 196 | 197 | it('should update the chart when chart type is changed', () => { 198 | const instance = renderer.create().getInstance(); 201 | 202 | const chartType = jest.fn(); 203 | instance.chartObj.chartType = chartType; 204 | 205 | changeComponentProps(instance, { type: 'bar2d' }); 206 | expect(chartType).toHaveBeenCalledWith('bar2d'); 207 | 208 | chartType.mockReset(); 209 | changeComponentProps(instance, { type: undefined }); 210 | expect(chartType).toHaveBeenCalledTimes(0); 211 | }); 212 | 213 | it('should update the chart when other chart attributes are changed', () => { 214 | const instance = renderer.create().getInstance(); 217 | 218 | instance.chartObj.options = { ...instance.oldOptions }; 219 | 220 | changeComponentProps(instance, { containerBackgroundColor: '#ffffff' }); 221 | expect(instance.chartObj.options.containerBackgroundColor).toBe('#ffffff'); 222 | }); 223 | }); 224 | 225 | 226 | describe('ReactFC.fcRoot(core, ...modules)', () => { 227 | it('should resolve the FusionCharts core with the specified modules', () => { 228 | const core = jest.fn(); 229 | const m1 = jest.fn(); 230 | const m2 = jest.fn(); 231 | 232 | ReactFC.fcRoot(core, m1, m2); 233 | 234 | expect(ReactFC.fusionChartsCore).toBe(core); 235 | expect(m1).toHaveBeenCalledTimes(1); 236 | expect(m2).toHaveBeenCalledTimes(1); 237 | }); 238 | }); 239 | -------------------------------------------------------------------------------- /__tests__/__snapshots__/ReactFC.spec.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`ReactFC should render as expected 1`] = ` 4 |
8 | `; 9 | -------------------------------------------------------------------------------- /__tests__/data.json: -------------------------------------------------------------------------------- 1 | { 2 | "chart": { 3 | "caption": "Actual Revenues, Targeted Revenues & Profits", 4 | "subcaption": "Last year", 5 | "xaxisname": "Month", 6 | "yaxisname": "Amount (In USD)", 7 | "numberprefix": "$", 8 | "borderThickness": 0, 9 | "theme": "fint" 10 | }, 11 | "categories": [ 12 | { 13 | "category": [ 14 | { 15 | "label": "Jan" 16 | }, 17 | { 18 | "label": "Feb" 19 | }, 20 | { 21 | "label": "Mar" 22 | }, 23 | { 24 | "label": "Apr" 25 | }, 26 | { 27 | "label": "May" 28 | }, 29 | { 30 | "label": "Jun" 31 | }, 32 | { 33 | "label": "Jul" 34 | }, 35 | { 36 | "label": "Aug" 37 | }, 38 | { 39 | "label": "Sep" 40 | }, 41 | { 42 | "label": "Oct" 43 | }, 44 | { 45 | "label": "Nov" 46 | }, 47 | { 48 | "label": "Dec" 49 | } 50 | ] 51 | } 52 | ], 53 | "dataset": [ 54 | { 55 | "seriesname": "Actual Revenue", 56 | "data": [ 57 | { 58 | "value": "16000" 59 | }, 60 | { 61 | "value": "20000" 62 | }, 63 | { 64 | "value": "18000" 65 | }, 66 | { 67 | "value": "19000" 68 | }, 69 | { 70 | "value": "15000" 71 | }, 72 | { 73 | "value": "21000" 74 | }, 75 | { 76 | "value": "16000" 77 | }, 78 | { 79 | "value": "20000" 80 | }, 81 | { 82 | "value": "17000" 83 | }, 84 | { 85 | "value": "25000" 86 | }, 87 | { 88 | "value": "19000" 89 | }, 90 | { 91 | "value": "23000" 92 | } 93 | ] 94 | }, 95 | { 96 | "seriesname": "Projected Revenue", 97 | "renderas": "line", 98 | "showvalues": "0", 99 | "data": [ 100 | { 101 | "value": "15000" 102 | }, 103 | { 104 | "value": "16000" 105 | }, 106 | { 107 | "value": "17000" 108 | }, 109 | { 110 | "value": "18000" 111 | }, 112 | { 113 | "value": "19000" 114 | }, 115 | { 116 | "value": "19000" 117 | }, 118 | { 119 | "value": "19000" 120 | }, 121 | { 122 | "value": "19000" 123 | }, 124 | { 125 | "value": "20000" 126 | }, 127 | { 128 | "value": "21000" 129 | }, 130 | { 131 | "value": "22000" 132 | }, 133 | { 134 | "value": "23000" 135 | } 136 | ] 137 | }, 138 | { 139 | "seriesname": "Profit", 140 | "renderas": "area", 141 | "showvalues": "0", 142 | "data": [ 143 | { 144 | "value": "4000" 145 | }, 146 | { 147 | "value": "5000" 148 | }, 149 | { 150 | "value": "3000" 151 | }, 152 | { 153 | "value": "4000" 154 | }, 155 | { 156 | "value": "1000" 157 | }, 158 | { 159 | "value": "7000" 160 | }, 161 | { 162 | "value": "1000" 163 | }, 164 | { 165 | "value": "4000" 166 | }, 167 | { 168 | "value": "1000" 169 | }, 170 | { 171 | "value": "8000" 172 | }, 173 | { 174 | "value": "2000" 175 | }, 176 | { 177 | "value": "7000" 178 | } 179 | ] 180 | } 181 | ] 182 | } -------------------------------------------------------------------------------- /__tests__/utils/options.spec.js: -------------------------------------------------------------------------------- 1 | import fusonChartsOptions from '../../src/utils/options'; 2 | 3 | /* global describe it expect */ 4 | describe('It', () => { 5 | it('should return all FusionCharts supported chart options', () => { 6 | expect(fusonChartsOptions).toEqual([ 7 | 'type', 8 | 'id', 9 | 'width', 10 | 'height', 11 | 'dataFormat', 12 | 'dataSource', 13 | 'events', 14 | 'link', 15 | 'showDataLoadingMessage', 16 | 'showChartLoadingMessage', 17 | 'baseChartMessageFont', 18 | 'baseChartMessageFontSize', 19 | 'baseChartMessageColor', 20 | 'dataLoadStartMessage', 21 | 'dataLoadErrorMessage', 22 | 'dataInvalidMessage', 23 | 'dataEmptyMessage', 24 | 'typeNotSupportedMessage', 25 | 'loadMessage', 26 | 'renderErrorMessage', 27 | 'containerBackgroundColor', 28 | 'containerBackgroundOpacity', 29 | 'containerClassName', 30 | 'baseChartMessageImageHAlign', 31 | 'baseChartMessageImageVAlign', 32 | 'baseChartMessageImageAlpha', 33 | 'baseChartMessageImageScale', 34 | 'typeNotSupportedMessageImageHAalign', 35 | 'typeNotSupportedMessageImageVAlign', 36 | 'typeNotSupportedMessageImageAlpha', 37 | 'typeNotSupportedMessageImageScale', 38 | 'dataLoadErrorMessageImageHAlign', 39 | 'dataLoadErrorMessageImageVAlign', 40 | 'dataLoadErrorMessageImageAlpha', 41 | 'dataLoadErrorMessageImageScale', 42 | 'dataLoadStartMessageImageHAlign', 43 | 'dataLoadStartMessageImageVAlign', 44 | 'dataLoadStartMessageImageAlpha', 45 | 'dataLoadStartMessageImageScale', 46 | 'dataInvalidMessageImageHAlign', 47 | 'dataInvalidMessageImageVAlign', 48 | 'dataInvalidMessageImageAlpha', 49 | 'dataInvalidMessageImageScale', 50 | 'dataEmptyMessageImageHAlign', 51 | 'dataEmptyMessageImageVAlign', 52 | 'dataEmptyMessageImageAlpha', 53 | 'dataEmptyMessageImageScale', 54 | 'renderErrorMessageImageHAlign', 55 | 'renderErrorMessageImageVAlign', 56 | 'renderErrorMessageImageAlpha', 57 | 'renderErrorMessageImageScale', 58 | 'loadMessageImageHAlign', 59 | 'loadMessageImageVAlign', 60 | 'loadMessageImageAlpha', 61 | 'loadMessageImageScale', 62 | ]); 63 | }); 64 | }); 65 | -------------------------------------------------------------------------------- /__tests__/utils/utils.spec.js: -------------------------------------------------------------------------------- 1 | import { 2 | isObject, 3 | isCallable, 4 | isSameObjectContent, 5 | isUndefined, 6 | deepCopyOf, 7 | } from '../../src/utils/utils'; 8 | 9 | /* global describe it expect */ 10 | describe('isObject(value)', () => { 11 | it('should return true for object value', () => { 12 | expect(isObject({})).toBeTruthy(); 13 | }); 14 | 15 | it('should return false for null value', () => { 16 | expect(isObject(null)).toBeFalsy(); 17 | }); 18 | 19 | it('should return false for non-object values', () => { 20 | expect(isObject(undefined)).toBeFalsy(); 21 | expect(isObject(22)).toBeFalsy(); 22 | expect(isObject('string')).toBeFalsy(); 23 | expect(isObject(() => { })).toBeFalsy(); 24 | expect(isObject(true)).toBeFalsy(); 25 | }); 26 | }); 27 | 28 | describe('isCallable(value)', () => { 29 | it('should return true for function value', () => { 30 | expect(isCallable(() => { })).toBeTruthy(); 31 | }); 32 | 33 | it('should return false for non-function values', () => { 34 | expect(isCallable(null)).toBeFalsy(); 35 | expect(isCallable(undefined)).toBeFalsy(); 36 | expect(isCallable({})).toBeFalsy(); 37 | expect(isCallable('string')).toBeFalsy(); 38 | expect(isCallable(true)).toBeFalsy(); 39 | expect(isCallable(33)).toBeFalsy(); 40 | }); 41 | }); 42 | 43 | describe('isSameObjectContent(obj1, obj2)', () => { 44 | it('should return true for two objects with same content of one deep', () => { 45 | expect(isSameObjectContent( 46 | { 47 | a: 1, 48 | b: 2, 49 | }, 50 | { 51 | a: 1, 52 | b: 2, 53 | }, 54 | )).toBeTruthy(); 55 | }); 56 | 57 | it('should return true for two objects with same content of multiple deeps', () => { 58 | expect(isSameObjectContent( 59 | { 60 | a: { 61 | c: 33, 62 | d: 77, 63 | }, 64 | b: { 65 | e: 22, 66 | }, 67 | }, 68 | { 69 | a: { 70 | c: 33, 71 | d: 77, 72 | }, 73 | b: { 74 | e: 22, 75 | }, 76 | }, 77 | )).toBeTruthy(); 78 | }); 79 | 80 | it('should return false for two objects with different content', () => { 81 | expect(isSameObjectContent( 82 | {}, 83 | { 84 | a: 44, 85 | }, 86 | )).toBeFalsy(); 87 | 88 | expect(isSameObjectContent( 89 | { 90 | a: { 91 | c: 33, 92 | d: 77, 93 | }, 94 | b: { 95 | e: 22, 96 | }, 97 | }, 98 | { 99 | a: { 100 | c: 33, 101 | d: 564, 102 | }, 103 | b: { 104 | m: 255, 105 | }, 106 | }, 107 | )).toBeFalsy(); 108 | }); 109 | 110 | expect(isSameObjectContent( 111 | { 112 | a: 2, 113 | }, 114 | { 115 | b: 2, 116 | }, 117 | )).toBeFalsy(); 118 | }); 119 | 120 | describe('isUndefined(value)', () => { 121 | it('should return true for undefined value', () => { 122 | expect(isUndefined(undefined)).toBeTruthy(); 123 | }); 124 | 125 | it('should return false for values other than undefined', () => { 126 | expect(isUndefined(78)).toBeFalsy(); 127 | expect(isUndefined('string')).toBeFalsy(); 128 | expect(isUndefined(null)).toBeFalsy(); 129 | expect(isUndefined({})).toBeFalsy(); 130 | expect(isUndefined(true)).toBeFalsy(); 131 | expect(isUndefined(() => { })).toBeFalsy(); 132 | }); 133 | }); 134 | 135 | describe('deepCopyOf(obj)', () => { 136 | it('should return different object instance with same content other than given object', () => { 137 | const obj = { 138 | a: 1, 139 | b: { 140 | c: 3, 141 | d: 44, 142 | }, 143 | }; 144 | const deepCopy = deepCopyOf(obj); 145 | expect(isSameObjectContent(obj, deepCopy) && obj !== deepCopy).toBeTruthy(); 146 | }); 147 | }); 148 | 149 | -------------------------------------------------------------------------------- /components/DrillDown.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports["default"] = void 0; 7 | var _react = _interopRequireDefault(require("react")); 8 | var _propTypes = _interopRequireDefault(require("prop-types")); 9 | var _ReactFC = _interopRequireDefault(require("../lib/ReactFC")); 10 | function _interopRequireDefault(e) { return e && e.__esModule ? e : { "default": e }; } 11 | function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); } 12 | function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); } 13 | function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; } 14 | function _slicedToArray(r, e) { return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest(); } 15 | function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } 16 | function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } } 17 | function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; } 18 | function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t["return"] && (u = t["return"](), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } } 19 | function _arrayWithHoles(r) { if (Array.isArray(r)) return r; } 20 | function _classCallCheck(a, n) { if (!(a instanceof n)) throw new TypeError("Cannot call a class as a function"); } 21 | function _defineProperties(e, r) { for (var t = 0; t < r.length; t++) { var o = r[t]; o.enumerable = o.enumerable || !1, o.configurable = !0, "value" in o && (o.writable = !0), Object.defineProperty(e, _toPropertyKey(o.key), o); } } 22 | function _createClass(e, r, t) { return r && _defineProperties(e.prototype, r), t && _defineProperties(e, t), Object.defineProperty(e, "prototype", { writable: !1 }), e; } 23 | function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; } 24 | function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); } 25 | function _callSuper(t, o, e) { return o = _getPrototypeOf(o), _possibleConstructorReturn(t, _isNativeReflectConstruct() ? Reflect.construct(o, e || [], _getPrototypeOf(t).constructor) : o.apply(t, e)); } 26 | function _possibleConstructorReturn(t, e) { if (e && ("object" == _typeof(e) || "function" == typeof e)) return e; if (void 0 !== e) throw new TypeError("Derived constructors may only return object or undefined"); return _assertThisInitialized(t); } 27 | function _assertThisInitialized(e) { if (void 0 === e) throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); return e; } 28 | function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); } catch (t) {} return (_isNativeReflectConstruct = function _isNativeReflectConstruct() { return !!t; })(); } 29 | function _getPrototypeOf(t) { return _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf.bind() : function (t) { return t.__proto__ || Object.getPrototypeOf(t); }, _getPrototypeOf(t); } 30 | function _inherits(t, e) { if ("function" != typeof e && null !== e) throw new TypeError("Super expression must either be null or a function"); t.prototype = Object.create(e && e.prototype, { constructor: { value: t, writable: !0, configurable: !0 } }), Object.defineProperty(t, "prototype", { writable: !1 }), e && _setPrototypeOf(t, e); } 31 | function _setPrototypeOf(t, e) { return _setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function (t, e) { return t.__proto__ = e, t; }, _setPrototypeOf(t, e); } 32 | var DrillDown = /*#__PURE__*/function (_React$Component) { 33 | function DrillDown(props) { 34 | var _this; 35 | _classCallCheck(this, DrillDown); 36 | _this = _callSuper(this, DrillDown, [props]); 37 | var btnConfig = props.btnConfig, 38 | btnStyle = props.btnStyle; 39 | _this.finalBtnConfig = Object.assign({}, DrillDown.defaultProps.btnConfig, btnConfig); 40 | var _this$finalBtnConfig$ = _this.finalBtnConfig.placement.split('-'); 41 | var _this$finalBtnConfig$2 = _slicedToArray(_this$finalBtnConfig$, 2); 42 | _this.positionV = _this$finalBtnConfig$2[0]; 43 | _this.positionH = _this$finalBtnConfig$2[1]; 44 | _this.state = { 45 | isBtnVisible: true, 46 | selectedChild: -1, 47 | isDrilledDown: false 48 | }; 49 | 50 | /* Function Bindings */ 51 | _this.plotClicked = _this.plotClicked.bind(_this); 52 | _this.onChildRendered = _this.onChildRendered.bind(_this); 53 | _this.toggleParentBtnVisibility = _this.toggleParentBtnVisibility.bind(_this); 54 | _this.onBtnClick = _this.onBtnClick.bind(_this); 55 | 56 | /* Default styles */ 57 | _this.wrapperStyle = { 58 | position: 'relative', 59 | display: 'inline-block' 60 | }; 61 | _this.defButtonStyle = _defineProperty(_defineProperty(_defineProperty({ 62 | border: "1px solid ".concat(_this.finalBtnConfig.borderColor), 63 | backgroundColor: "".concat(_this.finalBtnConfig.backgroundColor), 64 | color: "".concat(_this.finalBtnConfig.color), 65 | fontFamily: "".concat(_this.finalBtnConfig.fontFamily), 66 | fontSize: "".concat(_this.finalBtnConfig.fontSize), 67 | padding: "".concat(_this.finalBtnConfig.padding), 68 | fontWeight: "".concat(_this.finalBtnConfig.fontWeight), 69 | position: 'absolute' 70 | }, _this.positionH, "".concat(_this.finalBtnConfig.margin)), _this.positionV, "".concat(_this.finalBtnConfig.margin)), "cursor", 'pointer'); 71 | _this.finBtnStyle = typeof btnStyle === 'undefined' ? _this.defButtonStyle : btnStyle; 72 | return _this; 73 | } 74 | _inherits(DrillDown, _React$Component); 75 | return _createClass(DrillDown, [{ 76 | key: "determinePlotMapType", 77 | value: function determinePlotMapType(plotChildMap) { 78 | var isNumberFound = false; 79 | var isObjectFound = false; 80 | plotChildMap.forEach(function (val) { 81 | if (typeof val === 'undefined' || val === null) return; 82 | if (typeof val === 'number' && val > -1) isNumberFound = true; 83 | if (_typeof(val) === 'object') isObjectFound = true; 84 | }); 85 | if (isNumberFound && isObjectFound) { 86 | return 'invalid'; 87 | } 88 | if (isNumberFound) return 'number'; 89 | if (isObjectFound) return 'object'; 90 | return 'noop'; 91 | } 92 | }, { 93 | key: "plotClicked", 94 | value: function plotClicked(e) { 95 | var index = e.data.index; 96 | var propChildren = Array.isArray(this.props.children) ? this.props.children : [this.props.children]; 97 | var childrenLen = propChildren.length; 98 | var plotChildMap = this.props.plotChildMap; 99 | if (childrenLen === 0) return; 100 | 101 | // Further Optimization needed. 102 | var mapType = this.determinePlotMapType(plotChildMap); 103 | 104 | // Case : Array of numbers 105 | if (mapType === 'number') { 106 | var childPosition = plotChildMap[index]; 107 | if (childPosition === null || typeof childPosition === 'undefined' || childPosition >= childrenLen || childPosition < 0) return; 108 | this.setState({ 109 | selectedChild: childPosition, 110 | isDrilledDown: true 111 | }); 112 | } 113 | 114 | // Case : Array of objects 115 | if (mapType === 'object') { 116 | for (var i = 0; i < childrenLen; i++) { 117 | if (typeof plotChildMap[i] === 'undefined' || plotChildMap[i] === null) continue; 118 | var _plotChildMap$i = plotChildMap[i], 119 | plotPosition = _plotChildMap$i.plotPosition, 120 | _childPosition = _plotChildMap$i.childPosition; 121 | if (plotPosition === index && _childPosition !== null && typeof _childPosition !== 'undefined' && _childPosition < childrenLen && _childPosition > -1) { 122 | this.setState({ 123 | selectedChild: _childPosition, 124 | isDrilledDown: true 125 | }); 126 | return; 127 | } 128 | } 129 | } 130 | 131 | // Case : Heterogeneous 132 | if (mapType === 'invalid') { 133 | console.log('Invalid heterogeneous data: Please check proptypes for - plotChildMap'); 134 | } 135 | } 136 | }, { 137 | key: "cloneReactFCChild", 138 | value: function cloneReactFCChild(reactFCElem, customProps) { 139 | return /*#__PURE__*/_react["default"].cloneElement(reactFCElem, customProps); 140 | } 141 | }, { 142 | key: "onChildRendered", 143 | value: function onChildRendered() { 144 | if (this.props.toggleParentBtnVisibility) { 145 | this.props.toggleParentBtnVisibility(false); 146 | } 147 | } 148 | }, { 149 | key: "toggleParentBtnVisibility", 150 | value: function toggleParentBtnVisibility(isBtnVisible) { 151 | this.setState({ 152 | isBtnVisible: isBtnVisible 153 | }); 154 | } 155 | }, { 156 | key: "onBtnClick", 157 | value: function onBtnClick() { 158 | this.setState({ 159 | isDrilledDown: false 160 | }); 161 | if (this.props.toggleParentBtnVisibility) { 162 | this.props.toggleParentBtnVisibility(true); 163 | } 164 | } 165 | }, { 166 | key: "render", 167 | value: function render() { 168 | var component; 169 | var _this$state = this.state, 170 | selectedChild = _this$state.selectedChild, 171 | isBtnVisible = _this$state.isBtnVisible; 172 | var _this$props = this.props, 173 | children = _this$props.children, 174 | width = _this$props.width, 175 | height = _this$props.height; 176 | var clonedElemConfig = { 177 | width: width, 178 | height: height, 179 | onRender: this.onChildRendered, 180 | toggleParentBtnVisibility: this.toggleParentBtnVisibility 181 | }; 182 | if (!this.state.isDrilledDown) { 183 | component = /*#__PURE__*/_react["default"].createElement(_ReactFC["default"], _extends({}, this.props, { 184 | "fcEvent-dataplotClick": this.plotClicked 185 | })); 186 | } else { 187 | var propChildren = Array.isArray(children) ? children : [children]; 188 | component = /*#__PURE__*/_react["default"].createElement("div", { 189 | style: this.wrapperStyle 190 | }, this.cloneReactFCChild(propChildren[selectedChild], clonedElemConfig), isBtnVisible ? /*#__PURE__*/_react["default"].createElement("button", { 191 | style: this.finBtnStyle, 192 | onClick: this.onBtnClick 193 | }, this.finalBtnConfig.text) : null); 194 | } 195 | return component; 196 | } 197 | }], [{ 198 | key: "fcRoot", 199 | value: 200 | // Resolve FusionCharts 201 | function fcRoot(core) { 202 | for (var _len = arguments.length, modules = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { 203 | modules[_key - 1] = arguments[_key]; 204 | } 205 | _ReactFC["default"].fcRoot.apply(_ReactFC["default"], [core].concat(modules)); 206 | } 207 | }]); 208 | }(_react["default"].Component); 209 | DrillDown.defaultProps = { 210 | plotChildMap: [], 211 | btnConfig: { 212 | text: 'Back', 213 | color: '#000000', 214 | backgroundColor: '#F6F6F6', 215 | borderColor: '#000000', 216 | fontSize: '14px', 217 | fontWeight: 'bold', 218 | padding: '3px', 219 | fontFamily: 'Verdana, sans', 220 | placement: 'top-right', 221 | margin: '10px' 222 | }, 223 | btnStyle: undefined, 224 | dataSource: {}, 225 | dataFormat: 'json', 226 | type: '', 227 | height: '', 228 | width: '' 229 | }; 230 | DrillDown.propTypes = { 231 | plotChildMap: _propTypes["default"].oneOfType([_propTypes["default"].arrayOf(_propTypes["default"].shape({ 232 | plotPosition: _propTypes["default"].number, 233 | childPosition: _propTypes["default"].number 234 | })), _propTypes["default"].arrayOf(_propTypes["default"].number)]), 235 | btnConfig: _propTypes["default"].shape({ 236 | text: _propTypes["default"].string, 237 | color: _propTypes["default"].string, 238 | backgroundColor: _propTypes["default"].string, 239 | borderColor: _propTypes["default"].string, 240 | fontSize: _propTypes["default"].string, 241 | fontWeight: _propTypes["default"].string, 242 | padding: _propTypes["default"].string, 243 | fontFamily: _propTypes["default"].string, 244 | placement: _propTypes["default"].oneOf(['top-left', 'top-right', 'bottom-left', 'bottom-right']), 245 | margin: _propTypes["default"].string 246 | }), 247 | btnStyle: _propTypes["default"].object, 248 | dataSource: _propTypes["default"].object, 249 | dataFormat: _propTypes["default"].string, 250 | type: _propTypes["default"].string, 251 | height: _propTypes["default"].string, 252 | width: _propTypes["default"].string 253 | }; 254 | var _default = exports["default"] = DrillDown; -------------------------------------------------------------------------------- /dist/drill-down.min.js: -------------------------------------------------------------------------------- 1 | !function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t(require("react")):"function"==typeof define&&define.amd?define("DrillDown",["react"],t):"object"==typeof exports?exports.DrillDown=t(require("react")):e.DrillDown=t(e.React)}(self,(e=>(()=>{var t={359:(e,t,n)=>{"use strict";t.A=void 0;var r=c(n(442)),o=c(n(550)),a=function(e,t){if(e&&e.__esModule)return e;if(null===e||"object"!=l(e)&&"function"!=typeof e)return{default:e};var n=s(t);if(n&&n.has(e))return n.get(e);var r={__proto__:null},o=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var a in e)if("default"!==a&&{}.hasOwnProperty.call(e,a)){var i=o?Object.getOwnPropertyDescriptor(e,a):null;i&&(i.get||i.set)?Object.defineProperty(r,a,i):r[a]=e[a]}return r.default=e,n&&n.set(e,r),r}(n(104)),i=c(n(39));function s(e){if("function"!=typeof WeakMap)return null;var t=new WeakMap,n=new WeakMap;return(s=function(e){return e?n:t})(e)}function c(e){return e&&e.__esModule?e:{default:e}}function l(e){return l="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},l(e)}function u(e,t){for(var n=0;n1?n-1:0),o=1;o0&&(void 0===t.events?t.events=n:t.events=Object.assign(t.events,n)),this.chartObj=new this.FusionCharts(t),this.chartObj.render(),this.oldOptions=t,this.props.onRender&&"function"==typeof this.props.onRender&&this.props.onRender(this.chartObj)}},{key:"resolveChartOptions",value:function(e){var t=e.chartConfig?e.chartConfig:{},n=i.default.reduce((function(t,n){return t[n]=e[n],t}),{});return Object.assign(n,t),a.isObject(n.dataSource)&&!a.checkIfDataTableExists(n.dataSource)?n.dataSource=a.deepCopyOf(n.dataSource):a.isObject(n.dataSource)&&a.checkIfDataTableExists(n.dataSource)&&(n.dataSource=a.cloneDataSource(n.dataSource,"clone")),a.isObject(n.link)&&(n.link=a.deepCopyOf(n.link)),a.isObject(n.events)&&(n.events=Object.assign({},n.events)),n}},{key:"render",value:function(){return r.default.createElement("div",{className:this.props.className,id:this.containerId})}}])&&u(n.prototype,s),c&&u(n,c),Object.defineProperty(n,"prototype",{writable:!1}),n;var n,s,c}(r.default.Component);t.A=g},39:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0,t.default=["type","id","width","height","dataFormat","dataSource","events","link","showDataLoadingMessage","showChartLoadingMessage","baseChartMessageFont","baseChartMessageFontSize","baseChartMessageColor","dataLoadStartMessage","dataLoadErrorMessage","dataInvalidMessage","dataEmptyMessage","typeNotSupportedMessage","loadMessage","renderErrorMessage","containerBackgroundColor","containerBackgroundOpacity","containerClassName","baseChartMessageImageHAlign","baseChartMessageImageVAlign","baseChartMessageImageAlpha","baseChartMessageImageScale","typeNotSupportedMessageImageHAalign","typeNotSupportedMessageImageVAlign","typeNotSupportedMessageImageAlpha","typeNotSupportedMessageImageScale","dataLoadErrorMessageImageHAlign","dataLoadErrorMessageImageVAlign","dataLoadErrorMessageImageAlpha","dataLoadErrorMessageImageScale","dataLoadStartMessageImageHAlign","dataLoadStartMessageImageVAlign","dataLoadStartMessageImageAlpha","dataLoadStartMessageImageScale","dataInvalidMessageImageHAlign","dataInvalidMessageImageVAlign","dataInvalidMessageImageAlpha","dataInvalidMessageImageScale","dataEmptyMessageImageHAlign","dataEmptyMessageImageVAlign","dataEmptyMessageImageAlpha","dataEmptyMessageImageScale","renderErrorMessageImageHAlign","renderErrorMessageImageVAlign","renderErrorMessageImageAlpha","renderErrorMessageImageScale","loadMessageImageHAlign","loadMessageImageVAlign","loadMessageImageAlpha","loadMessageImageScale"]},104:(e,t)=>{"use strict";function n(e){return n="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},n(e)}function r(e){return null!==e&&"object"===n(e)}Object.defineProperty(t,"__esModule",{value:!0}),t.checkIfDataTableExists=function(e){return!!(e&&e.data&&e.data._dataStore)},t.cloneDataSource=function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"clone",r=n(e);if("string"===r||"number"===r||"function"===r||"boolean"===r)return e;if(null==e)return e;if(Array.isArray(e)){for(var o=[],a=0;a{"use strict";n.d(t,{default:()=>m});var r=n(442),o=n.n(r),a=n(556),i=n.n(a),s=n(359);function c(){return c=Object.assign?Object.assign.bind():function(e){for(var t=1;te.length)&&(t=e.length);for(var n=0,r=Array(t);n-1&&(t=!0),"object"===l(e)&&(n=!0))})),t&&n?"invalid":t?"number":n?"object":"noop"}},{key:"plotClicked",value:function(e){var t=e.data.index,n=(Array.isArray(this.props.children)?this.props.children:[this.props.children]).length,r=this.props.plotChildMap;if(0!==n){var o=this.determinePlotMapType(r);if("number"===o){var a=r[t];if(null==a||a>=n||a<0)return;this.setState({selectedChild:a,isDrilledDown:!0})}if("object"===o)for(var i=0;i-1)return void this.setState({selectedChild:l,isDrilledDown:!0})}"invalid"===o&&console.log("Invalid heterogeneous data: Please check proptypes for - plotChildMap")}}},{key:"cloneReactFCChild",value:function(e,t){return o().cloneElement(e,t)}},{key:"onChildRendered",value:function(){this.props.toggleParentBtnVisibility&&this.props.toggleParentBtnVisibility(!1)}},{key:"toggleParentBtnVisibility",value:function(e){this.setState({isBtnVisible:e})}},{key:"onBtnClick",value:function(){this.setState({isDrilledDown:!1}),this.props.toggleParentBtnVisibility&&this.props.toggleParentBtnVisibility(!0)}},{key:"render",value:function(){var e,t=this.state,n=t.selectedChild,r=t.isBtnVisible,a=this.props,i=a.children,l={width:a.width,height:a.height,onRender:this.onChildRendered,toggleParentBtnVisibility:this.toggleParentBtnVisibility};if(this.state.isDrilledDown){var u=Array.isArray(i)?i:[i];e=o().createElement("div",{style:this.wrapperStyle},this.cloneReactFCChild(u[n],l),r?o().createElement("button",{style:this.finBtnStyle,onClick:this.onBtnClick},this.finalBtnConfig.text):null)}else e=o().createElement(s.A,c({},this.props,{"fcEvent-dataplotClick":this.plotClicked}));return e}}],a=[{key:"fcRoot",value:function(e){for(var t=arguments.length,n=new Array(t>1?t-1:0),r=1;r{e.exports=n(343).default},694:(e,t,n)=>{"use strict";var r=n(925);function o(){}function a(){}a.resetWarningCache=o,e.exports=function(){function e(e,t,n,o,a,i){if(i!==r){var s=new Error("Calling PropTypes validators directly is not supported by the `prop-types` package. Use PropTypes.checkPropTypes() to call them. Read more at http://fb.me/use-check-prop-types");throw s.name="Invariant Violation",s}}function t(){return e}e.isRequired=e;var n={array:e,bigint:e,bool:e,func:e,number:e,object:e,string:e,symbol:e,any:e,arrayOf:t,element:e,elementType:e,instanceOf:t,node:e,objectOf:t,oneOf:t,oneOfType:t,shape:t,exact:t,checkPropTypes:a,resetWarningCache:o};return n.PropTypes=n,n}},556:(e,t,n)=>{e.exports=n(694)()},925:e=>{"use strict";e.exports="SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED"},471:e=>{for(var t=[],n=0;n<256;++n)t[n]=(n+256).toString(16).substr(1);e.exports=function(e,n){var r=n||0,o=t;return[o[e[r++]],o[e[r++]],o[e[r++]],o[e[r++]],"-",o[e[r++]],o[e[r++]],"-",o[e[r++]],o[e[r++]],"-",o[e[r++]],o[e[r++]],"-",o[e[r++]],o[e[r++]],o[e[r++]],o[e[r++]],o[e[r++]],o[e[r++]]].join("")}},814:e=>{var t="undefined"!=typeof crypto&&crypto.getRandomValues&&crypto.getRandomValues.bind(crypto)||"undefined"!=typeof msCrypto&&"function"==typeof window.msCrypto.getRandomValues&&msCrypto.getRandomValues.bind(msCrypto);if(t){var n=new Uint8Array(16);e.exports=function(){return t(n),n}}else{var r=new Array(16);e.exports=function(){for(var e,t=0;t<16;t++)3&t||(e=4294967296*Math.random()),r[t]=e>>>((3&t)<<3)&255;return r}}},550:(e,t,n)=>{var r=n(814),o=n(471);e.exports=function(e,t,n){var a=t&&n||0;"string"==typeof e&&(t="binary"===e?new Array(16):null,e=null);var i=(e=e||{}).random||(e.rng||r)();if(i[6]=15&i[6]|64,i[8]=63&i[8]|128,t)for(var s=0;s<16;++s)t[a+s]=i[s];return t||o(i)}},442:t=>{"use strict";t.exports=e}},n={};function r(e){var o=n[e];if(void 0!==o)return o.exports;var a=n[e]={exports:{}};return t[e](a,a.exports,r),a.exports}return r.n=e=>{var t=e&&e.__esModule?()=>e.default:()=>e;return r.d(t,{a:t}),t},r.d=(e,t)=>{for(var n in t)r.o(t,n)&&!r.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:t[n]})},r.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),r(840)})())); -------------------------------------------------------------------------------- /dist/react-fusioncharts.js: -------------------------------------------------------------------------------- 1 | (function webpackUniversalModuleDefinition(root, factory) { 2 | if(typeof exports === 'object' && typeof module === 'object') 3 | module.exports = factory(require("react")); 4 | else if(typeof define === 'function' && define.amd) 5 | define("ReactFC", ["react"], factory); 6 | else if(typeof exports === 'object') 7 | exports["ReactFC"] = factory(require("react")); 8 | else 9 | root["ReactFC"] = factory(root["React"]); 10 | })(self, (__WEBPACK_EXTERNAL_MODULE__2__) => { 11 | return /******/ (() => { // webpackBootstrap 12 | /******/ var __webpack_modules__ = ([ 13 | /* 0 */ 14 | /***/ ((module, __unused_webpack_exports, __webpack_require__) => { 15 | 16 | // import ReactFC from './src/ReactFC'; 17 | 18 | // Use this format to export ReactFC as default module 19 | // Ref: https://gist.github.com/iamakulov/966b91c0fc6363a16ff0650b51fb991b 20 | // export default ReactFC; 21 | 22 | module.exports = __webpack_require__(1)["default"]; 23 | 24 | /***/ }), 25 | /* 1 */ 26 | /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { 27 | 28 | "use strict"; 29 | __webpack_require__.r(__webpack_exports__); 30 | /* harmony export */ __webpack_require__.d(__webpack_exports__, { 31 | /* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) 32 | /* harmony export */ }); 33 | /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(2); 34 | /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__); 35 | /* harmony import */ var uuid_v4__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3); 36 | /* harmony import */ var uuid_v4__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(uuid_v4__WEBPACK_IMPORTED_MODULE_1__); 37 | /* harmony import */ var _utils_utils__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(6); 38 | /* harmony import */ var _utils_options__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(7); 39 | function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); } 40 | function _classCallCheck(a, n) { if (!(a instanceof n)) throw new TypeError("Cannot call a class as a function"); } 41 | function _defineProperties(e, r) { for (var t = 0; t < r.length; t++) { var o = r[t]; o.enumerable = o.enumerable || !1, o.configurable = !0, "value" in o && (o.writable = !0), Object.defineProperty(e, _toPropertyKey(o.key), o); } } 42 | function _createClass(e, r, t) { return r && _defineProperties(e.prototype, r), t && _defineProperties(e, t), Object.defineProperty(e, "prototype", { writable: !1 }), e; } 43 | function _callSuper(t, o, e) { return o = _getPrototypeOf(o), _possibleConstructorReturn(t, _isNativeReflectConstruct() ? Reflect.construct(o, e || [], _getPrototypeOf(t).constructor) : o.apply(t, e)); } 44 | function _possibleConstructorReturn(t, e) { if (e && ("object" == _typeof(e) || "function" == typeof e)) return e; if (void 0 !== e) throw new TypeError("Derived constructors may only return object or undefined"); return _assertThisInitialized(t); } 45 | function _assertThisInitialized(e) { if (void 0 === e) throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); return e; } 46 | function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); } catch (t) {} return (_isNativeReflectConstruct = function _isNativeReflectConstruct() { return !!t; })(); } 47 | function _getPrototypeOf(t) { return _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf.bind() : function (t) { return t.__proto__ || Object.getPrototypeOf(t); }, _getPrototypeOf(t); } 48 | function _inherits(t, e) { if ("function" != typeof e && null !== e) throw new TypeError("Super expression must either be null or a function"); t.prototype = Object.create(e && e.prototype, { constructor: { value: t, writable: !0, configurable: !0 } }), Object.defineProperty(t, "prototype", { writable: !1 }), e && _setPrototypeOf(t, e); } 49 | function _setPrototypeOf(t, e) { return _setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function (t, e) { return t.__proto__ = e, t; }, _setPrototypeOf(t, e); } 50 | function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; } 51 | function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; } 52 | function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); } 53 | 54 | 55 | 56 | 57 | var ReactFC = /*#__PURE__*/function (_React$Component) { 58 | function ReactFC(props) { 59 | var _this; 60 | _classCallCheck(this, ReactFC); 61 | _this = _callSuper(this, ReactFC, [props]); 62 | _defineProperty(_this, "initialUnmount", false); 63 | _this.containerId = uuid_v4__WEBPACK_IMPORTED_MODULE_1___default()(); 64 | _this.oldOptions = null; 65 | _this.FusionCharts = props.fcLibrary || ReactFC.fusionChartsCore || window.FusionCharts; 66 | return _this; 67 | } 68 | _inherits(ReactFC, _React$Component); 69 | return _createClass(ReactFC, [{ 70 | key: "componentDidMount", 71 | value: function componentDidMount() { 72 | this.renderChart(); 73 | } 74 | }, { 75 | key: "componentDidUpdate", 76 | value: function componentDidUpdate(prevProps) { 77 | if (prevProps !== this.props) { 78 | this.detectChanges(this.props); 79 | } 80 | } 81 | }, { 82 | key: "componentWillUnmount", 83 | value: function componentWillUnmount() { 84 | if (!this.initialUnmount) this.initialUnmount = true;else this.chartObj.dispose(); 85 | } 86 | }, { 87 | key: "detectChanges", 88 | value: function detectChanges(nextProps) { 89 | var currentOptions = this.resolveChartOptions(nextProps); 90 | var oldOptions = this.oldOptions; 91 | var optionsUpdatedNatively = ['width', 'height', 'type', 'dataFormat', 'dataSource', 'events']; 92 | this.checkAndUpdateChartDimensions(currentOptions, oldOptions); 93 | this.checkAndUpdateChartType(currentOptions, oldOptions); 94 | this.checkAndUpdateChartData(currentOptions, oldOptions); 95 | this.checkAndUpdateEvents(currentOptions, oldOptions); 96 | this.checkAndUpdateRestOptions(_utils_options__WEBPACK_IMPORTED_MODULE_3__["default"].filter(function (option) { 97 | return optionsUpdatedNatively.indexOf(option) === -1; 98 | }), currentOptions, oldOptions); 99 | this.oldOptions = currentOptions; 100 | } 101 | }, { 102 | key: "checkAndUpdateChartDimensions", 103 | value: function checkAndUpdateChartDimensions(currentOptions, oldOptions) { 104 | var currWidth = currentOptions.width; 105 | var currHeight = currentOptions.height; 106 | var oldWidth = oldOptions.width; 107 | var oldHeight = oldOptions.height; 108 | if (String(currWidth) !== String(oldWidth) || String(currHeight) !== String(oldHeight)) { 109 | if (!_utils_utils__WEBPACK_IMPORTED_MODULE_2__.isUndefined(currWidth) && !_utils_utils__WEBPACK_IMPORTED_MODULE_2__.isUndefined(currHeight)) { 110 | this.chartObj.resizeTo(currWidth, currHeight); 111 | } else { 112 | if (!_utils_utils__WEBPACK_IMPORTED_MODULE_2__.isUndefined(currWidth)) { 113 | this.chartObj.resizeTo({ 114 | w: currWidth 115 | }); 116 | } 117 | if (!_utils_utils__WEBPACK_IMPORTED_MODULE_2__.isUndefined(currHeight)) { 118 | this.chartObj.resizeTo({ 119 | h: currHeight 120 | }); 121 | } 122 | } 123 | } 124 | } 125 | }, { 126 | key: "checkAndUpdateChartType", 127 | value: function checkAndUpdateChartType(currentOptions, oldOptions) { 128 | var currType = currentOptions.type; 129 | var oldType = oldOptions.type; 130 | if (String(currType).toLowerCase() !== String(oldType).toLowerCase()) { 131 | if (!_utils_utils__WEBPACK_IMPORTED_MODULE_2__.isUndefined(currType)) { 132 | this.chartObj.chartType(String(currType).toLowerCase()); 133 | } 134 | } 135 | } 136 | }, { 137 | key: "checkAndUpdateChartData", 138 | value: function checkAndUpdateChartData(currentOptions, oldOptions) { 139 | var currDataFormat = currentOptions.dataFormat; 140 | var currData = currentOptions.dataSource; 141 | var oldDataFormat = oldOptions.dataFormat; 142 | var oldData = oldOptions.dataSource; 143 | if (String(currDataFormat).toLowerCase() !== String(oldDataFormat).toLowerCase()) { 144 | if (!_utils_utils__WEBPACK_IMPORTED_MODULE_2__.isUndefined(currDataFormat) && !_utils_utils__WEBPACK_IMPORTED_MODULE_2__.isUndefined(currData)) { 145 | this.chartObj.setChartData(currData, String(currDataFormat).toLowerCase()); 146 | // If the chart dataFormat is changed then 147 | // animate the chart to show the changes 148 | this.chartObj.render(); 149 | return; 150 | } 151 | } 152 | if (!this.isSameChartData(currData, oldData)) { 153 | if (!_utils_utils__WEBPACK_IMPORTED_MODULE_2__.isUndefined(currData)) { 154 | this.chartObj.setChartData(currData, 155 | // When dataFormat is not given, but data is changed, 156 | // then use 'json' as default dataFormat 157 | currDataFormat ? String(currDataFormat).toLowerCase() : 'json'); 158 | } 159 | } 160 | } 161 | }, { 162 | key: "isSameChartData", 163 | value: function isSameChartData(currData, oldData) { 164 | /* TODO 165 | 1. Current has DataStore and Old doesn't 166 | 2. Old has and Current doesn't 167 | 3. Both has, check ref is equal, return false only if not equal 168 | 4. Clone oldData for diff 169 | 5. Clone currentData for diff 170 | 6. return string check. 171 | */ 172 | // 1. Current has DataStore and Old doesn't 173 | if (_utils_utils__WEBPACK_IMPORTED_MODULE_2__.checkIfDataTableExists(currData) && !_utils_utils__WEBPACK_IMPORTED_MODULE_2__.checkIfDataTableExists(oldData)) { 174 | return false; 175 | } 176 | // 2. Old has and Current doesn't 177 | if (!_utils_utils__WEBPACK_IMPORTED_MODULE_2__.checkIfDataTableExists(currData) && _utils_utils__WEBPACK_IMPORTED_MODULE_2__.checkIfDataTableExists(oldData)) { 178 | return false; 179 | } 180 | // 3. Both has, check ref is equal, return false only if not equal 181 | if (_utils_utils__WEBPACK_IMPORTED_MODULE_2__.checkIfDataTableExists(currData) && _utils_utils__WEBPACK_IMPORTED_MODULE_2__.checkIfDataTableExists(oldData) && currData.data !== oldData.data) { 182 | return false; 183 | } 184 | // 4. Clone oldData for diff 185 | var oldDataStringified = JSON.stringify(_utils_utils__WEBPACK_IMPORTED_MODULE_2__.cloneDataSource(oldData, 'diff')); 186 | // 5. Clone currentData for diff 187 | var currentDataStringified = JSON.stringify(_utils_utils__WEBPACK_IMPORTED_MODULE_2__.cloneDataSource(currData, 'diff')); 188 | // 6. return string check. 189 | return oldDataStringified === currentDataStringified; 190 | } 191 | }, { 192 | key: "checkAndUpdateEvents", 193 | value: function checkAndUpdateEvents(currentOptions, oldOptions) { 194 | var _this2 = this; 195 | var currEvents = currentOptions.events; 196 | var oldEvents = oldOptions.events; 197 | var temp1; 198 | var temp2; 199 | if (this.detectChartEventsChange(currEvents, oldEvents)) { 200 | if (!_utils_utils__WEBPACK_IMPORTED_MODULE_2__.isUndefined(currEvents)) { 201 | temp1 = Object.assign({}, currEvents); 202 | temp2 = _utils_utils__WEBPACK_IMPORTED_MODULE_2__.isUndefined(oldEvents) ? {} : Object.assign({}, oldEvents); 203 | Object.keys(temp2).forEach(function (eventName) { 204 | if (temp2[eventName] === temp1[eventName]) { 205 | temp1[eventName] = undefined; 206 | } else { 207 | _this2.chartObj.removeEventListener(eventName, temp2[eventName]); 208 | } 209 | }); 210 | Object.keys(temp1).forEach(function (eventName) { 211 | if (temp1[eventName]) { 212 | _this2.chartObj.addEventListener(eventName, temp1[eventName]); 213 | } 214 | }); 215 | } 216 | } 217 | } 218 | }, { 219 | key: "detectChartEventsChange", 220 | value: function detectChartEventsChange(currEvents, oldEvents) { 221 | if (_utils_utils__WEBPACK_IMPORTED_MODULE_2__.isObject(currEvents) && _utils_utils__WEBPACK_IMPORTED_MODULE_2__.isObject(oldEvents)) { 222 | return !this.isSameChartEvents(currEvents, oldEvents); 223 | } 224 | return !(currEvents === oldEvents); 225 | } 226 | }, { 227 | key: "isSameChartEvents", 228 | value: function isSameChartEvents(currEvents, oldEvents) { 229 | if (Object.keys(currEvents).length !== Object.keys(oldEvents).length) { 230 | return false; 231 | } 232 | var currEventNames = Object.keys(currEvents); 233 | for (var i = 0; i < currEventNames.length; ++i) { 234 | var evName = currEventNames[i]; 235 | if (currEvents[evName] !== oldEvents[evName]) { 236 | return false; 237 | } 238 | } 239 | return true; 240 | } 241 | }, { 242 | key: "checkAndUpdateRestOptions", 243 | value: function checkAndUpdateRestOptions(restOptions, currentOptions, oldOptions) { 244 | var _this3 = this; 245 | var optionsUpdated = false; 246 | restOptions.forEach(function (optionName) { 247 | var currValue = currentOptions[optionName]; 248 | var oldValue = oldOptions[optionName]; 249 | if (!_this3.isSameOptionValue(currValue, oldValue)) { 250 | if (!_utils_utils__WEBPACK_IMPORTED_MODULE_2__.isUndefined(currValue)) { 251 | if (_this3.chartObj.options && _this3.chartObj.options.hasOwnProperty(optionName)) { 252 | _this3.chartObj.options[optionName] = currValue; 253 | optionsUpdated = true; 254 | } 255 | } 256 | } 257 | }); 258 | if (optionsUpdated) { 259 | this.chartObj.render(); // re-render the chart to reflect the changes 260 | } 261 | } 262 | }, { 263 | key: "isSameOptionValue", 264 | value: function isSameOptionValue(currValue, oldValue) { 265 | if (_utils_utils__WEBPACK_IMPORTED_MODULE_2__.isObject(currValue) && _utils_utils__WEBPACK_IMPORTED_MODULE_2__.isObject(oldValue)) { 266 | return _utils_utils__WEBPACK_IMPORTED_MODULE_2__.isSameObjectContent(currValue, oldValue); 267 | } 268 | return String(currValue) === String(oldValue); 269 | } 270 | }, { 271 | key: "renderChart", 272 | value: function renderChart() { 273 | var _this4 = this; 274 | var currentOptions = this.resolveChartOptions(this.props); 275 | var events = {}; 276 | currentOptions.renderAt = this.containerId; 277 | Object.keys(this.props).forEach(function (value) { 278 | var event = value.match(/^fcEvent-.*/i); 279 | if (event && typeof _this4.props[value] === 'function') { 280 | var eventName = value.replace(/^fcEvent-/i, ''); 281 | events[eventName] = _this4.props[value]; 282 | } 283 | }); 284 | if (Object.keys(events).length > 0) { 285 | if (currentOptions.events === undefined) { 286 | currentOptions.events = events; 287 | } else { 288 | currentOptions.events = Object.assign(currentOptions.events, events); 289 | } 290 | } 291 | this.chartObj = new this.FusionCharts(currentOptions); 292 | this.chartObj.render(); 293 | this.oldOptions = currentOptions; 294 | if (this.props.onRender && typeof this.props.onRender === 'function') { 295 | this.props.onRender(this.chartObj); 296 | } 297 | } 298 | }, { 299 | key: "resolveChartOptions", 300 | value: function resolveChartOptions(props) { 301 | var chartConfig = props.chartConfig ? props.chartConfig : {}; 302 | var inlineOptions = _utils_options__WEBPACK_IMPORTED_MODULE_3__["default"].reduce(function (options, optionName) { 303 | options[optionName] = props[optionName]; 304 | return options; 305 | }, {}); 306 | Object.assign(inlineOptions, chartConfig); 307 | if (_utils_utils__WEBPACK_IMPORTED_MODULE_2__.isObject(inlineOptions.dataSource) && !_utils_utils__WEBPACK_IMPORTED_MODULE_2__.checkIfDataTableExists(inlineOptions.dataSource)) { 308 | inlineOptions.dataSource = _utils_utils__WEBPACK_IMPORTED_MODULE_2__.deepCopyOf(inlineOptions.dataSource); 309 | } else if (_utils_utils__WEBPACK_IMPORTED_MODULE_2__.isObject(inlineOptions.dataSource) && _utils_utils__WEBPACK_IMPORTED_MODULE_2__.checkIfDataTableExists(inlineOptions.dataSource)) { 310 | inlineOptions.dataSource = _utils_utils__WEBPACK_IMPORTED_MODULE_2__.cloneDataSource(inlineOptions.dataSource, 'clone'); 311 | } 312 | if (_utils_utils__WEBPACK_IMPORTED_MODULE_2__.isObject(inlineOptions.link)) { 313 | inlineOptions.link = _utils_utils__WEBPACK_IMPORTED_MODULE_2__.deepCopyOf(inlineOptions.link); 314 | } 315 | if (_utils_utils__WEBPACK_IMPORTED_MODULE_2__.isObject(inlineOptions.events)) { 316 | inlineOptions.events = Object.assign({}, inlineOptions.events); 317 | } 318 | return inlineOptions; 319 | } 320 | }, { 321 | key: "render", 322 | value: function render() { 323 | return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { 324 | className: this.props.className, 325 | id: this.containerId 326 | }); 327 | } 328 | }], [{ 329 | key: "fcRoot", 330 | value: function fcRoot(core) { 331 | for (var _len = arguments.length, modules = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { 332 | modules[_key - 1] = arguments[_key]; 333 | } 334 | modules.forEach(function (m) { 335 | if (m.getName && m.getType || m.name && m.type) { 336 | core.addDep(m); 337 | } else { 338 | m(core); 339 | } 340 | }); 341 | ReactFC.fusionChartsCore = core; 342 | } 343 | }]); 344 | }((react__WEBPACK_IMPORTED_MODULE_0___default().Component)); 345 | /* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (ReactFC); 346 | 347 | /***/ }), 348 | /* 2 */ 349 | /***/ ((module) => { 350 | 351 | "use strict"; 352 | module.exports = __WEBPACK_EXTERNAL_MODULE__2__; 353 | 354 | /***/ }), 355 | /* 3 */ 356 | /***/ ((module, __unused_webpack_exports, __webpack_require__) => { 357 | 358 | var rng = __webpack_require__(4); 359 | var bytesToUuid = __webpack_require__(5); 360 | 361 | function v4(options, buf, offset) { 362 | var i = buf && offset || 0; 363 | 364 | if (typeof(options) == 'string') { 365 | buf = options === 'binary' ? new Array(16) : null; 366 | options = null; 367 | } 368 | options = options || {}; 369 | 370 | var rnds = options.random || (options.rng || rng)(); 371 | 372 | // Per 4.4, set bits for version and `clock_seq_hi_and_reserved` 373 | rnds[6] = (rnds[6] & 0x0f) | 0x40; 374 | rnds[8] = (rnds[8] & 0x3f) | 0x80; 375 | 376 | // Copy bytes to buffer, if provided 377 | if (buf) { 378 | for (var ii = 0; ii < 16; ++ii) { 379 | buf[i + ii] = rnds[ii]; 380 | } 381 | } 382 | 383 | return buf || bytesToUuid(rnds); 384 | } 385 | 386 | module.exports = v4; 387 | 388 | 389 | /***/ }), 390 | /* 4 */ 391 | /***/ ((module) => { 392 | 393 | // Unique ID creation requires a high quality random # generator. In the 394 | // browser this is a little complicated due to unknown quality of Math.random() 395 | // and inconsistent support for the `crypto` API. We do the best we can via 396 | // feature-detection 397 | 398 | // getRandomValues needs to be invoked in a context where "this" is a Crypto 399 | // implementation. Also, find the complete implementation of crypto on IE11. 400 | var getRandomValues = (typeof(crypto) != 'undefined' && crypto.getRandomValues && crypto.getRandomValues.bind(crypto)) || 401 | (typeof(msCrypto) != 'undefined' && typeof window.msCrypto.getRandomValues == 'function' && msCrypto.getRandomValues.bind(msCrypto)); 402 | 403 | if (getRandomValues) { 404 | // WHATWG crypto RNG - http://wiki.whatwg.org/wiki/Crypto 405 | var rnds8 = new Uint8Array(16); // eslint-disable-line no-undef 406 | 407 | module.exports = function whatwgRNG() { 408 | getRandomValues(rnds8); 409 | return rnds8; 410 | }; 411 | } else { 412 | // Math.random()-based (RNG) 413 | // 414 | // If all else fails, use Math.random(). It's fast, but is of unspecified 415 | // quality. 416 | var rnds = new Array(16); 417 | 418 | module.exports = function mathRNG() { 419 | for (var i = 0, r; i < 16; i++) { 420 | if ((i & 0x03) === 0) r = Math.random() * 0x100000000; 421 | rnds[i] = r >>> ((i & 0x03) << 3) & 0xff; 422 | } 423 | 424 | return rnds; 425 | }; 426 | } 427 | 428 | 429 | /***/ }), 430 | /* 5 */ 431 | /***/ ((module) => { 432 | 433 | /** 434 | * Convert array of 16 byte values to UUID string format of the form: 435 | * XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX 436 | */ 437 | var byteToHex = []; 438 | for (var i = 0; i < 256; ++i) { 439 | byteToHex[i] = (i + 0x100).toString(16).substr(1); 440 | } 441 | 442 | function bytesToUuid(buf, offset) { 443 | var i = offset || 0; 444 | var bth = byteToHex; 445 | // join used to fix memory issue caused by concatenation: https://bugs.chromium.org/p/v8/issues/detail?id=3175#c4 446 | return ([ 447 | bth[buf[i++]], bth[buf[i++]], 448 | bth[buf[i++]], bth[buf[i++]], '-', 449 | bth[buf[i++]], bth[buf[i++]], '-', 450 | bth[buf[i++]], bth[buf[i++]], '-', 451 | bth[buf[i++]], bth[buf[i++]], '-', 452 | bth[buf[i++]], bth[buf[i++]], 453 | bth[buf[i++]], bth[buf[i++]], 454 | bth[buf[i++]], bth[buf[i++]] 455 | ]).join(''); 456 | } 457 | 458 | module.exports = bytesToUuid; 459 | 460 | 461 | /***/ }), 462 | /* 6 */ 463 | /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { 464 | 465 | "use strict"; 466 | __webpack_require__.r(__webpack_exports__); 467 | /* harmony export */ __webpack_require__.d(__webpack_exports__, { 468 | /* harmony export */ checkIfDataTableExists: () => (/* binding */ checkIfDataTableExists), 469 | /* harmony export */ cloneDataSource: () => (/* binding */ cloneDataSource), 470 | /* harmony export */ deepCopyOf: () => (/* binding */ deepCopyOf), 471 | /* harmony export */ isCallable: () => (/* binding */ isCallable), 472 | /* harmony export */ isObject: () => (/* binding */ isObject), 473 | /* harmony export */ isSameObjectContent: () => (/* binding */ isSameObjectContent), 474 | /* harmony export */ isUndefined: () => (/* binding */ isUndefined) 475 | /* harmony export */ }); 476 | function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); } 477 | /* eslint-disable guard-for-in */ 478 | function isObject(value) { 479 | return value !== null && _typeof(value) === 'object'; 480 | } 481 | function isCallable(value) { 482 | return typeof value === 'function'; 483 | } 484 | function isSameObjectContent(obj1, obj2) { 485 | if (Object.keys(obj1).length !== Object.keys(obj2).length) { 486 | return false; 487 | } 488 | var keys = Object.keys(obj1); 489 | for (var i = 0; i < keys.length; i += 1) { 490 | var key = keys[i]; 491 | if (isObject(obj1[key]) && isObject(obj2[key])) { 492 | if (!isSameObjectContent(obj1[key], obj2[key])) { 493 | return false; 494 | } 495 | } else if (obj1[key] !== obj2[key]) { 496 | return false; 497 | } 498 | } 499 | return true; 500 | } 501 | function isUndefined(value) { 502 | // eslint-disable-next-line no-void 503 | var UNDEFINED = void 0; 504 | return value === UNDEFINED; 505 | } 506 | function deepCopyOf(obj) { 507 | return JSON.parse(JSON.stringify(obj)); 508 | } 509 | function checkIfDataTableExists(dataSource) { 510 | // eslint-disable-next-line no-underscore-dangle 511 | if (dataSource && dataSource.data && dataSource.data._dataStore) { 512 | return true; 513 | } 514 | return false; 515 | } 516 | function cloneDataSource(obj) { 517 | var purpose = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'clone'; 518 | var type = _typeof(obj); 519 | if (type === 'string' || type === 'number' || type === 'function' || type === 'boolean') { 520 | return obj; 521 | } 522 | if (obj === null || obj === undefined) { 523 | return obj; 524 | } 525 | if (Array.isArray(obj)) { 526 | var arr = []; 527 | for (var i = 0; i < obj.length; i++) { 528 | arr.push(this.cloneDataSource(obj[i])); 529 | } 530 | return arr; 531 | } 532 | if (_typeof(obj) === 'object') { 533 | var clonedObj = {}; 534 | // eslint-disable-next-line guard-for-in 535 | // eslint-disable-next-line no-restricted-syntax 536 | for (var prop in obj) { 537 | // Edge case handling for DataTable 538 | if (prop === 'data') { 539 | // eslint-disable-next-line no-underscore-dangle 540 | if (obj[prop]._dataStore && purpose === 'clone') { 541 | clonedObj[prop] = obj[prop]; 542 | // eslint-disable-next-line no-underscore-dangle 543 | } else if (obj[prop]._dataStore && purpose === 'diff') { 544 | clonedObj[prop] = '-'; 545 | } else { 546 | clonedObj[prop] = this.cloneDataSource(obj[prop]); 547 | } 548 | continue; 549 | } 550 | clonedObj[prop] = this.cloneDataSource(obj[prop]); 551 | } 552 | return clonedObj; 553 | } 554 | return undefined; 555 | } 556 | 557 | /***/ }), 558 | /* 7 */ 559 | /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { 560 | 561 | "use strict"; 562 | __webpack_require__.r(__webpack_exports__); 563 | /* harmony export */ __webpack_require__.d(__webpack_exports__, { 564 | /* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) 565 | /* harmony export */ }); 566 | /* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (['type', 'id', 'width', 'height', 'dataFormat', 'dataSource', 'events', 'link', 'showDataLoadingMessage', 'showChartLoadingMessage', 'baseChartMessageFont', 'baseChartMessageFontSize', 'baseChartMessageColor', 'dataLoadStartMessage', 'dataLoadErrorMessage', 'dataInvalidMessage', 'dataEmptyMessage', 'typeNotSupportedMessage', 'loadMessage', 'renderErrorMessage', 'containerBackgroundColor', 'containerBackgroundOpacity', 'containerClassName', 'baseChartMessageImageHAlign', 'baseChartMessageImageVAlign', 'baseChartMessageImageAlpha', 'baseChartMessageImageScale', 'typeNotSupportedMessageImageHAalign', 'typeNotSupportedMessageImageVAlign', 'typeNotSupportedMessageImageAlpha', 'typeNotSupportedMessageImageScale', 'dataLoadErrorMessageImageHAlign', 'dataLoadErrorMessageImageVAlign', 'dataLoadErrorMessageImageAlpha', 'dataLoadErrorMessageImageScale', 'dataLoadStartMessageImageHAlign', 'dataLoadStartMessageImageVAlign', 'dataLoadStartMessageImageAlpha', 'dataLoadStartMessageImageScale', 'dataInvalidMessageImageHAlign', 'dataInvalidMessageImageVAlign', 'dataInvalidMessageImageAlpha', 'dataInvalidMessageImageScale', 'dataEmptyMessageImageHAlign', 'dataEmptyMessageImageVAlign', 'dataEmptyMessageImageAlpha', 'dataEmptyMessageImageScale', 'renderErrorMessageImageHAlign', 'renderErrorMessageImageVAlign', 'renderErrorMessageImageAlpha', 'renderErrorMessageImageScale', 'loadMessageImageHAlign', 'loadMessageImageVAlign', 'loadMessageImageAlpha', 'loadMessageImageScale']); 567 | 568 | /***/ }) 569 | /******/ ]); 570 | /************************************************************************/ 571 | /******/ // The module cache 572 | /******/ var __webpack_module_cache__ = {}; 573 | /******/ 574 | /******/ // The require function 575 | /******/ function __webpack_require__(moduleId) { 576 | /******/ // Check if module is in cache 577 | /******/ var cachedModule = __webpack_module_cache__[moduleId]; 578 | /******/ if (cachedModule !== undefined) { 579 | /******/ return cachedModule.exports; 580 | /******/ } 581 | /******/ // Create a new module (and put it into the cache) 582 | /******/ var module = __webpack_module_cache__[moduleId] = { 583 | /******/ // no module.id needed 584 | /******/ // no module.loaded needed 585 | /******/ exports: {} 586 | /******/ }; 587 | /******/ 588 | /******/ // Execute the module function 589 | /******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__); 590 | /******/ 591 | /******/ // Return the exports of the module 592 | /******/ return module.exports; 593 | /******/ } 594 | /******/ 595 | /************************************************************************/ 596 | /******/ /* webpack/runtime/compat get default export */ 597 | /******/ (() => { 598 | /******/ // getDefaultExport function for compatibility with non-harmony modules 599 | /******/ __webpack_require__.n = (module) => { 600 | /******/ var getter = module && module.__esModule ? 601 | /******/ () => (module['default']) : 602 | /******/ () => (module); 603 | /******/ __webpack_require__.d(getter, { a: getter }); 604 | /******/ return getter; 605 | /******/ }; 606 | /******/ })(); 607 | /******/ 608 | /******/ /* webpack/runtime/define property getters */ 609 | /******/ (() => { 610 | /******/ // define getter functions for harmony exports 611 | /******/ __webpack_require__.d = (exports, definition) => { 612 | /******/ for(var key in definition) { 613 | /******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) { 614 | /******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] }); 615 | /******/ } 616 | /******/ } 617 | /******/ }; 618 | /******/ })(); 619 | /******/ 620 | /******/ /* webpack/runtime/hasOwnProperty shorthand */ 621 | /******/ (() => { 622 | /******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop)) 623 | /******/ })(); 624 | /******/ 625 | /******/ /* webpack/runtime/make namespace object */ 626 | /******/ (() => { 627 | /******/ // define __esModule on exports 628 | /******/ __webpack_require__.r = (exports) => { 629 | /******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { 630 | /******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); 631 | /******/ } 632 | /******/ Object.defineProperty(exports, '__esModule', { value: true }); 633 | /******/ }; 634 | /******/ })(); 635 | /******/ 636 | /************************************************************************/ 637 | /******/ 638 | /******/ // startup 639 | /******/ // Load entry module and return exports 640 | /******/ // This entry module used 'module' so it can't be inlined 641 | /******/ var __webpack_exports__ = __webpack_require__(0); 642 | /******/ 643 | /******/ return __webpack_exports__; 644 | /******/ })() 645 | ; 646 | }); -------------------------------------------------------------------------------- /dist/react-fusioncharts.min.js: -------------------------------------------------------------------------------- 1 | !function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t(require("react")):"function"==typeof define&&define.amd?define("ReactFC",["react"],t):"object"==typeof exports?exports.ReactFC=t(require("react")):e.ReactFC=t(e.React)}(self,(e=>(()=>{var t={987:(e,t,r)=>{e.exports=r(500).default},500:(e,t,r)=>{"use strict";r.d(t,{default:()=>C});var a=r(442),n=r.n(a),o=r(550),i=r.n(o);function s(e){return s="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},s(e)}function c(e){return null!==e&&"object"===s(e)}function u(e,t){if(Object.keys(e).length!==Object.keys(t).length)return!1;for(var r=Object.keys(e),a=0;a1&&void 0!==arguments[1]?arguments[1]:"clone",r=s(e);if("string"===r||"number"===r||"function"===r||"boolean"===r)return e;if(null==e)return e;if(Array.isArray(e)){for(var a=[],n=0;n1?r-1:0),n=1;n0&&(void 0===t.events?t.events=r:t.events=Object.assign(t.events,r)),this.chartObj=new this.FusionCharts(t),this.chartObj.render(),this.oldOptions=t,this.props.onRender&&"function"==typeof this.props.onRender&&this.props.onRender(this.chartObj)}},{key:"resolveChartOptions",value:function(e){var t=e.chartConfig?e.chartConfig:{},r=h.reduce((function(t,r){return t[r]=e[r],t}),{});return Object.assign(r,t),c(r.dataSource)&&!f(r.dataSource)?r.dataSource=l(r.dataSource):c(r.dataSource)&&f(r.dataSource)&&(r.dataSource=p(r.dataSource,"clone")),c(r.link)&&(r.link=l(r.link)),c(r.events)&&(r.events=Object.assign({},r.events)),r}},{key:"render",value:function(){return n().createElement("div",{className:this.props.className,id:this.containerId})}}])&&y(r.prototype,a),o&&y(r,o),Object.defineProperty(r,"prototype",{writable:!1}),r;var r,a,o}(n().Component)},471:e=>{for(var t=[],r=0;r<256;++r)t[r]=(r+256).toString(16).substr(1);e.exports=function(e,r){var a=r||0,n=t;return[n[e[a++]],n[e[a++]],n[e[a++]],n[e[a++]],"-",n[e[a++]],n[e[a++]],"-",n[e[a++]],n[e[a++]],"-",n[e[a++]],n[e[a++]],"-",n[e[a++]],n[e[a++]],n[e[a++]],n[e[a++]],n[e[a++]],n[e[a++]]].join("")}},814:e=>{var t="undefined"!=typeof crypto&&crypto.getRandomValues&&crypto.getRandomValues.bind(crypto)||"undefined"!=typeof msCrypto&&"function"==typeof window.msCrypto.getRandomValues&&msCrypto.getRandomValues.bind(msCrypto);if(t){var r=new Uint8Array(16);e.exports=function(){return t(r),r}}else{var a=new Array(16);e.exports=function(){for(var e,t=0;t<16;t++)3&t||(e=4294967296*Math.random()),a[t]=e>>>((3&t)<<3)&255;return a}}},550:(e,t,r)=>{var a=r(814),n=r(471);e.exports=function(e,t,r){var o=t&&r||0;"string"==typeof e&&(t="binary"===e?new Array(16):null,e=null);var i=(e=e||{}).random||(e.rng||a)();if(i[6]=15&i[6]|64,i[8]=63&i[8]|128,t)for(var s=0;s<16;++s)t[o+s]=i[s];return t||n(i)}},442:t=>{"use strict";t.exports=e}},r={};function a(e){var n=r[e];if(void 0!==n)return n.exports;var o=r[e]={exports:{}};return t[e](o,o.exports,a),o.exports}return a.n=e=>{var t=e&&e.__esModule?()=>e.default:()=>e;return a.d(t,{a:t}),t},a.d=(e,t)=>{for(var r in t)a.o(t,r)&&!a.o(e,r)&&Object.defineProperty(e,r,{enumerable:!0,get:t[r]})},a.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),a(987)})())); -------------------------------------------------------------------------------- /example/ChartViewer.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import FusionCharts from 'fusioncharts'; 3 | import Charts from 'fusioncharts/fusioncharts.charts'; 4 | import TimeSeries from 'fusioncharts/fusioncharts.timeseries'; 5 | import FusionTheme from 'fusioncharts/themes/fusioncharts.theme.fusion'; 6 | import ReactFC from '../lib/ReactFC'; 7 | 8 | ReactFC.fcRoot(FusionCharts, Charts, TimeSeries, FusionTheme); 9 | 10 | const BAR = 'bar2d'; 11 | 12 | class ChartViewer extends React.Component { 13 | constructor(props) { 14 | super(props); 15 | 16 | this.state = { 17 | inverted: false, 18 | type: BAR, 19 | dataSource: { 20 | chart: { 21 | caption: 'Countries With Most Oil Reserves [2017-18]', 22 | subCaption: 'In MMbbl = One Million barrels', 23 | xAxisName: 'Country', 24 | yAxisName: 'Reserves (MMbbl)', 25 | numberSuffix: 'K', 26 | theme: 'fusion' 27 | }, 28 | data: [ 29 | { label: 'Venezuela', value: '290' }, 30 | { label: 'Saudi', value: '260' }, 31 | { label: 'Canada', value: '180' }, 32 | { label: 'Iran', value: '140' }, 33 | { label: 'Russia', value: '115' }, 34 | { label: 'UAE', value: '100' }, 35 | { label: 'US', value: '30' }, 36 | { label: 'China', value: '300' } 37 | ] 38 | } 39 | }; 40 | this.onChange = this.onChange.bind(this); 41 | } 42 | 43 | onChange() { 44 | this.setState(({ dataSource }) => ({ 45 | dataSource: { 46 | ...dataSource, 47 | chart: { ...dataSource.chart, caption: 'CHANGED IT!!!!!!!' } 48 | } 49 | })); 50 | } 51 | 52 | render() { 53 | return ( 54 |
55 | 62 | 63 |
64 | ); 65 | } 66 | } 67 | 68 | export default ChartViewer; 69 | -------------------------------------------------------------------------------- /example/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | React-FusionCharts 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /example/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom/client'; 3 | import ChartViewer from './ChartViewer'; 4 | 5 | const root = ReactDOM.createRoot(document.getElementById('root')); 6 | 7 | root.render( 8 | 9 | 10 | 11 | ); 12 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | // import ReactFC from './src/ReactFC'; 2 | 3 | // Use this format to export ReactFC as default module 4 | // Ref: https://gist.github.com/iamakulov/966b91c0fc6363a16ff0650b51fb991b 5 | // export default ReactFC; 6 | 7 | module.exports = require('./src/ReactFC').default; 8 | -------------------------------------------------------------------------------- /lib/ReactFC.d.ts: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | declare abstract class ReactFC extends React.Component { 4 | static fcRoot(...args: any): void 5 | } 6 | 7 | export default ReactFC; -------------------------------------------------------------------------------- /lib/ReactFC.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports["default"] = void 0; 7 | var _react = _interopRequireDefault(require("react")); 8 | var _v = _interopRequireDefault(require("uuid/v4")); 9 | var utils = _interopRequireWildcard(require("./utils/utils")); 10 | var _options = _interopRequireDefault(require("./utils/options")); 11 | function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(e) { return e ? t : r; })(e); } 12 | function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != _typeof(e) && "function" != typeof e) return { "default": e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n["default"] = e, t && t.set(e, n), n; } 13 | function _interopRequireDefault(e) { return e && e.__esModule ? e : { "default": e }; } 14 | function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); } 15 | function _classCallCheck(a, n) { if (!(a instanceof n)) throw new TypeError("Cannot call a class as a function"); } 16 | function _defineProperties(e, r) { for (var t = 0; t < r.length; t++) { var o = r[t]; o.enumerable = o.enumerable || !1, o.configurable = !0, "value" in o && (o.writable = !0), Object.defineProperty(e, _toPropertyKey(o.key), o); } } 17 | function _createClass(e, r, t) { return r && _defineProperties(e.prototype, r), t && _defineProperties(e, t), Object.defineProperty(e, "prototype", { writable: !1 }), e; } 18 | function _callSuper(t, o, e) { return o = _getPrototypeOf(o), _possibleConstructorReturn(t, _isNativeReflectConstruct() ? Reflect.construct(o, e || [], _getPrototypeOf(t).constructor) : o.apply(t, e)); } 19 | function _possibleConstructorReturn(t, e) { if (e && ("object" == _typeof(e) || "function" == typeof e)) return e; if (void 0 !== e) throw new TypeError("Derived constructors may only return object or undefined"); return _assertThisInitialized(t); } 20 | function _assertThisInitialized(e) { if (void 0 === e) throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); return e; } 21 | function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); } catch (t) {} return (_isNativeReflectConstruct = function _isNativeReflectConstruct() { return !!t; })(); } 22 | function _getPrototypeOf(t) { return _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf.bind() : function (t) { return t.__proto__ || Object.getPrototypeOf(t); }, _getPrototypeOf(t); } 23 | function _inherits(t, e) { if ("function" != typeof e && null !== e) throw new TypeError("Super expression must either be null or a function"); t.prototype = Object.create(e && e.prototype, { constructor: { value: t, writable: !0, configurable: !0 } }), Object.defineProperty(t, "prototype", { writable: !1 }), e && _setPrototypeOf(t, e); } 24 | function _setPrototypeOf(t, e) { return _setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function (t, e) { return t.__proto__ = e, t; }, _setPrototypeOf(t, e); } 25 | function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; } 26 | function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; } 27 | function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); } 28 | var ReactFC = /*#__PURE__*/function (_React$Component) { 29 | function ReactFC(props) { 30 | var _this; 31 | _classCallCheck(this, ReactFC); 32 | _this = _callSuper(this, ReactFC, [props]); 33 | _defineProperty(_this, "initialUnmount", false); 34 | _this.containerId = (0, _v["default"])(); 35 | _this.oldOptions = null; 36 | _this.FusionCharts = props.fcLibrary || ReactFC.fusionChartsCore || window.FusionCharts; 37 | return _this; 38 | } 39 | _inherits(ReactFC, _React$Component); 40 | return _createClass(ReactFC, [{ 41 | key: "componentDidMount", 42 | value: function componentDidMount() { 43 | this.renderChart(); 44 | } 45 | }, { 46 | key: "componentDidUpdate", 47 | value: function componentDidUpdate(prevProps) { 48 | if (prevProps !== this.props) { 49 | this.detectChanges(this.props); 50 | } 51 | } 52 | }, { 53 | key: "componentWillUnmount", 54 | value: function componentWillUnmount() { 55 | if (!this.initialUnmount) this.initialUnmount = true;else this.chartObj.dispose(); 56 | } 57 | }, { 58 | key: "detectChanges", 59 | value: function detectChanges(nextProps) { 60 | var currentOptions = this.resolveChartOptions(nextProps); 61 | var oldOptions = this.oldOptions; 62 | var optionsUpdatedNatively = ['width', 'height', 'type', 'dataFormat', 'dataSource', 'events']; 63 | this.checkAndUpdateChartDimensions(currentOptions, oldOptions); 64 | this.checkAndUpdateChartType(currentOptions, oldOptions); 65 | this.checkAndUpdateChartData(currentOptions, oldOptions); 66 | this.checkAndUpdateEvents(currentOptions, oldOptions); 67 | this.checkAndUpdateRestOptions(_options["default"].filter(function (option) { 68 | return optionsUpdatedNatively.indexOf(option) === -1; 69 | }), currentOptions, oldOptions); 70 | this.oldOptions = currentOptions; 71 | } 72 | }, { 73 | key: "checkAndUpdateChartDimensions", 74 | value: function checkAndUpdateChartDimensions(currentOptions, oldOptions) { 75 | var currWidth = currentOptions.width; 76 | var currHeight = currentOptions.height; 77 | var oldWidth = oldOptions.width; 78 | var oldHeight = oldOptions.height; 79 | if (String(currWidth) !== String(oldWidth) || String(currHeight) !== String(oldHeight)) { 80 | if (!utils.isUndefined(currWidth) && !utils.isUndefined(currHeight)) { 81 | this.chartObj.resizeTo(currWidth, currHeight); 82 | } else { 83 | if (!utils.isUndefined(currWidth)) { 84 | this.chartObj.resizeTo({ 85 | w: currWidth 86 | }); 87 | } 88 | if (!utils.isUndefined(currHeight)) { 89 | this.chartObj.resizeTo({ 90 | h: currHeight 91 | }); 92 | } 93 | } 94 | } 95 | } 96 | }, { 97 | key: "checkAndUpdateChartType", 98 | value: function checkAndUpdateChartType(currentOptions, oldOptions) { 99 | var currType = currentOptions.type; 100 | var oldType = oldOptions.type; 101 | if (String(currType).toLowerCase() !== String(oldType).toLowerCase()) { 102 | if (!utils.isUndefined(currType)) { 103 | this.chartObj.chartType(String(currType).toLowerCase()); 104 | } 105 | } 106 | } 107 | }, { 108 | key: "checkAndUpdateChartData", 109 | value: function checkAndUpdateChartData(currentOptions, oldOptions) { 110 | var currDataFormat = currentOptions.dataFormat; 111 | var currData = currentOptions.dataSource; 112 | var oldDataFormat = oldOptions.dataFormat; 113 | var oldData = oldOptions.dataSource; 114 | if (String(currDataFormat).toLowerCase() !== String(oldDataFormat).toLowerCase()) { 115 | if (!utils.isUndefined(currDataFormat) && !utils.isUndefined(currData)) { 116 | this.chartObj.setChartData(currData, String(currDataFormat).toLowerCase()); 117 | // If the chart dataFormat is changed then 118 | // animate the chart to show the changes 119 | this.chartObj.render(); 120 | return; 121 | } 122 | } 123 | if (!this.isSameChartData(currData, oldData)) { 124 | if (!utils.isUndefined(currData)) { 125 | this.chartObj.setChartData(currData, 126 | // When dataFormat is not given, but data is changed, 127 | // then use 'json' as default dataFormat 128 | currDataFormat ? String(currDataFormat).toLowerCase() : 'json'); 129 | } 130 | } 131 | } 132 | }, { 133 | key: "isSameChartData", 134 | value: function isSameChartData(currData, oldData) { 135 | /* TODO 136 | 1. Current has DataStore and Old doesn't 137 | 2. Old has and Current doesn't 138 | 3. Both has, check ref is equal, return false only if not equal 139 | 4. Clone oldData for diff 140 | 5. Clone currentData for diff 141 | 6. return string check. 142 | */ 143 | // 1. Current has DataStore and Old doesn't 144 | if (utils.checkIfDataTableExists(currData) && !utils.checkIfDataTableExists(oldData)) { 145 | return false; 146 | } 147 | // 2. Old has and Current doesn't 148 | if (!utils.checkIfDataTableExists(currData) && utils.checkIfDataTableExists(oldData)) { 149 | return false; 150 | } 151 | // 3. Both has, check ref is equal, return false only if not equal 152 | if (utils.checkIfDataTableExists(currData) && utils.checkIfDataTableExists(oldData) && currData.data !== oldData.data) { 153 | return false; 154 | } 155 | // 4. Clone oldData for diff 156 | var oldDataStringified = JSON.stringify(utils.cloneDataSource(oldData, 'diff')); 157 | // 5. Clone currentData for diff 158 | var currentDataStringified = JSON.stringify(utils.cloneDataSource(currData, 'diff')); 159 | // 6. return string check. 160 | return oldDataStringified === currentDataStringified; 161 | } 162 | }, { 163 | key: "checkAndUpdateEvents", 164 | value: function checkAndUpdateEvents(currentOptions, oldOptions) { 165 | var _this2 = this; 166 | var currEvents = currentOptions.events; 167 | var oldEvents = oldOptions.events; 168 | var temp1; 169 | var temp2; 170 | if (this.detectChartEventsChange(currEvents, oldEvents)) { 171 | if (!utils.isUndefined(currEvents)) { 172 | temp1 = Object.assign({}, currEvents); 173 | temp2 = utils.isUndefined(oldEvents) ? {} : Object.assign({}, oldEvents); 174 | Object.keys(temp2).forEach(function (eventName) { 175 | if (temp2[eventName] === temp1[eventName]) { 176 | temp1[eventName] = undefined; 177 | } else { 178 | _this2.chartObj.removeEventListener(eventName, temp2[eventName]); 179 | } 180 | }); 181 | Object.keys(temp1).forEach(function (eventName) { 182 | if (temp1[eventName]) { 183 | _this2.chartObj.addEventListener(eventName, temp1[eventName]); 184 | } 185 | }); 186 | } 187 | } 188 | } 189 | }, { 190 | key: "detectChartEventsChange", 191 | value: function detectChartEventsChange(currEvents, oldEvents) { 192 | if (utils.isObject(currEvents) && utils.isObject(oldEvents)) { 193 | return !this.isSameChartEvents(currEvents, oldEvents); 194 | } 195 | return !(currEvents === oldEvents); 196 | } 197 | }, { 198 | key: "isSameChartEvents", 199 | value: function isSameChartEvents(currEvents, oldEvents) { 200 | if (Object.keys(currEvents).length !== Object.keys(oldEvents).length) { 201 | return false; 202 | } 203 | var currEventNames = Object.keys(currEvents); 204 | for (var i = 0; i < currEventNames.length; ++i) { 205 | var evName = currEventNames[i]; 206 | if (currEvents[evName] !== oldEvents[evName]) { 207 | return false; 208 | } 209 | } 210 | return true; 211 | } 212 | }, { 213 | key: "checkAndUpdateRestOptions", 214 | value: function checkAndUpdateRestOptions(restOptions, currentOptions, oldOptions) { 215 | var _this3 = this; 216 | var optionsUpdated = false; 217 | restOptions.forEach(function (optionName) { 218 | var currValue = currentOptions[optionName]; 219 | var oldValue = oldOptions[optionName]; 220 | if (!_this3.isSameOptionValue(currValue, oldValue)) { 221 | if (!utils.isUndefined(currValue)) { 222 | if (_this3.chartObj.options && _this3.chartObj.options.hasOwnProperty(optionName)) { 223 | _this3.chartObj.options[optionName] = currValue; 224 | optionsUpdated = true; 225 | } 226 | } 227 | } 228 | }); 229 | if (optionsUpdated) { 230 | this.chartObj.render(); // re-render the chart to reflect the changes 231 | } 232 | } 233 | }, { 234 | key: "isSameOptionValue", 235 | value: function isSameOptionValue(currValue, oldValue) { 236 | if (utils.isObject(currValue) && utils.isObject(oldValue)) { 237 | return utils.isSameObjectContent(currValue, oldValue); 238 | } 239 | return String(currValue) === String(oldValue); 240 | } 241 | }, { 242 | key: "renderChart", 243 | value: function renderChart() { 244 | var _this4 = this; 245 | var currentOptions = this.resolveChartOptions(this.props); 246 | var events = {}; 247 | currentOptions.renderAt = this.containerId; 248 | Object.keys(this.props).forEach(function (value) { 249 | var event = value.match(/^fcEvent-.*/i); 250 | if (event && typeof _this4.props[value] === 'function') { 251 | var eventName = value.replace(/^fcEvent-/i, ''); 252 | events[eventName] = _this4.props[value]; 253 | } 254 | }); 255 | if (Object.keys(events).length > 0) { 256 | if (currentOptions.events === undefined) { 257 | currentOptions.events = events; 258 | } else { 259 | currentOptions.events = Object.assign(currentOptions.events, events); 260 | } 261 | } 262 | this.chartObj = new this.FusionCharts(currentOptions); 263 | this.chartObj.render(); 264 | this.oldOptions = currentOptions; 265 | if (this.props.onRender && typeof this.props.onRender === 'function') { 266 | this.props.onRender(this.chartObj); 267 | } 268 | } 269 | }, { 270 | key: "resolveChartOptions", 271 | value: function resolveChartOptions(props) { 272 | var chartConfig = props.chartConfig ? props.chartConfig : {}; 273 | var inlineOptions = _options["default"].reduce(function (options, optionName) { 274 | options[optionName] = props[optionName]; 275 | return options; 276 | }, {}); 277 | Object.assign(inlineOptions, chartConfig); 278 | if (utils.isObject(inlineOptions.dataSource) && !utils.checkIfDataTableExists(inlineOptions.dataSource)) { 279 | inlineOptions.dataSource = utils.deepCopyOf(inlineOptions.dataSource); 280 | } else if (utils.isObject(inlineOptions.dataSource) && utils.checkIfDataTableExists(inlineOptions.dataSource)) { 281 | inlineOptions.dataSource = utils.cloneDataSource(inlineOptions.dataSource, 'clone'); 282 | } 283 | if (utils.isObject(inlineOptions.link)) { 284 | inlineOptions.link = utils.deepCopyOf(inlineOptions.link); 285 | } 286 | if (utils.isObject(inlineOptions.events)) { 287 | inlineOptions.events = Object.assign({}, inlineOptions.events); 288 | } 289 | return inlineOptions; 290 | } 291 | }, { 292 | key: "render", 293 | value: function render() { 294 | return /*#__PURE__*/_react["default"].createElement("div", { 295 | className: this.props.className, 296 | id: this.containerId 297 | }); 298 | } 299 | }], [{ 300 | key: "fcRoot", 301 | value: function fcRoot(core) { 302 | for (var _len = arguments.length, modules = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { 303 | modules[_key - 1] = arguments[_key]; 304 | } 305 | modules.forEach(function (m) { 306 | if (m.getName && m.getType || m.name && m.type) { 307 | core.addDep(m); 308 | } else { 309 | m(core); 310 | } 311 | }); 312 | ReactFC.fusionChartsCore = core; 313 | } 314 | }]); 315 | }(_react["default"].Component); 316 | var _default = exports["default"] = ReactFC; -------------------------------------------------------------------------------- /lib/utils/options.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports["default"] = void 0; 7 | var _default = exports["default"] = ['type', 'id', 'width', 'height', 'dataFormat', 'dataSource', 'events', 'link', 'showDataLoadingMessage', 'showChartLoadingMessage', 'baseChartMessageFont', 'baseChartMessageFontSize', 'baseChartMessageColor', 'dataLoadStartMessage', 'dataLoadErrorMessage', 'dataInvalidMessage', 'dataEmptyMessage', 'typeNotSupportedMessage', 'loadMessage', 'renderErrorMessage', 'containerBackgroundColor', 'containerBackgroundOpacity', 'containerClassName', 'baseChartMessageImageHAlign', 'baseChartMessageImageVAlign', 'baseChartMessageImageAlpha', 'baseChartMessageImageScale', 'typeNotSupportedMessageImageHAalign', 'typeNotSupportedMessageImageVAlign', 'typeNotSupportedMessageImageAlpha', 'typeNotSupportedMessageImageScale', 'dataLoadErrorMessageImageHAlign', 'dataLoadErrorMessageImageVAlign', 'dataLoadErrorMessageImageAlpha', 'dataLoadErrorMessageImageScale', 'dataLoadStartMessageImageHAlign', 'dataLoadStartMessageImageVAlign', 'dataLoadStartMessageImageAlpha', 'dataLoadStartMessageImageScale', 'dataInvalidMessageImageHAlign', 'dataInvalidMessageImageVAlign', 'dataInvalidMessageImageAlpha', 'dataInvalidMessageImageScale', 'dataEmptyMessageImageHAlign', 'dataEmptyMessageImageVAlign', 'dataEmptyMessageImageAlpha', 'dataEmptyMessageImageScale', 'renderErrorMessageImageHAlign', 'renderErrorMessageImageVAlign', 'renderErrorMessageImageAlpha', 'renderErrorMessageImageScale', 'loadMessageImageHAlign', 'loadMessageImageVAlign', 'loadMessageImageAlpha', 'loadMessageImageScale']; -------------------------------------------------------------------------------- /lib/utils/utils.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.checkIfDataTableExists = checkIfDataTableExists; 7 | exports.cloneDataSource = cloneDataSource; 8 | exports.deepCopyOf = deepCopyOf; 9 | exports.isCallable = isCallable; 10 | exports.isObject = isObject; 11 | exports.isSameObjectContent = isSameObjectContent; 12 | exports.isUndefined = isUndefined; 13 | function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); } 14 | /* eslint-disable guard-for-in */ 15 | function isObject(value) { 16 | return value !== null && _typeof(value) === 'object'; 17 | } 18 | function isCallable(value) { 19 | return typeof value === 'function'; 20 | } 21 | function isSameObjectContent(obj1, obj2) { 22 | if (Object.keys(obj1).length !== Object.keys(obj2).length) { 23 | return false; 24 | } 25 | var keys = Object.keys(obj1); 26 | for (var i = 0; i < keys.length; i += 1) { 27 | var key = keys[i]; 28 | if (isObject(obj1[key]) && isObject(obj2[key])) { 29 | if (!isSameObjectContent(obj1[key], obj2[key])) { 30 | return false; 31 | } 32 | } else if (obj1[key] !== obj2[key]) { 33 | return false; 34 | } 35 | } 36 | return true; 37 | } 38 | function isUndefined(value) { 39 | // eslint-disable-next-line no-void 40 | var UNDEFINED = void 0; 41 | return value === UNDEFINED; 42 | } 43 | function deepCopyOf(obj) { 44 | return JSON.parse(JSON.stringify(obj)); 45 | } 46 | function checkIfDataTableExists(dataSource) { 47 | // eslint-disable-next-line no-underscore-dangle 48 | if (dataSource && dataSource.data && dataSource.data._dataStore) { 49 | return true; 50 | } 51 | return false; 52 | } 53 | function cloneDataSource(obj) { 54 | var purpose = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'clone'; 55 | var type = _typeof(obj); 56 | if (type === 'string' || type === 'number' || type === 'function' || type === 'boolean') { 57 | return obj; 58 | } 59 | if (obj === null || obj === undefined) { 60 | return obj; 61 | } 62 | if (Array.isArray(obj)) { 63 | var arr = []; 64 | for (var i = 0; i < obj.length; i++) { 65 | arr.push(this.cloneDataSource(obj[i])); 66 | } 67 | return arr; 68 | } 69 | if (_typeof(obj) === 'object') { 70 | var clonedObj = {}; 71 | // eslint-disable-next-line guard-for-in 72 | // eslint-disable-next-line no-restricted-syntax 73 | for (var prop in obj) { 74 | // Edge case handling for DataTable 75 | if (prop === 'data') { 76 | // eslint-disable-next-line no-underscore-dangle 77 | if (obj[prop]._dataStore && purpose === 'clone') { 78 | clonedObj[prop] = obj[prop]; 79 | // eslint-disable-next-line no-underscore-dangle 80 | } else if (obj[prop]._dataStore && purpose === 'diff') { 81 | clonedObj[prop] = '-'; 82 | } else { 83 | clonedObj[prop] = this.cloneDataSource(obj[prop]); 84 | } 85 | continue; 86 | } 87 | clonedObj[prop] = this.cloneDataSource(obj[prop]); 88 | } 89 | return clonedObj; 90 | } 91 | return undefined; 92 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-fusioncharts", 3 | "version": "4.1.0", 4 | "description": "Simple and Lightweight React component for FusionCharts JavaScript Charting Library", 5 | "main": "lib/ReactFC.js", 6 | "author": { 7 | "name": "FusionCharts", 8 | "email": "support@fusioncharts.com" 9 | }, 10 | "license": "MIT", 11 | "contributors": [ 12 | { 13 | "name": "Meher Howji", 14 | "email": "meher.howji@idera.com", 15 | "url": "https://github.com/meherhowji/" 16 | } 17 | ], 18 | "homepage": "https://github.com/fusioncharts/react-fusioncharts-component", 19 | "repository": { 20 | "type": "git", 21 | "url": "https://github.com/fusioncharts/react-fusioncharts-component.git" 22 | }, 23 | "bugs": { 24 | "url": "https://github.com/fusioncharts/react-fusioncharts-component/issues" 25 | }, 26 | "dependencies": { 27 | "uuid": "^3.2.1" 28 | }, 29 | "devDependencies": { 30 | "@babel/cli": "^7.1.0", 31 | "@babel/core": "^7.1.0", 32 | "@babel/preset-env": "^7.1.0", 33 | "@babel/preset-es2015": "^7.0.0-beta.53", 34 | "@babel/preset-react": "^7.0.0", 35 | "@babel/preset-stage-0": "^7.0.0", 36 | "babel-loader": "^8.0.4", 37 | "eslint": "^4.19.0", 38 | "eslint-config-airbnb": "^16.1.0", 39 | "eslint-config-prettier": "^3.1.0", 40 | "eslint-plugin-import": "^2.9.0", 41 | "eslint-plugin-jsx-a11y": "^6.0.3", 42 | "eslint-plugin-react": "^7.7.0", 43 | "fusioncharts": "^3.18", 44 | "jest": "^22.4.2", 45 | "lodash": "^4.17.11", 46 | "nodemon": "^2.0.2", 47 | "prop-types": "^15.6.2", 48 | "react": "^18.3.1", 49 | "react-addons-test-utils": "15.3.0", 50 | "react-component-gulp-tasks": "^0.7.6", 51 | "react-dom": "^18.3.1", 52 | "react-test-renderer": "^16.2.0", 53 | "serve": "^10.0.1", 54 | "webpack": "5.91.0", 55 | "webpack-cli": "5.1.4" 56 | }, 57 | "peerDependencies": { 58 | "react": "0.14.0 - 18.x" 59 | }, 60 | "scripts": { 61 | "build:lib": "babel ./src -d ./lib && mv ./lib/DrillDown.js ./components/DrillDown.js", 62 | "build:umd": "webpack", 63 | "build": "npm run build:lib && npm run build:umd", 64 | "build:example": "npm run build:lib && webpack --config webpack.config.example.js", 65 | "start": "npm run build:example && serve example/", 66 | "test": "jest --coverage --verbose", 67 | "test:report": "npm test && serve coverage/lcov-report -o", 68 | "watch": "nodemon --ignore example/dist --watch example --watch src --exec \"npm run start\"" 69 | }, 70 | "keywords": [ 71 | "react", 72 | "react-component", 73 | "component", 74 | "fusioncharts", 75 | "javascript-charts", 76 | "interactive-charts", 77 | "charts", 78 | "graphs", 79 | "visualization", 80 | "data-visualization", 81 | "dataviz", 82 | "browserify", 83 | "webpack" 84 | ] 85 | } 86 | -------------------------------------------------------------------------------- /src/DrillDown.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import ReactFC from '../lib/ReactFC'; 4 | 5 | 6 | class DrillDown extends React.Component { 7 | // Resolve FusionCharts 8 | static fcRoot(core, ...modules) { 9 | ReactFC.fcRoot(core, ...modules); 10 | } 11 | 12 | constructor(props) { 13 | super(props); 14 | 15 | const { btnConfig, btnStyle } = props; 16 | this.finalBtnConfig = Object.assign({}, DrillDown.defaultProps.btnConfig, btnConfig); 17 | [this.positionV, this.positionH] = this.finalBtnConfig.placement.split('-'); 18 | this.state = { 19 | isBtnVisible: true, 20 | selectedChild: -1, 21 | isDrilledDown: false, 22 | }; 23 | 24 | /* Function Bindings */ 25 | this.plotClicked = this.plotClicked.bind(this); 26 | this.onChildRendered = this.onChildRendered.bind(this); 27 | this.toggleParentBtnVisibility = this.toggleParentBtnVisibility.bind(this); 28 | this.onBtnClick = this.onBtnClick.bind(this); 29 | 30 | /* Default styles */ 31 | this.wrapperStyle = { 32 | position: 'relative', 33 | display: 'inline-block', 34 | }; 35 | 36 | this.defButtonStyle = { 37 | border: `1px solid ${this.finalBtnConfig.borderColor}`, 38 | backgroundColor: `${this.finalBtnConfig.backgroundColor}`, 39 | color: `${this.finalBtnConfig.color}`, 40 | fontFamily: `${this.finalBtnConfig.fontFamily}`, 41 | fontSize: `${this.finalBtnConfig.fontSize}`, 42 | padding: `${this.finalBtnConfig.padding}`, 43 | fontWeight: `${this.finalBtnConfig.fontWeight}`, 44 | position: 'absolute', 45 | [this.positionH]: `${this.finalBtnConfig.margin}`, 46 | [this.positionV]: `${this.finalBtnConfig.margin}`, 47 | cursor: 'pointer', 48 | }; 49 | 50 | this.finBtnStyle = typeof btnStyle === 'undefined' ? this.defButtonStyle : btnStyle; 51 | } 52 | 53 | determinePlotMapType(plotChildMap) { 54 | let isNumberFound = false; 55 | let isObjectFound = false; 56 | plotChildMap.forEach((val) => { 57 | if (typeof val === 'undefined' || val === null) return; 58 | if (typeof val === 'number' && val > -1) isNumberFound = true; 59 | if (typeof val === 'object') isObjectFound = true; 60 | }); 61 | if (isNumberFound && isObjectFound) { 62 | return 'invalid'; 63 | } 64 | if (isNumberFound) return 'number'; 65 | if (isObjectFound) return 'object'; 66 | return 'noop'; 67 | } 68 | 69 | plotClicked(e) { 70 | const { index } = e.data; 71 | const propChildren = Array.isArray(this.props.children) ? 72 | this.props.children : [this.props.children]; 73 | const childrenLen = propChildren.length; 74 | const { plotChildMap } = this.props; 75 | if (childrenLen === 0) return; 76 | 77 | // Further Optimization needed. 78 | const mapType = this.determinePlotMapType(plotChildMap); 79 | 80 | // Case : Array of numbers 81 | if (mapType === 'number') { 82 | const childPosition = plotChildMap[index]; 83 | if (childPosition === null || typeof childPosition === 'undefined' || 84 | childPosition >= childrenLen || childPosition < 0) return; 85 | 86 | this.setState({ 87 | selectedChild: childPosition, 88 | isDrilledDown: true, 89 | }); 90 | } 91 | 92 | // Case : Array of objects 93 | if (mapType === 'object') { 94 | for (let i = 0; i < childrenLen; i++) { 95 | if (typeof plotChildMap[i] === 'undefined' || plotChildMap[i] === null) continue; 96 | const { plotPosition, childPosition } = plotChildMap[i]; 97 | if (plotPosition === index && 98 | (childPosition !== null && typeof childPosition !== 'undefined') && 99 | childPosition < childrenLen && childPosition > -1) { 100 | this.setState({ 101 | selectedChild: childPosition, 102 | isDrilledDown: true, 103 | }); 104 | return; 105 | } 106 | } 107 | } 108 | 109 | // Case : Heterogeneous 110 | if (mapType === 'invalid') { 111 | console.log('Invalid heterogeneous data: Please check proptypes for - plotChildMap'); 112 | } 113 | } 114 | 115 | cloneReactFCChild(reactFCElem, customProps) { 116 | return React.cloneElement( 117 | reactFCElem, 118 | customProps, 119 | ); 120 | } 121 | 122 | onChildRendered() { 123 | if (this.props.toggleParentBtnVisibility) { 124 | this.props.toggleParentBtnVisibility(false); 125 | } 126 | } 127 | 128 | toggleParentBtnVisibility(isBtnVisible) { 129 | this.setState({ isBtnVisible }); 130 | } 131 | 132 | onBtnClick() { 133 | this.setState({ 134 | isDrilledDown: false, 135 | }); 136 | if (this.props.toggleParentBtnVisibility) { 137 | this.props.toggleParentBtnVisibility(true); 138 | } 139 | } 140 | 141 | render() { 142 | let component; 143 | const { selectedChild, isBtnVisible } = this.state; 144 | const { 145 | children, width, height, 146 | } = this.props; 147 | const clonedElemConfig = { 148 | width, 149 | height, 150 | onRender: this.onChildRendered, 151 | toggleParentBtnVisibility: this.toggleParentBtnVisibility, 152 | }; 153 | 154 | if (!this.state.isDrilledDown) { 155 | component = ( 156 | 160 | ); 161 | } else { 162 | const propChildren = Array.isArray(children) ? children : [children]; 163 | component = ( 164 |
165 | {/* Children Chart */} 166 | {this.cloneReactFCChild(propChildren[selectedChild], clonedElemConfig)} 167 | {/* Back Button */} 168 | { 169 | isBtnVisible ? 170 | : null 176 | } 177 |
178 | ); 179 | } 180 | 181 | return component; 182 | } 183 | } 184 | 185 | DrillDown.defaultProps = { 186 | plotChildMap: [], 187 | btnConfig: { 188 | text: 'Back', 189 | color: '#000000', 190 | backgroundColor: '#F6F6F6', 191 | borderColor: '#000000', 192 | fontSize: '14px', 193 | fontWeight: 'bold', 194 | padding: '3px', 195 | fontFamily: 'Verdana, sans', 196 | placement: 'top-right', 197 | margin: '10px', 198 | }, 199 | btnStyle: undefined, 200 | dataSource: {}, 201 | dataFormat: 'json', 202 | type: '', 203 | height: '', 204 | width: '', 205 | }; 206 | 207 | DrillDown.propTypes = { 208 | plotChildMap: PropTypes.oneOfType([ 209 | PropTypes.arrayOf(PropTypes.shape({ 210 | plotPosition: PropTypes.number, 211 | childPosition: PropTypes.number, 212 | })), 213 | PropTypes.arrayOf(PropTypes.number), 214 | ]), 215 | btnConfig: PropTypes.shape({ 216 | text: PropTypes.string, 217 | color: PropTypes.string, 218 | backgroundColor: PropTypes.string, 219 | borderColor: PropTypes.string, 220 | fontSize: PropTypes.string, 221 | fontWeight: PropTypes.string, 222 | padding: PropTypes.string, 223 | fontFamily: PropTypes.string, 224 | placement: PropTypes.oneOf([ 225 | 'top-left', 226 | 'top-right', 227 | 'bottom-left', 228 | 'bottom-right', 229 | ]), 230 | margin: PropTypes.string, 231 | }), 232 | btnStyle: PropTypes.object, 233 | dataSource: PropTypes.object, 234 | dataFormat: PropTypes.string, 235 | type: PropTypes.string, 236 | height: PropTypes.string, 237 | width: PropTypes.string, 238 | }; 239 | 240 | export default DrillDown; 241 | -------------------------------------------------------------------------------- /src/ReactFC.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import uuid from 'uuid/v4'; 3 | import * as utils from './utils/utils'; 4 | import fusionChartsOptions from './utils/options'; 5 | 6 | class ReactFC extends React.Component { 7 | initialUnmount = false; 8 | static fcRoot(core, ...modules) { 9 | modules.forEach(m => { 10 | if ((m.getName && m.getType) || (m.name && m.type)) { 11 | core.addDep(m); 12 | } else { 13 | m(core); 14 | } 15 | }); 16 | ReactFC.fusionChartsCore = core; 17 | } 18 | 19 | constructor(props) { 20 | super(props); 21 | 22 | this.containerId = uuid(); 23 | this.oldOptions = null; 24 | this.FusionCharts = 25 | props.fcLibrary || ReactFC.fusionChartsCore || window.FusionCharts; 26 | } 27 | 28 | componentDidMount() { 29 | this.renderChart(); 30 | } 31 | 32 | componentDidUpdate(prevProps) { 33 | if (prevProps !== this.props) { 34 | this.detectChanges(this.props); 35 | } 36 | } 37 | 38 | componentWillUnmount() { 39 | if (!this.initialUnmount) this.initialUnmount = true; 40 | else this.chartObj.dispose(); 41 | } 42 | 43 | detectChanges(nextProps) { 44 | const currentOptions = this.resolveChartOptions(nextProps); 45 | const { oldOptions } = this; 46 | const optionsUpdatedNatively = [ 47 | 'width', 48 | 'height', 49 | 'type', 50 | 'dataFormat', 51 | 'dataSource', 52 | 'events' 53 | ]; 54 | 55 | this.checkAndUpdateChartDimensions(currentOptions, oldOptions); 56 | this.checkAndUpdateChartType(currentOptions, oldOptions); 57 | this.checkAndUpdateChartData(currentOptions, oldOptions); 58 | this.checkAndUpdateEvents(currentOptions, oldOptions); 59 | this.checkAndUpdateRestOptions( 60 | fusionChartsOptions.filter( 61 | option => optionsUpdatedNatively.indexOf(option) === -1 62 | ), 63 | currentOptions, 64 | oldOptions 65 | ); 66 | 67 | this.oldOptions = currentOptions; 68 | } 69 | 70 | checkAndUpdateChartDimensions(currentOptions, oldOptions) { 71 | const currWidth = currentOptions.width; 72 | const currHeight = currentOptions.height; 73 | const oldWidth = oldOptions.width; 74 | const oldHeight = oldOptions.height; 75 | 76 | if ( 77 | String(currWidth) !== String(oldWidth) || 78 | String(currHeight) !== String(oldHeight) 79 | ) { 80 | if (!utils.isUndefined(currWidth) && !utils.isUndefined(currHeight)) { 81 | this.chartObj.resizeTo(currWidth, currHeight); 82 | } else { 83 | if (!utils.isUndefined(currWidth)) { 84 | this.chartObj.resizeTo({ 85 | w: currWidth 86 | }); 87 | } 88 | if (!utils.isUndefined(currHeight)) { 89 | this.chartObj.resizeTo({ 90 | h: currHeight 91 | }); 92 | } 93 | } 94 | } 95 | } 96 | 97 | checkAndUpdateChartType(currentOptions, oldOptions) { 98 | const currType = currentOptions.type; 99 | const oldType = oldOptions.type; 100 | 101 | if (String(currType).toLowerCase() !== String(oldType).toLowerCase()) { 102 | if (!utils.isUndefined(currType)) { 103 | this.chartObj.chartType(String(currType).toLowerCase()); 104 | } 105 | } 106 | } 107 | 108 | checkAndUpdateChartData(currentOptions, oldOptions) { 109 | const currDataFormat = currentOptions.dataFormat; 110 | const currData = currentOptions.dataSource; 111 | const oldDataFormat = oldOptions.dataFormat; 112 | const oldData = oldOptions.dataSource; 113 | if ( 114 | String(currDataFormat).toLowerCase() !== 115 | String(oldDataFormat).toLowerCase() 116 | ) { 117 | if (!utils.isUndefined(currDataFormat) && !utils.isUndefined(currData)) { 118 | this.chartObj.setChartData( 119 | currData, 120 | String(currDataFormat).toLowerCase() 121 | ); 122 | // If the chart dataFormat is changed then 123 | // animate the chart to show the changes 124 | this.chartObj.render(); 125 | return; 126 | } 127 | } 128 | if (!this.isSameChartData(currData, oldData)) { 129 | if (!utils.isUndefined(currData)) { 130 | this.chartObj.setChartData( 131 | currData, 132 | // When dataFormat is not given, but data is changed, 133 | // then use 'json' as default dataFormat 134 | currDataFormat ? String(currDataFormat).toLowerCase() : 'json' 135 | ); 136 | } 137 | } 138 | } 139 | 140 | isSameChartData(currData, oldData) { 141 | /* TODO 142 | 1. Current has DataStore and Old doesn't 143 | 2. Old has and Current doesn't 144 | 3. Both has, check ref is equal, return false only if not equal 145 | 4. Clone oldData for diff 146 | 5. Clone currentData for diff 147 | 6. return string check. 148 | */ 149 | // 1. Current has DataStore and Old doesn't 150 | if ( 151 | utils.checkIfDataTableExists(currData) && 152 | !utils.checkIfDataTableExists(oldData) 153 | ) { 154 | return false; 155 | } 156 | // 2. Old has and Current doesn't 157 | if ( 158 | !utils.checkIfDataTableExists(currData) && 159 | utils.checkIfDataTableExists(oldData) 160 | ) { 161 | return false; 162 | } 163 | // 3. Both has, check ref is equal, return false only if not equal 164 | if ( 165 | utils.checkIfDataTableExists(currData) && 166 | utils.checkIfDataTableExists(oldData) && 167 | currData.data !== oldData.data 168 | ) { 169 | return false; 170 | } 171 | // 4. Clone oldData for diff 172 | const oldDataStringified = JSON.stringify( 173 | utils.cloneDataSource(oldData, 'diff') 174 | ); 175 | // 5. Clone currentData for diff 176 | const currentDataStringified = JSON.stringify( 177 | utils.cloneDataSource(currData, 'diff') 178 | ); 179 | // 6. return string check. 180 | return oldDataStringified === currentDataStringified; 181 | } 182 | 183 | checkAndUpdateEvents(currentOptions, oldOptions) { 184 | const currEvents = currentOptions.events; 185 | const oldEvents = oldOptions.events; 186 | let temp1; 187 | let temp2; 188 | 189 | if (this.detectChartEventsChange(currEvents, oldEvents)) { 190 | if (!utils.isUndefined(currEvents)) { 191 | temp1 = Object.assign({}, currEvents); 192 | temp2 = utils.isUndefined(oldEvents) 193 | ? {} 194 | : Object.assign({}, oldEvents); 195 | Object.keys(temp2).forEach(eventName => { 196 | if (temp2[eventName] === temp1[eventName]) { 197 | temp1[eventName] = undefined; 198 | } else { 199 | this.chartObj.removeEventListener(eventName, temp2[eventName]); 200 | } 201 | }); 202 | Object.keys(temp1).forEach(eventName => { 203 | if (temp1[eventName]) { 204 | this.chartObj.addEventListener(eventName, temp1[eventName]); 205 | } 206 | }); 207 | } 208 | } 209 | } 210 | 211 | detectChartEventsChange(currEvents, oldEvents) { 212 | if (utils.isObject(currEvents) && utils.isObject(oldEvents)) { 213 | return !this.isSameChartEvents(currEvents, oldEvents); 214 | } 215 | return !(currEvents === oldEvents); 216 | } 217 | 218 | isSameChartEvents(currEvents, oldEvents) { 219 | if (Object.keys(currEvents).length !== Object.keys(oldEvents).length) { 220 | return false; 221 | } 222 | const currEventNames = Object.keys(currEvents); 223 | for (let i = 0; i < currEventNames.length; ++i) { 224 | const evName = currEventNames[i]; 225 | if (currEvents[evName] !== oldEvents[evName]) { 226 | return false; 227 | } 228 | } 229 | return true; 230 | } 231 | 232 | checkAndUpdateRestOptions(restOptions, currentOptions, oldOptions) { 233 | let optionsUpdated = false; 234 | 235 | restOptions.forEach(optionName => { 236 | const currValue = currentOptions[optionName]; 237 | const oldValue = oldOptions[optionName]; 238 | 239 | if (!this.isSameOptionValue(currValue, oldValue)) { 240 | if (!utils.isUndefined(currValue)) { 241 | if ( 242 | this.chartObj.options && 243 | this.chartObj.options.hasOwnProperty(optionName) 244 | ) { 245 | this.chartObj.options[optionName] = currValue; 246 | optionsUpdated = true; 247 | } 248 | } 249 | } 250 | }); 251 | 252 | if (optionsUpdated) { 253 | this.chartObj.render(); // re-render the chart to reflect the changes 254 | } 255 | } 256 | 257 | isSameOptionValue(currValue, oldValue) { 258 | if (utils.isObject(currValue) && utils.isObject(oldValue)) { 259 | return utils.isSameObjectContent(currValue, oldValue); 260 | } 261 | return String(currValue) === String(oldValue); 262 | } 263 | 264 | renderChart() { 265 | const currentOptions = this.resolveChartOptions(this.props); 266 | const events = {}; 267 | 268 | currentOptions.renderAt = this.containerId; 269 | 270 | Object.keys(this.props).forEach(value => { 271 | const event = value.match(/^fcEvent-.*/i); 272 | if (event && typeof this.props[value] === 'function') { 273 | const eventName = value.replace(/^fcEvent-/i, ''); 274 | events[eventName] = this.props[value]; 275 | } 276 | }); 277 | 278 | if (Object.keys(events).length > 0) { 279 | if (currentOptions.events === undefined) { 280 | currentOptions.events = events; 281 | } else { 282 | currentOptions.events = Object.assign(currentOptions.events, events); 283 | } 284 | } 285 | 286 | this.chartObj = new this.FusionCharts(currentOptions); 287 | this.chartObj.render(); 288 | this.oldOptions = currentOptions; 289 | 290 | if (this.props.onRender && typeof this.props.onRender === 'function') { 291 | this.props.onRender(this.chartObj); 292 | } 293 | } 294 | 295 | resolveChartOptions(props) { 296 | const chartConfig = props.chartConfig ? props.chartConfig : {}; 297 | const inlineOptions = fusionChartsOptions.reduce((options, optionName) => { 298 | options[optionName] = props[optionName]; 299 | return options; 300 | }, {}); 301 | Object.assign(inlineOptions, chartConfig); 302 | 303 | if ( 304 | utils.isObject(inlineOptions.dataSource) && 305 | !utils.checkIfDataTableExists(inlineOptions.dataSource) 306 | ) { 307 | inlineOptions.dataSource = utils.deepCopyOf(inlineOptions.dataSource); 308 | } else if ( 309 | utils.isObject(inlineOptions.dataSource) && 310 | utils.checkIfDataTableExists(inlineOptions.dataSource) 311 | ) { 312 | inlineOptions.dataSource = utils.cloneDataSource( 313 | inlineOptions.dataSource, 314 | 'clone' 315 | ); 316 | } 317 | if (utils.isObject(inlineOptions.link)) { 318 | inlineOptions.link = utils.deepCopyOf(inlineOptions.link); 319 | } 320 | if (utils.isObject(inlineOptions.events)) { 321 | inlineOptions.events = Object.assign({}, inlineOptions.events); 322 | } 323 | return inlineOptions; 324 | } 325 | 326 | render() { 327 | return
; 328 | } 329 | } 330 | 331 | export default ReactFC; 332 | -------------------------------------------------------------------------------- /src/utils/options.js: -------------------------------------------------------------------------------- 1 | export default [ 2 | 'type', 3 | 'id', 4 | 'width', 5 | 'height', 6 | 'dataFormat', 7 | 'dataSource', 8 | 'events', 9 | 'link', 10 | 'showDataLoadingMessage', 11 | 'showChartLoadingMessage', 12 | 'baseChartMessageFont', 13 | 'baseChartMessageFontSize', 14 | 'baseChartMessageColor', 15 | 'dataLoadStartMessage', 16 | 'dataLoadErrorMessage', 17 | 'dataInvalidMessage', 18 | 'dataEmptyMessage', 19 | 'typeNotSupportedMessage', 20 | 'loadMessage', 21 | 'renderErrorMessage', 22 | 'containerBackgroundColor', 23 | 'containerBackgroundOpacity', 24 | 'containerClassName', 25 | 'baseChartMessageImageHAlign', 26 | 'baseChartMessageImageVAlign', 27 | 'baseChartMessageImageAlpha', 28 | 'baseChartMessageImageScale', 29 | 'typeNotSupportedMessageImageHAalign', 30 | 'typeNotSupportedMessageImageVAlign', 31 | 'typeNotSupportedMessageImageAlpha', 32 | 'typeNotSupportedMessageImageScale', 33 | 'dataLoadErrorMessageImageHAlign', 34 | 'dataLoadErrorMessageImageVAlign', 35 | 'dataLoadErrorMessageImageAlpha', 36 | 'dataLoadErrorMessageImageScale', 37 | 'dataLoadStartMessageImageHAlign', 38 | 'dataLoadStartMessageImageVAlign', 39 | 'dataLoadStartMessageImageAlpha', 40 | 'dataLoadStartMessageImageScale', 41 | 'dataInvalidMessageImageHAlign', 42 | 'dataInvalidMessageImageVAlign', 43 | 'dataInvalidMessageImageAlpha', 44 | 'dataInvalidMessageImageScale', 45 | 'dataEmptyMessageImageHAlign', 46 | 'dataEmptyMessageImageVAlign', 47 | 'dataEmptyMessageImageAlpha', 48 | 'dataEmptyMessageImageScale', 49 | 'renderErrorMessageImageHAlign', 50 | 'renderErrorMessageImageVAlign', 51 | 'renderErrorMessageImageAlpha', 52 | 'renderErrorMessageImageScale', 53 | 'loadMessageImageHAlign', 54 | 'loadMessageImageVAlign', 55 | 'loadMessageImageAlpha', 56 | 'loadMessageImageScale', 57 | ]; 58 | -------------------------------------------------------------------------------- /src/utils/utils.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable guard-for-in */ 2 | export function isObject(value) { 3 | return value !== null && typeof value === 'object'; 4 | } 5 | 6 | export function isCallable(value) { 7 | return typeof value === 'function'; 8 | } 9 | 10 | export function isSameObjectContent(obj1, obj2) { 11 | if (Object.keys(obj1).length !== Object.keys(obj2).length) { 12 | return false; 13 | } 14 | const keys = Object.keys(obj1); 15 | 16 | for (let i = 0; i < keys.length; i += 1) { 17 | const key = keys[i]; 18 | if (isObject(obj1[key]) && isObject(obj2[key])) { 19 | if (!isSameObjectContent(obj1[key], obj2[key])) { 20 | return false; 21 | } 22 | } else if (obj1[key] !== obj2[key]) { 23 | return false; 24 | } 25 | } 26 | return true; 27 | } 28 | 29 | export function isUndefined(value) { 30 | // eslint-disable-next-line no-void 31 | const UNDEFINED = void 0; 32 | return value === UNDEFINED; 33 | } 34 | 35 | export function deepCopyOf(obj) { 36 | return JSON.parse(JSON.stringify(obj)); 37 | } 38 | 39 | export function checkIfDataTableExists(dataSource) { 40 | // eslint-disable-next-line no-underscore-dangle 41 | if (dataSource && dataSource.data && dataSource.data._dataStore) { 42 | return true; 43 | } 44 | return false; 45 | } 46 | 47 | export function cloneDataSource(obj, purpose = 'clone') { 48 | const type = typeof obj; 49 | if ( 50 | type === 'string' || 51 | type === 'number' || 52 | type === 'function' || 53 | type === 'boolean' 54 | ) { 55 | return obj; 56 | } 57 | if (obj === null || obj === undefined) { 58 | return obj; 59 | } 60 | if (Array.isArray(obj)) { 61 | const arr = []; 62 | for (let i = 0; i < obj.length; i++) { 63 | arr.push(this.cloneDataSource(obj[i])); 64 | } 65 | return arr; 66 | } 67 | if (typeof obj === 'object') { 68 | const clonedObj = {}; 69 | // eslint-disable-next-line guard-for-in 70 | // eslint-disable-next-line no-restricted-syntax 71 | for (const prop in obj) { 72 | // Edge case handling for DataTable 73 | if (prop === 'data') { 74 | // eslint-disable-next-line no-underscore-dangle 75 | if (obj[prop]._dataStore && purpose === 'clone') { 76 | clonedObj[prop] = obj[prop]; 77 | // eslint-disable-next-line no-underscore-dangle 78 | } else if (obj[prop]._dataStore && purpose === 'diff') { 79 | clonedObj[prop] = '-'; 80 | } else { 81 | clonedObj[prop] = this.cloneDataSource(obj[prop]); 82 | } 83 | continue; 84 | } 85 | clonedObj[prop] = this.cloneDataSource(obj[prop]); 86 | } 87 | return clonedObj; 88 | } 89 | return undefined; 90 | } 91 | -------------------------------------------------------------------------------- /umd-src/DrillDown.js: -------------------------------------------------------------------------------- 1 | // import DrillDown from '../src/DrillDown'; 2 | 3 | // export default DrillDown; 4 | 5 | module.exports = require('../src/DrillDown').default; 6 | 7 | 8 | -------------------------------------------------------------------------------- /webpack.config.example.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | const config = { 4 | entry: path.join(__dirname, 'example', 'index.js'), 5 | output: { 6 | path: path.join(__dirname, 'example', 'dist'), 7 | filename: 'main.js', 8 | }, 9 | mode: 'none', 10 | module: { 11 | rules: [ 12 | { 13 | test: /\.js?$/, 14 | exclude: /node_modules/, 15 | use: { 16 | loader: 'babel-loader', 17 | options: { 18 | presets: ['@babel/preset-react'] 19 | } 20 | } 21 | } 22 | ], 23 | }, 24 | }; 25 | 26 | module.exports = config; 27 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const _ = require('lodash'); 3 | 4 | const reactFCBuilderConfig = { 5 | name: 'reactFC', 6 | entry: path.join(__dirname, 'index.js'), 7 | output: { 8 | library: 'ReactFC', 9 | libraryTarget: 'umd', 10 | path: path.join(__dirname, 'dist'), 11 | filename: 'react-fusioncharts.js', 12 | umdNamedDefine: true, 13 | }, 14 | mode: 'none', 15 | module: { 16 | rules: [ 17 | { 18 | test: /\.js?$/, 19 | exclude: /node_modules/, 20 | use: { 21 | loader: 'babel-loader', 22 | options: { 23 | presets: ['@babel/preset-react', '@babel/preset-env'] 24 | } 25 | } 26 | } 27 | ], 28 | }, 29 | externals: [ 30 | { 31 | react: { 32 | root: 'React', 33 | commonjs2: 'react', 34 | commonjs: 'react', 35 | amd: 'react', 36 | }, 37 | }, 38 | { 39 | fusioncharts: { 40 | root: 'FusionCharts', 41 | commonjs2: 'fusioncharts', 42 | commonjs: 'fusioncharts', 43 | amd: 'fusioncharts', 44 | }, 45 | }, 46 | ], 47 | }; 48 | 49 | const drillDownBuilderConfig = { 50 | name: 'drillDown', 51 | entry: path.join(__dirname, 'umd-src/DrillDown.js'), 52 | output: { 53 | library: 'DrillDown', 54 | libraryTarget: 'umd', 55 | path: path.join(__dirname, 'dist'), 56 | filename: 'drill-down.js', 57 | umdNamedDefine: true, 58 | }, 59 | mode: 'none', 60 | module: { 61 | rules: [ 62 | { 63 | test: /\.js?$/, 64 | exclude: /node_modules/, 65 | use: { 66 | loader: 'babel-loader', 67 | options: { 68 | presets: ['@babel/preset-react', '@babel/preset-env'] 69 | } 70 | } 71 | } 72 | ], 73 | }, 74 | externals: [ 75 | { 76 | react: { 77 | root: 'React', 78 | commonjs2: 'react', 79 | commonjs: 'react', 80 | amd: 'react', 81 | }, 82 | }, 83 | { 84 | fusioncharts: { 85 | root: 'FusionCharts', 86 | commonjs2: 'fusioncharts', 87 | commonjs: 'fusioncharts', 88 | amd: 'fusioncharts', 89 | }, 90 | }, 91 | { 92 | reactfc: { 93 | root: 'ReactFC', 94 | commonjs2: 'react-fusioncharts', 95 | commonjs: 'react-fusioncharts', 96 | amd: 'react-fusioncharts', 97 | }, 98 | }, 99 | ], 100 | }; 101 | 102 | 103 | const reactFCProd = _.cloneDeep(reactFCBuilderConfig); 104 | 105 | const drillFCProd = _.cloneDeep(drillDownBuilderConfig); 106 | 107 | reactFCProd.name = 'reactfcProd'; 108 | drillFCProd.name = 'drilldownProd'; 109 | 110 | reactFCProd.output.filename = 'react-fusioncharts.min.js'; 111 | drillFCProd.output.filename = 'drill-down.min.js'; 112 | 113 | reactFCProd.mode = 'production'; 114 | drillFCProd.mode = 'production'; 115 | 116 | module.exports = [ 117 | reactFCBuilderConfig, 118 | drillDownBuilderConfig, 119 | reactFCProd, 120 | drillFCProd 121 | ]; 122 | --------------------------------------------------------------------------------