From 114edf31095f18e53bc7c1adab31a1c4d9e98468 Mon Sep 17 00:00:00 2001 From: Levko Kravets Date: Fri, 30 Aug 2019 13:06:38 +0300 Subject: [PATCH 01/12] Migrate Counter to React: Renderer --- .../inc/visualizations/counter-render.less | 45 ----- client/app/assets/less/main.less | 1 - client/app/pages/dashboards/dashboard.less | 4 +- .../app/visualizations/counter/Renderer.jsx | 189 ++++++++++++++++++ client/app/visualizations/counter/index.js | 144 +------------ client/app/visualizations/counter/render.less | 46 +++++ 6 files changed, 241 insertions(+), 188 deletions(-) delete mode 100755 client/app/assets/less/inc/visualizations/counter-render.less create mode 100644 client/app/visualizations/counter/Renderer.jsx create mode 100755 client/app/visualizations/counter/render.less diff --git a/client/app/assets/less/inc/visualizations/counter-render.less b/client/app/assets/less/inc/visualizations/counter-render.less deleted file mode 100755 index c57c297a98..0000000000 --- a/client/app/assets/less/inc/visualizations/counter-render.less +++ /dev/null @@ -1,45 +0,0 @@ -counter-renderer { - display: block; - text-align: center; - padding: 15px 10px; - overflow: hidden; - - counter { - margin: 0; - padding: 0; - font-size: 80px; - line-height: normal; - overflow: hidden; - display: flex; - align-items: center; - justify-content: center; - - value, - counter-target { - font-size: 1em; - display: block; - } - - counter-name { - font-size: 0.5em; - display: block; - } - - &.positive value { - color: #5cb85c; - } - - &.negative value { - color: #d9534f; - } - } - - counter-target { - color: #ccc; - } - - counter-name { - font-size: 0.5em; - display: block; - } -} diff --git a/client/app/assets/less/main.less b/client/app/assets/less/main.less index f0c92409fa..8b0b26f3e6 100644 --- a/client/app/assets/less/main.less +++ b/client/app/assets/less/main.less @@ -53,7 +53,6 @@ @import 'inc/schema-browser'; @import 'inc/toast'; @import 'inc/visualizations/box'; -@import 'inc/visualizations/counter-render'; @import 'inc/visualizations/sankey'; @import 'inc/visualizations/pivot-table'; @import 'inc/visualizations/map'; diff --git a/client/app/pages/dashboards/dashboard.less b/client/app/pages/dashboards/dashboard.less index a644de4ba8..e672fada81 100644 --- a/client/app/pages/dashboards/dashboard.less +++ b/client/app/pages/dashboards/dashboard.less @@ -91,7 +91,7 @@ overflow: hidden; } - counter { + .counter-visualization-content { position: absolute; left: 10px; top: 15px; @@ -145,7 +145,7 @@ .dashboard__control { margin: 8px 0; - + .save-status { vertical-align: middle; margin-right: 7px; diff --git a/client/app/visualizations/counter/Renderer.jsx b/client/app/visualizations/counter/Renderer.jsx new file mode 100644 index 0000000000..4e2ee077ef --- /dev/null +++ b/client/app/visualizations/counter/Renderer.jsx @@ -0,0 +1,189 @@ +import { isNumber, isFinite, toString } from 'lodash'; +import numeral from 'numeral'; +import React, { useState, useEffect } from 'react'; +import cx from 'classnames'; +import resizeObserver from '@/services/resizeObserver'; +import { RendererPropTypes } from '@/visualizations'; + +import './render.less'; + + +// TODO: allow user to specify number format string instead of delimiters only +// It will allow to remove this function (move all that weird formatting logic to a migration +// that will set number format for all existing counter visualization) +function numberFormat(value, decimalPoints, decimalDelimiter, thousandsDelimiter) { + // Temporarily update locale data (restore defaults after formatting) + const locale = numeral.localeData(); + const savedDelimiters = locale.delimiters; + + // Mimic old behavior - AngularJS `number` filter defaults: + // - `,` as thousands delimiter + // - `.` as decimal delimiter + // - three decimal points + locale.delimiters = { + thousands: ',', + decimal: '.', + }; + let formatString = '0,0.000'; + if ( + (Number.isFinite(decimalPoints) && (decimalPoints >= 0)) || + decimalDelimiter || + thousandsDelimiter + ) { + locale.delimiters = { + thousands: thousandsDelimiter, + decimal: decimalDelimiter || '.', + }; + + formatString = '0,0'; + if (decimalPoints > 0) { + formatString += '.'; + while (decimalPoints > 0) { + formatString += '0'; + decimalPoints -= 1; + } + } + } + const result = numeral(value).format(formatString); + + locale.delimiters = savedDelimiters; + return result; +} + +// TODO: Need to review this function, it does not properly handle edge cases. +function getRowNumber(index, size) { + if (index >= 0) { + return index - 1; + } + + if (Math.abs(index) > size) { + index %= size; + } + + return size + index; +} + +function formatValue(value, { stringPrefix, stringSuffix, stringDecimal, stringDecChar, stringThouSep }) { + if (isNumber(value)) { + value = numberFormat(value, stringDecimal, stringDecChar, stringThouSep); + return toString(stringPrefix) + value + toString(stringSuffix); + } + return toString(value); +} + +function formatTooltip(value, formatString) { + if (isNumber(value)) { + return numeral(value).format(formatString); + } + return toString(value); +} + +function getCounterData(data, options, visualizationName) { + const result = {}; + + if (data.length > 0) { + const rowNumber = getRowNumber(options.rowNumber, data.length); + const targetRowNumber = getRowNumber(options.targetRowNumber, data.length); + const counterColName = options.counterColName; + const targetColName = options.targetColName; + const counterLabel = options.counterLabel; + + if (counterLabel) { + result.counterLabel = counterLabel; + } else { + result.counterLabel = visualizationName; + } + + if (options.countRow) { + result.counterValue = data.length; + } else if (counterColName) { + result.counterValue = data[rowNumber][counterColName]; + } + + result.showTrend = false; + if (targetColName) { + result.targetValue = data[targetRowNumber][targetColName]; + + if (Number.isFinite(result.counterValue) && isFinite(result.targetValue)) { + const delta = result.counterValue - result.targetValue; + result.showTrend = true; + result.trendPositive = delta >= 0; + } + } else { + result.targetValue = null; + } + + result.counterValueTooltip = formatTooltip(result.counterValue, options.tooltipFormat); + result.targetValueTooltip = formatTooltip(result.targetValue, options.tooltipFormat); + + result.counterValue = formatValue(result.counterValue, options); + + if (options.formatTargetValue) { + result.targetValue = formatValue(result.targetValue, options); + } else { + if (isFinite(result.targetValue)) { + result.targetValue = numeral(result.targetValue).format('0[.]00[0]'); + } + } + } + + return result; +} + +function getCounterStyles(scale) { + return { + OTransform: `scale(${scale})`, + MsTransform: `scale(${scale})`, + MozTransform: `scale(${scale})`, + WebkitTransform: `scale(${scale})`, + transform: `scale(${scale})`, + }; +} + +function getCounterScale(container) { + const inner = container.firstChild; + const scale = Math.min(container.offsetWidth / inner.offsetWidth, container.offsetHeight / inner.offsetHeight); + return Number(isFinite(scale) ? scale : 1).toFixed(2); // keep only two decimal places +} + +export default function Renderer({ data, options, visualizationName }) { + const [scale, setScale] = useState('1.00'); + const [container, setContainer] = useState(null); + + useEffect(() => { + if (container) { + setScale(getCounterScale(container)); // initial update + const unwatch = resizeObserver(container, () => { + setScale(getCounterScale(container)); + }); + return unwatch; + } + }, [container, setScale]); + + const { + showTrend, trendPositive, + counterValue, counterValueTooltip, + targetValue, targetValueTooltip, + counterLabel, + } = getCounterData(data.rows, options, visualizationName); + return ( +
+
+
+
{counterValue}
+ {targetValue && ( +
({targetValue})
+ )} +
{counterLabel}
+
+
+
+ ); +} + +Renderer.propTypes = RendererPropTypes; diff --git a/client/app/visualizations/counter/index.js b/client/app/visualizations/counter/index.js index 2ece76b2b2..254b9111c3 100644 --- a/client/app/visualizations/counter/index.js +++ b/client/app/visualizations/counter/index.js @@ -1,11 +1,11 @@ -import { isNumber, toString } from 'lodash'; -import numeral from 'numeral'; +import { isNumber } from 'lodash'; import { angular2react } from 'angular2react'; import { registerVisualization } from '@/visualizations'; -import counterTemplate from './counter.html'; import counterEditorTemplate from './counter-editor.html'; +import Renderer from './Renderer'; + const DEFAULT_OPTIONS = { counterLabel: '', counterColName: 'counter', @@ -17,48 +17,6 @@ const DEFAULT_OPTIONS = { tooltipFormat: '0,0.000', // TODO: Show in editor }; -// TODO: allow user to specify number format string instead of delimiters only -// It will allow to remove this function (move all that weird formatting logic to a migration -// that will set number format for all existing counter visualization) -function numberFormat(value, decimalPoints, decimalDelimiter, thousandsDelimiter) { - // Temporarily update locale data (restore defaults after formatting) - const locale = numeral.localeData(); - const savedDelimiters = locale.delimiters; - - // Mimic old behavior - AngularJS `number` filter defaults: - // - `,` as thousands delimiter - // - `.` as decimal delimiter - // - three decimal points - locale.delimiters = { - thousands: ',', - decimal: '.', - }; - let formatString = '0,0.000'; - if ( - (Number.isFinite(decimalPoints) && (decimalPoints >= 0)) || - decimalDelimiter || - thousandsDelimiter - ) { - locale.delimiters = { - thousands: thousandsDelimiter, - decimal: decimalDelimiter || '.', - }; - - formatString = '0,0'; - if (decimalPoints > 0) { - formatString += '.'; - while (decimalPoints > 0) { - formatString += '0'; - decimalPoints -= 1; - } - } - } - const result = numeral(value).format(formatString); - - locale.delimiters = savedDelimiters; - return result; -} - // TODO: Need to review this function, it does not properly handle edge cases. function getRowNumber(index, size) { if (index >= 0) { @@ -72,99 +30,6 @@ function getRowNumber(index, size) { return size + index; } -function formatValue(value, { stringPrefix, stringSuffix, stringDecimal, stringDecChar, stringThouSep }) { - if (isNumber(value)) { - value = numberFormat(value, stringDecimal, stringDecChar, stringThouSep); - return toString(stringPrefix) + value + toString(stringSuffix); - } - return toString(value); -} - -function formatTooltip(value, formatString) { - if (isNumber(value)) { - return numeral(value).format(formatString); - } - return toString(value); -} - -const CounterRenderer = { - template: counterTemplate, - bindings: { - data: '<', - options: '<', - visualizationName: '<', - }, - controller($scope, $element, $timeout) { - $scope.fontSize = '1em'; - - $scope.scale = 1; - const root = $element[0].querySelector('counter'); - const container = $element[0].querySelector('counter > div'); - $scope.handleResize = () => { - const scale = Math.min(root.offsetWidth / container.offsetWidth, root.offsetHeight / container.offsetHeight); - $scope.scale = Math.floor(scale * 100) / 100; // keep only two decimal places - }; - - const update = () => { - const options = this.options; - const data = this.data.rows; - - if (data.length > 0) { - const rowNumber = getRowNumber(options.rowNumber, data.length); - const targetRowNumber = getRowNumber(options.targetRowNumber, data.length); - const counterColName = options.counterColName; - const targetColName = options.targetColName; - const counterLabel = options.counterLabel; - - if (counterLabel) { - $scope.counterLabel = counterLabel; - } else { - $scope.counterLabel = this.visualizationName; - } - - if (options.countRow) { - $scope.counterValue = data.length; - } else if (counterColName) { - $scope.counterValue = data[rowNumber][counterColName]; - } - - $scope.showTrend = false; - if (targetColName) { - $scope.targetValue = data[targetRowNumber][targetColName]; - - if (Number.isFinite($scope.counterValue) && Number.isFinite($scope.targetValue)) { - const delta = $scope.counterValue - $scope.targetValue; - $scope.showTrend = true; - $scope.trendPositive = delta >= 0; - } - } else { - $scope.targetValue = null; - } - - $scope.counterValueTooltip = formatTooltip($scope.counterValue, options.tooltipFormat); - $scope.targetValueTooltip = formatTooltip($scope.targetValue, options.tooltipFormat); - - $scope.counterValue = formatValue($scope.counterValue, options); - - if (options.formatTargetValue) { - $scope.targetValue = formatValue($scope.targetValue, options); - } else { - if (Number.isFinite($scope.targetValue)) { - $scope.targetValue = numeral($scope.targetValue).format('0[.]00[0]'); - } - } - } - - $timeout(() => { - $scope.handleResize(); - }); - }; - - $scope.$watch('$ctrl.data', update); - $scope.$watch('$ctrl.options', update, true); - }, -}; - const CounterEditor = { template: counterEditorTemplate, bindings: { @@ -204,7 +69,6 @@ const CounterEditor = { }; export default function init(ngModule) { - ngModule.component('counterRenderer', CounterRenderer); ngModule.component('counterEditor', CounterEditor); ngModule.run(($injector) => { @@ -212,7 +76,7 @@ export default function init(ngModule) { type: 'COUNTER', name: 'Counter', getOptions: options => ({ ...DEFAULT_OPTIONS, ...options }), - Renderer: angular2react('counterRenderer', CounterRenderer, $injector), + Renderer, Editor: angular2react('counterEditor', CounterEditor, $injector), defaultColumns: 2, diff --git a/client/app/visualizations/counter/render.less b/client/app/visualizations/counter/render.less new file mode 100755 index 0000000000..252d0c0242 --- /dev/null +++ b/client/app/visualizations/counter/render.less @@ -0,0 +1,46 @@ +.counter-visualization-container { + display: block; + text-align: center; + padding: 15px 10px; + overflow: hidden; + + .counter-visualization-content { + margin: 0; + padding: 0; + font-size: 80px; + line-height: normal; + overflow: hidden; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + + .counter-visualization-value, + .counter-visualization-target { + font-size: 1em; + display: block; + } + + .counter-visualization-label { + font-size: 0.5em; + display: block; + } + + .counter-visualization-target { + color: #ccc; + } + + .counter-visualization-label { + font-size: 0.5em; + display: block; + } + } + + &.trend-positive .counter-visualization-value { + color: #5cb85c; + } + + &.trend-negative .counter-visualization-value { + color: #d9534f; + } +} From da8c54cdc7cac58dd0679f05f04d636cbb92e0b1 Mon Sep 17 00:00:00 2001 From: Levko Kravets Date: Fri, 30 Aug 2019 15:03:35 +0300 Subject: [PATCH 02/12] Migrate Counter to React: Editor --- client/app/visualizations/counter/Editor.jsx | 206 ++++++++++++++++++ .../app/visualizations/counter/Renderer.jsx | 128 +---------- client/app/visualizations/counter/index.js | 22 +- client/app/visualizations/counter/utils.js | 143 ++++++++++++ 4 files changed, 362 insertions(+), 137 deletions(-) create mode 100644 client/app/visualizations/counter/Editor.jsx create mode 100644 client/app/visualizations/counter/utils.js diff --git a/client/app/visualizations/counter/Editor.jsx b/client/app/visualizations/counter/Editor.jsx new file mode 100644 index 0000000000..cf48bed459 --- /dev/null +++ b/client/app/visualizations/counter/Editor.jsx @@ -0,0 +1,206 @@ +import { merge, map } from 'lodash'; +import React from 'react'; +import Tabs from 'antd/lib/tabs'; +import Input from 'antd/lib/input'; +import InputNumber from 'antd/lib/input-number'; +import Select from 'antd/lib/select'; +import Switch from 'antd/lib/switch'; +import * as Grid from 'antd/lib/grid'; +import { EditorPropTypes } from '@/visualizations'; + +import { isValueNumber } from './utils'; + +function GeneralSettings({ options, data, visualizationName, onOptionsChange }) { + return ( + + + + + + + onOptionsChange({ counterLabel: e.target.value })} + /> + + + + + + + + + + + + + + + + + + onOptionsChange({ rowNumber })} + /> + + + + + + + + + + + + + + + ); +} + +GeneralSettings.propTypes = EditorPropTypes; + +function FormatSettings({ options, data, onOptionsChange }) { + const inputsEnabled = isValueNumber(data.rows, options); + return ( + + + + + + + onOptionsChange({ stringDecimal })} + /> + + + + + + + + + onOptionsChange({ stringDecChar: e.target.value })} + /> + + + + + + + + + onOptionsChange({ stringThouSep: e.target.value })} + /> + + + + + + + + + onOptionsChange({ stringPrefix: e.target.value })} + /> + + + + + + + + + onOptionsChange({ stringSuffix: e.target.value })} + /> + + + + + + ); +} + +FormatSettings.propTypes = EditorPropTypes; + +export default function Editor(props) { + const { options, onOptionsChange } = props; + + const optionsChanged = (newOptions) => { + onOptionsChange(merge({}, options, newOptions)); + }; + + return ( + + + + + + + + + ); +} + +Editor.propTypes = EditorPropTypes; diff --git a/client/app/visualizations/counter/Renderer.jsx b/client/app/visualizations/counter/Renderer.jsx index 4e2ee077ef..080e4db323 100644 --- a/client/app/visualizations/counter/Renderer.jsx +++ b/client/app/visualizations/counter/Renderer.jsx @@ -1,134 +1,12 @@ -import { isNumber, isFinite, toString } from 'lodash'; -import numeral from 'numeral'; +import { isFinite } from 'lodash'; import React, { useState, useEffect } from 'react'; import cx from 'classnames'; import resizeObserver from '@/services/resizeObserver'; import { RendererPropTypes } from '@/visualizations'; -import './render.less'; - - -// TODO: allow user to specify number format string instead of delimiters only -// It will allow to remove this function (move all that weird formatting logic to a migration -// that will set number format for all existing counter visualization) -function numberFormat(value, decimalPoints, decimalDelimiter, thousandsDelimiter) { - // Temporarily update locale data (restore defaults after formatting) - const locale = numeral.localeData(); - const savedDelimiters = locale.delimiters; - - // Mimic old behavior - AngularJS `number` filter defaults: - // - `,` as thousands delimiter - // - `.` as decimal delimiter - // - three decimal points - locale.delimiters = { - thousands: ',', - decimal: '.', - }; - let formatString = '0,0.000'; - if ( - (Number.isFinite(decimalPoints) && (decimalPoints >= 0)) || - decimalDelimiter || - thousandsDelimiter - ) { - locale.delimiters = { - thousands: thousandsDelimiter, - decimal: decimalDelimiter || '.', - }; - - formatString = '0,0'; - if (decimalPoints > 0) { - formatString += '.'; - while (decimalPoints > 0) { - formatString += '0'; - decimalPoints -= 1; - } - } - } - const result = numeral(value).format(formatString); - - locale.delimiters = savedDelimiters; - return result; -} - -// TODO: Need to review this function, it does not properly handle edge cases. -function getRowNumber(index, size) { - if (index >= 0) { - return index - 1; - } - - if (Math.abs(index) > size) { - index %= size; - } - - return size + index; -} - -function formatValue(value, { stringPrefix, stringSuffix, stringDecimal, stringDecChar, stringThouSep }) { - if (isNumber(value)) { - value = numberFormat(value, stringDecimal, stringDecChar, stringThouSep); - return toString(stringPrefix) + value + toString(stringSuffix); - } - return toString(value); -} - -function formatTooltip(value, formatString) { - if (isNumber(value)) { - return numeral(value).format(formatString); - } - return toString(value); -} - -function getCounterData(data, options, visualizationName) { - const result = {}; - - if (data.length > 0) { - const rowNumber = getRowNumber(options.rowNumber, data.length); - const targetRowNumber = getRowNumber(options.targetRowNumber, data.length); - const counterColName = options.counterColName; - const targetColName = options.targetColName; - const counterLabel = options.counterLabel; - - if (counterLabel) { - result.counterLabel = counterLabel; - } else { - result.counterLabel = visualizationName; - } - - if (options.countRow) { - result.counterValue = data.length; - } else if (counterColName) { - result.counterValue = data[rowNumber][counterColName]; - } - - result.showTrend = false; - if (targetColName) { - result.targetValue = data[targetRowNumber][targetColName]; +import { getCounterData } from './utils'; - if (Number.isFinite(result.counterValue) && isFinite(result.targetValue)) { - const delta = result.counterValue - result.targetValue; - result.showTrend = true; - result.trendPositive = delta >= 0; - } - } else { - result.targetValue = null; - } - - result.counterValueTooltip = formatTooltip(result.counterValue, options.tooltipFormat); - result.targetValueTooltip = formatTooltip(result.targetValue, options.tooltipFormat); - - result.counterValue = formatValue(result.counterValue, options); - - if (options.formatTargetValue) { - result.targetValue = formatValue(result.targetValue, options); - } else { - if (isFinite(result.targetValue)) { - result.targetValue = numeral(result.targetValue).format('0[.]00[0]'); - } - } - } - - return result; -} +import './render.less'; function getCounterStyles(scale) { return { diff --git a/client/app/visualizations/counter/index.js b/client/app/visualizations/counter/index.js index 254b9111c3..8e09437c84 100644 --- a/client/app/visualizations/counter/index.js +++ b/client/app/visualizations/counter/index.js @@ -1,10 +1,10 @@ import { isNumber } from 'lodash'; -import { angular2react } from 'angular2react'; import { registerVisualization } from '@/visualizations'; import counterEditorTemplate from './counter-editor.html'; import Renderer from './Renderer'; +import Editor from './Editor'; const DEFAULT_OPTIONS = { counterLabel: '', @@ -71,17 +71,15 @@ const CounterEditor = { export default function init(ngModule) { ngModule.component('counterEditor', CounterEditor); - ngModule.run(($injector) => { - registerVisualization({ - type: 'COUNTER', - name: 'Counter', - getOptions: options => ({ ...DEFAULT_OPTIONS, ...options }), - Renderer, - Editor: angular2react('counterEditor', CounterEditor, $injector), - - defaultColumns: 2, - defaultRows: 5, - }); + registerVisualization({ + type: 'COUNTER', + name: 'Counter', + getOptions: options => ({ ...DEFAULT_OPTIONS, ...options }), + Renderer, + Editor, + + defaultColumns: 2, + defaultRows: 5, }); } diff --git a/client/app/visualizations/counter/utils.js b/client/app/visualizations/counter/utils.js new file mode 100644 index 0000000000..19143d80e5 --- /dev/null +++ b/client/app/visualizations/counter/utils.js @@ -0,0 +1,143 @@ +import { isNumber, isFinite, toString } from 'lodash'; +import numeral from 'numeral'; + +// TODO: allow user to specify number format string instead of delimiters only +// It will allow to remove this function (move all that weird formatting logic to a migration +// that will set number format for all existing counter visualization) +function numberFormat(value, decimalPoints, decimalDelimiter, thousandsDelimiter) { + // Temporarily update locale data (restore defaults after formatting) + const locale = numeral.localeData(); + const savedDelimiters = locale.delimiters; + + // Mimic old behavior - AngularJS `number` filter defaults: + // - `,` as thousands delimiter + // - `.` as decimal delimiter + // - three decimal points + locale.delimiters = { + thousands: ',', + decimal: '.', + }; + let formatString = '0,0.000'; + if ( + (Number.isFinite(decimalPoints) && (decimalPoints >= 0)) || + decimalDelimiter || + thousandsDelimiter + ) { + locale.delimiters = { + thousands: thousandsDelimiter, + decimal: decimalDelimiter || '.', + }; + + formatString = '0,0'; + if (decimalPoints > 0) { + formatString += '.'; + while (decimalPoints > 0) { + formatString += '0'; + decimalPoints -= 1; + } + } + } + const result = numeral(value).format(formatString); + + locale.delimiters = savedDelimiters; + return result; +} + +// TODO: Need to review this function, it does not properly handle edge cases. +function getRowNumber(index, size) { + if (index >= 0) { + return index - 1; + } + + if (Math.abs(index) > size) { + index %= size; + } + + return size + index; +} + +function formatValue(value, { stringPrefix, stringSuffix, stringDecimal, stringDecChar, stringThouSep }) { + if (isNumber(value)) { + value = numberFormat(value, stringDecimal, stringDecChar, stringThouSep); + return toString(stringPrefix) + value + toString(stringSuffix); + } + return toString(value); +} + +function formatTooltip(value, formatString) { + if (isNumber(value)) { + return numeral(value).format(formatString); + } + return toString(value); +} + +export function getCounterData(rows, options, visualizationName) { + const result = {}; + + const rowsCount = rows.length; + if (rowsCount > 0) { + const rowNumber = getRowNumber(options.rowNumber, rowsCount); + const targetRowNumber = getRowNumber(options.targetRowNumber, rowsCount); + const counterColName = options.counterColName; + const targetColName = options.targetColName; + const counterLabel = options.counterLabel; + + if (counterLabel) { + result.counterLabel = counterLabel; + } else { + result.counterLabel = visualizationName; + } + + if (options.countRow) { + result.counterValue = rowsCount; + } else if (counterColName) { + result.counterValue = rows[rowNumber][counterColName]; + } + + result.showTrend = false; + if (targetColName) { + result.targetValue = rows[targetRowNumber][targetColName]; + + if (Number.isFinite(result.counterValue) && isFinite(result.targetValue)) { + const delta = result.counterValue - result.targetValue; + result.showTrend = true; + result.trendPositive = delta >= 0; + } + } else { + result.targetValue = null; + } + + result.counterValueTooltip = formatTooltip(result.counterValue, options.tooltipFormat); + result.targetValueTooltip = formatTooltip(result.targetValue, options.tooltipFormat); + + result.counterValue = formatValue(result.counterValue, options); + + if (options.formatTargetValue) { + result.targetValue = formatValue(result.targetValue, options); + } else { + if (isFinite(result.targetValue)) { + result.targetValue = numeral(result.targetValue).format('0[.]00[0]'); + } + } + } + + return result; +} + +export function isValueNumber(rows, options) { + if (options.countRow) { + return true; // array length is always a number + } + + const rowsCount = rows.length; + if (rowsCount > 0) { + const rowNumber = getRowNumber(options.rowNumber, rowsCount); + console.log(rowNumber); + const counterColName = options.counterColName; + if (counterColName) { + return isNumber(rows[rowNumber][counterColName]); + } + } + + return false; +} From 3024d4176ab5511f0c32096bc860c83198e47c3e Mon Sep 17 00:00:00 2001 From: Levko Kravets Date: Fri, 30 Aug 2019 15:08:27 +0300 Subject: [PATCH 03/12] Cleanup --- .../counter/counter-editor.html | 95 ------------------- .../app/visualizations/counter/counter.html | 16 ---- client/app/visualizations/counter/index.js | 58 +---------- client/app/visualizations/counter/utils.js | 1 - 4 files changed, 1 insertion(+), 169 deletions(-) delete mode 100644 client/app/visualizations/counter/counter-editor.html delete mode 100644 client/app/visualizations/counter/counter.html diff --git a/client/app/visualizations/counter/counter-editor.html b/client/app/visualizations/counter/counter-editor.html deleted file mode 100644 index f47238c4ac..0000000000 --- a/client/app/visualizations/counter/counter-editor.html +++ /dev/null @@ -1,95 +0,0 @@ -
- -
-
- -
- -
-
-
- -
- -
-
-
- -
- -
-
-
- -
- -
-
-
- -
- -
-
-
-
- -
-
-
- -
-
- -
- -
-
-
- -
- -
-
-
- -
- -
-
-
- -
- -
-
-
- -
- -
-
-
-
- -
-
-
-
diff --git a/client/app/visualizations/counter/counter.html b/client/app/visualizations/counter/counter.html deleted file mode 100644 index d7493f4378..0000000000 --- a/client/app/visualizations/counter/counter.html +++ /dev/null @@ -1,16 +0,0 @@ - -
- {{ counterValue }} - ({{ targetValue }}) - {{counterLabel}} -
-
diff --git a/client/app/visualizations/counter/index.js b/client/app/visualizations/counter/index.js index 8e09437c84..07bd18af5d 100644 --- a/client/app/visualizations/counter/index.js +++ b/client/app/visualizations/counter/index.js @@ -1,8 +1,5 @@ -import { isNumber } from 'lodash'; import { registerVisualization } from '@/visualizations'; -import counterEditorTemplate from './counter-editor.html'; - import Renderer from './Renderer'; import Editor from './Editor'; @@ -17,60 +14,7 @@ const DEFAULT_OPTIONS = { tooltipFormat: '0,0.000', // TODO: Show in editor }; -// TODO: Need to review this function, it does not properly handle edge cases. -function getRowNumber(index, size) { - if (index >= 0) { - return index - 1; - } - - if (Math.abs(index) > size) { - index %= size; - } - - return size + index; -} - -const CounterEditor = { - template: counterEditorTemplate, - bindings: { - data: '<', - options: '<', - visualizationName: '<', - onOptionsChange: '<', - }, - controller($scope) { - this.currentTab = 'general'; - this.changeTab = (tab) => { - this.currentTab = tab; - }; - - this.isValueNumber = () => { - const options = this.options; - const data = this.data.rows; - - if (data.length > 0) { - const rowNumber = getRowNumber(options.rowNumber, data.length); - const counterColName = options.counterColName; - - if (options.countRow) { - this.counterValue = data.length; - } else if (counterColName) { - this.counterValue = data[rowNumber][counterColName]; - } - } - - return isNumber(this.counterValue); - }; - - $scope.$watch('$ctrl.options', (options) => { - this.onOptionsChange(options); - }, true); - }, -}; - -export default function init(ngModule) { - ngModule.component('counterEditor', CounterEditor); - +export default function init() { registerVisualization({ type: 'COUNTER', name: 'Counter', diff --git a/client/app/visualizations/counter/utils.js b/client/app/visualizations/counter/utils.js index 19143d80e5..2692ec92e5 100644 --- a/client/app/visualizations/counter/utils.js +++ b/client/app/visualizations/counter/utils.js @@ -132,7 +132,6 @@ export function isValueNumber(rows, options) { const rowsCount = rows.length; if (rowsCount > 0) { const rowNumber = getRowNumber(options.rowNumber, rowsCount); - console.log(rowNumber); const counterColName = options.counterColName; if (counterColName) { return isNumber(rows[rowNumber][counterColName]); From 9e1fc0140b8450253212c579352ff4d8aeae2654 Mon Sep 17 00:00:00 2001 From: Levko Kravets Date: Fri, 30 Aug 2019 15:42:12 +0300 Subject: [PATCH 04/12] Review and fix rows indexing algorithm --- client/app/visualizations/counter/Editor.jsx | 14 ++++++++++++++ client/app/visualizations/counter/utils.js | 18 ++++++++---------- 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/client/app/visualizations/counter/Editor.jsx b/client/app/visualizations/counter/Editor.jsx index cf48bed459..b48fae76ea 100644 --- a/client/app/visualizations/counter/Editor.jsx +++ b/client/app/visualizations/counter/Editor.jsx @@ -77,6 +77,20 @@ function GeneralSettings({ options, data, visualizationName, onOptionsChange }) + + + + + + onOptionsChange({ targetRowNumber })} + /> + + +