diff --git a/src/data-table/data-cell.jsx b/src/data-table/data-cell.jsx
new file mode 100644
index 0000000..7178553
--- /dev/null
+++ b/src/data-table/data-cell.jsx
@@ -0,0 +1,132 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { ApplyPreMask } from '../masking';
+import { addSeparators } from '../utilities';
+
+function formatMeasurementValue (measurement, styling) {
+ // TODO: measurement.name is a horrible propertyname, it's actually the column header
+ const isColumnPercentageBased = measurement.name.substring(0, 1) === '%';
+ let formattedMeasurementValue = '';
+ if (isColumnPercentageBased) {
+ if (isNaN(measurement.value)) {
+ formattedMeasurementValue = styling.symbolForNulls;
+ } else {
+ formattedMeasurementValue = ApplyPreMask('0,00%', measurement.value);
+ }
+ } else {
+ let magnitudeDivider;
+ switch (measurement.magnitude.toLowerCase()) {
+ case 'k':
+ magnitudeDivider = 1000;
+ break;
+ case 'm':
+ magnitudeDivider = 1000000;
+ break;
+ default:
+ magnitudeDivider = 1;
+ }
+ const formattingStringWithoutMagnitude = measurement.format.replace(/k|K|m|M/gi, '');
+ if (isNaN(measurement.value)) {
+ formattedMeasurementValue = styling.symbolForNulls;
+ } else {
+ let preFormatValue = measurement.value;
+ if (isColumnPercentageBased) {
+ preFormatValue *= 100;
+ }
+ switch (formattingStringWithoutMagnitude) {
+ case '#.##0':
+ formattedMeasurementValue = addSeparators((preFormatValue / magnitudeDivider), '.', ',', 0);
+ break;
+ case '#,##0':
+ formattedMeasurementValue = addSeparators((preFormatValue / magnitudeDivider), ',', '.', 0);
+ break;
+ default:
+ formattedMeasurementValue = ApplyPreMask(
+ formattingStringWithoutMagnitude,
+ (preFormatValue / magnitudeDivider)
+ );
+ break;
+ }
+ }
+ }
+ return formattedMeasurementValue;
+}
+
+function getSemaphoreColors (measurement, semaphoreColors) {
+ if (measurement < semaphoreColors.status.critical) {
+ return semaphoreColors.statusColors.critical;
+ }
+ if (measurement < semaphoreColors.status.medium) {
+ return semaphoreColors.statusColors.medium;
+ }
+ return semaphoreColors.statusColors.normal;
+}
+
+const DataCell = ({ data, general, measurement, styleBuilder, styling }) => {
+ const isColumnPercentageBased = measurement.name.substring(0, 1) === '%';
+ let formattedMeasurementValue = formatMeasurementValue(measurement, styling);
+ if (styleBuilder.hasComments()) {
+ formattedMeasurementValue = '.';
+ }
+
+ let cellStyle = {
+ fontFamily: styling.options.fontFamily,
+ ...styleBuilder.getStyle(),
+ paddingRight: '4px',
+ textAlign: 'right'
+
+ };
+ const { semaphoreColors } = styling;
+ const isValidSemaphoreValue = !styleBuilder.hasComments() && !isNaN(measurement.value);
+ const shouldHaveSemaphoreColors = semaphoreColors.fieldsToApplyTo.applyToAll || semaphoreColors.fieldsToApplyTo.specificFields.indexOf(measurement.name) !== -1;
+ if (isValidSemaphoreValue && shouldHaveSemaphoreColors) {
+ const { backgroundColor, color } = getSemaphoreColors(measurement, semaphoreColors);
+ cellStyle = {
+ backgroundColor,
+ color,
+ fontFamily: styling.options.fontFamily,
+ fontSize: styleBuilder.getStyle().fontSize,
+ paddingLeft: '4px',
+ textAlign: 'right'
+ };
+ }
+
+ let cellClass = 'grid-cells';
+ const shouldUseSmallCells = isColumnPercentageBased && data.headers.measurements.length > 1;
+ if (shouldUseSmallCells) {
+ cellClass = 'grid-cells-small';
+ }
+
+ return (
+
+ {formattedMeasurementValue}
+ |
+ );
+};
+
+DataCell.propTypes = {
+ data: PropTypes.shape({
+ headers: PropTypes.shape({
+ measurements: PropTypes.array.isRequired
+ }).isRequired
+ }).isRequired,
+ general: PropTypes.shape({
+ cellSuffix: PropTypes.string.isRequired
+ }).isRequired,
+ measurement: PropTypes.shape({
+ format: PropTypes.string,
+ name: PropTypes.string,
+ value: PropTypes.any
+ }).isRequired,
+ styleBuilder: PropTypes.shape({
+ hasComments: PropTypes.func.isRequired
+ }).isRequired,
+ styling: PropTypes.shape({
+ symbolForNulls: PropTypes.any.isRequired
+ }).isRequired
+};
+
+export default DataCell;
diff --git a/src/data-table/header-padding.jsx b/src/data-table/header-padding.jsx
new file mode 100644
index 0000000..45daea5
--- /dev/null
+++ b/src/data-table/header-padding.jsx
@@ -0,0 +1,28 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+
+const HeaderPadding = ({ styleBuilder, styling }) => {
+ if (styling.usePadding && !styleBuilder.hasCustomFileStyle()) {
+ const paddingStyle = {
+ fontFamily: styling.options.fontFamily,
+ marginLeft: '15px'
+ };
+ return (
+
+ );
+ }
+ return null;
+};
+
+HeaderPadding.propTypes = {
+ styleBuilder: PropTypes.shape({
+ hasCustomFileStyle: PropTypes.func.isRequired
+ }).isRequired,
+ styling: PropTypes.shape({
+ options: PropTypes.shape({
+ fontFamily: PropTypes.string.isRequired
+ }).isRequired
+ }).isRequired
+};
+
+export default HeaderPadding;
diff --git a/src/data-table/index.jsx b/src/data-table/index.jsx
new file mode 100644
index 0000000..afe4aa5
--- /dev/null
+++ b/src/data-table/index.jsx
@@ -0,0 +1,106 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import StyleBuilder from '../style-builder';
+import DataCell from './data-cell.jsx';
+import HeaderPadding from './header-padding.jsx';
+import { injectSeparators } from '../utilities';
+
+const DataTable = ({ data, general, styling }) => {
+ const {
+ headers: {
+ dimension1,
+ measurements
+ },
+ matrix
+ } = data;
+
+ return (
+
+
+ {dimension1.map((dimensionEntry, dimensionIndex) => {
+ const rowHeaderText = dimensionEntry.displayValue || '';
+ if (rowHeaderText === '-') {
+ return null;
+ }
+ const styleBuilder = new StyleBuilder(styling);
+ if (styling.hasCustomFileStyle) {
+ styleBuilder.parseCustomFileStyle(rowHeaderText);
+ } else {
+ styleBuilder.applyStandardAttributes(dimensionIndex);
+ styleBuilder.applyCustomStyle({
+ fontSize: `${14 + styling.options.fontSizeAdjustment}px`
+ });
+ }
+ const rowStyle = {
+ fontFamily: styling.options.fontFamily,
+ width: '230px',
+ ...styleBuilder.getStyle()
+ };
+
+ return (
+
+ |
+
+ {dimensionEntry.displayValue}
+ |
+ {injectSeparators(
+ matrix[dimensionIndex],
+ styling.useSeparatorColumns,
+ { atEvery: measurements.length }
+ ).map(measurementData => {
+ if (measurementData.isSeparator) {
+ const separatorStyle = {
+ color: 'white',
+ fontFamily: styling.options.fontFamily,
+ fontSize: `${12 + styling.options.fontSizeAdjustment}px`
+ };
+
+ return (
+
+ *
+ |
+ );
+ }
+ return (
+
+ );
+ })}
+
+ );
+ })}
+
+
+ );
+};
+
+DataTable.propTypes = {
+ data: PropTypes.shape({
+ headers: PropTypes.shape({
+ dimension1: PropTypes.array.isRequired
+ }).isRequired,
+ matrix: PropTypes.arrayOf(PropTypes.array.isRequired).isRequired
+ }).isRequired,
+ general: PropTypes.shape({}).isRequired,
+ styling: PropTypes.shape({
+ hasCustomFileStyle: PropTypes.bool.isRequired
+ }).isRequired
+};
+
+export default DataTable;
diff --git a/src/else-dimension-measures.jsx b/src/else-dimension-measures.jsx
deleted file mode 100644
index f06c7dd..0000000
--- a/src/else-dimension-measures.jsx
+++ /dev/null
@@ -1,222 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import { ApplyPreMask } from './masking';
-import { addSeparators } from './utilities';
-
-class ElseDimensionMeasures extends React.PureComponent {
- render () {
- const {
- vFontFamily,
- vSeparatorCols,
- measure_count,
- sufixCells,
- vSymbolForNulls,
- vLetterSize,
- vColorMetric1,
- vColorMetric1Text,
- vColorMetric2,
- vColorMetric2Text,
- vColorMetric3,
- vColorMetric3Text,
- vAllSemaphores,
- ConceptMatrixPivot,
- ConceptsAffectedMatrix,
- vAllMetrics,
- MetricsAffectedMatrix,
- vCritic,
- vMMedium,
- vNumMeasures,
- vNumMeasures2,
- MeasuresFormat,
- rowNumber,
- columnText,
- styleBuilder
- } = this.props;
-
- // modified in here
- let columnNumber,
- vMaskNum,
- vColorSemaphore,
- vColorSemaphoreText,
- vDivide;
-
- const measurementCells = [];
-
- var nMeasure7 = 0;
- var nMeasure72 = -1;
- var nMeasure72Semaphore = 0;
-
- for (var nMeasures22 = 1; nMeasures22 <= vNumMeasures2; nMeasures22++) {
- nMeasure7++;
- nMeasure72++;
- if (columnText.substring(0, 1) === '%') {
- columnNumber = ApplyPreMask('0,00%', ConceptMatrixPivot[rowNumber][nMeasures22]);
- var vSpecialF = '0,00%';
- } else {
- switch (MeasuresFormat[nMeasure72].substr(MeasuresFormat[nMeasure72].length - 1)) {
- case 'k':
- vDivide = 1000;
- break;
-
- case 'K':
- vDivide = 1000;
- break;
-
- case 'm':
- vDivide = 1000000;
- break;
-
- case 'M':
- vDivide = 1000000;
- break;
-
- default:
- vDivide = 1;
- break;
- }
- var vSpecialF = MeasuresFormat[nMeasure72].replace(/k|K|m|M/gi, '');
- if (!isNaN(ConceptMatrixPivot[rowNumber][nMeasures22])) {
- vMaskNum = ConceptMatrixPivot[rowNumber][nMeasures22];
- if (vSpecialF.substring(vSpecialF.length - 1) === '%') {
- vMaskNum = vMaskNum * 100;
- }
-
- switch (vSpecialF) {
- case '#.##0':
- columnNumber = addSeparators((vMaskNum / vDivide), '.', ',', 0);
- break;
- case '#,##0':
- columnNumber = addSeparators((vMaskNum / vDivide), ',', '.', 0);
- break;
- default:
- columnNumber = ApplyPreMask(vSpecialF, (vMaskNum / vDivide));
- break;
- }
- } else {
- columnNumber = vSymbolForNulls;
- }
- }
-
- if (vSeparatorCols && nMeasure7 === (measure_count + 1)) {
- const seperatorStyle = {
- color: 'white',
- fontFamily: vFontFamily,
- fontSize: (12 + vLetterSize) + 'px'
- };
- const seperatorElement = (
-
- *
- |
- );
- measurementCells.push(seperatorElement);
- nMeasure7 = 1;
- }
- if (nMeasure72 === (measure_count - 1)) {
- nMeasure72 = -1;
- nMeasure72Semaphore = measure_count;
- } else {
- nMeasure72Semaphore = nMeasure72 + 1;
- }
-
- // apply the semaphores where needed
- if (styleBuilder.hasComments()) {
- columnNumber = '.';
- }
-
- let cellElement;
- if ((vAllSemaphores || ConceptsAffectedMatrix.indexOf(columnText) >= 0) && (vAllMetrics || MetricsAffectedMatrix.indexOf(nMeasure72Semaphore) >= 0) && !isNaN(ConceptMatrixPivot[rowNumber][nMeasures22]) && !styleBuilder.hasComments()) {
- if (ConceptMatrixPivot[rowNumber][nMeasures22] < vCritic) {
- vColorSemaphore = vColorMetric1;
- vColorSemaphoreText = vColorMetric1Text;
- } else {
- if (ConceptMatrixPivot[rowNumber][nMeasures22] < vMMedium) {
- vColorSemaphore = vColorMetric2;
- vColorSemaphoreText = vColorMetric2Text;
- } else {
- vColorSemaphore = vColorMetric3;
- vColorSemaphoreText = vColorMetric3Text;
- }
- }
-
- const cellStyle = {
- fontFamily: vFontFamily,
- fontSize: styleBuilder.getStyle().fontSize,
- color: vColorSemaphoreText,
- backgroundColor: vColorSemaphore,
- textAlign: 'right',
- paddingLeft: '4px'
- };
- if (vSpecialF.substring(vSpecialF.length - 1) === '%' && vNumMeasures > 1) {
- cellElement = (
-
- {columnNumber}
- |
- );
- } else {
- cellElement = (
-
- {columnNumber}
- |
- );
- }
- } else {
- const cellStyle = {
- fontFamily: vFontFamily,
- ...styleBuilder.getStyle(),
- textAlign: 'right',
- paddingRight: '4px'
- };
- if (vSpecialF.substring(vSpecialF.length - 1) === '%' && vNumMeasures > 1) {
- cellElement = (
-
- {columnNumber}
- |
- );
- } else {
- cellElement = (
-
- {columnNumber}
- |
- );
- }
- }
- measurementCells.push(cellElement);
- }
-
- return (
-
- {measurementCells}
-
- );
- }
-}
-
-ElseDimensionMeasures.propTypes = {
- vFontFamily: PropTypes.any,
- vSeparatorCols: PropTypes.any,
- measure_count: PropTypes.any,
- sufixCells: PropTypes.any,
- vSymbolForNulls: PropTypes.any,
- vLetterSize: PropTypes.any,
- vColorMetric1: PropTypes.any,
- vColorMetric1Text: PropTypes.any,
- vColorMetric2: PropTypes.any,
- vColorMetric2Text: PropTypes.any,
- vColorMetric3: PropTypes.any,
- vColorMetric3Text: PropTypes.any,
- vAllSemaphores: PropTypes.any,
- ConceptMatrixPivot: PropTypes.any,
- ConceptsAffectedMatrix: PropTypes.any,
- vAllMetrics: PropTypes.any,
- MetricsAffectedMatrix: PropTypes.any,
- vCritic: PropTypes.any,
- vMMedium: PropTypes.any,
- vNumMeasures: PropTypes.any,
- vNumMeasures2: PropTypes.any,
- MeasuresFormat: PropTypes.any,
- rowNumber: PropTypes.any,
- columnText: PropTypes.any,
- styleBuilder: PropTypes.any
-};
-
-export default ElseDimensionMeasures;
diff --git a/src/header-wrapper.jsx b/src/header-wrapper.jsx
deleted file mode 100644
index 0b67b52..0000000
--- a/src/header-wrapper.jsx
+++ /dev/null
@@ -1,360 +0,0 @@
-import React, { PureComponent, Fragment } from 'react';
-import PropTypes from 'prop-types';
-import ExportButton from './export-button.jsx';
-
-/* TODO: the different render methods are very similar, split it into a few components
- and try to get rid of some duplication */
-class HeaderWrapper extends PureComponent {
- getBaseCSS () {
- const {
- vHeaderColorText,
- vFontFamily,
- vHeaderColorSchema,
- vHeaderAlignText
- } = this.props;
-
- const baseCSS = {
- backgroundColor: vHeaderColorSchema,
- color: vHeaderColorText,
- fontFamily: vFontFamily,
- textAlign: vHeaderAlignText
- };
- return baseCSS;
- }
-
- renderSecondDimensionTitles () {
- const {
- vFontFamily,
- vLetterSizeHeader,
- vExportToExcel,
- sufixCells,
- vNumDims,
- measure_count,
- LabelsArray,
- SecondHeader,
- vSeparatorCols,
- nSecond,
- vLetterSize,
- ExtraLabelsArray
- } = this.props;
-
- const baseCSS = this.getBaseCSS();
-
- if (vNumDims === 2) {
- if (measure_count > 1) {
- const thStyle = {
- ...baseCSS,
- cursor: 'default',
- fontSize: `${16 + vLetterSizeHeader} px`,
- height: '80px',
- verticalAlign: 'middle',
- width: '230px'
- };
- return (
-
-
-
- {LabelsArray[0]}
- |
- {SecondHeader.map((header, index) => {
- const emptyStyle = {
- color: 'white',
- fontFamily: vFontFamily,
- fontSize: `${13 + vLetterSizeHeader} px`
- };
- const style = {
- ...baseCSS,
- fontSize: `${14 + vLetterSizeHeader} px`,
- height: '45px',
- verticalAlign: 'middle'
- };
- return (
-
- {vSeparatorCols && index > 0 && (
-
- *
- |
- )}
-
- {header}
- |
-
- );
- })}
-
- );
- }
- const fDimCellsStyle = {
- ...baseCSS,
- cursor: 'default',
- fontSize: `${16 + vLetterSizeHeader} px`,
- height: '70px',
- verticalAlign: 'middle',
- width: '230px'
- };
-
- return (
-
-
-
- {LabelsArray[0] + ExtraLabelsArray[0]}
- |
- {SecondHeader.map((entry, entryIndex) => {
- // TODO: seperator element is reused a bunch, only difference being font-size
- const hasSeperator = vSeparatorCols && nSecond > 0;
- const seperatorStyle = {
- color: 'white',
- fontFamily: vFontFamily,
- fontSize: `${12 + vLetterSize} px`
- };
- const seperatorElement = (
-
- *
- |
- );
- let sufixWrap = '';
- if ((entry.length > 11 && vLetterSizeHeader === 0) || (entry.length > 12 && vLetterSizeHeader === -2)) {
- sufixWrap = '70';
- } else {
- sufixWrap = 'Empty';
- }
- const gridCells2Style = {
- ...baseCSS,
- fontSize: `${14 + vLetterSizeHeader} px`,
- height: '70px',
- verticalAlign: 'middle'
- };
- const wrapStyle = {
- fontFamily: vFontFamily
- };
- return (
-
- {hasSeperator && seperatorElement}
-
-
- {entry}
-
- |
-
- );
- })}
-
- );
- }
- }
-
- renderSecondDimensionSubTitles () {
- const {
- vFontFamily,
- vLetterSizeHeader,
- sufixCells,
- LabelsArray,
- SecondHeader,
- vSeparatorCols,
- vLetterSize,
- MeasuresFormat,
- ExtraLabelsArray
- } = this.props;
-
- const baseCSS = this.getBaseCSS();
-
- return SecondHeader.map((header, index) => {
- const emptyStyle = {
- color: 'white',
- fontFamily: vFontFamily,
- fontSize: `${12 + vLetterSizeHeader} px`
- };
- return (
-
- {vSeparatorCols && index > 0 && (
-
- *
- |
- )}
- {MeasuresFormat.map((measureFormat, measureFormatIndex) => {
- if (measureFormat.substring(measureFormat.length - 1) === '%') {
- const cells2SmallStyle = {
- ...baseCSS,
- cursor: 'default',
- fontSize: `${13 + vLetterSizeHeader} px`,
- height: '25px',
- verticalAlign: 'middle'
- };
- return (
-
-
- {LabelsArray[measureFormatIndex + 1]}
- {ExtraLabelsArray[measureFormatIndex]}
-
- |
- );
- }
- const cells2Style = {
- ...baseCSS,
- cursor: 'default',
- fontSize: `${14 + vLetterSizeHeader} px`,
- height: '25px',
- verticalAlign: 'middle'
- };
- return (
-
-
- {LabelsArray[measureFormatIndex + 1]}
- {ExtraLabelsArray[measureFormatIndex]}
-
- |
- );
- })}
-
- );
- });
- }
-
- renderMeasureInfos () {
- const {
- vFontFamily,
- vLetterSizeHeader,
- dim_count,
- vExtraLabel,
- sufixCells,
- measureInfos
- } = this.props;
-
- const baseCSS = this.getBaseCSS();
-
- if (dim_count === 1) {
- return measureInfos.map((measureInfo, measureInfoIndex) => {
- let sufixWrap = '';
- if (((measureInfo.qFallbackTitle + vExtraLabel).length > 11 && vLetterSizeHeader === 0)
- || ((measureInfo.qFallbackTitle + vExtraLabel).length > 12 && vLetterSizeHeader === -2)) {
- sufixWrap = '70';
- } else {
- sufixWrap = 'Empty';
- }
- const thStyle = {
- ...baseCSS,
- cursor: 'default',
- fontSize: `${15 + vLetterSizeHeader} px`,
- height: '70px',
- verticalAlign: 'middle'
- };
- return (
-
-
- {measureInfo.qFallbackTitle + vExtraLabel}
-
- |
- );
- });
- }
- return null;
- }
-
- renderDimensionInfos () {
- const {
- dimensionInfos,
- vLetterSizeHeader,
- vExportToExcel
- } = this.props;
-
- const baseCSS = this.getBaseCSS();
-
- return dimensionInfos.map((dimensionInfo, dimensionInfoIndex) => {
- // TODO: move static properties to css file
- const style = {
- ...baseCSS,
- cursor: 'default',
- fontSize: `${17 + vLetterSizeHeader} px`,
- height: '70px',
- verticalAlign: 'middle',
- width: '230px'
- };
-
- return (
-
-
- {dimensionInfo.qFallbackTitle}
- |
- );
- });
- }
-
- render () {
- const { vNumDims, measure_count } = this.props;
- return (
-
-
-
-
- {this.renderDimensionInfos()}
- {this.renderMeasureInfos()}
- {this.renderSecondDimensionTitles()}
-
- { vNumDims === 2 && measure_count > 1 && (
-
- {this.renderSecondDimensionSubTitles()}
-
- )}
-
-
-
- );
- }
-}
-
-// TODO: make any, object and array forbidden
-HeaderWrapper.propTypes = {
- vHeaderColorText: PropTypes.any,
- vFontFamily: PropTypes.any,
- vHeaderColorSchema: PropTypes.any,
- vExportToExcel: PropTypes.any,
- vNumDims: PropTypes.any,
- dimensionInfos: PropTypes.any,
- vLetterSizeHeader: PropTypes.any,
- vHeaderAlignText: PropTypes.any,
- MeasuresFormat: PropTypes.any,
- measure_count: PropTypes.any,
- sufixCells: PropTypes.any,
- LabelsArray: PropTypes.any,
- SecondHeader: PropTypes.any,
- vSeparatorCols: PropTypes.any,
- nSecond: PropTypes.any,
- vLetterSize: PropTypes.any,
- ExtraLabelsArray: PropTypes.any,
- dim_count: PropTypes.any,
- vExtraLabel: PropTypes.any,
- measureInfos: PropTypes.any
-};
-
-export default HeaderWrapper;
diff --git a/src/headers-table/column-header.jsx b/src/headers-table/column-header.jsx
new file mode 100644
index 0000000..3c9f251
--- /dev/null
+++ b/src/headers-table/column-header.jsx
@@ -0,0 +1,40 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+
+const ColumnHeader = ({ baseCSS, cellSuffix, colSpan, styling, title }) => {
+ const style = {
+ ...baseCSS,
+ fontSize: `${14 + styling.headerOptions.fontSizeAdjustment} px`,
+ height: '45px',
+ verticalAlign: 'middle'
+ };
+
+ return (
+
+ {title}
+ |
+ );
+};
+
+ColumnHeader.defaultProps = {
+ cellSuffix: '',
+ colSpan: 1
+};
+
+ColumnHeader.propTypes = {
+ baseCSS: PropTypes.shape({}).isRequired,
+ cellSuffix: PropTypes.string,
+ colSpan: PropTypes.number,
+ styling: PropTypes.shape({
+ headerOptions: PropTypes.shape({
+ fontSizeAdjustment: PropTypes.number.isRequired
+ }).isRequired
+ }).isRequired,
+ title: PropTypes.string.isRequired
+};
+
+export default ColumnHeader;
diff --git a/src/headers-table/export-column-header.jsx b/src/headers-table/export-column-header.jsx
new file mode 100644
index 0000000..640e89e
--- /dev/null
+++ b/src/headers-table/export-column-header.jsx
@@ -0,0 +1,40 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import ExportButton from '../export-button.jsx';
+
+const ExportColumnHeader = ({ baseCSS, title, allowExcelExport, hasSecondDimension, styling }) => {
+ const rowSpan = hasSecondDimension ? 2 : 1;
+ const style = {
+ ...baseCSS,
+ cursor: 'default',
+ fontSize: `${16 + styling.headerOptions.fontSizeAdjustment} px`,
+ height: '80px',
+ verticalAlign: 'middle',
+ width: '230px'
+ };
+
+ return (
+
+
+ {title}
+ |
+ );
+};
+
+ExportColumnHeader.propTypes = {
+ allowExcelExport: PropTypes.bool.isRequired,
+ baseCSS: PropTypes.shape({}).isRequired,
+ hasSecondDimension: PropTypes.bool.isRequired,
+ styling: PropTypes.shape({
+ headerOptions: PropTypes.shape({
+ fontSizeAdjustment: PropTypes.number.isRequired
+ }).isRequired
+ }).isRequired,
+ title: PropTypes.string.isRequired
+};
+
+export default ExportColumnHeader;
diff --git a/src/headers-table/index.jsx b/src/headers-table/index.jsx
new file mode 100644
index 0000000..c45fa48
--- /dev/null
+++ b/src/headers-table/index.jsx
@@ -0,0 +1,129 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import ExportColumnHeader from './export-column-header.jsx';
+import ColumnHeader from './column-header.jsx';
+import MeasurementColumnHeader from './measurement-column-header.jsx';
+import { injectSeparators } from '../utilities';
+
+const HeadersTable = ({ data, general, styling }) => {
+ const baseCSS = {
+ backgroundColor: styling.headerOptions.colorSchema,
+ color: styling.headerOptions.textColor,
+ fontFamily: styling.options.fontFamily,
+ textAlign: styling.headerOptions.alignment
+ };
+
+ const {
+ dimension1,
+ dimension2,
+ measurements
+ } = data.headers;
+
+ const hasSecondDimension = dimension2.length > 0;
+
+ return (
+
+
+
+
+
+ {!hasSecondDimension && measurements.map(measurementEntry => (
+
+ ))}
+ {hasSecondDimension && injectSeparators(dimension2, styling.useSeparatorColumns).map(entry => {
+ if (entry.isSeparator) {
+ const separatorStyle = {
+ color: 'white',
+ fontFamily: styling.options.fontFamily,
+ fontSize: `${13 + styling.headerOptions.fontSizeAdjustment}px`
+ };
+
+ return (
+ |
+ *
+ |
+ );
+ }
+ return (
+
+ );
+ })}
+
+ {hasSecondDimension && (
+
+ {injectSeparators(dimension2, styling.useSeparatorColumns).map(dimensionEntry => {
+ if (dimensionEntry.isSeparator) {
+ const separatorStyle = {
+ color: 'white',
+ fontFamily: styling.options.fontFamily,
+ fontSize: `${12 + styling.headerOptions.fontSizeAdjustment}px`
+ };
+
+ return (
+ |
+ *
+ |
+ );
+ }
+ return measurements.map(measurementEntry => (
+
+ ));
+ })}
+
+ )}
+
+
+
+ );
+};
+
+HeadersTable.propTypes = {
+ data: PropTypes.shape({
+ headers: PropTypes.shape({
+ dimension1: PropTypes.array,
+ dimension2: PropTypes.array,
+ measurements: PropTypes.array
+ })
+ }).isRequired,
+ general: PropTypes.shape({}).isRequired,
+ styling: PropTypes.shape({
+ headerOptions: PropTypes.shape({}),
+ options: PropTypes.shape({})
+ }).isRequired
+};
+
+export default HeadersTable;
diff --git a/src/headers-table/measurement-column-header.jsx b/src/headers-table/measurement-column-header.jsx
new file mode 100644
index 0000000..3523913
--- /dev/null
+++ b/src/headers-table/measurement-column-header.jsx
@@ -0,0 +1,77 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+
+const MeasurementColumnHeader = ({ baseCSS, general, hasSecondDimension, measurement, styling }) => {
+ const title = `${measurement.name} ${measurement.magnitudeLabelSuffix}`;
+ const { fontSizeAdjustment } = styling.headerOptions;
+ if (hasSecondDimension) {
+ const isPercentageFormat = measurement.format.substring(measurement.format.length - 1) === '%';
+ let baseFontSize = 14;
+ let cellClass = 'grid-cells2';
+ if (isPercentageFormat) {
+ baseFontSize = 13;
+ cellClass = 'grid-cells2-small';
+ }
+ const cellStyle = {
+ ...baseCSS,
+ cursor: 'default',
+ fontSize: `${baseFontSize + fontSizeAdjustment} px`,
+ height: '25px',
+ verticalAlign: 'middle'
+ };
+ return (
+
+
+ {title}
+
+ |
+ );
+ }
+ const isLong = (title.length > 11 && fontSizeAdjustment === 0) || (title.length > 12 && fontSizeAdjustment === -2);
+ const suffixWrap = isLong ? '70' : 'empty';
+ const style = {
+ ...baseCSS,
+ cursor: 'default',
+ fontSize: `${15 + fontSizeAdjustment} px`,
+ height: '70px',
+ verticalAlign: 'middle'
+ };
+ return (
+
+
+ {title}
+
+ |
+ );
+};
+
+MeasurementColumnHeader.defaultProps = {
+ hasSecondDimension: false
+};
+
+MeasurementColumnHeader.propTypes = {
+ baseCSS: PropTypes.shape({}).isRequired,
+ general: PropTypes.shape({
+ cellSuffix: PropTypes.string.isRequired
+ }).isRequired,
+ hasSecondDimension: PropTypes.bool,
+ measurement: PropTypes.shape({
+ name: PropTypes.string.isRequired
+ }).isRequired,
+ styling: PropTypes.shape({
+ headerOptions: PropTypes.shape({
+ fontSizeAdjustment: PropTypes.number.isRequired
+ }).isRequired
+ }).isRequired
+};
+
+export default MeasurementColumnHeader;
diff --git a/src/index.js b/src/index.js
index fbe6d4a..0ead03a 100644
--- a/src/index.js
+++ b/src/index.js
@@ -1,4 +1,4 @@
-import paint from './paint';
+import paint from './paint.jsx';
import definition from './definition';
import './main.less';
diff --git a/src/initialize-transformed.js b/src/initialize-transformed.js
new file mode 100644
index 0000000..b58f7ca
--- /dev/null
+++ b/src/initialize-transformed.js
@@ -0,0 +1,317 @@
+import jQuery from 'jquery';
+import { distinctArray } from './utilities';
+
+// TODO: rename colors
+function initializeColors ({ layout }) {
+ return {
+ vColLibBlue: layout.collibblue,
+ vColLibBlueP: layout.collibbluep,
+ vColLibClean: layout.collibclean,
+ vColLibCleanP: layout.collibcleanp,
+ vColLibCustom: layout.collibcustom,
+ vColLibCustomP: layout.collibcustomp,
+ vColLibDark: layout.collibdark,
+ vColLibDarkP: layout.collibdarkp,
+ vColLibGreen: layout.collibgreen,
+ vColLibGreenP: layout.collibgreenp,
+ vColLibNight: layout.collibnight,
+ vColLibNightP: layout.collibnightp,
+ vColLibOrange: layout.colliborange,
+ vColLibOrangeP: layout.colliborangep,
+ vColLibRed: layout.collibred,
+ vColLibRedP: layout.collibredp,
+ vColLibSoft: layout.collibsoft,
+ vColLibSoftP: layout.collibsoftp,
+ vColLibViolete: layout.collibviolete,
+ vColLibVioleteP: layout.collibvioletep
+ };
+}
+
+function getAlignment (option) {
+ const alignmentOptions = {
+ 1: 'left',
+ 2: 'center',
+ 3: 'right'
+ };
+
+ return alignmentOptions[option] || 'left';
+}
+
+function getFontSizeAdjustment (option) {
+ const fontSizeAdjustmentOptions = {
+ 1: -2,
+ 2: 0,
+ 3: 2
+ };
+
+ return fontSizeAdjustmentOptions[option] || 0;
+}
+
+function getCellSuffix (option) {
+ const cellSuffixOptions = {
+ 1: '-s',
+ 3: '-l'
+ };
+
+ return cellSuffixOptions[option] || '';
+}
+
+function getMeasurementFormat (measurement) {
+ if (measurement.qNumFormat.qType === 'U' || measurement.qNumFormat.qFmt === '##############') {
+ return '#.##0';
+ } else if (measurement.qNumFormat.qType === 'R') {
+ return measurement.qNumFormat.qFmt.replace(/(|)/gi, '');
+ }
+ return measurement.qNumFormat.qFmt;
+}
+
+function getMagnitudeLabelSuffix (magnitudeOption) {
+ const magnitudeLabelSuffixOptions = {
+ 'k': ' (k)',
+ 'm': ' (m)'
+ };
+
+ return magnitudeLabelSuffixOptions[magnitudeOption] || '';
+}
+
+function generateMeasurements (information) {
+ return information.map(measurement => {
+ const format = getMeasurementFormat(measurement);
+ const formatMagnitude = format.substr(format.length - 1).toLowerCase();
+ const transformedMeasurement = {
+ format,
+ magnitudeLabelSuffix: getMagnitudeLabelSuffix(formatMagnitude),
+ name: measurement.qFallbackTitle
+ };
+
+ return transformedMeasurement;
+ });
+}
+
+function generateDimensionEntry (information, data) {
+ return {
+ displayValue: data.qText,
+ name: information.qFallbackTitle,
+ value: data.qNum
+ };
+}
+
+function generateMatrixCell (information, data) {
+ return {
+ displayValue: data.qText,
+ elementNumber: data.qElemNumber,
+ format: information.format,
+ magnitude: information.magnitudeLabelSuffix.substring(
+ information.magnitudeLabelSuffix.length - 2,
+ information.magnitudeLabelSuffix.length - 1
+ ),
+ magnitudeLabelSuffix: information.magnitudeLabelSuffix,
+ name: information.name,
+ value: data.qNum
+ };
+}
+
+let lastRow = 0;
+function generateDataSet (component, dimensionsInformation, measurementsInformation) {
+ const dimension1 = [];
+ const dimension2 = [];
+ const measurements = generateMeasurements(measurementsInformation);
+ let matrix = [];
+
+ let previousDim1Entry;
+ const hasSecondDimension = dimensionsInformation.length > 1;
+ component.backendApi.eachDataRow((rowIndex, row) => {
+ lastRow += 1;
+ const dimension1Entry = generateDimensionEntry(dimensionsInformation[0], row[0]);
+ dimension1.push(dimension1Entry);
+ let dimension2Entry;
+ let firstDataCell = 1;
+ if (hasSecondDimension) {
+ dimension2Entry = generateDimensionEntry(dimensionsInformation[1], row[1]);
+ dimension2.push(dimension2Entry);
+ firstDataCell = 2;
+ }
+ const matrixRow = row
+ .slice(firstDataCell, row.length)
+ .map((cell, cellIndex) => {
+ const measurementInformation = measurements[cellIndex];
+ const generatedCell = generateMatrixCell(measurementInformation, cell);
+
+ return generatedCell;
+ });
+
+ if (hasSecondDimension) {
+ const currentDim1Entry = row[0].qText;
+ const isSameDimension1AsPrevious = currentDim1Entry === previousDim1Entry;
+ if (isSameDimension1AsPrevious) {
+ const updatedRow = matrix[matrix.length - 1].concat(matrixRow);
+
+ matrix = [
+ ...matrix.slice(0, matrix.length - 1),
+ updatedRow
+ ];
+ } else {
+ matrix[matrix.length] = matrixRow;
+ }
+ previousDim1Entry = currentDim1Entry;
+ } else {
+ matrix[matrix.length] = matrixRow;
+ }
+ });
+
+ // filter header dimensions to only have distinct values
+
+ return {
+ dimension1: distinctArray(dimension1),
+ dimension2: distinctArray(dimension2),
+ matrix,
+ measurements
+ };
+}
+
+async function initializeTransformed ({ $element, layout, component }) {
+ const colors = initializeColors({ layout });
+ const dimensionsInformation = component.backendApi.getDimensionInfos();
+ const measurementsInformation = component.backendApi.getMeasureInfos();
+ const dimensionCount = layout.qHyperCube.qDimensionInfo.length;
+ const rowCount = component.backendApi.getRowCount();
+ const maxLoops = layout.maxloops;
+ const {
+ dimension1,
+ dimension2,
+ measurements,
+ matrix
+ } = generateDataSet(component, dimensionsInformation, measurementsInformation);
+
+ const customSchemaBasic = [];
+ const customSchemaFull = [];
+ let customHeadersCount = 0;
+
+ function readCustomSchema () {
+ const url = `/Extensions/qlik-smart-pivot/${layout.customfile}`;
+
+ return jQuery.get(url).then(response => {
+ const allTextLines = response.split(/\r\n|\n/);
+ const headers = allTextLines[0].split(';');
+ customHeadersCount = headers.length;
+ for (let lineNumber = 0; lineNumber < allTextLines.length; lineNumber += 1) {
+ customSchemaFull[lineNumber] = new Array(headers.length);
+ const data = allTextLines[lineNumber].split(';');
+
+ if (data.length === headers.length) {
+ for (let headerIndex = 0; headerIndex < headers.length; headerIndex += 1) {
+ customSchemaBasic[lineNumber] = data[0];
+ customSchemaFull[lineNumber][headerIndex] = data[headerIndex];
+ }
+ }
+ }
+ });
+ }
+
+ const hasCustomSchema = (layout.customfilebool && layout.customfile.length > 4);
+ const schemaPromise = hasCustomSchema ? readCustomSchema() : Promise.resolve();
+ await schemaPromise;
+
+ // top level properties could be reducers and then components connect to grab what they want,
+ // possibly with reselect for some presentational transforms (moving some of the presentational logic like formatting and such)
+ const transformedProperties = {
+ data: {
+ headers: {
+ dimension1, // column headers
+ dimension2, // parent row headers if exists
+ measurements // row headers, looped for each dimension2 if exists
+ },
+ matrix, // 2d array of all rows/cells to render in body of datatable
+ meta: {
+ dimensionCount: dimensionsInformation.length
+ }
+ },
+ general: {
+ allowExcelExport: layout.allowexportxls,
+ cellSuffix: getCellSuffix(layout.columnwidthslider), // TOOD: move to matrix cells or is it headers.measurements?
+ errorMessage: layout.errormessage,
+ maxLoops
+ },
+ selection: {
+ dimensionSelectionCounts: dimensionsInformation.map(dimensionInfo => dimensionInfo.qStateCounts.qSelected)
+ },
+ styling: {
+ colors,
+ customCSV: {
+ basic: customSchemaBasic,
+ count: customHeadersCount,
+ full: customSchemaFull
+ },
+ hasCustomFileStyle: layout.customfilebool,
+ headerOptions: {
+ alignment: getAlignment(layout.HeaderAlign),
+ colorSchema: colors[`vColLib${layout.HeaderColorSchema}`],
+ fontSizeAdjustment: getFontSizeAdjustment(layout.lettersizeheader),
+ textColor: layout.HeaderTextColorSchema
+ },
+ options: {
+ backgroundColor: colors[`vColLib${layout.ColorSchema}`],
+ backgroundColorOdd: colors[`vColLib${layout.ColorSchemaP}`],
+ color: layout.BodyTextColorSchema,
+ fontFamily: layout.FontFamily,
+ fontSizeAdjustment: getFontSizeAdjustment(layout.lettersize)
+ },
+ semaphoreColors: {
+ fieldsToApplyTo: {
+ applyToAll: layout.allsemaphores,
+ specificFields: [
+ layout.conceptsemaphore1,
+ layout.conceptsemaphore2,
+ layout.conceptsemaphore3,
+ layout.conceptsemaphore4,
+ layout.conceptsemaphore5,
+ layout.conceptsemaphore6,
+ layout.conceptsemaphore7,
+ layout.conceptsemaphore9,
+ layout.conceptsemaphore10
+ ]
+ },
+ status: {
+ critical: layout.metricstatus1,
+ medium: layout.metricstatus2
+ },
+ statusColors: {
+ critical: {
+ backgroundColor: layout.colorstatus1.color,
+ color: layout.colorstatus1text.color
+ },
+ medium: {
+ backgroundColor: layout.colorstatus2.color,
+ color: layout.colorstatus2text.color
+ },
+ normal: {
+ backgroundColor: layout.colorstatus3.color,
+ color: layout.colorstatus3text.color
+ }
+ }
+ },
+ symbolForNulls: layout.symbolfornulls,
+ usePadding: layout.indentbool,
+ useSeparatorColumns: dimensionCount === 1 ? false : layout.separatorcols
+ }
+ };
+
+ if (rowCount > lastRow && rowCount <= (maxLoops * 1000)) {
+ const requestPage = [
+ {
+ qHeight: Math.min(1000, rowCount - lastRow),
+ qLeft: 0,
+ qTop: matrix.length,
+ qWidth: 10 // should be # of columns
+ }
+ ];
+ component.backendApi.getData(requestPage).then(() => {
+ component.paint($element, layout);
+ });
+ }
+
+
+ return transformedProperties;
+}
+
+export default initializeTransformed;
diff --git a/src/paint.js b/src/paint.jsx
similarity index 80%
rename from src/paint.js
rename to src/paint.jsx
index e26c71f..2ed949d 100644
--- a/src/paint.js
+++ b/src/paint.jsx
@@ -1,72 +1,64 @@
import $ from 'jquery';
import { enableExcelExport } from './excel-export';
-import HeaderWrapper from './header-wrapper.jsx';
-import RowWrapper, { prepareProps } from './row-wrapper.jsx';
import initializeStore from './store';
import React from 'react';
// import ReactDOM from 'react-dom';
import { renderToStaticMarkup } from 'react-dom/server';
+import HeadersTable from './headers-table/index.jsx';
+import DataTable from './data-table/index.jsx';
export default async function paint ($element, layout, component) {
- const state = initializeStore({
+ const state = await initializeStore({
$element,
- layout,
- component
+ component,
+ layout
});
+
const {
- ArrayGetSelectedCount,
- vNumDims,
ConceptMatrixColElem,
ConceptMatrixColElemTable,
- ConceptMatrixRowElem,
- vSeparatorCols
+ ConceptMatrixRowElem
} = state.properties;
- const rowWrapperProps = await prepareProps({
- state: {
- layout,
- colors: state.colors,
- ...state.properties
- }
- });
+ const {
+ data: { meta: { dimensionCount } },
+ selection: { dimensionSelectionCounts },
+ styling: { useSeparatorColumns }
+ } = state;
const jsx = (
-
-
-
-
);
// TODO: switch to render when jquery interaction stuff in renderData is gone
- // ReactDOM.render(jsx, $element[0]);
-
const html = renderToStaticMarkup(jsx);
-
$element.html(html);
+ // ReactDOM.render(jsx, $element[0]);
RenderData();
@@ -124,8 +116,8 @@ export default async function paint ($element, layout, component) {
// colgado el menú de confirm, por eso uso este sistema, que sí funciona.
// it can cause issues like error messages and wrong selections if there are null values
// and the check allow null values is active
- if (vNumDims > 1 && indextd > 0) {
- if (ArrayGetSelectedCount[1] > 0) {
+ if (dimensionCount > 1 && indextd > 0) {
+ if (dimensionSelectionCounts[1] > 0) {
const SelectB = JSON.parse(JSON.stringify(ConceptMatrixColElemTable));
component.backendApi.selectValues(1, SelectB, true);
$(this).toggleClass('selected');
@@ -136,7 +128,7 @@ export default async function paint ($element, layout, component) {
$(this).toggleClass('selected');
}
- if (indextd > 0 && ArrayGetSelectedCount[0] > 0) {
+ if (indextd > 0 && dimensionSelectionCounts[0] > 0) {
const SelectA = JSON.parse(JSON.stringify(ConceptMatrixRowElem));
component.backendApi.selectValues(0, SelectA, true);
$(this).toggleClass('selected');
@@ -155,13 +147,13 @@ export default async function paint ($element, layout, component) {
let SelectCol = 0;
- if (vNumDims > 1 && indextd > 0) {
- if (ArrayGetSelectedCount[1] > 0) {
+ if (dimensionCount > 1 && indextd > 0) {
+ if (dimensionSelectionCounts[1] > 0) {
const SelectB = JSON.parse(JSON.stringify(ConceptMatrixColElem));
component.backendApi.selectValues(1, SelectB, true);
$(this).toggleClass('selected');
}
- if (vSeparatorCols) {
+ if (useSeparatorColumns) {
SelectCol = ConceptMatrixColElem[(Math.round(indextd / 2) - 1)];
} else {
SelectCol = ConceptMatrixColElem[(Math.round(indextd) - 1)];
@@ -180,7 +172,7 @@ export default async function paint ($element, layout, component) {
let SelectRow = 0;
SelectRow = ConceptMatrixRowElem[(indextr)];
- if (ArrayGetSelectedCount[0] > 0) {
+ if (dimensionSelectionCounts[0] > 0) {
const SelectA = JSON.parse(JSON.stringify(ConceptMatrixRowElem));
component.backendApi.selectValues(0, SelectA, true);
$(this).toggleClass('selected');
diff --git a/src/row-list.jsx b/src/row-list.jsx
deleted file mode 100644
index 742fac1..0000000
--- a/src/row-list.jsx
+++ /dev/null
@@ -1,81 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import StyleBuilder from './style-builder';
-
-class RowList extends React.PureComponent {
- generatePaddingTextElement (hasCustomFileStyle) {
- const { vPadding, vFontFamily } = this.props;
- if (vPadding && !hasCustomFileStyle) {
- const paddingStyle = {
- fontFamily: vFontFamily,
- marginLeft: '15px'
- };
- return (
-
- );
- }
- return null;
- }
-
- render () {
- const {
- vLetterSize,
- vCustomFileBool,
- vFontFamily,
- tableData,
- MeasurementsComponent
- } = this.props;
-
- return (
-
- {tableData.map((row, rowNumber) => {
- const rowHeaderText = row[0] || '';
- if (rowHeaderText === '-') {
- return null;
- }
- const styleBuilder = new StyleBuilder(this.props);
- if (vCustomFileBool) {
- styleBuilder.parseCustomFileStyle(rowHeaderText);
- } else {
- styleBuilder.applyStandardAttributes(rowNumber);
- styleBuilder.applyCustomStyle({ fontSize: (14 + vLetterSize) + 'px' });
- }
-
- const rowStyle = {
- fontFamily: vFontFamily,
- width: '230px',
- ...styleBuilder.getStyle()
- };
- const paddingTextElement = this.generatePaddingTextElement(styleBuilder.hasCustomFileStyle());
- const measurementsProps = {
- rowHeaderText,
- rowNumber,
- styleBuilder
- };
- return (
-
- |
- {paddingTextElement}
- {rowHeaderText}
- |
-
-
- );
- })}
-
- );
- }
-}
-
-RowList.propTypes = {
- tableData: PropTypes.array.isRequired
-};
-
-export default RowList;
diff --git a/src/row-wrapper.jsx b/src/row-wrapper.jsx
deleted file mode 100644
index 985bd07..0000000
--- a/src/row-wrapper.jsx
+++ /dev/null
@@ -1,129 +0,0 @@
-import $ from 'jquery';
-import PropTypes from 'prop-types';
-import React from 'react';
-
-import ElseDimensionMeasures from './else-dimension-measures.jsx';
-import RowList from './row-list.jsx';
-import SingleDimensionMeasures from './single-dimension-measures.jsx';
-
-const RowWrapper = props => {
- const {
- ConceptMatrix,
- ConceptMatrixPivot,
- vNumDims
- } = props;
- let MeasurementsComponent,
- tableData;
- if (vNumDims === 1) {
- tableData = ConceptMatrix;
- MeasurementsComponent = SingleDimensionMeasures;
- } else {
- tableData = ConceptMatrixPivot.filter(array => array.length);
- MeasurementsComponent = ElseDimensionMeasures;
- }
-
- return (
-
- );
-};
-
-RowWrapper.propTypes = {
- ConceptMatrix: PropTypes.array.isRequired,
- ConceptMatrixPivot: PropTypes.array.isRequired
-};
-
-export default RowWrapper;
-
-export async function prepareProps ({ state }) {
- const { colors, layout, vAllSemaphores, vDynamicColorBody, vDynamicColorBodyP } = state;
- const props = {
- colors,
- vCustomFileBool: layout.customfilebool,
- vCustomFile: layout.customfile,
- vPadding: layout.indentbool,
- vPaddingText: '',
- vGlobalComas: 0,
- vGlobalComas2: 0,
- vGlobalComment: 0,
- vGlobalCommentColor: '',
- vGlobalFontSize: 0,
- vComas: 0,
- vMedium: false,
- vFontSize: '',
- vColorText: layout.BodyTextColorSchema,
- vDivide: 1,
- vSymbolForNulls: layout.symbolfornulls,
- vDynamicColorBody: 'vColLib' + layout.ColorSchema,
- vDynamicColorBodyP: 'vColLib' + layout.ColorSchema + 'P',
- vAllMetrics: layout.allmetrics,
- MetricsAffectedMatrix: JSON.parse('[' + layout.metricssemaphore + ']'),
- vColorMetric1: layout.colorstatus1.color,
- vColorMetric2: layout.colorstatus2.color,
- vColorMetric3: layout.colorstatus3.color,
- vColorMetric1Text: layout.colorstatus1text.color,
- vColorMetric2Text: layout.colorstatus2text.color,
- vColorMetric3Text: layout.colorstatus3text.color,
- vColorSemaphore: '',
- vColorSemaphoreText: '',
- vCritic: layout.metricsstatus1,
- vMMedium: layout.metricsstatus2,
- CustomArray: new Array(),
- CustomArrayBasic: new Array(),
- vNumCustomHeaders: 0,
- vColumnText: '',
- vColumnNum: '',
- vMaskNum: 0,
- StyleTags: '',
- vColorSchema: colors[vDynamicColorBody],
- vColorSchemaP: colors[vDynamicColorBodyP],
- vAllSemaphores: layout.allsemaphores,
- ConceptsAffectedMatrix: new Array(10)
- };
- if (vAllSemaphores == false) {
- props.ConceptsAffectedMatrix[0] = layout.conceptsemaphore1;
- props.ConceptsAffectedMatrix[1] = layout.conceptsemaphore2;
- props.ConceptsAffectedMatrix[2] = layout.conceptsemaphore3;
- props.ConceptsAffectedMatrix[3] = layout.conceptsemaphore4;
- props.ConceptsAffectedMatrix[4] = layout.conceptsemaphore5;
- props.ConceptsAffectedMatrix[5] = layout.conceptsemaphore6;
- props.ConceptsAffectedMatrix[6] = layout.conceptsemaphore7;
- props.ConceptsAffectedMatrix[7] = layout.conceptsemaphore8;
- props.ConceptsAffectedMatrix[8] = layout.conceptsemaphore9;
- props.ConceptsAffectedMatrix[9] = layout.conceptsemaphore10;
- }
-
- function ReadCustomSchema () {
- var Url = '/Extensions/qlik-smart-pivot/' + props.vCustomFile;
- return $.get(Url).then(function (response) {
- var allTextLines = response.split(/\r\n|\n/);
- var headers = allTextLines[0].split(';');
- props.vNumCustomHeaders = headers.length;
-
- for (var i = 0; i < allTextLines.length; i++) {
- props.CustomArray[i] = new Array(headers.length);
- var data = allTextLines[i].split(';');
-
- if (data.length == headers.length) {
- for (var j = 0; j < headers.length; j++) {
- props.CustomArrayBasic[i] = data[0];
- props.CustomArray[i][j] = data[j];
- }
- }
- }
- });
- }
-
- const hasCustomSchema = (props.vCustomFileBool && props.vCustomFile.length > 4);
- const schemaPromise = hasCustomSchema ? ReadCustomSchema() : Promise.resolve();
- await schemaPromise;
-
- return props;
-}
diff --git a/src/single-dimension-measures.jsx b/src/single-dimension-measures.jsx
deleted file mode 100644
index f50eb47..0000000
--- a/src/single-dimension-measures.jsx
+++ /dev/null
@@ -1,159 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import { ApplyPreMask } from './masking';
-import { addSeparators } from './utilities';
-
-class SingleDimensionMeasures extends React.PureComponent {
- render () {
- const {
- vFontFamily,
- vSymbolForNulls,
- vColorMetric1,
- vColorMetric1Text,
- vColorMetric2,
- vColorMetric2Text,
- vColorMetric3,
- vColorMetric3Text,
- ConceptMatrix,
- vAllSemaphores,
- ConceptsAffectedMatrix,
- vAllMetrics,
- MetricsAffectedMatrix,
- vCritic,
- vMMedium,
- vNumMeasures,
- MeasuresFormat,
- rowNumber,
- columnText,
- styleBuilder
- } = this.props;
-
- // modified in here
- let vColumnNum,
- vMaskNum,
- vColorSemaphore,
- vColorSemaphoreText,
- vDivide;
-
- const measurementCells = [];
-
- // TODO: map ConceptMatrix[rowNumber] into cells
- for (var nMeasures2 = 1; nMeasures2 <= vNumMeasures; nMeasures2++) {
- var vSpecialF = MeasuresFormat[nMeasures2 - 1].replace(/k|K|m|M/gi, '');
- if (columnText.substring(0, 1) == '%') {
- vColumnNum = ApplyPreMask('0,00%', ConceptMatrix[rowNumber][nMeasures2]);
- vSpecialF = '0,00%';
- } else {
- const magnitude = MeasuresFormat[nMeasures2 - 1].substr(MeasuresFormat[nMeasures2 - 1].length - 1);
- switch (magnitude.toLowerCase()) {
- case 'k':
- vDivide = 1000;
- break;
-
- case 'm':
- vDivide = 1000000;
- break;
-
- default:
- vDivide = 1;
- break;
- }
- if (!isNaN(ConceptMatrix[rowNumber][nMeasures2])) {
- vMaskNum = ConceptMatrix[rowNumber][nMeasures2];
- if (vSpecialF.substring(vSpecialF.length - 1) == '%') {
- vMaskNum = vMaskNum * 100;
- }
- switch (vSpecialF) {
- case '#.##0':
- vColumnNum = addSeparators((vMaskNum / vDivide), '.', ',', 0);
- break;
-
- case '#,##0':
- vColumnNum = addSeparators((vMaskNum / vDivide), ',', '.', 0);
- break;
-
- default:
- vColumnNum = ApplyPreMask(vSpecialF, (vMaskNum / vDivide));
- break;
- }
- } else {
- vColumnNum = vSymbolForNulls;
- }
- }
- if (styleBuilder.hasComments()) {
- vColumnNum = '.';
- }
- // apply the semaphore styles where needed
- let cellStyle;
- if ((vAllSemaphores || ConceptsAffectedMatrix.indexOf(columnText) >= 0) && (vAllMetrics || MetricsAffectedMatrix.indexOf(nMeasures2) >= 0) && !isNaN(ConceptMatrix[rowNumber][nMeasures2]) && !styleBuilder.hasComments()) {
- if (ConceptMatrix[rowNumber][nMeasures2] < vCritic) {
- vColorSemaphore = vColorMetric1;
- vColorSemaphoreText = vColorMetric1Text;
- } else {
- if (ConceptMatrix[rowNumber][nMeasures2] < vMMedium) {
- vColorSemaphore = vColorMetric2;
- vColorSemaphoreText = vColorMetric2Text;
- } else {
- vColorSemaphore = vColorMetric3;
- vColorSemaphoreText = vColorMetric3Text;
- }
- }
-
- cellStyle = {
- fontFamily: vFontFamily,
- fontSize: styleBuilder.getStyle().fontSize,
- color: vColorSemaphoreText,
- backgroundColor: vColorSemaphore,
- textAlign: 'right',
- paddingLeft: '4px'
- };
- } else {
- cellStyle = {
- fontFamily: vFontFamily,
- textAlign: 'right',
- paddingLeft: '4px',
- ...styleBuilder.getStyle()
- };
- }
-
- const measurementCell = (
-
- {vColumnNum}
- |
- );
-
- measurementCells.push(measurementCell);
- }
-
- return (
-
- {measurementCells}
-
- );
- }
-}
-
-SingleDimensionMeasures.propTypes = {
- vFontFamily: PropTypes.any,
- vSymbolForNulls: PropTypes.any,
- vColorMetric1: PropTypes.any,
- vColorMetric1Text: PropTypes.any,
- vColorMetric2: PropTypes.any,
- vColorMetric2Text: PropTypes.any,
- vColorMetric3: PropTypes.any,
- vColorMetric3Text: PropTypes.any,
- ConceptMatrix: PropTypes.any,
- vAllSemaphores: PropTypes.any,
- ConceptsAffectedMatrix: PropTypes.any,
- vAllMetrics: PropTypes.any,
- MetricsAffectedMatrix: PropTypes.any,
- vCritic: PropTypes.any,
- vMMedium: PropTypes.any,
- vNumMeasures: PropTypes.any,
- MeasuresFormat: PropTypes.any,
- rowNumber: PropTypes.any,
- columnText: PropTypes.any,
- styleBuilder: PropTypes.any
-};
-
-export default SingleDimensionMeasures;
diff --git a/src/store.js b/src/store.js
index 602df3d..7f4b3f3 100644
--- a/src/store.js
+++ b/src/store.js
@@ -1,96 +1,30 @@
import { onlyUnique } from './utilities';
+import initializeTransformed from './initialize-transformed';
-function initialize ({ $element, layout, component }) {
- const colors = {
- vColLibClean: layout.collibclean,
- vColLibSoft: layout.collibsoft,
- vColLibDark: layout.collibdark,
- vColLibNight: layout.collibnight,
- vColLibRed: layout.collibred,
- vColLibOrange: layout.colliborange,
- vColLibBlue: layout.collibblue,
- vColLibGreen: layout.collibgreen,
- vColLibViolete: layout.collibviolete,
- vColLibCustom: layout.collibcustom,
- vColLibCleanP: layout.collibcleanp,
- vColLibSoftP: layout.collibsoftp,
- vColLibDarkP: layout.collibdarkp,
- vColLibNightP: layout.collibnightp,
- vColLibRedP: layout.collibredp,
- vColLibOrangeP: layout.colliborangep,
- vColLibBlueP: layout.collibbluep,
- vColLibGreenP: layout.collibgreenp,
- vColLibVioleteP: layout.collibvioletep,
- vColLibCustomP: layout.collibcustomp
- };
+async function initialize ({ $element, layout, component }) {
+ const transformedProperties = await initializeTransformed({
+ $element,
+ component,
+ layout
+ });
- const nMeasAux = 0;
+ // TODO: remove everything from here to return statement once jquery parts in paint has been refactored
const vMaxLoops = layout.maxloops;
const vErrorMessage = layout.errormessage;
- const vDynamicColorHeader = `vColLib${layout.HeaderColorSchema}`;
- const vHeaderColorSchema = colors[vDynamicColorHeader];
- const vExportToExcel = layout.allowexportxls;
- const vHeaderColorText = layout.HeaderTextColorSchema;
- const vHeaderAlign = layout.HeaderAlign;
- let vHeaderAlignText = '';
- switch (vHeaderAlign) {
- case 1:
- vHeaderAlignText = 'left';
- break;
- case 2:
- vHeaderAlignText = 'center';
- break;
- case 3:
- vHeaderAlignText = 'right';
- break;
- }
- let vLetterSizeHeader = 0;
- switch (layout.lettersizeheader) {
- case 1:
- vLetterSizeHeader = -2;
- break;
- case 2:
- vLetterSizeHeader = 0;
- break;
- case 3:
- vLetterSizeHeader = 2;
- break;
- }
let vDimName = '';
const ConceptMatrixFirst = new Array();
const ConceptMatrixSecond = new Array();
- let SecondHeaderLength = 0;
const LabelsArray = new Array();
- const ExtraLabelsArray = new Array();
- let vExtraLabel = '';
- const vExcelButtonCode = '';
const ArrayGetSelectedCount = new Array();
let vNumDims = 0;
let vNumMeasures = 0;
- let vNumMeasures2 = 0;
const MeasuresFormat = new Array();
- let sufixCells = '';
- switch (layout.columnwidthslider) {
- case 1:
- sufixCells += '-s';
- break;
- case 2:
- sufixCells = String(sufixCells);
- break;
- case 3:
- sufixCells += '-l';
- break;
- default:
- sufixCells = String(sufixCells);
- break;
- }
const dim_count = layout.qHyperCube.qDimensionInfo.length;
const measure_count = layout.qHyperCube.qMeasureInfo.length;
let vSeparatorCols = layout.separatorcols;
if (dim_count == 1) {
vSeparatorCols = false;
}
- const vFontFamily = layout.FontFamily;
let lastrow = 0;
const ConceptMatrix = new Array();
let ConceptMatrixRowElem = new Array();
@@ -98,18 +32,6 @@ function initialize ({ $element, layout, component }) {
const ConceptMatrixColElemTable = new Array();
const ConceptMatrixPivot = new Array();
let ConceptMatrixFirstClean = new Array();
- let vLetterSize = 0;
- switch (layout.lettersize) {
- case 1:
- vLetterSize = -2;
- break;
- case 2:
- vLetterSize = -1;
- break;
- case 3:
- vLetterSize = 2;
- break;
- }
const nRows = component.backendApi.getRowCount();
const dimensionInfos = component.backendApi.getDimensionInfos();
@@ -134,20 +56,6 @@ function initialize ({ $element, layout, component }) {
MeasuresFormat.push(mfor);
- switch (mfor.substr(mfor.length - 1).toLowerCase()) {
- case 'm':
- vExtraLabel = ' (M)';
- ExtraLabelsArray.push(' (M)');
- break;
- case 'k':
- vExtraLabel = ' (k)';
- ExtraLabelsArray.push(' (k)');
- break;
- default:
- vExtraLabel = '';
- ExtraLabelsArray.push('');
- break;
- }
vNumMeasures++;
});
@@ -203,9 +111,6 @@ function initialize ({ $element, layout, component }) {
}
}
- SecondHeaderLength = SecondHeader.length;
- vNumMeasures2 = vNumMeasures * SecondHeaderLength;
-
let ConceptPos = 0;
let nMeas3 = 0;
let vHeaderIndex = 0;
@@ -224,75 +129,15 @@ function initialize ({ $element, layout, component }) {
}
}
- if (nRows > (lastrow + 1) && nRows <= (vMaxLoops * 1000)) {
- const requestPage = [
- {
- qTop: lastrow + 1,
- qLeft: 0,
- qWidth: 10, // should be # of columns
- qHeight: Math.min(1000, nRows - lastrow)
- }
- ];
- component.backendApi.getData(requestPage).then(() => {
- component.paint($element);
- });
- }
-
const properties = {
- ArrayGetSelectedCount,
- ConceptMatrix,
ConceptMatrixColElem,
ConceptMatrixColElemTable,
- ConceptMatrixRowElem,
- ConceptMatrixFirstClean,
- ConceptMatrixPivot,
- vHeaderColorText,
- vFontFamily,
- vHeaderColorSchema,
- vExportToExcel,
- vNumDims,
- nMeasAux,
- dimensionInfos,
- vLetterSizeHeader,
- vHeaderAlignText,
- MeasuresFormat,
- measure_count,
- vExcelButtonCode,
- sufixCells,
- LabelsArray,
- SecondHeader,
- vSeparatorCols,
- nSecond: SecondHeaderLength - 1,
- nSecond2: SecondHeaderLength - 1,
- vLetterSize,
- ExtraLabelsArray,
- dim_count,
- vExtraLabel,
- vNumMeasures,
- vNumMeasures2,
- lastrow,
- measureInfos
- };
-
- // TODO: figure out a reasonable datastructure and use these for component
- const transformedProperties = {
- dimensions: [],
- headers: LabelsArray,
- headerOptions: {
- colorSchema: vHeaderColorSchema,
- textColor: vHeaderColorText
- },
- options: {
- fontFamily: vFontFamily
- }
+ ConceptMatrixRowElem
};
return {
- colors,
- dimensionInfos,
- measureInfos,
properties,
- transformedProperties
+ ...transformedProperties
};
}
diff --git a/src/style-builder.js b/src/style-builder.js
index 391d5d0..72ecefb 100644
--- a/src/style-builder.js
+++ b/src/style-builder.js
@@ -1,16 +1,11 @@
-function StyleBuilder (state) {
+function StyleBuilder (styling) {
const {
- CustomArray,
- CustomArrayBasic,
- vNumCustomHeaders,
- vColorSchema,
- vColorText,
- vColorSchemaP,
- vLetterSize,
- colors
- } = state;
+ colors,
+ customCSV,
+ options
+ } = styling;
let style = {
- fontSize: `${14 + vLetterSize}px`
+ fontSize: `${14 + options.fontSizeAdjustment}px`
};
let hasComments = false;
let commentColor;
@@ -18,9 +13,9 @@ function StyleBuilder (state) {
function applyStandardAttributes (rowNumber) {
const isEven = rowNumber % 2 === 0;
- style.backgroundColor = isEven ? vColorSchema : vColorSchemaP;
- style.color = vColorText;
- style.fontSize = `${14 + vLetterSize}px`;
+ style.backgroundColor = isEven ? options.backgroundColor : options.backgroundColorOdd;
+ style.color = options.color;
+ style.fontSize = `${13 + options.fontSizeAdjustment}px`;
}
function applyColor (color) {
@@ -28,6 +23,7 @@ function StyleBuilder (state) {
commentColor = color;
}
+ /* eslint-disable sort-keys*/
const properties = {
'': () => { hasComments = true; },
// text
@@ -46,17 +42,18 @@ function StyleBuilder (state) {
// font color TODO: this is a color just like the others, but it applies to text instead.. any way to make it less weird?
'': () => { style.color = 'white'; },
// font size
- '': () => { style.fontSize = `${15 + vLetterSize}px`; },
- '': () => { style.fontSize = `${14 + vLetterSize}px`; },
- '': () => { style.fontSize = `${13 + vLetterSize}px`; },
+ '': () => { style.fontSize = `${15 + options.fontSizeAdjustment}px`; },
+ '': () => { style.fontSize = `${14 + options.fontSizeAdjustment}px`; },
+ '': () => { style.fontSize = `${13 + options.fontSizeAdjustment}px`; },
// text alignment
'': () => { style.textAlign = 'center'; }
};
+ /* eslint-enable sort-keys */
// TODO: need to improve this, it has way too many false positives
function isCSSColor (property) {
- const isHexColor = property.substring(0, 1) == '#';
- const isRGBColor = property.substring(0, 3).toUpperCase() == 'RGB';
+ const isHexColor = property.substring(0, 1) === '#';
+ const isRGBColor = property.substring(0, 3).toUpperCase() === 'RGB';
return isHexColor || isRGBColor;
}
@@ -84,27 +81,27 @@ function StyleBuilder (state) {
}
function parseCustomFileStyle (columnText) {
- hasCustomFileStyle = true;
- for (let csvAttribute = 1; csvAttribute < vNumCustomHeaders; csvAttribute += 1) {
+ for (let csvAttribute = 1; csvAttribute < customCSV.count; csvAttribute += 1) {
let customAttribute = '';
- if (CustomArrayBasic.indexOf(columnText) < 0) {
+ if (customCSV.basic.indexOf(columnText) < 0) {
customAttribute = 'none';
} else {
- customAttribute = CustomArray[CustomArrayBasic.indexOf(columnText)][csvAttribute];
+ hasCustomFileStyle = true;
+ customAttribute = customCSV.full[customCSV.basic.indexOf(columnText)][csvAttribute];
}
applyProperty(customAttribute);
}
}
return {
+ applyCustomStyle,
+ applyProperty,
+ applyStandardAttributes,
getCommentColor: () => commentColor,
getStyle: () => style,
+ hasComments: () => hasComments,
hasCustomFileStyle: () => hasCustomFileStyle,
hasFontSize: () => Boolean(style.fontSize),
- hasComments: () => hasComments,
- applyStandardAttributes,
- applyProperty,
- applyCustomStyle,
parseCustomFileStyle
};
}
diff --git a/src/utilities.js b/src/utilities.js
index 0e47a6d..3ad9f54 100644
--- a/src/utilities.js
+++ b/src/utilities.js
@@ -2,6 +2,13 @@ export function onlyUnique (value, index, self) {
return self.indexOf(value) === index;
}
+export function distinctArray (array) {
+ return array
+ .map(entry => JSON.stringify(entry))
+ .filter(onlyUnique)
+ .map(entry => JSON.parse(entry));
+}
+
export function addSeparators (nStr, thousandsSep, decimalSep, numDecimals) {
let x1;
nStr = nStr.toFixed(numDecimals);
@@ -21,3 +28,25 @@ export function Deferred () {
this.reject = reject;
});
}
+
+export function injectSeparators (array, shouldHaveSeparator, suppliedOptions) {
+ const defaultOptions = {
+ atEvery: 1,
+ separator: { isSeparator: true }
+ };
+ const options = {
+ ...defaultOptions,
+ ...suppliedOptions
+ };
+
+ if (!shouldHaveSeparator) {
+ return array;
+ }
+ return array.reduce((result, entry, index) => {
+ result.push(entry);
+ if (index < array.length - 1 && (index + 1) % options.atEvery === 0) {
+ result.push(options.separator);
+ }
+ return result;
+ }, []);
+}