@6thquake/react-material
Version:
React components that implement Google's Material Design.
332 lines (288 loc) • 10.6 kB
JavaScript
import _extends from "@babel/runtime/helpers/extends";
/**
* @ignore - do not document.
*/
import React from 'react';
import PropTypes from 'prop-types';
import { CrossTabulationData } from './CrossTabulationUtilities';
import Table from '..';
import TableBody from '../../TableBody';
import TableCell from '../../TableCell';
import TableHead from '../../TableHead';
import TableRow from '../../TableRow';
import { withStyles } from '../../styles';
const styles = theme => ({
table: {
fontSize: '8pt',
textAlign: 'left',
borderCollapse: 'collapse',
marginTop: '3px',
marginLeft: '3px',
fontFamily: theme.typography.fontFamily
},
th: {
backgroundColor: '#ebf0f8',
border: '1px solid #c8d4e3',
fontSize: '8pt',
padding: '5px !important'
},
td: {
color: '#2a3f5f',
padding: '5px !important',
backgroundColor: '#fff',
border: '1px solid #c8d4e3',
verticalAlign: 'top',
textAlign: 'right'
},
tf: {
backgroundColor: '#ebf0f8',
border: '1px solid #c8d4e3',
fontSize: '8pt',
padding: '5px !important'
}
}); // helper function for setting row/col-span in CrossTabulationRenderer
const spanSize = function (arr, i, j) {
let x;
if (i !== 0) {
let asc;
let end;
let noDraw = true;
for (x = 0, end = j, asc = end >= 0; asc ? x <= end : x >= end; asc ? x++ : x--) {
if (arr[i - 1][x] !== arr[i][x]) {
noDraw = false;
}
}
if (noDraw) {
return -1;
}
}
let len = 0;
while (i + len < arr.length) {
let asc1;
let end1;
let stop = false;
for (x = 0, end1 = j, asc1 = end1 >= 0; asc1 ? x <= end1 : x >= end1; asc1 ? x++ : x--) {
if (arr[i][x] !== arr[i + len][x]) {
stop = true;
}
}
if (stop) {
break;
}
len++;
}
return len;
};
function redColorScaleGenerator(values) {
const min = Math.min(...values);
const max = Math.max(...values);
return x => {
// eslint-disable-next-line no-magic-numbers
const nonRed = 255 - Math.round(255 * (x - min) / (max - min));
return {
backgroundColor: `rgb(255,${nonRed},${nonRed})`
};
};
}
function makeRenderer(opts = {}) {
class TableRenderer extends React.PureComponent {
render() {
const {
classes
} = this.props;
const crossTableData = new CrossTabulationData(this.props);
const colAttrs = crossTableData.props.cols;
const rowAttrs = crossTableData.props.rows;
const rowKeys = crossTableData.getRowKeys();
const colKeys = crossTableData.getColKeys();
const grandTotalAggregator = crossTableData.getAggregator([], []);
let valueCellColors = () => {};
let rowTotalColors = () => {};
let colTotalColors = () => {};
if (opts.heatmapMode) {
const colorScaleGenerator = this.props.tableColorScaleGenerator;
const rowTotalValues = colKeys.map(x => crossTableData.getAggregator([], x).value());
rowTotalColors = colorScaleGenerator(rowTotalValues);
const colTotalValues = rowKeys.map(x => crossTableData.getAggregator(x, []).value());
colTotalColors = colorScaleGenerator(colTotalValues);
if (opts.heatmapMode === 'full') {
const allValues = [];
rowKeys.map(r => colKeys.map(c => allValues.push(crossTableData.getAggregator(r, c).value())));
const colorScale = colorScaleGenerator(allValues);
valueCellColors = (r, c, v) => colorScale(v);
} else if (opts.heatmapMode === 'row') {
const rowColorScales = {};
rowKeys.map(r => {
const rowValues = colKeys.map(x => crossTableData.getAggregator(r, x).value());
rowColorScales[r] = colorScaleGenerator(rowValues);
});
valueCellColors = (r, c, v) => rowColorScales[r](v);
} else if (opts.heatmapMode === 'col') {
const colColorScales = {};
colKeys.map(c => {
const colValues = rowKeys.map(x => crossTableData.getAggregator(x, c).value());
colColorScales[c] = colorScaleGenerator(colValues);
});
valueCellColors = (r, c, v) => colColorScales[c](v);
}
}
const getClickHandler = this.props.tableOptions && this.props.tableOptions.clickCallback ? (value, rowValues, colValues) => {
const filters = {};
for (const i of Object.keys(colAttrs || {})) {
const attr = colAttrs[i];
if (colValues[i] !== null) {
filters[attr] = colValues[i];
}
}
for (const i of Object.keys(rowAttrs || {})) {
const attr = rowAttrs[i];
if (rowValues[i] !== null) {
filters[attr] = rowValues[i];
}
}
return e => this.props.tableOptions.clickCallback(e, value, filters, crossTableData);
} : null;
return React.createElement(Table, {
className: classes.table
}, React.createElement(TableHead, null, colAttrs.map((c, j) => {
return React.createElement(TableRow, {
key: `colAttr${j}`
}, j === 0 && rowAttrs.length !== 0 && React.createElement(TableCell, {
className: classes.th,
colSpan: rowAttrs.length,
rowSpan: colAttrs.length
}), React.createElement(TableCell, {
className: classes.th
}, c), colKeys.map((colKey, i) => {
const x = spanSize(colKeys, i, j);
if (x === -1) {
return null;
}
return React.createElement(TableCell, {
className: classes.th,
key: `colKey${i}`,
colSpan: x,
rowSpan: j === colAttrs.length - 1 && rowAttrs.length !== 0 ? 2 : 1
}, colKey[j]);
}), j === 0 && React.createElement(TableCell, {
className: classes.th,
rowSpan: colAttrs.length + (rowAttrs.length === 0 ? 0 : 1)
}, "Totals"));
}), rowAttrs.length !== 0 && React.createElement(TableRow, null, rowAttrs.map((r, i) => {
return React.createElement(TableCell, {
className: classes.th,
key: `rowAttr${i}`
}, r);
}), React.createElement(TableCell, {
className: classes.th
}, colAttrs.length === 0 ? 'Totals' : null))), React.createElement(TableBody, null, rowKeys.map((rowKey, i) => {
const totalAggregator = crossTableData.getAggregator(rowKey, []);
return React.createElement(TableRow, {
key: `rowKeyRow${i}`
}, rowKey.map((txt, j) => {
const x = spanSize(rowKeys, i, j);
if (x === -1) {
return null;
}
return React.createElement(TableCell, {
key: `rowKeyLabel${i}-${j}`,
className: classes.td,
rowSpan: x,
colSpan: j === rowAttrs.length - 1 && colAttrs.length !== 0 ? 2 : 1
}, txt);
}), colKeys.map((colKey, j) => {
const aggregator = crossTableData.getAggregator(rowKey, colKey);
return React.createElement(TableCell, {
className: classes.td,
key: `rm-ct-td-${i}-${j}`,
onClick: getClickHandler && getClickHandler(aggregator.value(), rowKey, colKey),
style: valueCellColors(rowKey, colKey, aggregator.value())
}, aggregator.format(aggregator.value()));
}), React.createElement(TableCell, {
className: classes.td,
onClick: getClickHandler && getClickHandler(totalAggregator.value(), rowKey, [null]),
style: colTotalColors(totalAggregator.value())
}, totalAggregator.format(totalAggregator.value())));
}), React.createElement(TableRow, null, React.createElement(TableCell, {
className: classes.tf,
colSpan: rowAttrs.length + (colAttrs.length === 0 ? 0 : 1)
}, "Totals"), colKeys.map((colKey, i) => {
const totalAggregator = crossTableData.getAggregator([], colKey);
return React.createElement(TableCell, {
className: classes.tf,
key: `total${i}`,
onClick: getClickHandler && getClickHandler(totalAggregator.value(), [null], colKey),
style: rowTotalColors(totalAggregator.value())
}, totalAggregator.format(totalAggregator.value()));
}), React.createElement(TableCell, {
onClick: getClickHandler && getClickHandler(grandTotalAggregator.value(), [null], [null]),
className: classes.tf
}, grandTotalAggregator.format(grandTotalAggregator.value())))));
}
}
TableRenderer.defaultProps = _extends({}, CrossTabulationData.defaultProps, {
tableColorScaleGenerator: redColorScaleGenerator,
tableOptions: {}
});
process.env.NODE_ENV !== "production" ? TableRenderer.propTypes = _extends({}, CrossTabulationData.defaultProps, {
tableColorScaleGenerator: PropTypes.func,
tableOptions: PropTypes.object
}) : void 0;
return withStyles(styles, {
name: 'AwesomeTableRenderer'
})(TableRenderer);
}
/**
* @ignore - internal component.
*/
class TSVExportRenderer extends React.PureComponent {
render() {
const crossTableData = new CrossTabulationData(this.props);
const rowKeys = crossTableData.getRowKeys();
const colKeys = crossTableData.getColKeys();
if (rowKeys.length === 0) {
rowKeys.push([]);
}
if (colKeys.length === 0) {
colKeys.push([]);
}
const headerRow = crossTableData.props.rows.map(r => r);
if (colKeys.length === 1 && colKeys[0].length === 0) {
headerRow.push(this.props.aggregatorName);
} else {
colKeys.map(c => headerRow.push(c.join('-')));
}
const result = rowKeys.map(r => {
const row = r.map(x => x);
colKeys.map(c => {
const v = crossTableData.getAggregator(r, c).value();
row.push(v || '');
});
return row;
});
result.unshift(headerRow);
return React.createElement("textarea", {
value: result.map(r => r.join('\t')).join('\n'),
style: {
width: window.innerWidth / 2,
height: window.innerHeight / 2
},
readOnly: true
});
}
}
TSVExportRenderer.defaultProps = CrossTabulationData.defaultProps;
process.env.NODE_ENV !== "production" ? TSVExportRenderer.propTypes = CrossTabulationData.propTypes : void 0;
export default {
Table: makeRenderer(),
'Table Heatmap': makeRenderer({
heatmapMode: 'full'
}),
'Table Col Heatmap': makeRenderer({
heatmapMode: 'col'
}),
'Table Row Heatmap': makeRenderer({
heatmapMode: 'row'
}),
'Exportable TSV': TSVExportRenderer
};