Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2bdd98aaca | ||
|
|
d723451656 | ||
|
|
f2f201c6e2 |
@@ -5,7 +5,7 @@ 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) === '%';
|
||||
const isColumnPercentageBased = measurement.parents.measurement.header.substring(0, 1) === '%';
|
||||
let formattedMeasurementValue = '';
|
||||
if (isColumnPercentageBased) {
|
||||
if (isNaN(measurement.value)) {
|
||||
@@ -62,50 +62,74 @@ function getSemaphoreColors (measurement, semaphoreColors) {
|
||||
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 = '.';
|
||||
class DataCell extends React.PureComponent {
|
||||
constructor (props) {
|
||||
super(props);
|
||||
|
||||
this.handleSelect = this.handleSelect.bind(this);
|
||||
}
|
||||
|
||||
let cellStyle = {
|
||||
fontFamily: styling.options.fontFamily,
|
||||
...styleBuilder.getStyle(),
|
||||
paddingRight: '4px',
|
||||
textAlign: 'right'
|
||||
handleSelect () {
|
||||
const { data: { meta: { dimensionCount } }, general: { allowFilteringByClick }, measurement, qlik } = this.props;
|
||||
const hasSecondDimension = dimensionCount > 1;
|
||||
if (!allowFilteringByClick) {
|
||||
return;
|
||||
}
|
||||
|
||||
};
|
||||
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,
|
||||
qlik.backendApi.selectValues(0, [measurement.parents.dimension1.elementNumber], true);
|
||||
|
||||
if (hasSecondDimension) {
|
||||
qlik.backendApi.selectValues(1, [measurement.parents.dimension2.elementNumber], true);
|
||||
}
|
||||
}
|
||||
|
||||
render () {
|
||||
const { data, general, measurement, styleBuilder, styling } = this.props;
|
||||
const isColumnPercentageBased = measurement.name.substring(0, 1) === '%';
|
||||
let formattedMeasurementValue = formatMeasurementValue(measurement, styling);
|
||||
if (styleBuilder.hasComments()) {
|
||||
formattedMeasurementValue = '.';
|
||||
}
|
||||
|
||||
let cellStyle = {
|
||||
fontFamily: styling.options.fontFamily,
|
||||
fontSize: styleBuilder.getStyle().fontSize,
|
||||
...styleBuilder.getStyle(),
|
||||
paddingLeft: '4px',
|
||||
textAlign: 'right'
|
||||
|
||||
};
|
||||
}
|
||||
const { semaphoreColors } = styling;
|
||||
const isValidSemaphoreValue = !styleBuilder.hasComments() && !isNaN(measurement.value);
|
||||
const shouldHaveSemaphoreColors = semaphoreColors.fieldsToApplyTo.applyToAll || semaphoreColors.fieldsToApplyTo.specificFields.indexOf(measurement.parents.dimension1.header) !== -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';
|
||||
}
|
||||
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>
|
||||
);
|
||||
};
|
||||
return (
|
||||
<td
|
||||
className={`${cellClass}${general.cellSuffix}`}
|
||||
onClick={this.handleSelect}
|
||||
style={cellStyle}
|
||||
>
|
||||
{formattedMeasurementValue}
|
||||
</td>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
DataCell.propTypes = {
|
||||
data: PropTypes.shape({
|
||||
@@ -121,6 +145,11 @@ DataCell.propTypes = {
|
||||
name: PropTypes.string,
|
||||
value: PropTypes.any
|
||||
}).isRequired,
|
||||
qlik: PropTypes.shape({
|
||||
backendApi: PropTypes.shape({
|
||||
selectValues: PropTypes.func.isRequired
|
||||
}).isRequired
|
||||
}).isRequired,
|
||||
styleBuilder: PropTypes.shape({
|
||||
hasComments: PropTypes.func.isRequired
|
||||
}).isRequired,
|
||||
|
||||
@@ -3,9 +3,10 @@ import PropTypes from 'prop-types';
|
||||
import StyleBuilder from '../style-builder';
|
||||
import DataCell from './data-cell.jsx';
|
||||
import HeaderPadding from './header-padding.jsx';
|
||||
import RowHeader from './row-header.jsx';
|
||||
import { injectSeparators } from '../utilities';
|
||||
|
||||
const DataTable = ({ data, general, styling }) => {
|
||||
const DataTable = ({ data, general, qlik, renderData, styling }) => {
|
||||
const {
|
||||
headers: {
|
||||
dimension1,
|
||||
@@ -17,79 +18,85 @@ const DataTable = ({ data, general, styling }) => {
|
||||
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()
|
||||
};
|
||||
<tbody>
|
||||
{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
|
||||
return (
|
||||
<tr key={dimensionEntry.displayValue}>
|
||||
<RowHeader
|
||||
entry={dimensionEntry}
|
||||
qlik={qlik}
|
||||
rowStyle={rowStyle}
|
||||
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`
|
||||
};
|
||||
{renderData && injectSeparators(
|
||||
matrix[dimensionIndex],
|
||||
styling.useSeparatorColumns,
|
||||
{ atEvery: measurements.length }
|
||||
).map((measurementData, index) => {
|
||||
if (measurementData.isSeparator) {
|
||||
const separatorStyle = {
|
||||
color: 'white',
|
||||
fontFamily: styling.options.fontFamily,
|
||||
fontSize: `${12 + styling.options.fontSizeAdjustment}px`
|
||||
};
|
||||
|
||||
return (
|
||||
<td
|
||||
className="empty"
|
||||
key={`${dimensionEntry.displayValue}-${index}-separator`}
|
||||
style={separatorStyle}
|
||||
>
|
||||
*
|
||||
</td>
|
||||
);
|
||||
}
|
||||
const { dimension1: dimension1Info, dimension2, measurement } = measurementData.parents;
|
||||
const id = `${dimension1Info.elementNumber}-${dimension2 && dimension2.elementNumber}-${measurement.header}`;
|
||||
return (
|
||||
<td
|
||||
className="empty"
|
||||
key={`${dimensionEntry.displayValue}-${measurementData.name}-separator`}
|
||||
style={separatorStyle}
|
||||
>
|
||||
*
|
||||
</td>
|
||||
<DataCell
|
||||
data={data}
|
||||
general={general}
|
||||
key={`${dimensionEntry.displayValue}-${id}`}
|
||||
measurement={measurementData}
|
||||
qlik={qlik}
|
||||
styleBuilder={styleBuilder}
|
||||
styling={styling}
|
||||
/>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<DataCell
|
||||
data={data}
|
||||
general={general}
|
||||
key={`${dimensionEntry.displayValue}-${measurementData.name}`}
|
||||
measurement={measurementData}
|
||||
styleBuilder={styleBuilder}
|
||||
styling={styling}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</tr>
|
||||
);
|
||||
})}
|
||||
})}
|
||||
</tr>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
DataTable.defaultProps = {
|
||||
renderData: true
|
||||
};
|
||||
|
||||
DataTable.propTypes = {
|
||||
data: PropTypes.shape({
|
||||
headers: PropTypes.shape({
|
||||
@@ -98,6 +105,8 @@ DataTable.propTypes = {
|
||||
matrix: PropTypes.arrayOf(PropTypes.array.isRequired).isRequired
|
||||
}).isRequired,
|
||||
general: PropTypes.shape({}).isRequired,
|
||||
qlik: PropTypes.shape({}).isRequired,
|
||||
renderData: PropTypes.bool,
|
||||
styling: PropTypes.shape({
|
||||
hasCustomFileStyle: PropTypes.bool.isRequired
|
||||
}).isRequired
|
||||
|
||||
49
src/data-table/row-header.jsx
Normal file
49
src/data-table/row-header.jsx
Normal file
@@ -0,0 +1,49 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import HeaderPadding from './header-padding.jsx';
|
||||
|
||||
class RowHeader extends React.PureComponent {
|
||||
constructor (props) {
|
||||
super(props);
|
||||
|
||||
this.handleSelect = this.handleSelect.bind(this);
|
||||
}
|
||||
|
||||
handleSelect () {
|
||||
const { entry, qlik } = this.props;
|
||||
qlik.backendApi.selectValues(0, [entry.elementNumber], true);
|
||||
}
|
||||
|
||||
render () {
|
||||
const { entry, rowStyle, styleBuilder, styling } = this.props;
|
||||
return (
|
||||
<td
|
||||
className="fdim-cells"
|
||||
onClick={this.handleSelect}
|
||||
style={rowStyle}
|
||||
>
|
||||
<HeaderPadding
|
||||
styleBuilder={styleBuilder}
|
||||
styling={styling}
|
||||
/>
|
||||
{entry.displayValue}
|
||||
</td>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
RowHeader.propTypes = {
|
||||
entry: PropTypes.shape({
|
||||
displayValue: PropTypes.string.isRequired
|
||||
}).isRequired,
|
||||
qlik: PropTypes.shape({
|
||||
backendApi: PropTypes.shape({
|
||||
selectValues: PropTypes.func.isRequired
|
||||
}).isRequired
|
||||
}).isRequired,
|
||||
rowStyle: PropTypes.shape({}).isRequired,
|
||||
styleBuilder: PropTypes.shape({}).isRequired,
|
||||
styling: PropTypes.shape({}).isRequired
|
||||
};
|
||||
|
||||
export default RowHeader;
|
||||
@@ -1,24 +1,39 @@
|
||||
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'
|
||||
};
|
||||
class ColumnHeader extends React.PureComponent {
|
||||
constructor (props) {
|
||||
super(props);
|
||||
|
||||
return (
|
||||
<th
|
||||
className={`grid-cells2${cellSuffix}`}
|
||||
colSpan={colSpan}
|
||||
style={style}
|
||||
>
|
||||
{title}
|
||||
</th>
|
||||
);
|
||||
};
|
||||
this.handleSelect = this.handleSelect.bind(this);
|
||||
}
|
||||
|
||||
handleSelect () {
|
||||
const { entry, qlik } = this.props;
|
||||
qlik.backendApi.selectValues(1, [entry.elementNumber], true);
|
||||
}
|
||||
|
||||
render () {
|
||||
const { baseCSS, cellSuffix, colSpan, entry, styling } = this.props;
|
||||
const style = {
|
||||
...baseCSS,
|
||||
fontSize: `${14 + styling.headerOptions.fontSizeAdjustment} px`,
|
||||
height: '45px',
|
||||
verticalAlign: 'middle'
|
||||
};
|
||||
|
||||
return (
|
||||
<th
|
||||
className={`grid-cells2${cellSuffix}`}
|
||||
colSpan={colSpan}
|
||||
onClick={this.handleSelect}
|
||||
style={style}
|
||||
>
|
||||
{entry.displayValue}
|
||||
</th>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ColumnHeader.defaultProps = {
|
||||
cellSuffix: '',
|
||||
@@ -29,12 +44,20 @@ ColumnHeader.propTypes = {
|
||||
baseCSS: PropTypes.shape({}).isRequired,
|
||||
cellSuffix: PropTypes.string,
|
||||
colSpan: PropTypes.number,
|
||||
entry: PropTypes.shape({
|
||||
elementNumber: PropTypes.number.isRequired,
|
||||
name: PropTypes.string.isRequired
|
||||
}).isRequired,
|
||||
qlik: PropTypes.shape({
|
||||
backendApi: PropTypes.shape({
|
||||
selectValues: PropTypes.func.isRequired
|
||||
}).isRequired
|
||||
}).isRequired,
|
||||
styling: PropTypes.shape({
|
||||
headerOptions: PropTypes.shape({
|
||||
fontSizeAdjustment: PropTypes.number.isRequired
|
||||
}).isRequired
|
||||
}).isRequired,
|
||||
title: PropTypes.string.isRequired
|
||||
}).isRequired
|
||||
};
|
||||
|
||||
export default ColumnHeader;
|
||||
|
||||
@@ -5,7 +5,7 @@ import ColumnHeader from './column-header.jsx';
|
||||
import MeasurementColumnHeader from './measurement-column-header.jsx';
|
||||
import { injectSeparators } from '../utilities';
|
||||
|
||||
const HeadersTable = ({ data, general, styling }) => {
|
||||
const HeadersTable = ({ data, general, qlik, styling }) => {
|
||||
const baseCSS = {
|
||||
backgroundColor: styling.headerOptions.colorSchema,
|
||||
color: styling.headerOptions.textColor,
|
||||
@@ -43,7 +43,7 @@ const HeadersTable = ({ data, general, styling }) => {
|
||||
styling={styling}
|
||||
/>
|
||||
))}
|
||||
{hasSecondDimension && injectSeparators(dimension2, styling.useSeparatorColumns).map(entry => {
|
||||
{hasSecondDimension && injectSeparators(dimension2, styling.useSeparatorColumns).map((entry, index) => {
|
||||
if (entry.isSeparator) {
|
||||
const separatorStyle = {
|
||||
color: 'white',
|
||||
@@ -54,6 +54,7 @@ const HeadersTable = ({ data, general, styling }) => {
|
||||
return (
|
||||
<th
|
||||
className="empty"
|
||||
key={index}
|
||||
style={separatorStyle}
|
||||
>
|
||||
*
|
||||
@@ -65,16 +66,17 @@ const HeadersTable = ({ data, general, styling }) => {
|
||||
baseCSS={baseCSS}
|
||||
cellSuffix={general.cellSuffix}
|
||||
colSpan={measurements.length}
|
||||
entry={entry}
|
||||
key={entry.displayValue}
|
||||
qlik={qlik}
|
||||
styling={styling}
|
||||
title={entry.displayValue}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</tr>
|
||||
{hasSecondDimension && (
|
||||
<tr>
|
||||
{injectSeparators(dimension2, styling.useSeparatorColumns).map(dimensionEntry => {
|
||||
{injectSeparators(dimension2, styling.useSeparatorColumns).map((dimensionEntry, index) => {
|
||||
if (dimensionEntry.isSeparator) {
|
||||
const separatorStyle = {
|
||||
color: 'white',
|
||||
@@ -85,6 +87,7 @@ const HeadersTable = ({ data, general, styling }) => {
|
||||
return (
|
||||
<th
|
||||
className="empty"
|
||||
key={index}
|
||||
style={separatorStyle}
|
||||
>
|
||||
*
|
||||
@@ -120,6 +123,11 @@ HeadersTable.propTypes = {
|
||||
})
|
||||
}).isRequired,
|
||||
general: PropTypes.shape({}).isRequired,
|
||||
qlik: PropTypes.shape({
|
||||
backendApi: PropTypes.shape({
|
||||
selectValues: PropTypes.func.isRequired
|
||||
}).isRequired
|
||||
}).isRequired,
|
||||
styling: PropTypes.shape({
|
||||
headerOptions: PropTypes.shape({}),
|
||||
options: PropTypes.shape({})
|
||||
|
||||
@@ -91,24 +91,41 @@ function generateMeasurements (information) {
|
||||
function generateDimensionEntry (information, data) {
|
||||
return {
|
||||
displayValue: data.qText,
|
||||
elementNumber: data.qElemNumber,
|
||||
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
|
||||
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: information.magnitudeLabelSuffix,
|
||||
name: information.name,
|
||||
value: data.qNum
|
||||
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;
|
||||
@@ -135,7 +152,14 @@ function generateDataSet (component, dimensionsInformation, measurementsInformat
|
||||
.slice(firstDataCell, row.length)
|
||||
.map((cell, cellIndex) => {
|
||||
const measurementInformation = measurements[cellIndex];
|
||||
const generatedCell = generateMatrixCell(measurementInformation, cell);
|
||||
const dimension1Information = row[0]; // eslint-disable-line prefer-destructuring
|
||||
const dimension2Information = hasSecondDimension ? row[1] : null;
|
||||
const generatedCell = generateMatrixCell({
|
||||
cell,
|
||||
dimension1Information,
|
||||
dimension2Information,
|
||||
measurementInformation
|
||||
});
|
||||
|
||||
return generatedCell;
|
||||
});
|
||||
@@ -228,6 +252,7 @@ async function initializeTransformed ({ $element, layout, component }) {
|
||||
},
|
||||
general: {
|
||||
allowExcelExport: layout.allowexportxls,
|
||||
allowFilteringByClick: layout.filteroncellclick,
|
||||
cellSuffix: getCellSuffix(layout.columnwidthslider), // TOOD: move to matrix cells or is it headers.measurements?
|
||||
errorMessage: layout.errormessage,
|
||||
maxLoops
|
||||
|
||||
195
src/paint.jsx
195
src/paint.jsx
@@ -1,9 +1,7 @@
|
||||
import $ from 'jquery';
|
||||
import { enableExcelExport } from './excel-export';
|
||||
import initializeStore from './store';
|
||||
import React from 'react';
|
||||
// import ReactDOM from 'react-dom';
|
||||
import { renderToStaticMarkup } from 'react-dom/server';
|
||||
import ReactDOM from 'react-dom';
|
||||
import HeadersTable from './headers-table/index.jsx';
|
||||
import DataTable from './data-table/index.jsx';
|
||||
|
||||
@@ -14,29 +12,20 @@ export default async function paint ($element, layout, component) {
|
||||
layout
|
||||
});
|
||||
|
||||
const {
|
||||
ConceptMatrixColElem,
|
||||
ConceptMatrixColElemTable,
|
||||
ConceptMatrixRowElem
|
||||
} = state.properties;
|
||||
|
||||
const {
|
||||
data: { meta: { dimensionCount } },
|
||||
selection: { dimensionSelectionCounts },
|
||||
styling: { useSeparatorColumns }
|
||||
} = state;
|
||||
|
||||
const jsx = (
|
||||
<React.Fragment>
|
||||
<div className="kpi-table">
|
||||
<HeadersTable
|
||||
data={state.data}
|
||||
general={state.general}
|
||||
qlik={component}
|
||||
styling={state.styling}
|
||||
/>
|
||||
<DataTable
|
||||
data={state.data}
|
||||
general={state.general}
|
||||
qlik={component}
|
||||
renderData={false}
|
||||
styling={state.styling}
|
||||
/>
|
||||
</div>
|
||||
@@ -44,159 +33,57 @@ export default async function paint ($element, layout, component) {
|
||||
<HeadersTable
|
||||
data={state.data}
|
||||
general={state.general}
|
||||
qlik={component}
|
||||
styling={state.styling}
|
||||
/>
|
||||
<DataTable
|
||||
data={state.data}
|
||||
general={state.general}
|
||||
qlik={component}
|
||||
styling={state.styling}
|
||||
/>
|
||||
</div>
|
||||
</React.Fragment>
|
||||
);
|
||||
|
||||
// TODO: switch to render when jquery interaction stuff in renderData is gone
|
||||
const html = renderToStaticMarkup(jsx);
|
||||
$element.html(html);
|
||||
// ReactDOM.render(jsx, $element[0]);
|
||||
ReactDOM.render(jsx, $element[0]);
|
||||
|
||||
RenderData();
|
||||
|
||||
// TODO: move jquery interactions into their respective components
|
||||
// Hook up interactions and some html mangling
|
||||
function RenderData () {
|
||||
$('.data-table .row-wrapper').on('scroll', function () {
|
||||
$(`[tid="${layout.qInfo.qId}"] .kpi-table .row-wrapper`).scrollTop($(this).scrollTop());
|
||||
// TODO: skipped the following as they weren't blockers for letting react handle rendering,
|
||||
// they are however the only reason we still depend on jQuery and should be removed as part of unnecessary dependencies issue
|
||||
$(`[tid="${layout.qInfo.qId}"] .data-table .row-wrapper`).on('scroll', function () {
|
||||
$(`[tid="${layout.qInfo.qId}"] .kpi-table .row-wrapper`).scrollTop($(this).scrollTop());
|
||||
});
|
||||
|
||||
// freeze first column
|
||||
$(`[tid="${layout.qInfo.qId}"] .qv-object-content-container`).on('scroll', (t) => {
|
||||
$(`[tid="${layout.qInfo.qId}"] .kpi-table`).css('left', `${Math.round(t.target.scrollLeft)}px`);
|
||||
});
|
||||
|
||||
// TODO: fixing tooltips has a seperate issue, make sure to remove this as part of that issue
|
||||
$(`[tid="${layout.qInfo.qId}"] .header-wrapper th`).hover(function () {
|
||||
$(`[tid="${layout.qInfo.qId}"] .tooltip`).delay(500)
|
||||
.show(0);
|
||||
$(`[tid="${layout.qInfo.qId}"] .header-wrapper th`).children(`[tid="${layout.qInfo.qId}"] .tooltip`)
|
||||
.remove();
|
||||
|
||||
const element = $(this);
|
||||
const offset = element.offset();
|
||||
const toolTip = $('<div class="tooltip"></div>');
|
||||
|
||||
toolTip.css({
|
||||
left: offset.left,
|
||||
top: offset.top
|
||||
});
|
||||
|
||||
// on hover popup with cell value, only in headers
|
||||
$('.header-wrapper th').hover(function () {
|
||||
$('.tooltip').delay(500)
|
||||
.show(0);
|
||||
$('.header-wrapper th').children('.tooltip')
|
||||
.remove();
|
||||
toolTip.text(element.text());
|
||||
$(`[tid="${layout.qInfo.qId}"] .header-wrapper th`).append(toolTip);
|
||||
}, () => {
|
||||
$(`[tid="${layout.qInfo.qId}"] .tooltip`).delay(0)
|
||||
.hide(0);
|
||||
});
|
||||
|
||||
const element = $(this);
|
||||
const offset = element.offset();
|
||||
const toolTip = $("<div class='tooltip'></div>");
|
||||
|
||||
toolTip.css({
|
||||
top: offset.top,
|
||||
left: offset.left
|
||||
});
|
||||
|
||||
toolTip.text(element.text());
|
||||
$('.header-wrapper th').append(toolTip);
|
||||
}, () => {
|
||||
$('.tooltip').delay(0)
|
||||
.hide(0);
|
||||
});
|
||||
|
||||
// allow making selections inside the table
|
||||
$('.data-table td').on('click', function () {
|
||||
if (layout.filteroncellclick == false) {
|
||||
return;
|
||||
}
|
||||
const indextr = $(this).parent()
|
||||
.parent()
|
||||
.children()
|
||||
.index($(this).parent()); // identifica la row
|
||||
const indextd = $(this).parent()
|
||||
.children()
|
||||
.index($(this)); // identifica la col
|
||||
|
||||
let SelectRow = 0;
|
||||
let SelectCol = 0;
|
||||
|
||||
SelectRow = ConceptMatrixRowElem[(indextr)];
|
||||
|
||||
// este if verifica primero si hay selecciones hechas en la dimensión, si las hay
|
||||
// las reselecciona para poder borrar antes de poder seleccionar lo que quiero
|
||||
// no es viable pedirle que seleccione a la vez elementos de 2 selecciones, se queda
|
||||
// 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 (dimensionCount > 1 && indextd > 0) {
|
||||
if (dimensionSelectionCounts[1] > 0) {
|
||||
const SelectB = JSON.parse(JSON.stringify(ConceptMatrixColElemTable));
|
||||
component.backendApi.selectValues(1, SelectB, true);
|
||||
$(this).toggleClass('selected');
|
||||
}
|
||||
SelectCol = ConceptMatrixColElemTable[(indextd)];
|
||||
|
||||
component.backendApi.selectValues(1, [SelectCol], true);
|
||||
$(this).toggleClass('selected');
|
||||
}
|
||||
|
||||
if (indextd > 0 && dimensionSelectionCounts[0] > 0) {
|
||||
const SelectA = JSON.parse(JSON.stringify(ConceptMatrixRowElem));
|
||||
component.backendApi.selectValues(0, SelectA, true);
|
||||
$(this).toggleClass('selected');
|
||||
}
|
||||
|
||||
if (indextd > 0) {
|
||||
component.backendApi.selectValues(0, [SelectRow], true);
|
||||
$(this).toggleClass('selected');
|
||||
}
|
||||
});
|
||||
// allow selections through the header of the second dimension
|
||||
$('.header-wrapper th').on('click', function () {
|
||||
const indextd = $(this).parent()
|
||||
.children()
|
||||
.index($(this)); // identifica la col
|
||||
|
||||
let SelectCol = 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 (useSeparatorColumns) {
|
||||
SelectCol = ConceptMatrixColElem[(Math.round(indextd / 2) - 1)];
|
||||
} else {
|
||||
SelectCol = ConceptMatrixColElem[(Math.round(indextd) - 1)];
|
||||
}
|
||||
|
||||
component.backendApi.selectValues(1, [SelectCol], true);
|
||||
$(this).toggleClass('selected');
|
||||
}
|
||||
});
|
||||
// allow selections in desc dimension cells
|
||||
$('.kpi-table td').on('click', function () {
|
||||
const indextr = $(this).parent()
|
||||
.parent()
|
||||
.children()
|
||||
.index($(this).parent()); // identifica la row
|
||||
let SelectRow = 0;
|
||||
SelectRow = ConceptMatrixRowElem[(indextr)];
|
||||
|
||||
if (dimensionSelectionCounts[0] > 0) {
|
||||
const SelectA = JSON.parse(JSON.stringify(ConceptMatrixRowElem));
|
||||
component.backendApi.selectValues(0, SelectA, true);
|
||||
$(this).toggleClass('selected');
|
||||
}
|
||||
|
||||
component.backendApi.selectValues(0, [SelectRow], true);
|
||||
$(this).toggleClass('selected');
|
||||
});
|
||||
|
||||
enableExcelExport(layout, html);
|
||||
|
||||
// freeze first column
|
||||
$('.qv-object-content-container').on('scroll', (t) => {
|
||||
$('.kpi-table').css('left', `${Math.round(t.target.scrollLeft)}px`);
|
||||
});
|
||||
$('.kpi-table .row-wrapper tr').each(function () {
|
||||
$(this).find('th:not(.fdim-cells)')
|
||||
.remove();
|
||||
$(this).find('td:not(.fdim-cells)')
|
||||
.remove();
|
||||
});
|
||||
$('.kpi-table .header-wrapper tr').each(function () {
|
||||
$(this).find('th:not(.fdim-cells)')
|
||||
.remove();
|
||||
});
|
||||
}
|
||||
// TODO: excel export is broken in most browsers, fixing it has an issue of it's own (leaving it disabled for now)
|
||||
// import { enableExcelExport } from './excel-export';
|
||||
// enableExcelExport(layout, html);
|
||||
}
|
||||
|
||||
133
src/store.js
133
src/store.js
@@ -1,4 +1,3 @@
|
||||
import { onlyUnique } from './utilities';
|
||||
import initializeTransformed from './initialize-transformed';
|
||||
|
||||
async function initialize ({ $element, layout, component }) {
|
||||
@@ -8,137 +7,7 @@ async function initialize ({ $element, layout, component }) {
|
||||
layout
|
||||
});
|
||||
|
||||
// TODO: remove everything from here to return statement once jquery parts in paint has been refactored
|
||||
const vMaxLoops = layout.maxloops;
|
||||
const vErrorMessage = layout.errormessage;
|
||||
let vDimName = '';
|
||||
const ConceptMatrixFirst = new Array();
|
||||
const ConceptMatrixSecond = new Array();
|
||||
const LabelsArray = new Array();
|
||||
const ArrayGetSelectedCount = new Array();
|
||||
let vNumDims = 0;
|
||||
let vNumMeasures = 0;
|
||||
const MeasuresFormat = new Array();
|
||||
const dim_count = layout.qHyperCube.qDimensionInfo.length;
|
||||
const measure_count = layout.qHyperCube.qMeasureInfo.length;
|
||||
let vSeparatorCols = layout.separatorcols;
|
||||
if (dim_count == 1) {
|
||||
vSeparatorCols = false;
|
||||
}
|
||||
let lastrow = 0;
|
||||
const ConceptMatrix = new Array();
|
||||
let ConceptMatrixRowElem = new Array();
|
||||
let ConceptMatrixColElem = new Array();
|
||||
const ConceptMatrixColElemTable = new Array();
|
||||
const ConceptMatrixPivot = new Array();
|
||||
let ConceptMatrixFirstClean = new Array();
|
||||
const nRows = component.backendApi.getRowCount();
|
||||
|
||||
const dimensionInfos = component.backendApi.getDimensionInfos();
|
||||
LabelsArray.push(dimensionInfos[0].qFallbackTitle);
|
||||
ArrayGetSelectedCount.concat(dimensionInfos.map(dimensionInfo => dimensionInfo.qStateCounts.qSelected));
|
||||
vNumDims += dimensionInfos.length;
|
||||
|
||||
const measureInfos = component.backendApi.getMeasureInfos();
|
||||
measureInfos.forEach(measureInfo => {
|
||||
vDimName = measureInfo.qFallbackTitle;
|
||||
LabelsArray.push(vDimName);
|
||||
let mfor = '';
|
||||
|
||||
if (measureInfo.qNumFormat.qType == 'U' || measureInfo.qNumFormat.qFmt == '##############') {
|
||||
mfor = '#.##0'; // in case of undefined
|
||||
} else if (measureInfo.qNumFormat.qType == 'R') {
|
||||
mfor = measureInfo.qNumFormat.qFmt;
|
||||
mfor = mfor.replace(/(|)/gi, '');
|
||||
} else {
|
||||
mfor = measureInfo.qNumFormat.qFmt;
|
||||
}
|
||||
|
||||
MeasuresFormat.push(mfor);
|
||||
|
||||
vNumMeasures++;
|
||||
});
|
||||
|
||||
component.backendApi.eachDataRow((t, a) => {
|
||||
lastrow = t;
|
||||
|
||||
const vNumMeasuresPlus = vNumMeasures + 1;
|
||||
|
||||
ConceptMatrix[t] = new Array();
|
||||
ConceptMatrix[t][0] = a[0].qText;
|
||||
|
||||
ConceptMatrixFirst[t] = a[0].qText;
|
||||
ConceptMatrixRowElem[t] = a[0].qElemNumber;
|
||||
let nMeasures = 0;
|
||||
if (vNumDims == 1) {
|
||||
for (nMeasures = 1; nMeasures <= vNumMeasures; nMeasures++) {
|
||||
ConceptMatrix[t][nMeasures] = a[nMeasures].qNum;
|
||||
}
|
||||
} else {
|
||||
ConceptMatrix[t][1] = a[1].qText;
|
||||
ConceptMatrixColElem[t] = a[1].qElemNumber;
|
||||
ConceptMatrixSecond[t] = a[1].qText;
|
||||
// set the hipercube in a plain array without pivoting
|
||||
for (nMeasures = 2; nMeasures <= vNumMeasuresPlus; nMeasures++) {
|
||||
ConceptMatrix[t][nMeasures] = a[nMeasures].qNum;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
ConceptMatrixFirstClean = ConceptMatrixFirst.filter(onlyUnique);
|
||||
|
||||
if (nRows >= (vMaxLoops * 1000)) {
|
||||
alert(vErrorMessage);
|
||||
}
|
||||
|
||||
if (vNumDims == 2) {
|
||||
// new array with unique values for 2nd dim
|
||||
var SecondHeader = ConceptMatrixSecond.filter(onlyUnique);// second dimension concepts
|
||||
ConceptMatrixRowElem = ConceptMatrixRowElem.filter(onlyUnique);// first dimension concepts
|
||||
ConceptMatrixColElem = ConceptMatrixColElem.filter(onlyUnique);// dimension code for further selections
|
||||
const eo = ConceptMatrixColElem.length;
|
||||
let vLoopColsMeasures = 1;
|
||||
ConceptMatrixColElemTable[0] = ConceptMatrixColElem[0];
|
||||
for (let xx = 0; xx < eo; xx++) {
|
||||
if (vSeparatorCols && xx > 0) {
|
||||
ConceptMatrixColElemTable[vLoopColsMeasures] = ConceptMatrixColElem[xx];
|
||||
vLoopColsMeasures++;
|
||||
}
|
||||
|
||||
for (let xxx = 0; xxx < vNumMeasures; xxx++) {
|
||||
ConceptMatrixColElemTable[vLoopColsMeasures] = ConceptMatrixColElem[xx];
|
||||
vLoopColsMeasures++;
|
||||
}
|
||||
}
|
||||
|
||||
let ConceptPos = 0;
|
||||
let nMeas3 = 0;
|
||||
let vHeaderIndex = 0;
|
||||
let MeasurePos = 0;
|
||||
for (let nPivotElems = 0; nPivotElems <= lastrow; nPivotElems++) {
|
||||
ConceptMatrixPivot[nPivotElems] = new Array();
|
||||
ConceptPos = ConceptMatrixFirstClean.indexOf(ConceptMatrix[nPivotElems][0]);
|
||||
ConceptMatrixPivot[ConceptPos][0] = ConceptMatrix[nPivotElems][0];
|
||||
|
||||
for (let nMeas2 = 1; nMeas2 <= measure_count; nMeas2++) {
|
||||
nMeas3 = nMeas2 + 1;
|
||||
vHeaderIndex = (SecondHeader.indexOf(ConceptMatrix[nPivotElems][1]) + 1);
|
||||
MeasurePos = (vHeaderIndex * measure_count) + (nMeas2 - measure_count);
|
||||
ConceptMatrixPivot[ConceptPos][MeasurePos] = ConceptMatrix[nPivotElems][nMeas3];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const properties = {
|
||||
ConceptMatrixColElem,
|
||||
ConceptMatrixColElemTable,
|
||||
ConceptMatrixRowElem
|
||||
};
|
||||
|
||||
return {
|
||||
properties,
|
||||
...transformedProperties
|
||||
};
|
||||
return transformedProperties;
|
||||
}
|
||||
|
||||
export default initialize;
|
||||
|
||||
Reference in New Issue
Block a user