UNPKG

react-pivottable

Version:
584 lines (523 loc) 25.5 kB
'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); var _react = require('react'); var _react2 = _interopRequireDefault(_react); var _propTypes = require('prop-types'); var _propTypes2 = _interopRequireDefault(_propTypes); var _immutabilityHelper = require('immutability-helper'); var _immutabilityHelper2 = _interopRequireDefault(_immutabilityHelper); var _Utilities = require('./Utilities'); var _PivotTable = require('./PivotTable'); var _PivotTable2 = _interopRequireDefault(_PivotTable); var _reactSortablejs = require('react-sortablejs'); var _reactSortablejs2 = _interopRequireDefault(_reactSortablejs); var _reactDraggable = require('react-draggable'); var _reactDraggable2 = _interopRequireDefault(_reactDraggable); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /* eslint-disable react/prop-types */ // eslint can't see inherited propTypes! var DraggableAttribute = function (_React$Component) { _inherits(DraggableAttribute, _React$Component); function DraggableAttribute(props) { _classCallCheck(this, DraggableAttribute); var _this = _possibleConstructorReturn(this, (DraggableAttribute.__proto__ || Object.getPrototypeOf(DraggableAttribute)).call(this, props)); _this.state = { open: false, top: 0, left: 0, filterText: '' }; return _this; } _createClass(DraggableAttribute, [{ key: 'toggleValue', value: function toggleValue(value) { if (value in this.props.valueFilter) { this.props.removeValuesFromFilter(this.props.name, [value]); } else { this.props.addValuesToFilter(this.props.name, [value]); } } }, { key: 'matchesFilter', value: function matchesFilter(x) { return x.toLowerCase().trim().includes(this.state.filterText.toLowerCase().trim()); } }, { key: 'selectOnly', value: function selectOnly(e, value) { e.stopPropagation(); this.props.setValuesInFilter(this.props.name, Object.keys(this.props.attrValues).filter(function (y) { return y !== value; })); } }, { key: 'getFilterBox', value: function getFilterBox() { var _this2 = this; var showMenu = Object.keys(this.props.attrValues).length < this.props.menuLimit; var values = Object.keys(this.props.attrValues); var shown = values.filter(this.matchesFilter.bind(this)).sort(this.props.sorter); return _react2.default.createElement( _reactDraggable2.default, { handle: '.pvtDragHandle' }, _react2.default.createElement( 'div', { className: 'pvtFilterBox', style: { display: 'block', cursor: 'initial', top: this.state.top + 'px', left: this.state.left + 'px' } }, _react2.default.createElement( 'a', { onClick: function onClick() { return _this2.setState({ open: false }); }, className: 'pvtCloseX' }, '\xD7' ), _react2.default.createElement( 'span', { className: 'pvtDragHandle' }, '\u2630' ), _react2.default.createElement( 'h4', null, this.props.name ), showMenu || _react2.default.createElement( 'p', null, '(too many values to show)' ), showMenu && _react2.default.createElement( 'p', null, _react2.default.createElement('input', { type: 'text', placeholder: 'Filter values', className: 'pvtSearch', value: this.state.filterText, onChange: function onChange(e) { return _this2.setState({ filterText: e.target.value }); } }), _react2.default.createElement('br', null), _react2.default.createElement( 'button', { type: 'button', onClick: function onClick() { return _this2.props.removeValuesFromFilter(_this2.props.name, Object.keys(_this2.props.attrValues).filter(_this2.matchesFilter.bind(_this2))); } }, 'Select ', values.length === shown.length ? 'All' : shown.length ), ' ', _react2.default.createElement( 'button', { type: 'button', onClick: function onClick() { return _this2.props.addValuesToFilter(_this2.props.name, Object.keys(_this2.props.attrValues).filter(_this2.matchesFilter.bind(_this2))); } }, 'Deselect ', values.length === shown.length ? 'All' : shown.length ) ), showMenu && _react2.default.createElement( 'div', { className: 'pvtCheckContainer' }, shown.map(function (x) { return _react2.default.createElement( 'p', { key: x, onClick: function onClick() { return _this2.toggleValue(x); }, className: x in _this2.props.valueFilter ? '' : 'selected' }, _react2.default.createElement( 'a', { className: 'pvtOnly', onClick: function onClick(e) { return _this2.selectOnly(e, x); } }, 'only' ), _react2.default.createElement( 'a', { className: 'pvtOnlySpacer' }, '\xA0' ), x === '' ? _react2.default.createElement( 'em', null, 'null' ) : x ); }) ) ) ); } }, { key: 'toggleFilterBox', value: function toggleFilterBox(event) { var bodyRect = document.body.getBoundingClientRect(); var rect = event.nativeEvent.target.getBoundingClientRect(); this.setState({ open: !this.state.open, top: 10 + rect.top - bodyRect.top, left: 10 + rect.left - bodyRect.left }); } }, { key: 'render', value: function render() { var filtered = Object.keys(this.props.valueFilter).length !== 0 ? 'pvtFilteredAttribute' : ''; return _react2.default.createElement( 'li', { 'data-id': this.props.name }, _react2.default.createElement( 'span', { className: 'pvtAttr ' + filtered }, this.props.name, _react2.default.createElement( 'span', { className: 'pvtTriangle', onClick: this.toggleFilterBox.bind(this) }, ' \u25BE' ) ), this.state.open ? this.getFilterBox() : null ); } }]); return DraggableAttribute; }(_react2.default.Component); DraggableAttribute.defaultProps = { valueFilter: {} }; DraggableAttribute.propTypes = { name: _propTypes2.default.string.isRequired, addValuesToFilter: _propTypes2.default.func.isRequired, removeValuesFromFilter: _propTypes2.default.func.isRequired, attrValues: _propTypes2.default.objectOf(_propTypes2.default.number).isRequired, valueFilter: _propTypes2.default.objectOf(_propTypes2.default.bool), sorter: _propTypes2.default.func.isRequired, menuLimit: _propTypes2.default.number }; var PivotTableUI = function (_React$PureComponent) { _inherits(PivotTableUI, _React$PureComponent); function PivotTableUI(props) { _classCallCheck(this, PivotTableUI); var _this3 = _possibleConstructorReturn(this, (PivotTableUI.__proto__ || Object.getPrototypeOf(PivotTableUI)).call(this, props)); _this3.state = { unusedOrder: [] }; return _this3; } _createClass(PivotTableUI, [{ key: 'componentWillMount', value: function componentWillMount() { this.materializeInput(this.props.data); } }, { key: 'componentWillUpdate', value: function componentWillUpdate(nextProps) { this.materializeInput(nextProps.data); } }, { key: 'materializeInput', value: function materializeInput(nextData) { if (this.data === nextData) { return; } this.data = nextData; var attrValues = {}; var materializedInput = []; var recordsProcessed = 0; _Utilities.PivotData.forEachRecord(this.data, this.props.derivedAttributes, function (record) { materializedInput.push(record); var _iteratorNormalCompletion = true; var _didIteratorError = false; var _iteratorError = undefined; try { for (var _iterator = Object.keys(record)[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { var _attr = _step.value; if (!(_attr in attrValues)) { attrValues[_attr] = {}; if (recordsProcessed > 0) { attrValues[_attr].null = recordsProcessed; } } } } catch (err) { _didIteratorError = true; _iteratorError = err; } finally { try { if (!_iteratorNormalCompletion && _iterator.return) { _iterator.return(); } } finally { if (_didIteratorError) { throw _iteratorError; } } } for (var attr in attrValues) { var 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; } }, { key: 'sendPropUpdate', value: function sendPropUpdate(command) { this.props.onChange((0, _immutabilityHelper2.default)(this.props, command)); } }, { key: 'propUpdater', value: function propUpdater(key) { var _this4 = this; return function (value) { return _this4.sendPropUpdate(_defineProperty({}, key, { $set: value })); }; } }, { key: 'setValuesInFilter', value: function setValuesInFilter(attribute, values) { this.sendPropUpdate({ valueFilter: _defineProperty({}, attribute, { $set: values.reduce(function (r, v) { r[v] = true;return r; }, {}) }) }); } }, { key: 'addValuesToFilter', value: function addValuesToFilter(attribute, values) { if (attribute in this.props.valueFilter) { this.sendPropUpdate({ valueFilter: _defineProperty({}, attribute, values.reduce(function (r, v) { r[v] = { $set: true };return r; }, {})) }); } else { this.setValuesInFilter(attribute, values); } } }, { key: 'removeValuesFromFilter', value: function removeValuesFromFilter(attribute, values) { this.sendPropUpdate({ valueFilter: _defineProperty({}, attribute, { $unset: values }) }); } }, { key: 'makeDnDCell', value: function makeDnDCell(items, onChange, classes) { var _this5 = this; return _react2.default.createElement( _reactSortablejs2.default, { options: { group: 'shared', ghostClass: 'pvtPlaceholder', filter: '.pvtFilterBox', preventOnFilter: false }, tag: 'td', className: classes, onChange: onChange }, items.map(function (x) { return _react2.default.createElement(DraggableAttribute, { name: x, key: x, attrValues: _this5.attrValues[x], valueFilter: _this5.props.valueFilter[x] || {}, sorter: (0, _Utilities.getSort)(_this5.props.sorters, x), menuLimit: _this5.props.menuLimit, setValuesInFilter: _this5.setValuesInFilter.bind(_this5), addValuesToFilter: _this5.addValuesToFilter.bind(_this5), removeValuesFromFilter: _this5.removeValuesFromFilter.bind(_this5) }); }) ); } }, { key: 'render', value: function render() { var _this6 = this; var numValsAllowed = this.props.aggregators[this.props.aggregatorName]([])().numInputs || 0; var rendererName = this.props.rendererName in this.props.renderers ? this.props.rendererName : Object.keys(this.props.renderers)[0]; var rendererCell = _react2.default.createElement( 'td', { className: 'pvtRenderers' }, _react2.default.createElement( 'select', { value: rendererName, onChange: function onChange(_ref) { var value = _ref.target.value; return _this6.propUpdater('rendererName')(value); } }, Object.keys(this.props.renderers).map(function (r) { return _react2.default.createElement( 'option', { value: r, key: r }, r ); }) ) ); var 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' } }; var aggregatorCell = _react2.default.createElement( 'td', { className: 'pvtVals' }, _react2.default.createElement( 'select', { value: this.props.aggregatorName, onChange: function onChange(_ref2) { var value = _ref2.target.value; return _this6.propUpdater('aggregatorName')(value); } }, Object.keys(this.props.aggregators).map(function (n) { return _react2.default.createElement( 'option', { key: 'agg' + n, value: n }, n ); }) ), _react2.default.createElement( 'a', { role: 'button', className: 'pvtRowOrder', onClick: function onClick() { return _this6.propUpdater('rowOrder')(sortIcons[_this6.props.rowOrder].next); } }, sortIcons[this.props.rowOrder].rowSymbol ), _react2.default.createElement( 'a', { role: 'button', className: 'pvtColOrder', onClick: function onClick() { return _this6.propUpdater('colOrder')(sortIcons[_this6.props.colOrder].next); } }, sortIcons[this.props.colOrder].colSymbol ), numValsAllowed > 0 && _react2.default.createElement('br', null), new Array(numValsAllowed).fill().map(function (n, i) { return _react2.default.createElement( 'select', { value: _this6.props.vals[i], key: 'val' + i, onChange: function onChange(_ref3) { var value = _ref3.target.value; return _this6.sendPropUpdate({ vals: { $splice: [[i, 1, value]] } }); } }, _react2.default.createElement('option', { key: 'none' + i, value: '' }), Object.keys(_this6.attrValues).filter(function (e) { return !_this6.props.hiddenAttributes.includes(e) && !_this6.props.hiddenFromAggregators.includes(e); }).map(function (v, j) { return _react2.default.createElement( 'option', { key: i + '-' + j, value: v }, v ); }) ); }) ); var unusedAttrs = Object.keys(this.attrValues).filter(function (e) { return !_this6.props.rows.includes(e) && !_this6.props.cols.includes(e) && !_this6.props.hiddenAttributes.includes(e) && !_this6.props.hiddenFromDragDrop.includes(e); }).sort((0, _Utilities.sortAs)(this.state.unusedOrder)); var unusedLength = unusedAttrs.reduce(function (r, e) { return r + e.length; }, 0); var horizUnused = unusedLength < this.props.unusedOrientationCutoff; var unusedAttrsCell = this.makeDnDCell(unusedAttrs, function (order) { return _this6.setState({ unusedOrder: order }); }, 'pvtAxisContainer pvtUnused ' + (horizUnused ? 'pvtHorizList' : 'pvtVertList')); var colAttrs = this.props.cols.filter(function (e) { return !_this6.props.hiddenAttributes.includes(e) && !_this6.props.hiddenFromDragDrop.includes(e); }); var colAttrsCell = this.makeDnDCell(colAttrs, this.propUpdater('cols'), 'pvtAxisContainer pvtHorizList pvtCols'); var rowAttrs = this.props.rows.filter(function (e) { return !_this6.props.hiddenAttributes.includes(e) && !_this6.props.hiddenFromDragDrop.includes(e); }); var rowAttrsCell = this.makeDnDCell(rowAttrs, this.propUpdater('rows'), 'pvtAxisContainer pvtVertList pvtRows'); var outputCell = _react2.default.createElement( 'td', { valign: 'top' }, _react2.default.createElement(_PivotTable2.default, (0, _immutabilityHelper2.default)(this.props, { data: { $set: this.materializedInput } })) ); if (horizUnused) { return _react2.default.createElement( 'table', { className: 'pvtUi' }, _react2.default.createElement( 'tbody', null, _react2.default.createElement( 'tr', null, rendererCell, unusedAttrsCell ), _react2.default.createElement( 'tr', null, aggregatorCell, colAttrsCell ), _react2.default.createElement( 'tr', null, rowAttrsCell, outputCell ) ) ); } return _react2.default.createElement( 'table', { className: 'pvtUi' }, _react2.default.createElement( 'tbody', null, _react2.default.createElement( 'tr', null, rendererCell, aggregatorCell, colAttrsCell ), _react2.default.createElement( 'tr', null, unusedAttrsCell, rowAttrsCell, outputCell ) ) ); } }]); return PivotTableUI; }(_react2.default.PureComponent); PivotTableUI.propTypes = Object.assign({}, _PivotTable2.default.propTypes, { onChange: _propTypes2.default.func.isRequired, hiddenAttributes: _propTypes2.default.arrayOf(_propTypes2.default.string), hiddenFromAggregators: _propTypes2.default.arrayOf(_propTypes2.default.string), hiddenFromDragDrop: _propTypes2.default.arrayOf(_propTypes2.default.string), unusedOrientationCutoff: _propTypes2.default.number, menuLimit: _propTypes2.default.number }); PivotTableUI.defaultProps = Object.assign({}, _PivotTable2.default.defaultProps, { hiddenAttributes: [], hiddenFromAggregators: [], hiddenFromDragDrop: [], unusedOrientationCutoff: 85, menuLimit: 500 }); exports.default = PivotTableUI; module.exports = exports['default']; //# sourceMappingURL=PivotTableUI.js.map