-According to the documentation it should only be possible to set two dimensions. Previously you were able to set three, where the third one was used for styling. This wasn't obvious to the user though. Now, the design field is set under Appearance -> Table format. Issue: DEB-156
345 lines
10 KiB
JavaScript
345 lines
10 KiB
JavaScript
import { distinctArray } from './utilities';
|
|
|
|
export const HEADER_FONT_SIZE = {
|
|
SMALL: -1,
|
|
MEDIUM: 1
|
|
};
|
|
|
|
function getAlignment (option) {
|
|
const alignmentOptions = {
|
|
1: 'left',
|
|
2: 'center',
|
|
3: 'right'
|
|
};
|
|
|
|
return alignmentOptions[option] || 'left';
|
|
}
|
|
|
|
function getFontSizeAdjustment (option) {
|
|
const fontSizeAdjustmentOptions = {
|
|
1: HEADER_FONT_SIZE.SMALL,
|
|
2: HEADER_FONT_SIZE.MEDIUM,
|
|
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,
|
|
elementNumber: data.qElemNumber,
|
|
name: information.qFallbackTitle,
|
|
value: data.qNum
|
|
};
|
|
}
|
|
|
|
function generateMatrixCell ({ cell, dimension1Information, dimension2Information, measurementInformation }) {
|
|
const matrixCell = {
|
|
displayValue: cell.qText,
|
|
format: measurementInformation.format,
|
|
magnitude: measurementInformation.magnitudeLabelSuffix.substring(
|
|
measurementInformation.magnitudeLabelSuffix.length - 2,
|
|
measurementInformation.magnitudeLabelSuffix.length - 1
|
|
),
|
|
magnitudeLabelSuffix: measurementInformation.magnitudeLabelSuffix,
|
|
name: measurementInformation.name,
|
|
parents: {
|
|
dimension1: {
|
|
elementNumber: dimension1Information.qElemNumber,
|
|
header: dimension1Information.qText
|
|
},
|
|
measurement: {
|
|
header: measurementInformation.name
|
|
}
|
|
},
|
|
value: cell.qNum
|
|
};
|
|
|
|
if (dimension2Information) {
|
|
matrixCell.parents.dimension2 = {
|
|
elementNumber: dimension2Information.qElemNumber
|
|
};
|
|
}
|
|
|
|
return matrixCell;
|
|
}
|
|
|
|
let lastRow = 0;
|
|
function generateDataSet (
|
|
component, dimensionsInformation, measurementsInformation, dataCube) {
|
|
|
|
const measurements = generateMeasurements(measurementsInformation);
|
|
let dimension1 = [];
|
|
let dimension2 = [];
|
|
let matrix = [];
|
|
|
|
const hasSecondDimension = dimensionsInformation.length > 1;
|
|
dataCube.forEach(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;
|
|
}
|
|
let matrixRow = row
|
|
.slice(firstDataCell, row.length)
|
|
.map((cell, cellIndex) => {
|
|
const measurementInformation = measurements[cellIndex];
|
|
const dimension1Information = row[0]; // eslint-disable-line prefer-destructuring
|
|
const dimension2Information = hasSecondDimension ? row[1] : null;
|
|
const generatedCell = generateMatrixCell({
|
|
cell,
|
|
dimension1Information,
|
|
dimension2Information,
|
|
measurementInformation
|
|
});
|
|
|
|
return generatedCell;
|
|
});
|
|
|
|
let appendToRowIndex = matrix.length;
|
|
if (hasSecondDimension) {
|
|
// See if there already is a row for the current dim1
|
|
for (let i = 0; i < matrix.length; i++) {
|
|
if (matrix[i][0].parents.dimension1.header === matrixRow[0].parents.dimension1.header) {
|
|
appendToRowIndex = i;
|
|
matrixRow = matrix[i].concat(matrixRow);
|
|
}
|
|
}
|
|
}
|
|
matrix[appendToRowIndex] = matrixRow;
|
|
});
|
|
|
|
// filter header dimensions to only have distinct values
|
|
dimension1 = distinctArray(dimension1);
|
|
dimension2 = distinctArray(dimension2);
|
|
|
|
// Make sure all rows are saturated, otherwise data risks being displayed in the wrong column
|
|
matrix = matrix.map((row, rowIndex) => {
|
|
if (row.length == dimension2.length) {
|
|
// Row is saturated
|
|
return row;
|
|
}
|
|
|
|
// Row is not saturated, so must add empty cells to fill the gaps
|
|
let newRow = [];
|
|
let cellIndex = 0;
|
|
dimension2.forEach(dim => {
|
|
measurements.forEach(measurement => {
|
|
if (cellIndex < row.length
|
|
&& row[cellIndex].parents.dimension2.elementNumber === dim.elementNumber
|
|
&& row[cellIndex].parents.measurement.header === measurement.name) {
|
|
newRow.push(row[cellIndex]);
|
|
cellIndex++;
|
|
} else {
|
|
newRow.push({
|
|
displayValue: '',
|
|
parents: {
|
|
dimension1: { elementNumber: rowIndex },
|
|
dimension2: { elementNumber: dim.elementNumber },
|
|
measurement: { header: measurement.name }
|
|
}
|
|
});
|
|
}
|
|
});
|
|
});
|
|
|
|
return newRow;
|
|
});
|
|
|
|
return {
|
|
dimension1: dimension1,
|
|
dimension2: dimension2,
|
|
matrix,
|
|
measurements
|
|
};
|
|
}
|
|
|
|
function initializeTransformed ({ $element, component, dataCube, designList, 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, dataCube);
|
|
|
|
const customSchemaBasic = [];
|
|
const customSchemaFull = [];
|
|
let customHeadersCount = 0;
|
|
|
|
if (designList && designList.length > 0) {
|
|
const headers = designList[0].split(';');
|
|
customHeadersCount = headers.length;
|
|
for (let lineNumber = 0; lineNumber < designList.length; lineNumber += 1) {
|
|
customSchemaFull[lineNumber] = new Array(headers.length);
|
|
const data = designList[lineNumber].split(';');
|
|
|
|
if (data.length === headers.length) {
|
|
for (let headerIndex = 0; headerIndex < headers.length; headerIndex += 1) {
|
|
[customSchemaBasic[lineNumber]] = data;
|
|
customSchemaFull[lineNumber][headerIndex] = data[headerIndex];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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,
|
|
allowFilteringByClick: layout.filteroncellclick,
|
|
cellSuffix: getCellSuffix(layout.columnwidthslider), // TOOD: move to matrix cells or is it headers.measurements?
|
|
errorMessage: layout.errormessage,
|
|
footnote: layout.footnote,
|
|
maxLoops,
|
|
subtitle: layout.subtitle,
|
|
title: layout.title
|
|
},
|
|
selection: {
|
|
dimensionSelectionCounts: dimensionsInformation.map(dimensionInfo => dimensionInfo.qStateCounts.qSelected)
|
|
},
|
|
styling: {
|
|
customCSV: {
|
|
basic: customSchemaBasic,
|
|
count: customHeadersCount,
|
|
full: customSchemaFull
|
|
},
|
|
hasCustomFileStyle: Boolean(designList),
|
|
headerOptions: {
|
|
alignment: getAlignment(layout.HeaderAlign),
|
|
colorSchema: layout.HeaderColorSchema.color,
|
|
fontSizeAdjustment: getFontSizeAdjustment(layout.lettersizeheader),
|
|
textColor: layout.HeaderTextColorSchema.color
|
|
},
|
|
options: {
|
|
backgroundColor: layout.rowEvenBGColor,
|
|
backgroundColorOdd: layout.rowOddBGColor,
|
|
color: layout.BodyTextColorSchema,
|
|
fontFamily: layout.FontFamily,
|
|
fontSizeAdjustment: getFontSizeAdjustment(layout.lettersize),
|
|
textAlignment: layout.cellTextAlignment
|
|
},
|
|
semaphoreColors: {
|
|
fieldsToApplyTo: {
|
|
applyToAll: layout.allsemaphores,
|
|
applyToMetric: layout.allmetrics,
|
|
specificFields: [
|
|
layout.conceptsemaphore1,
|
|
layout.conceptsemaphore2,
|
|
layout.conceptsemaphore3,
|
|
layout.conceptsemaphore4,
|
|
layout.conceptsemaphore5,
|
|
layout.conceptsemaphore6,
|
|
layout.conceptsemaphore7,
|
|
layout.conceptsemaphore9,
|
|
layout.conceptsemaphore10
|
|
],
|
|
metricsSpecificFields: layout.metricssemaphore.split(',').map(entry => Number(entry))
|
|
},
|
|
status: {
|
|
critical: layout.metricsstatus1,
|
|
medium: layout.metricsstatus2
|
|
},
|
|
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;
|