PluginProbe
Visualizer – Tables & Charts Manager with Built-in AI Generator / 3.11.10
Visualizer – Tables & Charts Manager with Built-in AI Generator v3.11.10
4.0.7 4.0.6 4.0.5 4.0.4 4.0.3 3.0.5 3.0.6 3.0.7 3.0.8 3.0.9 3.1.0 3.1.1 3.1.2 3.1.3 3.10.0 3.10.1 3.10.10 3.10.11 3.10.12 3.10.13 3.10.14 3.10.15 3.10.2 3.10.3 3.10.4 All 148 releases
visualizer / classes / Visualizer / Gutenberg / src / Editor.js

Editor.js in Visualizer – Tables & Charts Manager with Built-in AI Generator 3.11.10, at classes/Visualizer/Gutenberg/src/Editor.js

685 lines 18.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /**
2 * External dependencies
3 */
4 import Charts from './Components/Charts.js';
5
6 import ChartSelect from './Components/ChartSelect.js';
7
8 import ChartRender from './Components/ChartRender.js';
9
10 import { CSVToArray, buildChartPopup, tryGetPublishedChartData } from './utils.js';
11
12 /**
13 * WordPress dependencies
14 */
15 const { __ } = wp.i18n;
16
17 const {
18 apiFetch,
19 apiRequest
20 } = wp;
21
22 const {
23 Component,
24 Fragment
25 } = wp.element;
26
27 const {
28 Button,
29 ButtonGroup,
30 Dashicon,
31 Placeholder,
32 Notice,
33 Spinner
34 } = wp.components;
35
36 class Editor extends Component {
37 constructor() {
38 super( ...arguments );
39
40 this.getChart = this.getChart.bind( this );
41 this.editChart = this.editChart.bind( this );
42 this.editSettings = this.editSettings.bind( this );
43 this.editPermissions = this.editPermissions.bind( this );
44 this.readUploadedFile = this.readUploadedFile.bind( this );
45 this.editURL = this.editURL.bind( this );
46 this.editSchedule = this.editSchedule.bind( this );
47 this.editJSONSchedule = this.editJSONSchedule.bind( this );
48 this.editJSONURL = this.editJSONURL.bind( this );
49 this.editJSONHeaders = this.editJSONHeaders.bind( this );
50 this.editJSONRoot = this.editJSONRoot.bind( this );
51 this.editJSONPaging = this.editJSONPaging.bind( this );
52 this.JSONImportData = this.JSONImportData.bind( this );
53 this.editDatabaseSchedule = this.editDatabaseSchedule.bind( this );
54 this.databaseImportData = this.databaseImportData.bind( this );
55 this.uploadData = this.uploadData.bind( this );
56 this.getChartData = this.getChartData.bind( this );
57 this.editChartData = this.editChartData.bind( this );
58 this.updateChart = this.updateChart.bind( this );
59 this.createChart = this.createChart.bind( this );
60
61 this.state = {
62
63 /**
64 * Block Route Status
65 *
66 * home - Initial screen.
67 * showCharts - Display list of charts to pick.
68 * chartSelect - Chart selected.
69 * renderChart - Chart render.
70 */
71 route: ( this.props.attributes.route ? this.props.attributes.route : 'home' ),
72 chart: null,
73 isModified: false,
74 isLoading: false,
75 isScheduled: false
76 };
77 }
78
79 async componentDidMount() {
80
81 // Fetch review again if block loaded after saving.
82 if ( this.props.attributes.id ) {
83 let result = await apiFetch({ path: `wp/v2/visualizer/${this.props.attributes.id}` }).catch( function( error ) {
84 });
85
86 if ( result ) {
87 this.setState({
88 chart: result['chart_data']
89 });
90 } else {
91
92 // if the chart is not found.
93 this.setState({
94 route: 'error'
95 });
96 }
97 }
98 }
99
100 async getChart( id ) {
101 await this.setState({
102 isLoading: 'getChart'
103 });
104
105 const chartDataRequest = await tryGetPublishedChartData( id );
106
107 if ( 'publish' !== chartDataRequest.chartStatus ) {
108 this.setState({ route: 'showCharts', isLoading: false });
109 this.props.setAttributes({ route: 'showCharts' });
110 return;
111 }
112
113 this.setState({
114 route: 'chartSelect',
115 chart: chartDataRequest.result['chart_data'],
116 isLoading: true,
117 isModified: true
118 });
119
120 this.props.setAttributes({
121 id,
122 route: 'chartSelect',
123 lazy: -1
124 });
125 }
126
127 editChart() {
128 this.setState({ route: 'chartSelect' });
129 this.props.setAttributes({ route: 'chartSelect' });
130 }
131
132 editSettings( settings ) {
133 let chart = { ...this.state.chart };
134 if ( '1' !== settings.pagination ) {
135 delete settings.pageSize;
136 }
137 chart['visualizer-settings'] = settings;
138 this.setState({
139 chart,
140 isModified: true
141 });
142 }
143
144 editPermissions( permissions ) {
145 let chart = { ...this.state.chart };
146 chart['visualizer-permissions'] = permissions;
147 this.setState({
148 chart,
149 isModified: true
150 });
151 }
152
153 readUploadedFile( e ) {
154 const fileTobeRead = e.current.files[0];
155 const fileReader = new FileReader();
156 fileReader.onload = () => {
157 const data = CSVToArray( fileReader.result );
158 this.editChartData( data, 'Visualizer_Source_Csv' );
159 };
160 fileReader.readAsText( fileTobeRead );
161 }
162
163 editURL( url ) {
164 let chart = { ...this.state.chart };
165 chart['visualizer-chart-url'] = url;
166 this.setState({ chart });
167 }
168
169 editSchedule( schedule ) {
170 let chart = { ...this.state.chart };
171 chart['visualizer-chart-schedule'] = schedule;
172 this.setState({
173 chart,
174 isModified: true
175 });
176 }
177
178 editJSONSchedule( schedule ) {
179 let chart = { ...this.state.chart };
180 chart['visualizer-json-schedule'] = schedule;
181 this.setState({
182 chart,
183 isModified: true
184 });
185 }
186
187 editJSONURL( url ) {
188 let chart = { ...this.state.chart };
189 chart['visualizer-json-url'] = url;
190 this.setState({ chart });
191 }
192
193 editJSONHeaders( headers ) {
194 let chart = { ...this.state.chart };
195 delete headers.username;
196 delete headers.password;
197 chart['visualizer-json-headers'] = headers;
198 this.setState({ chart });
199 }
200
201 editJSONRoot( root ) {
202 let chart = { ...this.state.chart };
203 chart['visualizer-json-root'] = root;
204 this.setState({ chart });
205 }
206
207 editJSONPaging( root ) {
208 let chart = { ...this.state.chart };
209 chart['visualizer-json-paging'] = root;
210 this.setState({ chart });
211 }
212
213 JSONImportData( name, series, data ) {
214 let chart = { ...this.state.chart };
215 chart['visualizer-source'] = name;
216 chart['visualizer-default-data'] = 0;
217 chart['visualizer-series'] = series;
218 chart['visualizer-data'] = data;
219 this.setState({
220 chart,
221 isModified: true
222 });
223 }
224
225 editDatabaseSchedule( schedule ) {
226 let chart = { ...this.state.chart };
227 chart['visualizer-db-schedule'] = schedule;
228 this.setState({
229 chart,
230 isModified: true
231 });
232 }
233
234 databaseImportData( query, name, series, data ) {
235 let chart = { ...this.state.chart };
236 chart['visualizer-source'] = name;
237 chart['visualizer-default-data'] = 0;
238 chart['visualizer-series'] = series;
239 chart['visualizer-data'] = data;
240 chart['visualizer-db-query'] = query;
241 this.setState({
242 chart,
243 isModified: true
244 });
245 }
246
247 uploadData( scheduled = false ) {
248 this.setState({
249 isLoading: 'uploadData',
250 isScheduled: scheduled
251 });
252
253 apiRequest({ path: `/visualizer/v1/upload-data?url=${this.state.chart['visualizer-chart-url']}`, method: 'POST' }).then(
254 ( data ) => {
255 if ( 2 <= Object.keys( data ).length ) {
256 let chart = { ...this.state.chart };
257
258 chart['visualizer-source'] = 'Visualizer_Source_Csv_Remote';
259 chart['visualizer-default-data'] = 0;
260 chart['visualizer-series'] = data.series;
261 chart['visualizer-data'] = data.data;
262
263 let series = chart['visualizer-series'];
264 let settings = chart['visualizer-settings'];
265 let map = series;
266 let fieldName = 'series';
267
268 if ( 'pie' === chart['visualizer-chart-type']) {
269 map = chart['visualizer-data'];
270 fieldName = 'slices';
271 }
272
273 map.map( ( i, index ) => {
274 if ( 'pie' !== chart['visualizer-chart-type'] && 0 === index ) {
275 return;
276 }
277
278 const seriesIndex = 'pie' !== chart['visualizer-chart-type'] ? index - 1 : index;
279
280 if ( settings[fieldName][seriesIndex] === undefined ) {
281 settings[fieldName][seriesIndex] = {};
282 settings[fieldName][seriesIndex].temp = 1;
283 }
284 });
285
286 settings[fieldName] = settings[fieldName].filter( ( i, index ) => {
287 const length = 'pie' !== chart['visualizer-chart-type'] ? map.length - 1 : map.length;
288 return index < length;
289 });
290
291 chart['visualizer-settings'] = settings;
292
293 this.setState({
294 chart,
295 isModified: true,
296 isLoading: false
297 });
298
299 return data;
300 }
301
302 this.setState({
303 isLoading: false
304 });
305 },
306 ( err ) => {
307 this.setState({
308 isLoading: false
309 });
310
311 return err;
312 }
313 );
314 }
315
316 async getChartData( id ) {
317 await this.setState({
318 isLoading: 'getChartData'
319 });
320
321 let result = await apiFetch({ path: `wp/v2/visualizer/${id}` });
322
323 let chart = { ...this.state.chart };
324
325 chart['visualizer-source'] = 'Visualizer_Source_Csv';
326 chart['visualizer-default-data'] = 0;
327 chart['visualizer-series'] = result['chart_data']['visualizer-series'];
328 chart['visualizer-data'] = result['chart_data']['visualizer-data'];
329
330 this.setState({
331 isLoading: false,
332 chart
333 });
334 }
335
336 editChartData( chartData, source ) {
337 let chart = { ...this.state.chart };
338 let series = [];
339 let settings = { ...chart['visualizer-settings'] };
340 let type = chart['visualizer-chart-type'];
341 chartData[0].map( ( i, index ) => {
342 series[index] = {
343 label: i,
344 type: chartData[1][index]
345 };
346 });
347
348 chartData.splice( 0, 2 );
349
350 let map = series;
351 let fieldName = 'series';
352
353 switch ( type ) {
354 case 'pie':
355 map = chartData;
356 fieldName = 'slices';
357
358 // pie charts are finicky about a number being a number
359 // and editing a number makes it a string
360 // so let's convert it back into a number.
361 chartData.map( ( i, index ) => {
362 switch ( series[1].type ) {
363 case 'number':
364 i[1] = parseFloat( i[1]);
365 break;
366 }
367 });
368 break;
369 case 'tabular':
370
371 // table charts are finicky about a boolean being a boolean
372 // and editing a boolean makes it a string
373 // so let's convert it back into a boolean.
374 chartData.map( ( i, index ) => {
375 series.map( ( seriesObject, seriesIndex ) => {
376 switch ( seriesObject.type ) {
377 case 'boolean':
378 if ( 'string' === typeof i[seriesIndex]) {
379 i[seriesIndex] = 'true' === i[seriesIndex];
380 }
381 break;
382 }
383 });
384 });
385 break;
386 }
387
388 map.map( ( i, index ) => {
389 if ( 'pie' !== type && 0 === index ) {
390 return;
391 }
392
393 const seriesIndex = 'pie' !== type ? index - 1 : index;
394
395 if ( Array.isArray( settings[fieldName]) && settings[fieldName][seriesIndex] === undefined ) {
396 settings[fieldName][seriesIndex] = {};
397 settings[fieldName][seriesIndex].temp = 1;
398 }
399 });
400
401 if ( Array.isArray( settings[fieldName]) ) {
402 settings[fieldName] = settings[fieldName].filter( ( i, index ) => {
403 const length = -1 >= [ 'pie', 'tabular', 'dataTable' ].indexOf( type ) ? map.length - 1 : map.length;
404 return index < length;
405 });
406 }
407
408 chart['visualizer-source'] = source;
409 chart['visualizer-default-data'] = 0;
410 chart['visualizer-data'] = chartData;
411 chart['visualizer-series'] = series;
412 chart['visualizer-settings'] = settings;
413 chart['visualizer-chart-url'] = '';
414
415 this.setState({
416 chart,
417 isModified: true,
418 isScheduled: false
419 });
420 }
421
422 updateChart() {
423 this.setState({ isLoading: 'updateChart' });
424
425 const data = this.state.chart;
426
427 if ( false === this.state.isScheduled ) {
428 data['visualizer-chart-schedule'] = '';
429 }
430
431 let dataChartStylingOption = 'series';
432
433 if ( 'pie' === data['visualizer-chart-type']) {
434 dataChartStylingOption = 'slices';
435 }
436
437 // no series for bubble and timeline charts.
438 if (
439 undefined !== data['visualizer-settings'][dataChartStylingOption] &&
440 -1 >= [ 'bubble', 'timeline' ].indexOf( data['visualizer-chart-type'])
441 ) {
442 Object.keys( data['visualizer-settings'][dataChartStylingOption])
443 .map( i => {
444 if ( data['visualizer-settings'][dataChartStylingOption][i] !== undefined ) {
445 if ( data['visualizer-settings'][dataChartStylingOption][i].temp !== undefined ) {
446 delete data['visualizer-settings'][dataChartStylingOption][i].temp;
447 }
448 }
449 }
450 );
451 }
452
453 apiRequest({ path: `/visualizer/v1/update-chart?id=${ this.props.attributes.id }`, method: 'POST', data: data }).then(
454 ( data ) => {
455
456 this.setState({
457 isLoading: false,
458 isModified: false
459 });
460
461 return data;
462 },
463 ( err ) => {
464 return err;
465 }
466 );
467 }
468
469 /**
470 * Create a new chart via popup.
471 *
472 * @returns {void}
473 */
474 async createChart() {
475
476 // Use the same popup like in Chart Library.
477 const createChartPopup = new ( buildChartPopup() )({
478 action: visualizerLocalize.createChart
479 });
480
481 // eslint-disable-next-line camelcase
482 window.send_to_editor = () => {
483 createChartPopup.close();
484 };
485
486 window.parent.addEventListener( 'message', ( event ) => {
487 if ( 'visualizer:mediaframe:close' === event.data ) {
488 createChartPopup.close();
489 } else if ( event.data.chartID && null === this.state.chart ) {
490 const chartID = parseInt( event.data.chartID, 10 );
491 this.getChart( chartID );
492 }
493 }, false );
494
495 createChartPopup.open();
496 }
497
498 render() {
499 if ( 'error' === this.state.route ) {
500 return (
501 <Notice
502 status="error"
503 isDismissible={ false }
504 >
505 <Dashicon icon="chart-pie" />
506 { __( 'This chart is not available; it might have been deleted. Please delete this block and resubmit your chart.' ) }
507 </Notice>
508 );
509 }
510
511 if ( '1' === visualizerLocalize.isFullSiteEditor ) {
512 return (
513 <Notice
514 status="error"
515 isDismissible={ false }
516 >
517 <Dashicon icon="chart-pie" />
518 { __( 'Visualizer block charts are currently not available for selection here, you must visit the library, get the shortcode, and add the chart here in a shortcode tag.' ) }
519 </Notice>
520 );
521 }
522
523 if ( 'renderChart' === this.state.route && null !== this.state.chart ) {
524 return (
525 <ChartRender
526 id={ this.props.attributes.id }
527 chart={ this.state.chart }
528 className={ this.props.className }
529 editChart={ this.editChart }
530 />
531 );
532 }
533
534 return (
535 <div className="visualizer-settings">
536
537 <div className="visualizer-settings__title">
538
539 <Dashicon icon="chart-pie" />
540 { __( 'Visualizer' ) }
541
542 </div>
543
544 { 'home' === this.state.route && (
545 <div className="visualizer-settings__content">
546 <div className="visualizer-settings__content-description">
547 { __( 'Make a new chart or display an existing one?' ) }
548 </div>
549
550 { /* You can apply "locked" class to lock any of the following list items. */ }
551
552 <a
553 onClick={ this.createChart }
554 target="_blank"
555 className="visualizer-settings__content-option"
556 >
557
558 <span className="visualizer-settings__content-option-title">
559 { __( 'Create a new chart' ) }
560 </span>
561
562 <div className="visualizer-settings__content-option-icon">
563 <Dashicon icon="arrow-right-alt2" />
564 </div>
565
566 </a>
567
568 <div
569 className="visualizer-settings__content-option"
570 onClick={ () => {
571 this.setState({ route: 'showCharts' });
572 this.props.setAttributes({ route: 'showCharts' });
573 } }
574 >
575
576 <span className="visualizer-settings__content-option-title">
577 { __( 'Display an existing chart' ) }
578 </span>
579
580 <div className="visualizer-settings__content-option-icon">
581 <Dashicon icon="arrow-right-alt2" />
582 </div>
583
584 </div>
585
586 </div>
587 ) }
588
589 { ( ( 'getChart' === this.state.isLoading ) || ( 'chartSelect' === this.state.route && null === this.state.chart ) || ( 'renderChart' === this.state.route && null === this.state.chart ) ) && (
590 <Placeholder>
591 <Spinner/>
592 </Placeholder>
593 ) }
594
595 { ( 'showCharts' === this.state.route && false === this.state.isLoading ) && <Charts getChart={ this.getChart }/> }
596
597 { ( 'chartSelect' === this.state.route && null !== this.state.chart ) &&
598 <ChartSelect
599 id={ this.props.attributes.id }
600 attributes={ this.props.attributes }
601 chart={ this.state.chart }
602 editSettings={ this.editSettings }
603 editPermissions={ this.editPermissions }
604 url={ this.state.url }
605 readUploadedFile={ this.readUploadedFile }
606 editURL={ this.editURL }
607 editSchedule={ this.editSchedule }
608 editJSONURL={ this.editJSONURL }
609 editJSONHeaders={ this.editJSONHeaders }
610 editJSONSchedule={ this.editJSONSchedule }
611 editJSONRoot={ this.editJSONRoot }
612 editJSONPaging={ this.editJSONPaging }
613 JSONImportData={ this.JSONImportData }
614 editDatabaseSchedule={ this.editDatabaseSchedule }
615 databaseImportData={ this.databaseImportData }
616 uploadData={ this.uploadData }
617 getChartData={ this.getChartData }
618 editChartData={ this.editChartData }
619 isLoading={ this.state.isLoading }
620 />
621 }
622
623 <div className="visualizer-settings__controls">
624
625 { ( 'showCharts' === this.state.route || 'chartSelect' === this.state.route ) &&
626 <ButtonGroup>
627
628 <Button
629 variant="secondary"
630 isLarge
631 onClick={ () => {
632 let route;
633 if ( 'showCharts' === this.state.route ) {
634 route = 'home';
635 } else if ( 'chartSelect' === this.state.route ) {
636 route = 'showCharts';
637 }
638 this.setState({ route, isLoading: false });
639 this.props.setAttributes({ route });
640 } }
641 >
642 { __( 'Back' ) }
643 </Button>
644
645 { 'chartSelect' === this.state.route &&
646 <Fragment>
647
648 { false === this.state.isModified ?
649 <Button
650 variant="secondary"
651 isLarge
652 className="visualizer-bttn-done"
653 onClick={ () => {
654 this.setState({ route: 'renderChart', isModified: true });
655 this.props.setAttributes({ route: 'renderChart' });
656 } }
657 >
658 { __( 'Done' ) }
659 </Button> :
660 <Button
661 isPrimary
662 isLarge
663 className="visualizer-bttn-save"
664 isBusy={ 'updateChart' === this.state.isLoading }
665 disabled={ 'updateChart' === this.state.isLoading }
666 onClick={ this.updateChart }
667 >
668 { __( 'Save' ) }
669 </Button>
670 }
671
672 </Fragment>
673 }
674
675 </ButtonGroup>
676 }
677
678 </div>
679 </div>
680 );
681 }
682 }
683
684 export default Editor;
685