Add support for sortable property (bool/string) on datatable columns to make them sortable

This commit is contained in:
SachaG 2018-06-12 15:24:17 +09:00
parent 4ec9c08500
commit 4da86c44a3
2 changed files with 65 additions and 9 deletions

View file

@ -28,10 +28,23 @@ class Datatable extends PureComponent {
this.updateQuery = this.updateQuery.bind(this);
this.state = {
value: '',
query: ''
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();
@ -66,7 +79,7 @@ class Datatable extends PureComponent {
return (
<div className={`datatable datatable-${collection.options.collectionName}`}>
<Components.DatatableAbove {...this.props} collection={collection} canInsert={canInsert} value={this.state.value} updateQuery={this.updateQuery} />
<DatatableWithList {...this.props} collection={collection} terms={{query: this.state.query}} currentUser={this.props.currentUser}/>
<DatatableWithList {...this.props} collection={collection} terms={{query: this.state.query, orderBy: this.state.currentSort }} currentUser={this.props.currentUser} toggleSort={this.toggleSort} currentSort={this.state.currentSort}/>
</div>
)
}
@ -116,7 +129,7 @@ registerComponent('DatatableAbove', DatatableAbove);
DatatableHeader Component
*/
const DatatableHeader = ({ collection, column }, { intl }) => {
const DatatableHeader = ({ collection, column, toggleSort, currentSort }, { intl }) => {
const columnName = typeof column === 'string' ? column : column.label || column.name;
@ -135,7 +148,9 @@ const DatatableHeader = ({ collection, column }, { intl }) => {
const defaultMessage = schema[columnName] ? schema[columnName].label : Utils.camelToSpaces(columnName);
const formattedLabel = intl.formatMessage({ id: `${collection._name}.${columnName}`, defaultMessage });
return <th>{formattedLabel}</th>;
// 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 ? <Components.DatatableSorter name={sortPropertyName} label={formattedLabel} toggleSort={toggleSort} currentSort={currentSort} sortable={column.sortable}/> : <th>{formattedLabel}</th>;
} else {
@ -151,6 +166,43 @@ DatatableHeader.contextTypes = {
registerComponent('DatatableHeader', DatatableHeader);
const SortNone = () =>
<svg width='16' height='16' viewBox='0 0 438 438' fill='none' xmlns='http://www.w3.org/2000/svg'>
<path d='M25.7368 247.243H280.263C303.149 247.243 314.592 274.958 298.444 291.116L171.18 418.456C161.128 428.515 144.872 428.515 134.926 418.456L7.55631 291.116C-8.59221 274.958 2.85078 247.243 25.7368 247.243ZM298.444 134.884L171.18 7.54408C161.128 -2.51469 144.872 -2.51469 134.926 7.54408L7.55631 134.884C-8.59221 151.042 2.85078 178.757 25.7368 178.757H280.263C303.149 178.757 314.592 151.042 298.444 134.884Z' transform='translate(66 6)' fill='#000' fillOpacity='0.2' />
</svg>
const SortDesc = () =>
<svg width="16" height="16" viewBox="0 0 438 438" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M25.7368 0H280.263C303.149 0 314.592 27.7151 298.444 43.8734L171.18 171.213C161.128 181.272 144.872 181.272 134.926 171.213L7.55631 43.8734C-8.59221 27.7151 2.85078 0 25.7368 0Z" transform="translate(66 253.243)" fill="black" fillOpacity="0.7"/>
<path d="M171.18 7.54408L298.444 134.884C314.592 151.042 303.149 178.757 280.263 178.757H25.7368C2.85078 178.757 -8.59221 151.042 7.55631 134.884L134.926 7.54408C144.872 -2.51469 161.128 -2.51469 171.18 7.54408Z" transform="translate(66 6)" fill="black" fillOpacity="0.2"/>
</svg>
const SortAsc = () =>
<svg width="16" height="16" viewBox="0 0 438 438" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M298.444 134.884L171.18 7.54408C161.128 -2.51469 144.872 -2.51469 134.926 7.54408L7.55631 134.884C-8.59221 151.042 2.85078 178.757 25.7368 178.757H280.263C303.149 178.757 314.592 151.042 298.444 134.884Z" transform="translate(66 6)" fill="black" fillOpacity="0.7"/>
<path d="M280.263 0H25.7368C2.85078 0 -8.59221 27.7151 7.55631 43.8734L134.926 171.213C144.872 181.272 161.128 181.272 171.18 171.213L298.444 43.8734C314.592 27.7151 303.149 0 280.263 0Z" transform="translate(66 253.243)" fill="black" fillOpacity="0.2"/>
</svg>
const DatatableSorter = ({ name, label, toggleSort, currentSort }) =>
<th>
<div className="datatable-sorter" onClick={() => {toggleSort(name)}}>
<span className="datatable-sorter-label">{label}</span>
<span className="sort-icon">
{!currentSort[name] ? (
<SortNone/>
) : currentSort[name] === 1 ? (
<SortDesc/>
) : (
<SortAsc/>
)
}
</span>
</div>
</th>
registerComponent('DatatableSorter', DatatableSorter);
/*
DatatableContents Component
@ -159,11 +211,11 @@ DatatableContents Component
const DatatableContents = (props) => {
const {collection, columns, results, loading, loadMore, count, totalCount, networkStatus, showEdit, currentUser, emptyState} = props;
const {collection, columns, results, loading, loadMore, count, totalCount, networkStatus, showEdit, currentUser, emptyState, toggleSort, currentSort } = props;
if (loading) {
return <div className="datatable-list datatable-list-loading"><Components.Loading /></div>;
} else if (!results.length) {
} else if (!results || !results.length) {
return emptyState || null;
}
@ -175,7 +227,7 @@ const DatatableContents = (props) => {
<table className="table">
<thead>
<tr>
{_.sortBy(columns, column => column.order).map((column, index) => <Components.DatatableHeader key={index} collection={collection} column={column}/>)}
{_.sortBy(columns, column => column.order).map((column, index) => <Components.DatatableHeader key={index} collection={collection} column={column} toggleSort={toggleSort} currentSort={currentSort} />)}
{showEdit ? <th><FormattedMessage id="datatable.edit"/></th> : null}
</tr>
</thead>

View file

@ -6,7 +6,6 @@ import { runCallbacks } from './callbacks.js';
import { getSetting, registerSetting } from './settings.js';
import { registerFragment, getDefaultFragmentText } from './fragments.js';
import escapeStringRegexp from 'escape-string-regexp';
import { isIntlField } from './intl.js';
const wrapAsync = (Meteor.wrapAsync)? Meteor.wrapAsync : Meteor._wrapAsync;
// import { debug } from './debug.js';
@ -297,6 +296,11 @@ export const createCollection = options => {
parameters = runCallbacks(`${collectionName.toLowerCase()}.parameters.server`, parameters, _.clone(terms), context);
}
// sort using terms.orderBy (overwrite defaultView's sort)
if (terms.orderBy && !_.isEmpty(terms.orderBy)) {
parameters.options.sort = terms.orderBy
}
// if there is no sort, default to sorting by createdAt descending
if (!parameters.options.sort) {
parameters.options.sort = { createdAt: -1 };
@ -318,7 +322,7 @@ export const createCollection = options => {
});
}
if(terms.query) {
if (terms.query) {
const query = escapeStringRegexp(terms.query);