Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b3b17e8d0c | ||
|
|
5d45f57e00 | ||
|
|
2f59d97cf3 | ||
|
|
b8d6b0a53e | ||
|
|
6cc82e7b38 | ||
|
|
cc7e3e62ed | ||
|
|
ef7926dd13 | ||
|
|
af307fd24b |
@@ -26,21 +26,26 @@ async function buildDataCube (originCubeDefinition, hasTwoDimensions, app) {
|
||||
}
|
||||
|
||||
export async function initializeDataCube (component, layout) {
|
||||
const app = qlik.currApp(component);
|
||||
|
||||
let properties;
|
||||
if (component.backendApi.isSnapshot) {
|
||||
// Fetch properties of source
|
||||
properties = (await app.getObjectProperties(layout.sourceObjectId)).properties;
|
||||
} else {
|
||||
properties = await component.backendApi.getProperties();
|
||||
return layout.snapshotData.dataCube;
|
||||
}
|
||||
|
||||
return buildDataCube(
|
||||
properties.qHyperCubeDef, layout.qHyperCube.qDimensionInfo.length === 2, app);
|
||||
const app = qlik.currApp(component);
|
||||
const properties = (await component.backendApi.getProperties());
|
||||
|
||||
// If this is a master object, fetch the hyperCubeDef of the original object
|
||||
const hyperCubeDef = properties.qExtendsId
|
||||
? (await app.getObjectProperties(properties.qExtendsId)).properties.qHyperCubeDef
|
||||
: properties.qHyperCubeDef;
|
||||
|
||||
return buildDataCube(hyperCubeDef, layout.qHyperCube.qDimensionInfo.length === 2, app);
|
||||
}
|
||||
|
||||
export function initializeDesignList (component, layout) {
|
||||
if (component.backendApi.isSnapshot) {
|
||||
return layout.snapshotData.designList;
|
||||
}
|
||||
|
||||
if (!layout.stylingfield) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
const conditionalColoring = {
|
||||
type: 'items',
|
||||
label: 'Color by condition',
|
||||
label: 'Color by performance',
|
||||
items: {
|
||||
Enabled: {
|
||||
ref: 'conditionalcoloring.enabled',
|
||||
@@ -22,7 +22,7 @@ const conditionalColoring = {
|
||||
ColorAllRows: {
|
||||
ref: 'conditionalcoloring.colorall',
|
||||
type: 'boolean',
|
||||
label: 'Color all rows by condition',
|
||||
label: 'Color all rows',
|
||||
component: 'switch',
|
||||
defaultValue: true,
|
||||
options: [
|
||||
@@ -83,7 +83,7 @@ const conditionalColoring = {
|
||||
},
|
||||
Measures: {
|
||||
ref: 'conditionalcoloring.measures',
|
||||
translation: 'Measure indices (ex: 0,3)',
|
||||
translation: 'Measures by index (ex: 0,3)',
|
||||
type: 'string',
|
||||
defaultValue: '',
|
||||
show (data) {
|
||||
@@ -93,7 +93,7 @@ const conditionalColoring = {
|
||||
},
|
||||
ThresholdPoor: {
|
||||
ref: 'conditionalcoloring.threshold_poor',
|
||||
translation: 'Poor is less than',
|
||||
translation: 'Poor range limit',
|
||||
type: 'number',
|
||||
defaultValue: -0.1,
|
||||
show (data) {
|
||||
@@ -102,7 +102,7 @@ const conditionalColoring = {
|
||||
},
|
||||
ColorPoor: {
|
||||
ref: 'conditionalcoloring.color_poor',
|
||||
label: 'Poor color fill',
|
||||
label: 'Poor background color',
|
||||
type: 'object',
|
||||
component: 'color-picker',
|
||||
dualOutput: true,
|
||||
@@ -130,7 +130,7 @@ const conditionalColoring = {
|
||||
},
|
||||
ThresholdFair: {
|
||||
ref: 'conditionalcoloring.threshold_fair',
|
||||
translation: 'Fair is less than',
|
||||
translation: 'Fair range limit',
|
||||
type: 'number',
|
||||
defaultValue: 0,
|
||||
show (data) {
|
||||
@@ -139,7 +139,7 @@ const conditionalColoring = {
|
||||
},
|
||||
ColorFair: {
|
||||
ref: 'conditionalcoloring.color_fair',
|
||||
label: 'Fair color fill',
|
||||
label: 'Fair background color',
|
||||
type: 'object',
|
||||
component: 'color-picker',
|
||||
dualOutput: true,
|
||||
@@ -167,7 +167,7 @@ const conditionalColoring = {
|
||||
},
|
||||
ColorGood: {
|
||||
ref: 'conditionalcoloring.color_good',
|
||||
label: 'Good color fill',
|
||||
label: 'Good background color',
|
||||
type: 'object',
|
||||
component: 'color-picker',
|
||||
dualOutput: true,
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
const header = {
|
||||
type: 'items',
|
||||
label: 'Header Format',
|
||||
label: 'Header format',
|
||||
items: {
|
||||
Align: {
|
||||
ref: 'HeaderAlign',
|
||||
translation: 'Header Alignment',
|
||||
translation: 'Header alignment',
|
||||
type: 'number',
|
||||
component: 'buttongroup',
|
||||
options: [
|
||||
@@ -29,14 +29,14 @@ const header = {
|
||||
index: 6,
|
||||
color: '#4477aa'
|
||||
},
|
||||
label: 'Background Header Color',
|
||||
label: 'Background color',
|
||||
ref: 'HeaderColorSchema',
|
||||
type: 'object',
|
||||
dualOutput: true
|
||||
},
|
||||
HeaderTextColor: {
|
||||
ref: 'HeaderTextColorSchema',
|
||||
label: 'Text Header Color',
|
||||
label: 'Text color',
|
||||
component: 'color-picker',
|
||||
defaultValue: {
|
||||
index: 1,
|
||||
@@ -47,7 +47,7 @@ const header = {
|
||||
},
|
||||
HeaderFontSize: {
|
||||
ref: 'lettersizeheader',
|
||||
translation: 'Font Size',
|
||||
translation: 'Font size',
|
||||
type: 'number',
|
||||
component: 'buttongroup',
|
||||
options: [
|
||||
|
||||
@@ -6,7 +6,7 @@ const pagination = {
|
||||
ref: 'maxloops',
|
||||
type: 'number',
|
||||
component: 'dropdown',
|
||||
label: 'Max Pagination Loops',
|
||||
label: 'Max pagination loops',
|
||||
options: [
|
||||
{
|
||||
value: 1,
|
||||
|
||||
@@ -29,14 +29,14 @@ function getFieldList () {
|
||||
|
||||
const tableFormat = {
|
||||
type: 'items',
|
||||
label: 'Table Format',
|
||||
label: 'Table format',
|
||||
items: {
|
||||
StylingField: {
|
||||
ref: 'stylingfield',
|
||||
disabledRef: '',
|
||||
type: 'string',
|
||||
component: 'dropdown',
|
||||
label: 'Style with field',
|
||||
label: 'Style template field',
|
||||
options: function () {
|
||||
return getFieldList().then(function (items) {
|
||||
items.unshift(
|
||||
@@ -58,7 +58,7 @@ const tableFormat = {
|
||||
SeparatorColumns: {
|
||||
ref: 'separatorcols',
|
||||
type: 'boolean',
|
||||
label: 'Separator Columns',
|
||||
label: 'Column separators',
|
||||
defaultValue: false
|
||||
},
|
||||
rowEvenBGColor: {
|
||||
@@ -89,7 +89,7 @@ const tableFormat = {
|
||||
ref: 'BodyTextColorSchema',
|
||||
type: 'string',
|
||||
component: 'dropdown',
|
||||
label: 'Text Body Color',
|
||||
label: 'Text body color',
|
||||
options: [
|
||||
{
|
||||
value: 'Black',
|
||||
@@ -139,7 +139,7 @@ const tableFormat = {
|
||||
ref: 'FontFamily',
|
||||
type: 'string',
|
||||
component: 'dropdown',
|
||||
label: 'FontFamily',
|
||||
label: 'Font family',
|
||||
options: [
|
||||
{
|
||||
value: 'QlikView Sans, -apple-system, sans-serif',
|
||||
@@ -174,7 +174,7 @@ const tableFormat = {
|
||||
},
|
||||
DataFontSize: {
|
||||
ref: 'lettersize',
|
||||
translation: 'Font Size',
|
||||
translation: 'Font size',
|
||||
type: 'number',
|
||||
component: 'buttongroup',
|
||||
options: [
|
||||
@@ -191,7 +191,7 @@ const tableFormat = {
|
||||
},
|
||||
textAlignment: {
|
||||
ref: 'cellTextAlignment',
|
||||
label: 'Cell Text alignment',
|
||||
label: 'Cell text alignment',
|
||||
component: 'buttongroup',
|
||||
options: [
|
||||
{
|
||||
@@ -212,7 +212,7 @@ const tableFormat = {
|
||||
ColumnWidthSlider: {
|
||||
type: 'number',
|
||||
component: 'slider',
|
||||
label: 'Column Width',
|
||||
label: 'Column width',
|
||||
ref: 'columnwidthslider',
|
||||
min: 1,
|
||||
max: 3,
|
||||
@@ -246,7 +246,7 @@ const tableFormat = {
|
||||
ref: 'filteroncellclick',
|
||||
type: 'boolean',
|
||||
component: 'switch',
|
||||
label: 'Filter data when cell clicked',
|
||||
label: 'Allow selection in cells',
|
||||
options: [
|
||||
{
|
||||
value: true,
|
||||
|
||||
@@ -1,19 +1,32 @@
|
||||
function removeAllTooltips (node) {
|
||||
const tooltips = node.querySelectorAll('.tooltip');
|
||||
[].forEach.call(tooltips, tooltip => {
|
||||
if (tooltip.parentNode) {
|
||||
tooltip.parentNode.removeChild(tooltip);
|
||||
function cleanupNodes (node) {
|
||||
const removables = node.querySelectorAll('.tooltip,input');
|
||||
[].forEach.call(removables, removeable => {
|
||||
if (removeable.parentNode) {
|
||||
removeable.parentNode.removeChild(removeable);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function buildTableHTML (title, subtitle, footnote) {
|
||||
function buildTableHTML (id, title, subtitle, footnote) {
|
||||
const titleHTML = `<p style="font-size:15pt"><b>${title}</b></p>`;
|
||||
const subtitleHTML = `<p style="font-size:11pt">${subtitle}</p>`;
|
||||
const footnoteHTML = `<p style="font-size:11pt"><i>Note:</i>${footnote}</p>`;
|
||||
const dataTableClone = document.querySelector('.data-table').cloneNode(true);
|
||||
const footnoteHTML = `<p style="font-size:11pt">${footnote}</p>`;
|
||||
const container = document.querySelector(`[tid="${id}"]`);
|
||||
const kpiTableClone = container.querySelector('.kpi-table').cloneNode(true);
|
||||
const dataTableClone = container.querySelector('.data-table').cloneNode(true);
|
||||
cleanupNodes(kpiTableClone);
|
||||
cleanupNodes(kpiTableClone);
|
||||
|
||||
removeAllTooltips(dataTableClone);
|
||||
const kpiTableBodies = kpiTableClone.querySelectorAll('tbody');
|
||||
const dataTableBodies = dataTableClone.querySelectorAll('tbody');
|
||||
const kpiHeader = kpiTableBodies[0].querySelector('tr');
|
||||
const dataTableHeaders = dataTableBodies[0].querySelectorAll('tr');
|
||||
const kpiRows = kpiTableBodies[1].querySelectorAll('tr');
|
||||
const dataRows = dataTableBodies[1].querySelectorAll('tr');
|
||||
let combinedRows = '';
|
||||
for (let i = 0; i < kpiRows.length; i++) {
|
||||
combinedRows += `<tr>${kpiRows[i].innerHTML}${dataRows[i].innerHTML}</tr>`;
|
||||
}
|
||||
|
||||
const tableHTML = `
|
||||
<html
|
||||
@@ -41,8 +54,23 @@ function buildTableHTML (title, subtitle, footnote) {
|
||||
<body>
|
||||
${titleHTML.length > 0 ? titleHTML : ''}
|
||||
${subtitleHTML.length > 0 ? subtitleHTML : ''}
|
||||
<div>
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
${kpiHeader.innerHTML}
|
||||
${dataTableHeaders[0].innerHTML}
|
||||
</tr>
|
||||
${dataTableHeaders.length > 1 ? dataTableHeaders[1].outerHTML : ''}
|
||||
</tbody>
|
||||
</table>
|
||||
<table>
|
||||
<tbody>
|
||||
${combinedRows}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
${footnoteHTML.length > 0 ? footnoteHTML : ''}
|
||||
${dataTableClone.outerHTML}
|
||||
</body>
|
||||
</html>
|
||||
`.split('>.<')
|
||||
@@ -55,15 +83,15 @@ function buildTableHTML (title, subtitle, footnote) {
|
||||
|
||||
function downloadXLS (html) {
|
||||
const filename = 'analysis.xls';
|
||||
const blobObject = new Blob([html]);
|
||||
|
||||
// IE/Edge
|
||||
if (window.navigator.msSaveOrOpenBlob) {
|
||||
const blobObject = new Blob([html]);
|
||||
return window.navigator.msSaveOrOpenBlob(blobObject, filename);
|
||||
}
|
||||
|
||||
const dataURI = generateDataURI(html);
|
||||
const link = window.document.createElement('a');
|
||||
link.href = dataURI;
|
||||
link.href = URL.createObjectURL(blobObject);
|
||||
link.download = filename;
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
@@ -72,15 +100,8 @@ function downloadXLS (html) {
|
||||
return true;
|
||||
}
|
||||
|
||||
function generateDataURI (html) {
|
||||
const dataType = 'data:application/vnd.ms-excel;base64,';
|
||||
const data = window.btoa(unescape(encodeURIComponent(html)));
|
||||
|
||||
return `${dataType}${data}`;
|
||||
}
|
||||
|
||||
export function exportXLS (title, subtitle, footnote) {
|
||||
export function exportXLS (id, title, subtitle, footnote) {
|
||||
// original was removing icon when starting export, disable and some spinner instead, shouldn't take enough time to warrant either..?
|
||||
const table = buildTableHTML(title, subtitle, footnote);
|
||||
const table = buildTableHTML(id, title, subtitle, footnote);
|
||||
downloadXLS(table);
|
||||
}
|
||||
|
||||
@@ -9,10 +9,10 @@ class ExportButton extends React.PureComponent {
|
||||
}
|
||||
|
||||
handleExport () {
|
||||
const { excelExport, general } = this.props;
|
||||
const { id, excelExport, general } = this.props;
|
||||
const { title, subtitle, footnote } = general;
|
||||
if (excelExport) {
|
||||
exportXLS(title, subtitle, footnote);
|
||||
exportXLS(id, title, subtitle, footnote);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ ExportButton.defaultProps = {
|
||||
};
|
||||
|
||||
ExportButton.propTypes = {
|
||||
id: PropTypes.string.isRequired,
|
||||
excelExport: PropTypes.bool,
|
||||
general: PropTypes.shape({}).isRequired
|
||||
};
|
||||
|
||||
@@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
|
||||
import ExportButton from '../export-button.jsx';
|
||||
import { HEADER_FONT_SIZE } from '../initialize-transformed';
|
||||
|
||||
const ExportColumnHeader = ({ baseCSS, general, title, allowExcelExport, hasSecondDimension, styling }) => {
|
||||
const ExportColumnHeader = ({ id, baseCSS, general, title, allowExcelExport, hasSecondDimension, styling }) => {
|
||||
const rowSpan = hasSecondDimension ? 2 : 1;
|
||||
const isMediumFontSize = styling.headerOptions.fontSizeAdjustment === HEADER_FONT_SIZE.MEDIUM;
|
||||
const style = {
|
||||
@@ -22,6 +22,7 @@ const ExportColumnHeader = ({ baseCSS, general, title, allowExcelExport, hasSeco
|
||||
style={style}
|
||||
>
|
||||
<ExportButton
|
||||
id={id}
|
||||
excelExport={allowExcelExport}
|
||||
general={general}
|
||||
/>
|
||||
@@ -31,6 +32,7 @@ const ExportColumnHeader = ({ baseCSS, general, title, allowExcelExport, hasSeco
|
||||
};
|
||||
|
||||
ExportColumnHeader.propTypes = {
|
||||
id: PropTypes.string.isRequired,
|
||||
allowExcelExport: PropTypes.bool.isRequired,
|
||||
baseCSS: PropTypes.shape({}).isRequired,
|
||||
general: PropTypes.shape({}).isRequired,
|
||||
|
||||
@@ -28,6 +28,7 @@ const HeadersTable = ({ data, general, qlik, styling, isKpi }) => {
|
||||
<tr>
|
||||
{isKpi ?
|
||||
<ExportColumnHeader
|
||||
id={qlik.options.id}
|
||||
allowExcelExport={general.allowExcelExport}
|
||||
baseCSS={baseCSS}
|
||||
general={general}
|
||||
|
||||
38
src/index.js
38
src/index.js
@@ -1,5 +1,9 @@
|
||||
import paint from './paint.jsx';
|
||||
import definition from './definition';
|
||||
import { initializeDataCube, initializeDesignList } from './dataset';
|
||||
import initializeStore from './store';
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import Root from './root.jsx';
|
||||
import './main.less';
|
||||
|
||||
if (!window._babelPolyfill) { // eslint-disable-line no-underscore-dangle
|
||||
@@ -53,16 +57,34 @@ export default {
|
||||
exportData: true,
|
||||
snapshot: true
|
||||
},
|
||||
paint ($element, layout) {
|
||||
try {
|
||||
paint($element, layout, this);
|
||||
} catch (exception) {
|
||||
console.error(exception); // eslint-disable-line no-console
|
||||
throw exception;
|
||||
}
|
||||
paint: async function ($element, layout) {
|
||||
const dataCube = await initializeDataCube(this, layout);
|
||||
const designList = await initializeDesignList(this, layout);
|
||||
const state = await initializeStore({
|
||||
$element,
|
||||
component: this,
|
||||
dataCube,
|
||||
designList,
|
||||
layout
|
||||
});
|
||||
const editmodeClass = this.inAnalysisState() ? '' : 'edit-mode';
|
||||
const jsx = (
|
||||
<Root
|
||||
editmodeClass={editmodeClass}
|
||||
qlik={this}
|
||||
state={state}
|
||||
/>
|
||||
);
|
||||
|
||||
ReactDOM.render(jsx, $element[0]);
|
||||
},
|
||||
snapshot: {
|
||||
canTakeSnapshot: true
|
||||
},
|
||||
setSnapshotData: async function (snapshotLayout) {
|
||||
snapshotLayout.snapshotData.dataCube = await initializeDataCube(this, snapshotLayout);
|
||||
snapshotLayout.snapshotData.designList = await initializeDesignList(this, snapshotLayout);
|
||||
return snapshotLayout;
|
||||
},
|
||||
version: 1.0
|
||||
};
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
}
|
||||
|
||||
tr {
|
||||
height: 23px;
|
||||
height: 25px;
|
||||
}
|
||||
|
||||
td,
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
import initializeStore from './store';
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import Root from './root.jsx';
|
||||
import { initializeDataCube, initializeDesignList } from './dataset';
|
||||
|
||||
export default async function paint ($element, layout, component) {
|
||||
const dataCube = await initializeDataCube(component, layout);
|
||||
const designList = await initializeDesignList(component, layout);
|
||||
const state = await initializeStore({
|
||||
$element,
|
||||
component,
|
||||
dataCube,
|
||||
designList,
|
||||
layout
|
||||
});
|
||||
const editmodeClass = component.inAnalysisState() ? '' : 'edit-mode';
|
||||
const jsx = (
|
||||
<Root
|
||||
editmodeClass={editmodeClass}
|
||||
qlik={component}
|
||||
state={state}
|
||||
/>
|
||||
);
|
||||
|
||||
ReactDOM.render(jsx, $element[0]);
|
||||
}
|
||||
Reference in New Issue
Block a user