UNPKG

@bigfishtv/cockpit

Version:

485 lines (426 loc) 17.5 kB
var _class, _temp; var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; 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; } function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; } import React, { Component } from 'react'; import PropTypes from 'prop-types'; import newId from '../../utils/newId'; import * as Conditions from '../../constants/Conditions'; import { showDeletePrompt } from '../../utils/promptUtils'; import { isObject } from '../../utils/typeUtils'; import { post } from '../../api/xhrUtils'; import { titleCase } from '../../utils/stringUtils'; import { filterOutProtectedSchema, removeAssocationsFromSchema } from '../../utils/formUtils'; import { getFieldsFromSchemaAndAssociations, filterDataByQueryWithFields, filterDataByFilterset } from '../../utils/tableUtils'; import TableView from '../table/Table'; import Spinner from '../Spinner'; import MainContent from '../container/MainContent'; import Panel from '../container/panel/Panel'; import Bulkhead from '../page/Bulkhead'; import FilterInput from '../input/SearchInput'; import Hint from '../Hint'; import Button from '../button/Button'; var DefaultPanelToolbar = function DefaultPanelToolbar(_ref) { var movable = _ref.movable, duplicable = _ref.duplicable, handleMove = _ref.handleMove, handleDuplicate = _ref.handleDuplicate, exportable = _ref.exportable, handleExport = _ref.handleExport, selectedIds = _ref.selectedIds, data = _ref.data, handleDelete = _ref.handleDelete, props = _objectWithoutProperties(_ref, ['movable', 'duplicable', 'handleMove', 'handleDuplicate', 'exportable', 'handleExport', 'selectedIds', 'data', 'handleDelete']); return React.createElement( 'div', null, duplicable && React.createElement(Button, { text: 'Duplicate', onClick: handleDuplicate, disabled: selectedIds.length !== 1 }), movable && React.createElement(Button, { text: 'Move', onClick: handleMove, disabled: !selectedIds.length }), React.createElement(Button, { text: 'Delete', onClick: handleDelete, style: 'error', disabled: !selectedIds.length }), exportable && React.createElement(Button, { text: selectedIds.length > 0 ? 'Export ' + selectedIds.length : 'Export', onClick: handleExport }) ); }; var DefaultBulkheadToolbar = function DefaultBulkheadToolbar(_ref2) { var modelLabel = _ref2.modelLabel, model = _ref2.model, addUrl = _ref2.addUrl; return React.createElement(Button, { text: 'New ' + titleCase(modelLabel || model), onClick: function onClick() { return window.location.href = addUrl; }, style: 'primary', size: 'large' }); }; var DefaultPanelDrawer = function DefaultPanelDrawer(_ref3) { var data = _ref3.data, originalData = _ref3.originalData, filterset = _ref3.filterset, query = _ref3.query, selectedIds = _ref3.selectedIds; var filters = Object.keys(filterset).reduce(function (obj, key) { var _extends2; return filterset[key] !== null ? _extends({}, obj, (_extends2 = {}, _extends2[key] = filterset[key], _extends2)) : obj; }, {}); var filterKeys = Object.keys(filters); var numKeys = filterKeys.length; var queryStr = ''; if (!!query || numKeys > 0) queryStr += 'Filtered '; if (!!query) queryStr += 'by "' + query + '" '; if (numKeys > 0) queryStr += 'where '; return React.createElement( 'div', { className: 'panel-drawer' }, React.createElement( 'span', { style: { float: 'left' } }, 'Showing ', React.createElement( 'strong', null, data.length ), ' of ', React.createElement( 'strong', null, originalData.length ), selectedIds.length > 0 && React.createElement( 'span', null, '\xA0\xA0\u2013\xA0\xA0', React.createElement( 'strong', null, selectedIds.length === data.length ? 'All' : selectedIds.length ), ' selected' ) ), (!!query || numKeys > 0) && React.createElement( 'span', { style: { float: 'right' } }, queryStr, numKeys > 0 && filterKeys.map(function (key, i) { var value = filters[key]; if (isObject(value)) value = ' is ' + JSON.encode(value);else if (typeof value == 'function') value = ' matches a custom function';else value = ' is ' + value.toString(); return React.createElement( 'span', { key: key }, titleCase(key), value, i < numKeys - 2 ? ', ' : i < numKeys - 1 ? ' and ' : '.' ); }) ) ); }; export var AutoTableIndexContainer = (_temp = _class = function (_Component) { _inherits(AutoTableIndexContainer, _Component); function AutoTableIndexContainer(props) { _classCallCheck(this, AutoTableIndexContainer); var _this = _possibleConstructorReturn(this, _Component.call(this, props)); _this.handleQueryChange = function (value) { var query = typeof value == 'string' ? value : value.target.value; _this.setState({ query: query, selectedIds: [] }); }; _this.handleSelectionChange = function (selectedIds) { _this.setState({ selectedIds: selectedIds }); }; _this.handleNegativeHeightChange = function (negativeHeight) { _this.setState({ negativeHeight: negativeHeight }); }; _this.handleFilterChange = function (property, value) { var filterset = _this.state.filterset; if (isObject(property)) { Object.keys(property).forEach(function (key) { return filterset[key] = property[key]; }); } else { filterset[property] = value; } _this.setState({ filterset: filterset }); }; _this.handleEdit = function (item) { var isNew = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; if (_this.props.onSelect) _this.props.onSelect(item);else window.location.href = _this.props.updateUrl + item.id; }; _this.handleDelete = function () { var _this$state = _this.state, data = _this$state.data, selectedIds = _this$state.selectedIds; showDeletePrompt({ subject: _this.props.modelLabel || null, selectedIds: selectedIds, renderListItem: _this.props.renderListItem, data: data.filter(function (item) { return selectedIds.indexOf(item.id) >= 0; }), queryUrl: _this.props.deleteUrl, callback: function callback(deletedData) { var deletedIds = selectedIds; // deletedData.map(item => item.id) var data = _this.state.data.filter(function (item) { return deletedIds.indexOf(item.id) < 0; }); _this.setState({ data: data, selectedIds: [] }); } }); }; _this.handleDuplicate = function () { var _this$props = _this.props, model = _this$props.model, duplicateUrl = _this$props.duplicateUrl, duplicateTransformer = _this$props.duplicateTransformer; var _this$state2 = _this.state, data = _this$state2.data, selectedIds = _this$state2.selectedIds; var selectedItem = data.filter(function (item) { return item.id === selectedIds[0]; })[0]; var url = duplicateUrl || '/admin/' + model + '/' + selectedItem.id + '.json?clone=1'; post({ url: url, data: _extends({}, duplicateTransformer(selectedItem), { id: newId() }), callback: function callback(result) { return _this.setState({ data: [].concat(data, [result]) }); } }); }; _this.handleExport = function () { var _this$state3 = _this.state, data = _this$state3.data, selectedIds = _this$state3.selectedIds; var itemIds = selectedIds.length > 0 ? selectedIds : data.map(function (item) { return item.id; }); var query = itemIds.map(function (id) { return 'ids[]=' + id; }).join('&'); window.open(_this.props.exportUrl + '?' + query); }; _this.handleMove = function () { if (_this.props.onMove) { var _this$state4 = _this.state, data = _this$state4.data, selectedIds = _this$state4.selectedIds; var selectedItems = data.filter(function (item) { return ~selectedIds.indexOf(item.id); }); _this.props.onMove(selectedItems, _this.handleUpdateCallback); } }; _this.handleUpdateCallback = function (mapperFunc) { var data = _this.state.data; var newData = mapperFunc(data); _this.setState({ data: newData, originalData: newData }); }; _this.handleDataUpdate = function (data) { _this.setState({ data: data, originalData: data }); }; _this.state = { data: props.defaultValue, selectedIds: [], filterset: props.defaultFilterset, query: props.defaultQuery, loading: false, negativeHeight: props.hint ? props.negativeHeight + 49 : props.negativeHeight }; _this.fields = getFieldsFromSchemaAndAssociations(props.schema, props.associations, props.componentResolver, props.attributeModifier); return _this; } AutoTableIndexContainer.prototype.getSubmitUrl = function getSubmitUrl(data) { return data.id ? this.props.updateUrl + '/' + data.id + '.json' : this.props.addUrl; }; /** * Overloaded function -- handles updates to filterset object, usually used by dropdown selectors * @param {String / Object} property - is property or object of key values * @param {String} value - is value of property, otherwise leave blank */ AutoTableIndexContainer.prototype.render = function render() { var _props = this.props, schema = _props.schema, associations = _props.associations, protectedFieldWhitelist = _props.protectedFieldWhitelist, panelProps = _props.panelProps; var _state = this.state, data = _state.data, query = _state.query, selectedIds = _state.selectedIds, loading = _state.loading, filterset = _state.filterset, negativeHeight = _state.negativeHeight; var _schema = filterOutProtectedSchema(removeAssocationsFromSchema(associations, schema), protectedFieldWhitelist); var _data = filterDataByFilterset(filterDataByQueryWithFields(data, this.fields, query), filterset, Conditions.AND); var _panelProps = _extends({ handleEdit: this.handleEdit, handleDelete: this.handleDelete, handleExport: this.handleExport, handleMove: this.handleMove, handleDuplicate: this.handleDuplicate, handleQueryChange: this.handleQueryChange, handleFilterChange: this.handleFilterChange, handleDataUpdate: this.handleDataUpdate, handleSelectionChange: this.handleSelectionChange, handleNegativeHeightChange: this.handleNegativeHeightChange, movable: this.props.movable, duplicable: this.props.duplicable, exportable: this.props.exportable, originalData: data, data: _data, selectedIds: selectedIds, filterset: filterset, query: query }, panelProps); return this.props.children(_extends({}, this.props, _panelProps, { panelProps: _panelProps, schema: _schema, data: _data, loading: loading, negativeHeight: negativeHeight })); }; return AutoTableIndexContainer; }(Component), _class.propTypes = { /** the lowercase plural model e.g. volunteer_applications */ model: PropTypes.string, /** array of table schema passed in from backend */ schema: PropTypes.array, /** array of entity assocations passed in from backend */ assocations: PropTypes.array, /** array of entity assocations passed in from backend */ hint: PropTypes.string, /** function that receives assocations and schema column, returns a component */ componentResolver: PropTypes.func, /** function that receives assocations and schema column, returns a field object to be merged with auto-generated field e.g. {width: 60} */ attributeModifier: PropTypes.func, /** function to handle when a row is double clicked */ onSelect: PropTypes.func, /** default form value provided from backend, typically array of entity objects */ defaultValue: PropTypes.array, /** fields that start with an underscore (_) get excluded by default, this offers a way to whitelist some of those */ protectedFieldWhitelist: PropTypes.array, /** panel title component to replace default search field */ PanelTitle: PropTypes.func, /** panel toolbar component to replace default one, takes props: selectedIds, data, originalData, duplicable, handleDuplicate, handleDelete */ PanelToolbar: PropTypes.func, /** Any extra props to pass through to PanelToolbar */ panelProps: PropTypes.object, /** Function for rendering item titles, used in delete prompt */ renderListItem: PropTypes.func, /** panel toolbar component to replace default one, takes props: selectedIds, data, originalData, duplicable, handleDuplicate, handleDelete */ BulkheadToolbar: PropTypes.func, children: PropTypes.func, duplicateTransformer: PropTypes.func }, _class.defaultProps = { defaultValue: [], schema: [], associations: [], model: 'model', negativeHeight: 228, movable: false, duplicable: false, exportable: false, defaultQuery: '', defaultFilterset: {}, panelProps: {}, renderListItem: null, protectedFieldWhitelist: [], PanelToolbar: DefaultPanelToolbar, PanelDrawer: DefaultPanelDrawer, BulkheadToolbar: DefaultBulkheadToolbar, duplicateTransformer: function duplicateTransformer(item) { return item; } }, _temp); export var AutoTableIndexBase = function (_Component2) { _inherits(AutoTableIndexBase, _Component2); function AutoTableIndexBase() { _classCallCheck(this, AutoTableIndexBase); return _possibleConstructorReturn(this, _Component2.apply(this, arguments)); } AutoTableIndexBase.prototype.render = function render() { var _props2 = this.props, PanelToolbar = _props2.PanelToolbar, PanelDrawer = _props2.PanelDrawer, PanelTitle = _props2.PanelTitle, panelProps = _props2.panelProps, handleQueryChange = _props2.handleQueryChange, handleSelectionChange = _props2.handleSelectionChange, handleEdit = _props2.handleEdit, handleNegativeHeightChange = _props2.handleNegativeHeightChange, query = _props2.query, hint = _props2.hint, negativeHeight = _props2.negativeHeight, loading = _props2.loading, schema = _props2.schema, associations = _props2.associations, data = _props2.data, selectedIds = _props2.selectedIds, props = _objectWithoutProperties(_props2, ['PanelToolbar', 'PanelDrawer', 'PanelTitle', 'panelProps', 'handleQueryChange', 'handleSelectionChange', 'handleEdit', 'handleNegativeHeightChange', 'query', 'hint', 'negativeHeight', 'loading', 'schema', 'associations', 'data', 'selectedIds']); return React.createElement( Panel, _extends({ PanelToolbar: PanelToolbar, PanelDrawer: PanelDrawer, title: PanelTitle !== undefined ? PanelTitle : React.createElement(FilterInput, { value: query, onChange: handleQueryChange }) }, panelProps), hint && React.createElement(Hint, { title: hint, onHide: function onHide() { return handleNegativeHeightChange(negativeHeight - 49); } }), loading ? React.createElement( 'div', { className: 'loader-center margin-top-xlarge' }, React.createElement(Spinner, { spinnerName: 'circle' }) ) : React.createElement(TableView, _extends({}, props, { schema: schema, associations: associations, data: data, selectedIds: selectedIds, onSelect: handleEdit, onSelectionChange: handleSelectionChange, negativeHeight: negativeHeight })) ); }; return AutoTableIndexBase; }(Component); var AutoTableIndex = function (_Component3) { _inherits(AutoTableIndex, _Component3); function AutoTableIndex() { _classCallCheck(this, AutoTableIndex); return _possibleConstructorReturn(this, _Component3.apply(this, arguments)); } AutoTableIndex.prototype.render = function render() { return React.createElement( AutoTableIndexContainer, this.props, function (props) { var model = props.model, panelProps = props.panelProps, BulkheadToolbar = props.BulkheadToolbar; return React.createElement( MainContent, null, React.createElement(Bulkhead, { title: titleCase(model), Toolbar: function Toolbar() { return React.createElement(BulkheadToolbar, _extends({}, props, panelProps)); } }), React.createElement( 'div', { className: 'finder' }, React.createElement( 'div', { className: 'finder-content', ref: 'finderContent' }, React.createElement(AutoTableIndexBase, props) ) ) ); } ); }; return AutoTableIndex; }(Component); export { AutoTableIndex as default };