@bigfishtv/cockpit
Version:
485 lines (426 loc) • 17.5 kB
JavaScript
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 };