From 73c27318553cc62727fc1c6f1df80c97e2d58398 Mon Sep 17 00:00:00 2001 From: Levko Kravets Date: Mon, 9 Sep 2019 21:36:04 +0300 Subject: [PATCH 1/4] Migrate Chart visualization: Renderer --- .../chart/Renderer/CustomPlotlyChart.jsx | 8 +++ .../chart/Renderer/PlotlyChart.jsx | 69 +++++++++++++++++++ .../visualizations/chart/Renderer/index.jsx | 14 ++++ client/app/visualizations/chart/index.js | 4 +- .../app/visualizations/chart/plotly/index.js | 8 +++ 5 files changed, 102 insertions(+), 1 deletion(-) create mode 100644 client/app/visualizations/chart/Renderer/CustomPlotlyChart.jsx create mode 100644 client/app/visualizations/chart/Renderer/PlotlyChart.jsx create mode 100644 client/app/visualizations/chart/Renderer/index.jsx diff --git a/client/app/visualizations/chart/Renderer/CustomPlotlyChart.jsx b/client/app/visualizations/chart/Renderer/CustomPlotlyChart.jsx new file mode 100644 index 0000000000..9cb146510e --- /dev/null +++ b/client/app/visualizations/chart/Renderer/CustomPlotlyChart.jsx @@ -0,0 +1,8 @@ +import React from 'react'; +import { RendererPropTypes } from '@/visualizations'; + +export default function CustomPlotlyChart() { + return
Custom Plotly Chart
; +} + +CustomPlotlyChart.propTypes = RendererPropTypes; diff --git a/client/app/visualizations/chart/Renderer/PlotlyChart.jsx b/client/app/visualizations/chart/Renderer/PlotlyChart.jsx new file mode 100644 index 0000000000..6356252bb0 --- /dev/null +++ b/client/app/visualizations/chart/Renderer/PlotlyChart.jsx @@ -0,0 +1,69 @@ +import { isArray, isObject } from 'lodash'; +import React, { useState, useEffect, useMemo } from 'react'; +import { RendererPropTypes } from '@/visualizations'; +import resizeObserver from '@/services/resizeObserver'; + +import getChartData from '../getChartData'; +import { Plotly, prepareData, prepareLayout, updateData, applyLayoutFixes } from '../plotly'; + +export default function PlotlyChart(props) { + const [container, setContainer] = useState(null); + + const options = useMemo(() => { + const result = { ...props.options }; + if (['normal', 'percent'].indexOf(result.series.stacking) >= 0) { + // Backward compatibility + result.series = { + ...result.series, + percentValues: result.series.stacking === 'percent', + stacking: 'stack', + }; + } + return result; + }, [props.options]); + + const plotlyData = useMemo(() => { + const series = getChartData(props.data.rows, options); + return prepareData(series, options); + }, [props.data, options]); + + const plotlyLayout = useMemo(() => { + if (container) { + return prepareLayout(container, options, plotlyData); + } + return null; + }, [container, options, plotlyData]); + + useEffect(() => { + if (container && plotlyLayout) { + const plotlyOptions = { showLink: false, displaylogo: false }; + + // It will auto-purge previous graph + Plotly.newPlot(container, plotlyData, plotlyLayout, plotlyOptions).then(() => { + applyLayoutFixes(container, plotlyLayout, (e, u) => Plotly.relayout(e, u)); + }); + + container.on('plotly_restyle', (updates) => { + // This event is triggered if some plotly data/layout has changed. + // We need to catch only changes of traces visibility to update stacking + if (isArray(updates) && isObject(updates[0]) && updates[0].visible) { + updateData(plotlyData, options); + Plotly.relayout(container, plotlyLayout); + } + }); + } + }, [options, plotlyData, plotlyLayout, container]); + + useEffect(() => { + if (container) { + const unwatch = resizeObserver(container, () => { + applyLayoutFixes(container, plotlyLayout, (e, u) => Plotly.relayout(e, u)); + }); + return unwatch; + } + }, [plotlyLayout, container]); + + return
; +} + +PlotlyChart.propTypes = RendererPropTypes; diff --git a/client/app/visualizations/chart/Renderer/index.jsx b/client/app/visualizations/chart/Renderer/index.jsx new file mode 100644 index 0000000000..2cfd47cc07 --- /dev/null +++ b/client/app/visualizations/chart/Renderer/index.jsx @@ -0,0 +1,14 @@ +import React from 'react'; +import { RendererPropTypes } from '@/visualizations'; + +import PlotlyChart from './PlotlyChart'; +import CustomPlotlyChart from './CustomPlotlyChart'; + +export default function Renderer({ options, ...props }) { + if (options.globalSeriesType === 'custom') { + return ; + } + return ; +} + +Renderer.propTypes = RendererPropTypes; diff --git a/client/app/visualizations/chart/index.js b/client/app/visualizations/chart/index.js index f4da59f3c9..3e0dcc9579 100644 --- a/client/app/visualizations/chart/index.js +++ b/client/app/visualizations/chart/index.js @@ -9,6 +9,8 @@ import getChartData from './getChartData'; import template from './chart.html'; import editorTemplate from './chart-editor.html'; +import Renderer from './Renderer'; + const DEFAULT_OPTIONS = { globalSeriesType: 'column', sortX: true, @@ -318,7 +320,7 @@ export default function init(ngModule) { showDataLabels: options.globalSeriesType === 'pie', dateTimeFormat: clientConfig.dateTimeFormat, }, options), - Renderer: angular2react('chartRenderer', ChartRenderer, $injector), + Renderer, Editor: angular2react('chartEditor', ChartEditor, $injector), defaultColumns: 3, diff --git a/client/app/visualizations/chart/plotly/index.js b/client/app/visualizations/chart/plotly/index.js index 19d6f87e13..4a0e689023 100644 --- a/client/app/visualizations/chart/plotly/index.js +++ b/client/app/visualizations/chart/plotly/index.js @@ -14,6 +14,14 @@ import prepareLayout from './prepareLayout'; import updateData from './updateData'; import applyLayoutFixes from './applyLayoutFixes'; +export { + Plotly, + prepareData, + prepareLayout, + updateData, + applyLayoutFixes, +}; + Plotly.register([bar, pie, histogram, box, heatmap]); Plotly.setPlotConfig({ modeBarButtonsToRemove: ['sendDataToCloud'], From ee47078c8e7f492684774aac3c461387a0a26a6f Mon Sep 17 00:00:00 2001 From: Levko Kravets Date: Tue, 10 Sep 2019 13:27:16 +0300 Subject: [PATCH 2/4] Refine PlotlyChart component; move stylesheets to visualization's folder --- client/app/assets/less/main.less | 1 - client/app/pages/dashboards/dashboard.less | 2 +- .../EditVisualizationDialog.jsx | 1 + .../chart/Renderer/PlotlyChart.jsx | 50 ++++++------------- .../visualizations/chart/Renderer/index.jsx | 2 + .../chart/Renderer/renderer.less} | 2 +- client/app/visualizations/chart/index.js | 18 +++++-- .../app/visualizations/chart/plotly/index.js | 6 +-- 8 files changed, 38 insertions(+), 44 deletions(-) rename client/app/{assets/less/inc/visualizations/chart.less => visualizations/chart/Renderer/renderer.less} (54%) diff --git a/client/app/assets/less/main.less b/client/app/assets/less/main.less index 8b0b26f3e6..0f764f8144 100644 --- a/client/app/assets/less/main.less +++ b/client/app/assets/less/main.less @@ -56,7 +56,6 @@ @import 'inc/visualizations/sankey'; @import 'inc/visualizations/pivot-table'; @import 'inc/visualizations/map'; -@import 'inc/visualizations/chart'; @import 'inc/visualizations/sunburst'; @import 'inc/visualizations/cohort'; @import 'inc/visualizations/misc'; diff --git a/client/app/pages/dashboards/dashboard.less b/client/app/pages/dashboards/dashboard.less index e672fada81..97e039c625 100644 --- a/client/app/pages/dashboards/dashboard.less +++ b/client/app/pages/dashboards/dashboard.less @@ -80,7 +80,7 @@ .map-visualization-container, .word-cloud-visualization-container, .box-plot-deprecated-visualization-container, - .plotly-chart-container { + .chart-visualization-container { position: absolute; left: 0; top: 0; diff --git a/client/app/visualizations/EditVisualizationDialog.jsx b/client/app/visualizations/EditVisualizationDialog.jsx index db2a94384c..4316e82757 100644 --- a/client/app/visualizations/EditVisualizationDialog.jsx +++ b/client/app/visualizations/EditVisualizationDialog.jsx @@ -191,6 +191,7 @@ function EditVisualizationDialog({ dialog, visualization, query, queryResult }) options={options} visualizationName={name} onOptionsChange={onOptionsChanged} + context="query" />
diff --git a/client/app/visualizations/chart/Renderer/PlotlyChart.jsx b/client/app/visualizations/chart/Renderer/PlotlyChart.jsx index 6356252bb0..df96f730a6 100644 --- a/client/app/visualizations/chart/Renderer/PlotlyChart.jsx +++ b/client/app/visualizations/chart/Renderer/PlotlyChart.jsx @@ -1,43 +1,22 @@ import { isArray, isObject } from 'lodash'; -import React, { useState, useEffect, useMemo } from 'react'; +import React, { useState, useEffect } from 'react'; import { RendererPropTypes } from '@/visualizations'; import resizeObserver from '@/services/resizeObserver'; import getChartData from '../getChartData'; import { Plotly, prepareData, prepareLayout, updateData, applyLayoutFixes } from '../plotly'; -export default function PlotlyChart(props) { +export default function PlotlyChart({ options, data }) { const [container, setContainer] = useState(null); - const options = useMemo(() => { - const result = { ...props.options }; - if (['normal', 'percent'].indexOf(result.series.stacking) >= 0) { - // Backward compatibility - result.series = { - ...result.series, - percentValues: result.series.stacking === 'percent', - stacking: 'stack', - }; - } - return result; - }, [props.options]); - - const plotlyData = useMemo(() => { - const series = getChartData(props.data.rows, options); - return prepareData(series, options); - }, [props.data, options]); - - const plotlyLayout = useMemo(() => { - if (container) { - return prepareLayout(container, options, plotlyData); - } - return null; - }, [container, options, plotlyData]); - useEffect(() => { - if (container && plotlyLayout) { + if (container) { const plotlyOptions = { showLink: false, displaylogo: false }; + const chartData = getChartData(data.rows, options); + const plotlyData = prepareData(chartData, options); + const plotlyLayout = prepareLayout(container, options, plotlyData); + // It will auto-purge previous graph Plotly.newPlot(container, plotlyData, plotlyLayout, plotlyOptions).then(() => { applyLayoutFixes(container, plotlyLayout, (e, u) => Plotly.relayout(e, u)); @@ -51,19 +30,22 @@ export default function PlotlyChart(props) { Plotly.relayout(container, plotlyLayout); } }); - } - }, [options, plotlyData, plotlyLayout, container]); - useEffect(() => { - if (container) { const unwatch = resizeObserver(container, () => { applyLayoutFixes(container, plotlyLayout, (e, u) => Plotly.relayout(e, u)); }); return unwatch; } - }, [plotlyLayout, container]); + }, [options, data, container]); + + // Cleanup when component destroyed + useEffect(() => { + if (container) { + return () => Plotly.purge(container); + } + }, [container]); - return
; + return
; } PlotlyChart.propTypes = RendererPropTypes; diff --git a/client/app/visualizations/chart/Renderer/index.jsx b/client/app/visualizations/chart/Renderer/index.jsx index 2cfd47cc07..f44f3065af 100644 --- a/client/app/visualizations/chart/Renderer/index.jsx +++ b/client/app/visualizations/chart/Renderer/index.jsx @@ -4,6 +4,8 @@ import { RendererPropTypes } from '@/visualizations'; import PlotlyChart from './PlotlyChart'; import CustomPlotlyChart from './CustomPlotlyChart'; +import './renderer.less'; + export default function Renderer({ options, ...props }) { if (options.globalSeriesType === 'custom') { return ; diff --git a/client/app/assets/less/inc/visualizations/chart.less b/client/app/visualizations/chart/Renderer/renderer.less similarity index 54% rename from client/app/assets/less/inc/visualizations/chart.less rename to client/app/visualizations/chart/Renderer/renderer.less index a9b3616285..524cec77b9 100644 --- a/client/app/assets/less/inc/visualizations/chart.less +++ b/client/app/visualizations/chart/Renderer/renderer.less @@ -1,4 +1,4 @@ -.plotly-chart-container { +.chart-visualization-container { height: 400px; overflow: hidden; } diff --git a/client/app/visualizations/chart/index.js b/client/app/visualizations/chart/index.js index 3e0dcc9579..ea5a091f7c 100644 --- a/client/app/visualizations/chart/index.js +++ b/client/app/visualizations/chart/index.js @@ -316,10 +316,20 @@ export default function init(ngModule) { type: 'CHART', name: 'Chart', isDefault: true, - getOptions: options => merge({}, DEFAULT_OPTIONS, { - showDataLabels: options.globalSeriesType === 'pie', - dateTimeFormat: clientConfig.dateTimeFormat, - }, options), + getOptions: (options) => { + const result = merge({}, DEFAULT_OPTIONS, { + showDataLabels: options.globalSeriesType === 'pie', + dateTimeFormat: clientConfig.dateTimeFormat, + }, options); + + // Backward compatibility + if (['normal', 'percent'].indexOf(result.series.stacking) >= 0) { + result.series.percentValues = result.series.stacking === 'percent'; + result.series.stacking = 'stack'; + } + + return result; + }, Renderer, Editor: angular2react('chartEditor', ChartEditor, $injector), diff --git a/client/app/visualizations/chart/plotly/index.js b/client/app/visualizations/chart/plotly/index.js index 4a0e689023..f5e46d8533 100644 --- a/client/app/visualizations/chart/plotly/index.js +++ b/client/app/visualizations/chart/plotly/index.js @@ -29,13 +29,13 @@ Plotly.setPlotConfig({ const PlotlyChart = () => ({ restrict: 'E', - template: '
', + template: '
', scope: { options: '=', series: '=', }, link(scope, element) { - const plotlyElement = element[0].querySelector('.plotly-chart-container'); + const plotlyElement = element[0].querySelector('.chart-visualization-container'); const plotlyOptions = { showLink: false, displaylogo: false }; let layout = {}; let data = []; @@ -85,7 +85,7 @@ const PlotlyChart = () => ({ const CustomPlotlyChart = clientConfig => ({ restrict: 'E', - template: '
', + template: '
', scope: { series: '=', options: '=', From 986aaadfc697a41c4e59155d86811c0d3dedefd0 Mon Sep 17 00:00:00 2001 From: Levko Kravets Date: Tue, 10 Sep 2019 20:34:32 +0300 Subject: [PATCH 3/4] Migrate Custom JS Chart to React --- .../chart/Renderer/CustomPlotlyChart.jsx | 46 +++++++++++++++++-- .../visualizations/chart/chart-editor.html | 2 +- .../chart/plotly/customChartUtils.js | 40 ++++++++++++++++ .../app/visualizations/chart/plotly/index.js | 3 ++ 4 files changed, 87 insertions(+), 4 deletions(-) create mode 100644 client/app/visualizations/chart/plotly/customChartUtils.js diff --git a/client/app/visualizations/chart/Renderer/CustomPlotlyChart.jsx b/client/app/visualizations/chart/Renderer/CustomPlotlyChart.jsx index 9cb146510e..d60a43c213 100644 --- a/client/app/visualizations/chart/Renderer/CustomPlotlyChart.jsx +++ b/client/app/visualizations/chart/Renderer/CustomPlotlyChart.jsx @@ -1,8 +1,48 @@ -import React from 'react'; +import React, { useState, useEffect, useMemo } from 'react'; import { RendererPropTypes } from '@/visualizations'; -export default function CustomPlotlyChart() { - return
Custom Plotly Chart
; +import { clientConfig } from '@/services/auth'; +import resizeObserver from '@/services/resizeObserver'; + +import getChartData from '../getChartData'; +import { Plotly, prepareCustomChartData, createCustomChartRenderer } from '../plotly'; + +export default function CustomPlotlyChart({ options, data }) { + if (!clientConfig.allowCustomJSVisualizations) { + return null; + } + + const [container, setContainer] = useState(null); + + const renderCustomChart = useMemo( + () => createCustomChartRenderer(options.customCode, options.enableConsoleLogs), + [options.customCode, options.enableConsoleLogs], + ); + + const plotlyData = useMemo( + () => prepareCustomChartData(getChartData(data.rows, options)), + [options, data], + ); + + useEffect(() => { + if (container) { + const unwatch = resizeObserver(container, () => { + // Clear existing data with blank data for succeeding codeCall adds data to existing plot. + Plotly.purge(container); + renderCustomChart(plotlyData.x, plotlyData.ys, container, Plotly); + }); + return unwatch; + } + }, [container, plotlyData]); + + // Cleanup when component destroyed + useEffect(() => { + if (container) { + return () => Plotly.purge(container); + } + }, [container]); + + return
; } CustomPlotlyChart.propTypes = RendererPropTypes; diff --git a/client/app/visualizations/chart/chart-editor.html b/client/app/visualizations/chart/chart-editor.html index f782912051..ebe23910fa 100644 --- a/client/app/visualizations/chart/chart-editor.html +++ b/client/app/visualizations/chart/chart-editor.html @@ -180,7 +180,7 @@
-
diff --git a/client/app/visualizations/chart/plotly/customChartUtils.js b/client/app/visualizations/chart/plotly/customChartUtils.js new file mode 100644 index 0000000000..a6970100e0 --- /dev/null +++ b/client/app/visualizations/chart/plotly/customChartUtils.js @@ -0,0 +1,40 @@ +import { each } from 'lodash'; +import { normalizeValue } from './utils'; + +export function prepareCustomChartData(series) { + const x = []; + const ys = {}; + + each(series, ({ name, data }) => { + ys[name] = []; + each(data, (point) => { + x.push(normalizeValue(point.x)); + ys[name].push(normalizeValue(point.y)); + }); + }); + + return { x, ys }; +} + +export function createCustomChartRenderer(code, logErrorsToConsole = false) { + // Create a function from custom code; catch syntax errors + let render = () => {}; + try { + render = new Function('x, ys, element, Plotly', code); // eslint-disable-line no-new-func + } catch (err) { + if (logErrorsToConsole) { + console.log(`Error while executing custom graph: ${err}`); // eslint-disable-line no-console + } + } + + // Return function that will invoke custom code; catch runtime errors + return (x, ys, element, Plotly) => { + try { + render(x, ys, element, Plotly); + } catch (err) { + if (logErrorsToConsole) { + console.log(`Error while executing custom graph: ${err}`); // eslint-disable-line no-console + } + } + }; +} diff --git a/client/app/visualizations/chart/plotly/index.js b/client/app/visualizations/chart/plotly/index.js index f5e46d8533..ea515a632b 100644 --- a/client/app/visualizations/chart/plotly/index.js +++ b/client/app/visualizations/chart/plotly/index.js @@ -13,6 +13,7 @@ import prepareData from './prepareData'; import prepareLayout from './prepareLayout'; import updateData from './updateData'; import applyLayoutFixes from './applyLayoutFixes'; +import { prepareCustomChartData, createCustomChartRenderer } from './customChartUtils'; export { Plotly, @@ -20,6 +21,8 @@ export { prepareLayout, updateData, applyLayoutFixes, + prepareCustomChartData, + createCustomChartRenderer, }; Plotly.register([bar, pie, histogram, box, heatmap]); From 308104dc0c2ef41056594e9c19ee3fc6a274a07f Mon Sep 17 00:00:00 2001 From: Levko Kravets Date: Tue, 10 Sep 2019 20:37:05 +0300 Subject: [PATCH 4/4] Cleanup --- client/app/visualizations/chart/chart.html | 6 - client/app/visualizations/chart/index.js | 22 --- .../app/visualizations/chart/plotly/index.js | 132 +----------------- 3 files changed, 5 insertions(+), 155 deletions(-) delete mode 100644 client/app/visualizations/chart/chart.html diff --git a/client/app/visualizations/chart/chart.html b/client/app/visualizations/chart/chart.html deleted file mode 100644 index 8c2cbedc34..0000000000 --- a/client/app/visualizations/chart/chart.html +++ /dev/null @@ -1,6 +0,0 @@ -
- -
-
- -
diff --git a/client/app/visualizations/chart/index.js b/client/app/visualizations/chart/index.js index ea5a091f7c..d8a3e2fed5 100644 --- a/client/app/visualizations/chart/index.js +++ b/client/app/visualizations/chart/index.js @@ -6,7 +6,6 @@ import { registerVisualization } from '@/visualizations'; import { clientConfig } from '@/services/auth'; import ColorPalette from '@/visualizations/ColorPalette'; import getChartData from './getChartData'; -import template from './chart.html'; import editorTemplate from './chart-editor.html'; import Renderer from './Renderer'; @@ -73,26 +72,6 @@ function initEditorForm(options, columns) { return result; } -const ChartRenderer = { - template, - bindings: { - data: '<', - options: '<', - }, - controller($scope) { - this.chartSeries = []; - - const update = () => { - if (this.data) { - this.chartSeries = getChartData(this.data.rows, this.options); - } - }; - - $scope.$watch('$ctrl.data', update); - $scope.$watch('$ctrl.options', update, true); - }, -}; - const ChartEditor = { template: editorTemplate, bindings: { @@ -308,7 +287,6 @@ const ChartEditor = { }; export default function init(ngModule) { - ngModule.component('chartRenderer', ChartRenderer); ngModule.component('chartEditor', ChartEditor); ngModule.run(($injector) => { diff --git a/client/app/visualizations/chart/plotly/index.js b/client/app/visualizations/chart/plotly/index.js index ea515a632b..3aa3ad382c 100644 --- a/client/app/visualizations/chart/plotly/index.js +++ b/client/app/visualizations/chart/plotly/index.js @@ -1,5 +1,3 @@ -import { each, debounce, isArray, isObject } from 'lodash'; - import Plotly from 'plotly.js/lib/core'; import bar from 'plotly.js/lib/bar'; import pie from 'plotly.js/lib/pie'; @@ -7,14 +5,17 @@ import histogram from 'plotly.js/lib/histogram'; import box from 'plotly.js/lib/box'; import heatmap from 'plotly.js/lib/heatmap'; -import { normalizeValue } from './utils'; - import prepareData from './prepareData'; import prepareLayout from './prepareLayout'; import updateData from './updateData'; import applyLayoutFixes from './applyLayoutFixes'; import { prepareCustomChartData, createCustomChartRenderer } from './customChartUtils'; +Plotly.register([bar, pie, histogram, box, heatmap]); +Plotly.setPlotConfig({ + modeBarButtonsToRemove: ['sendDataToCloud'], +}); + export { Plotly, prepareData, @@ -24,126 +25,3 @@ export { prepareCustomChartData, createCustomChartRenderer, }; - -Plotly.register([bar, pie, histogram, box, heatmap]); -Plotly.setPlotConfig({ - modeBarButtonsToRemove: ['sendDataToCloud'], -}); - -const PlotlyChart = () => ({ - restrict: 'E', - template: '
', - scope: { - options: '=', - series: '=', - }, - link(scope, element) { - const plotlyElement = element[0].querySelector('.chart-visualization-container'); - const plotlyOptions = { showLink: false, displaylogo: false }; - let layout = {}; - let data = []; - - function update() { - if (['normal', 'percent'].indexOf(scope.options.series.stacking) >= 0) { - // Backward compatibility - scope.options.series.percentValues = scope.options.series.stacking === 'percent'; - scope.options.series.stacking = 'stack'; - } - - data = prepareData(scope.series, scope.options); - layout = prepareLayout(plotlyElement, scope.options, data); - - // It will auto-purge previous graph - Plotly.newPlot(plotlyElement, data, layout, plotlyOptions).then(() => { - applyLayoutFixes(plotlyElement, layout, (e, u) => Plotly.relayout(e, u)); - }); - - plotlyElement.on('plotly_restyle', (updates) => { - // This event is triggered if some plotly data/layout has changed. - // We need to catch only changes of traces visibility to update stacking - if (isArray(updates) && isObject(updates[0]) && updates[0].visible) { - updateData(data, scope.options); - Plotly.relayout(plotlyElement, layout); - } - }); - } - update(); - - scope.$watch('series', (oldValue, newValue) => { - if (oldValue !== newValue) { - update(); - } - }); - scope.$watch('options', (oldValue, newValue) => { - if (oldValue !== newValue) { - update(); - } - }, true); - - scope.handleResize = debounce(() => { - applyLayoutFixes(plotlyElement, layout, (e, u) => Plotly.relayout(e, u)); - }, 50); - }, -}); - -const CustomPlotlyChart = clientConfig => ({ - restrict: 'E', - template: '
', - scope: { - series: '=', - options: '=', - }, - link(scope, element) { - if (!clientConfig.allowCustomJSVisualizations) { - return; - } - - const refresh = () => { - // Clear existing data with blank data for succeeding codeCall adds data to existing plot. - Plotly.newPlot(element[0].firstChild); - - try { - // eslint-disable-next-line no-new-func - const codeCall = new Function('x, ys, element, Plotly', scope.options.customCode); - codeCall(scope.x, scope.ys, element[0].children[0], Plotly); - } catch (err) { - if (scope.options.enableConsoleLogs) { - // eslint-disable-next-line no-console - console.log(`Error while executing custom graph: ${err}`); - } - } - }; - - const timeSeriesToPlotlySeries = () => { - scope.x = []; - scope.ys = {}; - each(scope.series, (series) => { - scope.ys[series.name] = []; - each(series.data, (point) => { - scope.x.push(normalizeValue(point.x)); - scope.ys[series.name].push(normalizeValue(point.y)); - }); - }); - }; - - scope.handleResize = () => { - refresh(); - }; - - scope.$watch('[options.customCode, options.autoRedraw]', () => { - refresh(); - }, true); - - scope.$watch('series', () => { - timeSeriesToPlotlySeries(); - refresh(); - }, true); - }, -}); - -export default function init(ngModule) { - ngModule.directive('plotlyChart', PlotlyChart); - ngModule.directive('customPlotlyChart', CustomPlotlyChart); -} - -init.init = true;