@6thquake/react-material
Version:
React components that implement Google's Material Design.
287 lines (261 loc) • 9.06 kB
JavaScript
import _extends from "@babel/runtime/helpers/extends";
/**
* @ignore - do not document.
*/
import React from 'react';
import ReactDOM from 'react-dom';
import PropTypes from 'prop-types';
import update from 'immutability-helper';
import { CrossTabulationData, sortAs } from './CrossTabulationUtilities';
import CrossTabulation from './CrossTabulation';
import CrossTabulationAttribute from './CrossTabulationAttribute';
import DropZone from './CrossTabulationDropZone';
import { withStyles } from '../../styles';
const styles = theme => ({
table: {
color: '#2a3f5f',
fontFamily: theme.typography.fontFamily,
borderCollapse: 'collapse'
},
renderers: {
border: '1px solid #a2b1c6',
background: '#f2f5fa',
paddingLeft: '5px',
userSelect: 'none'
},
axis: {
border: '1px solid #a2b1c6',
background: '#f2f5fa',
padding: '5px',
minWidth: '20px',
minHeight: '20px',
height: '20px'
},
vaxis: {
verticalAlign: 'top'
},
orders: {
cursor: 'pointer',
width: '15px',
marginLeft: '5px',
display: 'inline-block',
userSelect: 'none',
textDecoration: 'none !important'
}
});
/**
* @ignore - internal component.
*/
var _ref = React.createElement("br", null);
class AbundantCrossTabulationContent extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
unusedOrder: [],
zIndices: {},
maxZIndex: 1000
};
}
componentWillMount() {
this.materializeInput(this.props.data);
}
componentWillUpdate(nextProps) {
this.materializeInput(nextProps.data);
}
materializeInput(nextData) {
if (this.data === nextData) {
return;
}
this.data = nextData;
const attrValues = {};
const materializedInput = [];
let recordsProcessed = 0;
CrossTabulationData.forEachRecord(this.data, this.props.derivedAttributes, record => {
materializedInput.push(record);
for (const attr of Object.keys(record)) {
if (!(attr in attrValues)) {
attrValues[attr] = {};
if (recordsProcessed > 0) {
attrValues[attr].null = recordsProcessed;
}
}
}
for (const attr in attrValues) {
const value = attr in record ? record[attr] : 'null';
if (!(value in attrValues[attr])) {
attrValues[attr][value] = 0;
}
attrValues[attr][value]++;
}
recordsProcessed++;
});
this.materializedInput = materializedInput;
this.attrValues = attrValues;
}
sendPropUpdate(command) {
this.props.onChange(update(this.props, command));
}
propUpdater(key) {
return value => this.sendPropUpdate({
[key]: {
$set: value
}
});
}
setValuesInFilter(attribute, values) {
this.sendPropUpdate({
valueFilter: {
[attribute]: {
$set: values.reduce((r, v) => {
r[v] = true;
return r;
}, {})
}
}
});
}
addValuesToFilter(attribute, values) {
if (attribute in this.props.valueFilter) {
this.sendPropUpdate({
valueFilter: {
[attribute]: values.reduce((r, v) => {
r[v] = {
$set: true
};
return r;
}, {})
}
});
} else {
this.setValuesInFilter(attribute, values);
}
}
removeValuesFromFilter(attribute, values) {
this.sendPropUpdate({
valueFilter: {
[attribute]: {
$unset: values
}
}
});
}
makeDnDCell(items, onChange, classes) {
return React.createElement("td", {
className: classes
}, React.createElement(DropZone, {
items: items,
onDrop: onChange,
attrValuess: this.attrValues,
valueFilters: this.props.valueFilter,
sorters: this.props.sorters,
menuLimit: this.props.menuLimit,
setValuesInFilter: this.setValuesInFilter.bind(this),
addValuesToFilter: this.addValuesToFilter.bind(this),
removeValuesFromFilter: this.removeValuesFromFilter.bind(this),
zIndices: this.state.zIndices,
maxZIndex: this.state.maxZIndex
}));
}
render() {
const {
classes
} = this.props;
const numValsAllowed = this.props.aggregators[this.props.aggregatorName]([])().numInputs || 0;
const rendererName = this.props.rendererName in this.props.renderers ? this.props.rendererName : Object.keys(this.props.renderers)[0];
const rendererCell = React.createElement("td", {
className: classes.renderers
}, React.createElement(CrossTabulationAttribute, {
label: "renderer",
current: rendererName,
values: Object.keys(this.props.renderers),
setValue: this.propUpdater('rendererName')
}));
const sortIcons = {
key_a_to_z: {
rowSymbol: '↕',
colSymbol: '↔',
next: 'value_a_to_z'
},
value_a_to_z: {
rowSymbol: '↓',
colSymbol: '→',
next: 'value_z_to_a'
},
value_z_to_a: {
rowSymbol: '↑',
colSymbol: '←',
next: 'key_a_to_z'
}
};
const aggregatorCell = React.createElement("td", {
className: classes.axis
}, React.createElement(CrossTabulationAttribute, {
label: "aggregators",
current: this.props.aggregatorName,
values: Object.keys(this.props.aggregators),
setValue: this.propUpdater('aggregatorName')
}), React.createElement("a", {
role: "button",
className: classes.orders,
onClick: () => this.propUpdater('rowOrder')(sortIcons[this.props.rowOrder].next)
}, sortIcons[this.props.rowOrder].rowSymbol), React.createElement("a", {
role: "button",
className: classes.orders,
onClick: () => this.propUpdater('colOrder')(sortIcons[this.props.colOrder].next)
}, sortIcons[this.props.colOrder].colSymbol), numValsAllowed > 0 && _ref, new Array(numValsAllowed).fill().map((n, i) => [React.createElement(CrossTabulationAttribute, {
label: "value",
key: i,
current: this.props.vals[i],
values: Object.keys(this.attrValues).filter(e => !this.props.hiddenAttributes.includes(e) && !this.props.hiddenFromAggregators.includes(e)),
setValue: value => this.sendPropUpdate({
vals: {
$splice: [[i, 1, value]]
}
})
}), i + 1 !== numValsAllowed ? React.createElement("br", {
key: `br${i}`
}) : null]));
const unusedAttrs = Object.keys(this.attrValues).filter(e => !this.props.rows.includes(e) && !this.props.cols.includes(e) && !this.props.hiddenAttributes.includes(e) && !this.props.hiddenFromDragDrop.includes(e)).sort(sortAs(this.state.unusedOrder));
const unusedLength = unusedAttrs.reduce((r, e) => r + e.length, 0);
const horizUnused = unusedLength < this.props.unusedOrientationCutoff;
const unusedAttrsCell = this.makeDnDCell(unusedAttrs, order => this.setState({
unusedOrder: order
}), classes.axis);
const colAttrs = this.props.cols.filter(e => !this.props.hiddenAttributes.includes(e) && !this.props.hiddenFromDragDrop.includes(e));
const colAttrsCell = this.makeDnDCell(colAttrs, this.propUpdater('cols').bind(this), classes.axis);
const rowAttrs = this.props.rows.filter(e => !this.props.hiddenAttributes.includes(e) && !this.props.hiddenFromDragDrop.includes(e));
const rowAttrsCell = this.makeDnDCell(rowAttrs, this.propUpdater('rows').bind(this), classes.axis);
const outputCell = React.createElement("td", null, React.createElement(CrossTabulation, update(this.props, {
data: {
$set: this.materializedInput
}
})));
if (horizUnused) {
return React.createElement("table", {
className: classes.table,
width: "100%"
}, React.createElement("tbody", null, React.createElement("tr", null, rendererCell, unusedAttrsCell), React.createElement("tr", null, aggregatorCell, colAttrsCell), React.createElement("tr", null, rowAttrsCell, outputCell)));
}
return React.createElement("table", {
className: classes.table
}, React.createElement("tbody", null, React.createElement("tr", null, rendererCell, aggregatorCell, colAttrsCell), React.createElement("tr", null, unusedAttrsCell, rowAttrsCell, outputCell)));
}
}
process.env.NODE_ENV !== "production" ? AbundantCrossTabulationContent.propTypes = _extends({}, CrossTabulation.propTypes, {
onChange: PropTypes.func.isRequired,
hiddenAttributes: PropTypes.arrayOf(PropTypes.string),
hiddenFromAggregators: PropTypes.arrayOf(PropTypes.string),
hiddenFromDragDrop: PropTypes.arrayOf(PropTypes.string),
unusedOrientationCutoff: PropTypes.number,
menuLimit: PropTypes.number
}) : void 0;
AbundantCrossTabulationContent.defaultProps = _extends({}, CrossTabulation.defaultProps, {
hiddenAttributes: [],
hiddenFromAggregators: [],
hiddenFromDragDrop: [],
unusedOrientationCutoff: 85,
menuLimit: 500
});
export default withStyles(styles, {
name: 'RMAbundantCrossTabulationContent'
})(AbundantCrossTabulationContent);