refactor row-wrapper according to new data structure

This commit is contained in:
Kristoffer Lind
2019-02-07 10:20:37 +01:00
parent 6233b9dbae
commit b979a579f9
12 changed files with 356 additions and 665 deletions

View File

@@ -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 (
<td
className={`${cellClass}${general.cellSuffix}`}
style={cellStyle}
>
{formattedMeasurementValue}
</td>
);
};
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;

View File

@@ -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 (
<span style={paddingStyle} />
);
}
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;

106
src/data-table/index.jsx Normal file
View File

@@ -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 (
<div className="row-wrapper">
<table>
{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 (
<tr key={dimensionEntry.displayValue}>
<td
className="fdim-cells"
style={rowStyle}
>
<HeaderPadding
styleBuilder={styleBuilder}
styling={styling}
/>
{dimensionEntry.displayValue}
</td>
{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 (
<td
className="empty"
key={`${dimensionEntry.displayValue}-${measurementData.name}-separator`}
style={separatorStyle}
>
*
</td>
);
}
return (
<DataCell
data={data}
general={general}
key={`${dimensionEntry.displayValue}-${measurementData.name}`}
measurement={measurementData}
styleBuilder={styleBuilder}
styling={styling}
/>
);
})}
</tr>
);
})}
</table>
</div>
);
};
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;

View File

@@ -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 = (
<th key={`${nMeasures22}-header`} className="empty" style={seperatorStyle}>
*
</th>
);
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 = (
<td key={nMeasures22} className={'grid-cells-small' + sufixCells} style={cellStyle}>
{columnNumber}
</td>
);
} else {
cellElement = (
<td key={nMeasures22} className={'grid-cells' + sufixCells} style={cellStyle}>
{columnNumber}
</td>
);
}
} else {
const cellStyle = {
fontFamily: vFontFamily,
...styleBuilder.getStyle(),
textAlign: 'right',
paddingRight: '4px'
};
if (vSpecialF.substring(vSpecialF.length - 1) === '%' && vNumMeasures > 1) {
cellElement = (
<td key={nMeasures22} className={'grid-cells-small' + sufixCells} style={cellStyle}>
{columnNumber}
</td>
);
} else {
cellElement = (
<td key={nMeasures22} className={'grid-cells' + sufixCells} style={cellStyle}>
{columnNumber}
</td>
);
}
}
measurementCells.push(cellElement);
}
return (
<React.Fragment>
{measurementCells}
</React.Fragment>
);
}
}
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;

View File

@@ -1,5 +1,5 @@
import '@babel/polyfill';
import paint from './paint';
import paint from './paint.jsx';
import definition from './definition';
import './main.less';

View File

@@ -3,7 +3,6 @@ import { distinctArray } from './utilities';
// TODO: rename colors
function initializeColors ({ layout }) {
console.log(layout);
return {
vColLibBlue: layout.collibblue,
vColLibBlueP: layout.collibbluep,
@@ -142,7 +141,6 @@ function generateDataSet (component, dimensionsInformation, measurementsInformat
});
if (hasSecondDimension) {
// console.log(row[0]);
const currentDim1Entry = row[0].qText;
const isSameDimension1AsPrevious = currentDim1Entry === previousDim1Entry;
if (isSameDimension1AsPrevious) {

View File

@@ -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 = (
<React.Fragment>
<div className="kpi-table">
<HeaderWrapper
dimensionInfos={state.dimensionInfos}
measureInfos={state.measureInfos}
{...state.properties}
<HeadersTable
data={state.data}
general={state.general}
styling={state.styling}
/>
<RowWrapper
colors={state.colors}
layout={layout}
{...state.properties}
{...rowWrapperProps}
<DataTable
data={state.data}
general={state.general}
styling={state.styling}
/>
</div>
<div className="data-table">
<HeaderWrapper
dimensionInfos={state.dimensionInfos}
measureInfos={state.measureInfos}
{...state.properties}
<HeadersTable
data={state.data}
general={state.general}
styling={state.styling}
/>
<RowWrapper
colors={state.colors}
layout={layout}
{...state.properties}
{...rowWrapperProps}
<DataTable
data={state.data}
general={state.general}
styling={state.styling}
/>
</div>
</React.Fragment>
);
// 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');

View File

@@ -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 (
<span style={paddingStyle} />
);
}
return null;
}
render () {
const {
vLetterSize,
vCustomFileBool,
vFontFamily,
tableData,
MeasurementsComponent
} = this.props;
return (
<tbody>
{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 (
<tr key={rowNumber}>
<td
className="fdim-cells"
style={rowStyle}
>
{paddingTextElement}
{rowHeaderText}
</td>
<MeasurementsComponent
columnText={rowHeaderText}
{...this.props}
{...measurementsProps}
/>
</tr>
);
})}
</tbody>
);
}
}
RowList.propTypes = {
tableData: PropTypes.array.isRequired
};
export default RowList;

View File

@@ -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 (
<div className="row-wrapper">
<table>
<RowList
MeasurementsComponent={MeasurementsComponent}
tableData={tableData}
{...props}
/>
</table>
</div>
);
};
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;
}

View File

@@ -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 = (
<td key={nMeasures2} className="grid-cells' + sufixCells + '" style={cellStyle}>
{vColumnNum}
</td>
);
measurementCells.push(measurementCell);
}
return (
<React.Fragment>
{measurementCells}
</React.Fragment>
);
}
}
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;

View File

@@ -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 = {
'<comment>': () => { 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?
'<white>': () => { style.color = 'white'; },
// font size
'<large>': () => { style.fontSize = `${15 + vLetterSize}px`; },
'<medium>': () => { style.fontSize = `${14 + vLetterSize}px`; },
'<small>': () => { style.fontSize = `${13 + vLetterSize}px`; },
'<large>': () => { style.fontSize = `${15 + options.fontSizeAdjustment}px`; },
'<medium>': () => { style.fontSize = `${14 + options.fontSizeAdjustment}px`; },
'<small>': () => { style.fontSize = `${13 + options.fontSizeAdjustment}px`; },
// text alignment
'<center>': () => { 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
};
}

View File

@@ -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;
}, []);
}