Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
98401db922 | ||
|
|
75771e4815 | ||
|
|
a12205c840 | ||
|
|
ebfee69c7b | ||
|
|
05c25c72cb | ||
|
|
d2a24dd256 | ||
|
|
a6a6ef24b5 | ||
|
|
8374ecce85 | ||
|
|
0135d4fc64 | ||
|
|
47d7f33cb9 | ||
|
|
3ec5d13ae6 | ||
|
|
2133bb44ef | ||
|
|
a518432db4 | ||
|
|
b1a9130663 |
@@ -59,7 +59,10 @@ class DataCell extends React.PureComponent {
|
||||
const isValidConditionalColoringValue = !styleBuilder.hasComments() && !isNaN(measurement.value);
|
||||
const isSpecifiedRow =
|
||||
conditionalColoring.rows.indexOf(measurement.parents.dimension1.header) !== -1;
|
||||
const shouldHaveConditionalColoring = conditionalColoring.colorAllRows || isSpecifiedRow;
|
||||
const isSpecifiedMeasure =
|
||||
conditionalColoring.measures.indexOf(measurement.parents.measurement.index) !== -1;
|
||||
const shouldHaveConditionalColoring = (conditionalColoring.colorAllRows || isSpecifiedRow)
|
||||
&& (conditionalColoring.colorAllMeasures || isSpecifiedMeasure);
|
||||
if (isValidConditionalColoringValue && shouldHaveConditionalColoring) {
|
||||
const { color, textColor } = getConditionalColor(measurement, conditionalColoring);
|
||||
cellStyle.backgroundColor = color.color;
|
||||
|
||||
@@ -61,6 +61,36 @@ const conditionalColoring = {
|
||||
return data.conditionalcoloring.enabled && !data.conditionalcoloring.colorall;
|
||||
}
|
||||
},
|
||||
ColorAllMeasures: {
|
||||
ref: 'conditionalcoloring.colorallmeasures',
|
||||
type: 'boolean',
|
||||
label: 'Color all measures',
|
||||
component: 'switch',
|
||||
defaultValue: true,
|
||||
options: [
|
||||
{
|
||||
value: true,
|
||||
label: 'All measures'
|
||||
},
|
||||
{
|
||||
value: false,
|
||||
label: 'Specified measures'
|
||||
}
|
||||
],
|
||||
show (data) {
|
||||
return data.conditionalcoloring.enabled;
|
||||
}
|
||||
},
|
||||
Measures: {
|
||||
ref: 'conditionalcoloring.measures',
|
||||
translation: 'Measure indices (ex: 0,3)',
|
||||
type: 'string',
|
||||
defaultValue: '',
|
||||
show (data) {
|
||||
return data.conditionalcoloring.enabled
|
||||
&& data.conditionalcoloring.colorallmeasures === false;
|
||||
}
|
||||
},
|
||||
ThresholdPoor: {
|
||||
ref: 'conditionalcoloring.threshold_poor',
|
||||
translation: 'Poor is less than',
|
||||
|
||||
@@ -22,7 +22,7 @@ class ColumnHeader extends React.PureComponent {
|
||||
const style = {
|
||||
...baseCSS,
|
||||
fontSize: `${14 + styling.headerOptions.fontSizeAdjustment}px`,
|
||||
height: isMediumFontSize ? '45px' : '35px',
|
||||
height: isMediumFontSize ? '43px' : '33px',
|
||||
verticalAlign: 'middle'
|
||||
};
|
||||
|
||||
|
||||
@@ -77,7 +77,7 @@ const HeadersTable = ({ data, general, qlik, styling, isKpi }) => {
|
||||
);
|
||||
})}
|
||||
</tr>
|
||||
{hasSecondDimension && (
|
||||
{!isKpi && hasSecondDimension && (
|
||||
<tr>
|
||||
{injectSeparators(dimension2, styling.useSeparatorColumns).map((dimensionEntry, index) => {
|
||||
if (dimensionEntry.isSeparator) {
|
||||
|
||||
@@ -18,7 +18,6 @@ const MeasurementColumnHeader = ({ baseCSS, general, hasSecondDimension, measure
|
||||
}
|
||||
const cellStyle = {
|
||||
...baseCSS,
|
||||
cursor: 'default',
|
||||
fontSize: `${baseFontSize + fontSizeAdjustment}px`,
|
||||
height: isMediumFontSize ? '45px' : '35px',
|
||||
verticalAlign: 'middle'
|
||||
@@ -40,7 +39,6 @@ const MeasurementColumnHeader = ({ baseCSS, general, hasSecondDimension, measure
|
||||
|
||||
const style = {
|
||||
...baseCSS,
|
||||
cursor: 'default',
|
||||
fontSize: `${15 + fontSizeAdjustment}px`,
|
||||
height: isMediumFontSize ? '90px' : '70px',
|
||||
verticalAlign: 'middle'
|
||||
|
||||
@@ -67,7 +67,7 @@ function generateMeasurements (information) {
|
||||
|
||||
function generateDimensionEntry (information, data) {
|
||||
return {
|
||||
displayValue: data.qText,
|
||||
displayValue: data.qText || data.qNum,
|
||||
elementNumber: data.qElemNumber,
|
||||
name: information.qFallbackTitle,
|
||||
value: data.qNum
|
||||
@@ -90,7 +90,8 @@ function generateMatrixCell ({ cell, dimension1Information, dimension2Informatio
|
||||
header: dimension1Information.qText
|
||||
},
|
||||
measurement: {
|
||||
header: measurementInformation.name
|
||||
header: measurementInformation.name,
|
||||
index: measurementInformation.index
|
||||
}
|
||||
},
|
||||
value: cell.qNum
|
||||
@@ -130,6 +131,7 @@ function generateDataSet (
|
||||
.slice(firstDataCell, row.length)
|
||||
.map((cell, cellIndex) => {
|
||||
const measurementInformation = measurements[cellIndex];
|
||||
measurementInformation.index = cellIndex;
|
||||
const dimension1Information = row[0]; // eslint-disable-line prefer-destructuring
|
||||
const dimension2Information = hasSecondDimension ? row[1] : null;
|
||||
const generatedCell = generateMatrixCell({
|
||||
@@ -161,33 +163,24 @@ function generateDataSet (
|
||||
|
||||
// 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) {
|
||||
if ((hasSecondDimension && row.length == (dimension2.length * measurements.length))
|
||||
|| (!hasSecondDimension && row.length == measurements.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 }
|
||||
}
|
||||
});
|
||||
}
|
||||
if (hasSecondDimension) {
|
||||
// Got a second dimension, so need to add measurements for all values of the second dimension
|
||||
let rowDataIndex = 0;
|
||||
dimension2.forEach(dim => {
|
||||
rowDataIndex = appendMissingCells(
|
||||
row, newRow, rowDataIndex, measurements, rowIndex, dim.elementNumber);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
appendMissingCells(row, newRow, 0, measurements, rowIndex);
|
||||
}
|
||||
|
||||
return newRow;
|
||||
});
|
||||
@@ -200,6 +193,42 @@ function generateDataSet (
|
||||
};
|
||||
}
|
||||
|
||||
/*
|
||||
* Appends the cells of the source row, as well as those missing, to the destination row, starting
|
||||
* from the given source index. Returns the source index of the next source cell after this has
|
||||
* completed. If there is a second dimension the dim2ElementNumber should be set to the current
|
||||
* index of the dimension2 value being processed.
|
||||
*/
|
||||
function appendMissingCells (
|
||||
sourceRow, destRow, sourceIndex, measurements, dim1ElementNumber, dim2ElementNumber = -1) {
|
||||
|
||||
let index = sourceIndex;
|
||||
measurements.forEach((measurement, measureIndex) => {
|
||||
if (index < sourceRow.length
|
||||
&& (dim2ElementNumber === -1
|
||||
|| sourceRow[index].parents.dimension2.elementNumber === dim2ElementNumber)
|
||||
&& sourceRow[index].parents.measurement.header === measurement.name) {
|
||||
// Source contains the expected cell
|
||||
destRow.push(sourceRow[index]);
|
||||
index++;
|
||||
} else {
|
||||
// Source doesn't contain the expected cell, so add empty
|
||||
destRow.push({
|
||||
displayValue: '',
|
||||
parents: {
|
||||
dimension1: { elementNumber: dim1ElementNumber },
|
||||
dimension2: { elementNumber: dim2ElementNumber },
|
||||
measurement: {
|
||||
header: measurement.name,
|
||||
index: measureIndex
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
return index;
|
||||
}
|
||||
|
||||
function initializeTransformed ({ $element, component, dataCube, designList, layout }) {
|
||||
const dimensionsInformation = component.backendApi.getDimensionInfos();
|
||||
const measurementsInformation = component.backendApi.getMeasureInfos();
|
||||
@@ -285,6 +314,10 @@ function initializeTransformed ({ $element, component, dataCube, designList, lay
|
||||
enabled: layout.conditionalcoloring.enabled,
|
||||
colorAllRows: layout.conditionalcoloring.colorall,
|
||||
rows: layout.conditionalcoloring.rows.map(row => row.rowname),
|
||||
colorAllMeasures: typeof layout.conditionalcoloring.colorallmeasures === 'undefined'
|
||||
|| layout.conditionalcoloring.colorallmeasures,
|
||||
measures: !layout.conditionalcoloring.measures
|
||||
? [] : layout.conditionalcoloring.measures.split(',').map(index => Number(index)),
|
||||
threshold: {
|
||||
poor: layout.conditionalcoloring.threshold_poor,
|
||||
fair: layout.conditionalcoloring.threshold_fair
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
/* eslint-disable */
|
||||
.qv-object-qlik-smart-pivot {
|
||||
@TableBorder: 1px solid #d3d3d3;
|
||||
@KpiTableWidth: 230px;
|
||||
|
||||
*,
|
||||
@@ -33,18 +32,13 @@
|
||||
}
|
||||
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
border-spacing: 0;
|
||||
border-collapse: separate;
|
||||
border-spacing: 1px;
|
||||
width: auto;
|
||||
border-left: @TableBorder;
|
||||
border-right: @TableBorder;
|
||||
border-top: @TableBorder;
|
||||
}
|
||||
|
||||
td,
|
||||
th {
|
||||
border: 1px solid #fff;
|
||||
border-collapse: collapse;
|
||||
padding: 5px !important; // prevent overwriting from single object
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
@@ -70,7 +64,6 @@
|
||||
th.main-kpi {
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
border-bottom: @TableBorder;
|
||||
}
|
||||
|
||||
.numeric {
|
||||
@@ -146,12 +139,7 @@
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.fdim-cells:hover {
|
||||
background-color: #808080 !important;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
tbody tr:hover {
|
||||
tbody tr:hover td {
|
||||
cursor: default;
|
||||
background-color: #808080 !important;
|
||||
color: #fff;
|
||||
@@ -178,29 +166,50 @@
|
||||
border: groove;
|
||||
}
|
||||
|
||||
.root {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: nowrap;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.kpi-table .fdim-cells,
|
||||
.data-table td {
|
||||
line-height: 1em !important;
|
||||
}
|
||||
|
||||
.kpi-table {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-wrap: nowrap;
|
||||
flex: none;
|
||||
width: @KpiTableWidth !important;
|
||||
overflow: hidden !important;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
border-right: 1px solid #fff;
|
||||
box-shadow: 4px 2px 8px #e1e1e1;
|
||||
|
||||
.header-wrapper {
|
||||
flex: none;
|
||||
box-shadow: 4px 2px 8px #e1e1e1;
|
||||
}
|
||||
|
||||
.row-wrapper {
|
||||
height: calc(~"100% - 92px");
|
||||
overflow: scroll;
|
||||
position: absolute;
|
||||
margin: 0;
|
||||
margin-bottom: 8px;
|
||||
padding: 0;
|
||||
margin-top: 0;
|
||||
box-shadow: 4px 2px 8px #e1e1e1;
|
||||
min-height: 0; /* This is to make flex size-filling work */
|
||||
|
||||
/* Adapt for Edge */
|
||||
@supports (-ms-ime-align: auto) {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
/* Adapt for IE11 */
|
||||
@media screen and (-ms-high-contrast: none) {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -209,27 +218,50 @@
|
||||
}
|
||||
|
||||
.data-table {
|
||||
height: 100%;
|
||||
width: calc(100% - 243px);
|
||||
position: absolute;
|
||||
margin-left: @KpiTableWidth + 13px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-wrap: nowrap;
|
||||
margin-left: 13px;
|
||||
min-width: 0; /* This is to make flex size-filling work */
|
||||
|
||||
.header-wrapper {
|
||||
flex: none;
|
||||
overflow: scroll;
|
||||
width: 100%;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.row-wrapper {
|
||||
height: calc(~"100% - 92px");
|
||||
width: 100%;
|
||||
overflow: scroll;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
margin-top: 0;
|
||||
min-height: 0; /* This is to make flex size-filling work */
|
||||
|
||||
/* Style scrollbar for FF */
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: #d3d3d3 transparent;
|
||||
}
|
||||
|
||||
/* Adapt for Edge */
|
||||
@supports (-ms-ime-align: auto) {
|
||||
.header-wrapper {
|
||||
margin-right: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
/* Adapt for IE11 */
|
||||
@media screen and (-ms-high-contrast: none) {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
.header-wrapper {
|
||||
margin-right: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// hide scrollbars
|
||||
.kpi-table .header-wrapper,
|
||||
.kpi-table .row-wrapper,
|
||||
.data-table .header-wrapper {
|
||||
// stylelint-disable-next-line property-no-unknown
|
||||
scrollbar-width: none;
|
||||
|
||||
74
src/root.jsx
74
src/root.jsx
@@ -5,48 +5,50 @@ import DataTable from './data-table/index.jsx';
|
||||
import { LinkedScrollWrapper, LinkedScrollSection } from './linked-scroll';
|
||||
|
||||
const Root = ({ state, qlik, editmodeClass }) => (
|
||||
<LinkedScrollWrapper>
|
||||
<div className={`kpi-table ${editmodeClass}`}>
|
||||
<HeadersTable
|
||||
data={state.data}
|
||||
general={state.general}
|
||||
isKpi
|
||||
qlik={qlik}
|
||||
styling={state.styling}
|
||||
/>
|
||||
<LinkedScrollSection linkVertical>
|
||||
<DataTable
|
||||
data={state.data}
|
||||
general={state.general}
|
||||
qlik={qlik}
|
||||
renderData={false}
|
||||
styling={state.styling}
|
||||
/>
|
||||
</LinkedScrollSection>
|
||||
</div>
|
||||
<div className={`data-table ${editmodeClass}`}>
|
||||
<LinkedScrollSection linkHorizontal>
|
||||
<div className="root">
|
||||
<LinkedScrollWrapper>
|
||||
<div className={`kpi-table ${editmodeClass}`}>
|
||||
<HeadersTable
|
||||
data={state.data}
|
||||
general={state.general}
|
||||
isKpi={false}
|
||||
isKpi
|
||||
qlik={qlik}
|
||||
styling={state.styling}
|
||||
/>
|
||||
</LinkedScrollSection>
|
||||
<LinkedScrollSection
|
||||
linkHorizontal
|
||||
linkVertical
|
||||
>
|
||||
<DataTable
|
||||
data={state.data}
|
||||
general={state.general}
|
||||
qlik={qlik}
|
||||
styling={state.styling}
|
||||
/>
|
||||
</LinkedScrollSection>
|
||||
</div>
|
||||
</LinkedScrollWrapper>
|
||||
<LinkedScrollSection linkVertical>
|
||||
<DataTable
|
||||
data={state.data}
|
||||
general={state.general}
|
||||
qlik={qlik}
|
||||
renderData={false}
|
||||
styling={state.styling}
|
||||
/>
|
||||
</LinkedScrollSection>
|
||||
</div>
|
||||
<div className={`data-table ${editmodeClass}`}>
|
||||
<LinkedScrollSection linkHorizontal>
|
||||
<HeadersTable
|
||||
data={state.data}
|
||||
general={state.general}
|
||||
isKpi={false}
|
||||
qlik={qlik}
|
||||
styling={state.styling}
|
||||
/>
|
||||
</LinkedScrollSection>
|
||||
<LinkedScrollSection
|
||||
linkHorizontal
|
||||
linkVertical
|
||||
>
|
||||
<DataTable
|
||||
data={state.data}
|
||||
general={state.general}
|
||||
qlik={qlik}
|
||||
styling={state.styling}
|
||||
/>
|
||||
</LinkedScrollSection>
|
||||
</div>
|
||||
</LinkedScrollWrapper>
|
||||
</div>
|
||||
);
|
||||
|
||||
Root.propTypes = {
|
||||
|
||||
@@ -84,7 +84,7 @@ module.exports = {
|
||||
'indentation': 2,
|
||||
'length-zero-no-unit': true,
|
||||
'max-empty-lines': 1,
|
||||
'max-nesting-depth': 3,
|
||||
'max-nesting-depth': 5,
|
||||
'media-feature-colon-space-after': 'always',
|
||||
'media-feature-colon-space-before': 'never',
|
||||
'media-feature-name-case': 'lower',
|
||||
|
||||
Reference in New Issue
Block a user