├── .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 |
--------------------------------------------------------------------------------