import { registerComponent, getCollection, Utils } from 'meteor/vulcan:lib';
import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';
import withCurrentUser from '../containers/withCurrentUser.js';
import withComponents from '../containers/withComponents';
import withMulti from '../containers/withMulti.js';
import { FormattedMessage, intlShape } from 'meteor/vulcan:i18n';
import { getFieldValue } from './Card.jsx';
import _sortBy from 'lodash/sortBy';
/*
Datatable Component
*/
// see: http://stackoverflow.com/questions/1909441/jquery-keyup-delay
const delay = (function(){
var timer = 0;
return function(callback, ms){
clearTimeout (timer);
timer = setTimeout(callback, ms);
};
})();
const getColumnName = column => (
typeof column === 'string'
? column
: column.label || column.name
);
class Datatable extends PureComponent {
constructor() {
super();
this.updateQuery = this.updateQuery.bind(this);
this.state = {
value: '',
query: '',
currentSort: {}
};
}
toggleSort = column => {
let currentSort;
if (!this.state.currentSort[column]) {
currentSort = { [column] : 1 };
} else if (this.state.currentSort[column] === 1) {
currentSort = { [column] : -1 };
} else {
currentSort = {};
}
this.setState({ currentSort });
}
updateQuery(e) {
e.persist();
e.preventDefault();
this.setState({
value: e.target.value
});
delay(() => {
this.setState({
query: e.target.value
});
}, 700 );
}
render() {
const { Components } = this.props;
if (this.props.data) { // static JSON datatable
return ;
} else { // dynamic datatable with data loading
const collection = this.props.collection || getCollection(this.props.collectionName);
const options = {
collection,
...this.props.options
};
const DatatableWithMulti = withMulti(options)(Components.DatatableContents);
const canInsert = collection.options && collection.options.mutations && collection.options.mutations.new && collection.options.mutations.new.check(this.props.currentUser);
// add _id to orderBy when we want to sort a column, to avoid breaking the graphql() hoc;
// see https://github.com/VulcanJS/Vulcan/issues/2090#issuecomment-433860782
// this.state.currentSort !== {} is always false, even when console.log(this.state.currentSort) displays {}. So we test on the length of keys for this object.
const orderBy = Object.keys(this.state.currentSort).length == 0 ? {} : { ...this.state.currentSort, _id: -1 };
return (
);
}
}
}
Datatable.propTypes = {
title: PropTypes.string,
collection: PropTypes.object,
columns: PropTypes.array,
data: PropTypes.array,
options: PropTypes.object,
showEdit: PropTypes.bool,
showNew: PropTypes.bool,
showSearch: PropTypes.bool,
newFormOptions: PropTypes.object,
editFormOptions: PropTypes.object,
emptyState: PropTypes.object,
Components: PropTypes.object.isRequired,
};
Datatable.defaultProps = {
showNew: true,
showEdit: true,
showSearch: true,
};
registerComponent({ name: 'Datatable', component: Datatable, hocs: [withCurrentUser, withComponents] });
export default Datatable;
const DatatableLayout = ({ collectionName, children }) => (
{children}
);
registerComponent({ name: 'DatatableLayout', component: DatatableLayout });
/*
DatatableAbove Component
*/
const DatatableAbove = (props, { intl }) => {
const { collection, currentUser, showSearch, showNew, canInsert,
value, updateQuery, options, newFormOptions, Components } = props;
return (
{showSearch && (
)}
{showNew && canInsert && }
);
};
DatatableAbove.contextTypes = {
intl: intlShape,
};
DatatableAbove.propTypes = {
Components: PropTypes.object.isRequired
};
registerComponent('DatatableAbove', DatatableAbove);
const DatatableAboveSearchInput = (props) => (
);
registerComponent({ name: 'DatatableAboveSearchInput', component: DatatableAboveSearchInput });
const DatatableAboveLayout = ({ children }) => (
{children}
);
registerComponent({ name: 'DatatableAboveLayout', component: DatatableAboveLayout });
/*
DatatableHeader Component
*/
const DatatableHeader = ({ collection, column, toggleSort, currentSort, Components }, { intl }) => {
const columnName = getColumnName(column);
if (collection) {
const schema = collection.simpleSchema()._schema;
/*
use either:
1. the column name translation
2. the column name label in the schema (if the column name matches a schema field)
3. the raw column name.
*/
const defaultMessage = schema[columnName] ? schema[columnName].label : Utils.camelToSpaces(columnName);
const formattedLabel = intl.formatMessage({ id: `${collection._name}.${columnName}`, defaultMessage });
// if sortable is a string, use it as the name of the property to sort by. If it's just `true`, use column.name
const sortPropertyName = typeof column.sortable === 'string' ? column.sortable : column.name;
return column.sortable
?
: {formattedLabel};
} else {
const formattedLabel = intl.formatMessage({ id: columnName, defaultMessage: columnName });
return (
{formattedLabel}
);
}
};
DatatableHeader.contextTypes = {
intl: intlShape
};
DatatableHeader.propTypes = {
Components: PropTypes.object.isRequired
};
registerComponent('DatatableHeader', DatatableHeader);
const DatatableHeaderCellLayout = ({ children, ...otherProps }) => (
{children} |
);
registerComponent({ name: 'DatatableHeaderCellLayout', component: DatatableHeaderCellLayout });
const SortNone = () =>
;
const SortDesc = () =>
;
const SortAsc = () =>
;
const DatatableSorter = ({ name, label, toggleSort, currentSort }) =>
{toggleSort(name);}}>
{label}
{!currentSort[name] ? (
) : currentSort[name] === 1 ? (
) : (
)
}
| ;
registerComponent('DatatableSorter', DatatableSorter);
/*
DatatableContents Component
*/
const DatatableContents = (props) => {
// if no columns are provided, default to using keys of first array item
const { title, collection, results, columns, loading, loadMore,
count, totalCount, networkStatus, showEdit, currentUser, emptyState,
toggleSort, currentSort,
Components } = props;
if (loading) {
return
;
} else if (!results || !results.length) {
return emptyState || null;
}
const isLoadingMore = networkStatus === 2;
const hasMore = totalCount > results.length;
const sortedColumns = _sortBy(columns, column => column.order);
return (
{title && }
{
sortedColumns
.map((column, index) => (
)
)
}
{showEdit ? | : null}
{results.map((document, index) => )}
{hasMore &&
{isLoadingMore
?
: (
{ e.preventDefault(); loadMore(); }}>
Load More ({count}/{totalCount})
)
}
}
);
};
DatatableContents.propTypes = {
Components: PropTypes.object.isRequired
};
registerComponent('DatatableContents', DatatableContents);
const DatatableContentsLayout = ({ children }) => (
{children}
);
registerComponent({ name: 'DatatableContentsLayout', component: DatatableContentsLayout });
const DatatableContentsInnerLayout = ({ children }) => (
);
registerComponent({ name: 'DatatableContentsInnerLayout', component: DatatableContentsInnerLayout });
const DatatableContentsHeadLayout = ({ children }) => (
{children}
);
registerComponent({ name: 'DatatableContentsHeadLayout', component: DatatableContentsHeadLayout });
const DatatableContentsBodyLayout = ({ children }) => (
{children}
);
registerComponent({ name: 'DatatableContentsBodyLayout', component: DatatableContentsBodyLayout });
const DatatableContentsMoreLayout = ({ children }) => (
{children}
);
registerComponent({ name: 'DatatableContentsMoreLayout', component: DatatableContentsMoreLayout });
const DatatableLoadMoreButton = ({ count, totalCount, Components, children, ...otherProps }) => (
{children}
);
registerComponent({ name: 'DatatableLoadMoreButton', component: DatatableLoadMoreButton });
/*
DatatableTitle Component
*/
const DatatableTitle = ({ title }) =>
{title}
;
registerComponent('DatatableTitle', DatatableTitle);
/*
DatatableRow Component
*/
const DatatableRow = (props, { intl }) => {
const { collection, columns, document, showEdit,
currentUser, options, editFormOptions, rowClass, Components } = props;
const canEdit = collection && collection.options && collection.options.mutations && collection.options.mutations.edit && collection.options.mutations.edit.check(currentUser, document);
const row = typeof rowClass === 'function' ? rowClass(document) : rowClass || '';
const modalProps = { title: {document._id}
};
const sortedColumns = _sortBy(columns, column => column.order);
return (
{
sortedColumns
.map((column, index) => (
))}
{showEdit && canEdit ?
: null}
);
};
DatatableRow.propTypes = {
Components: PropTypes.object.isRequired
};
registerComponent('DatatableRow', DatatableRow);
DatatableRow.contextTypes = {
intl: intlShape
};
const DatatableRowLayout = ({ children, ...otherProps }) => (
{children}
);
registerComponent({ name: 'DatatableRowLayout', component: DatatableRowLayout });
/*
DatatableCell Component
*/
const DatatableCell = ({ column, document, currentUser, Components }) => {
const Component = column.component
|| column.componentName && Components[column.componentName]
|| Components.DatatableDefaultCell;
const columnName = getColumnName(column);
return (
);
};
DatatableCell.propTypes = {
Components: PropTypes.object.isRequired
};
registerComponent('DatatableCell', DatatableCell);
const DatatableCellLayout = ({ children, ...otherProps }) => (
{children} |
);
registerComponent({ name: 'DatatableCellLayout', component: DatatableCellLayout });
/*
DatatableDefaultCell Component
*/
const DatatableDefaultCell = ({ column, document }) =>
{typeof column === 'string' ? getFieldValue(document[column]) : getFieldValue(document[column.name])}
;
registerComponent('DatatableDefaultCell', DatatableDefaultCell);