mirror of
https://github.com/gosticks/react-bootstrap-table2.git
synced 2026-08-12 12:00:16 +00:00
implement date filter
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import TextFilter from './src/components/text';
|
||||
import SelectFilter from './src/components/select';
|
||||
import NumberFilter from './src/components/number';
|
||||
import DateFilter from './src/components/date';
|
||||
import wrapperFactory from './src/wrapper';
|
||||
import * as Comparison from './src/comparison';
|
||||
|
||||
@@ -25,3 +26,8 @@ export const numberFilter = (props = {}) => ({
|
||||
Filter: NumberFilter,
|
||||
props
|
||||
});
|
||||
|
||||
export const dateFilter = (props = {}) => ({
|
||||
Filter: DateFilter,
|
||||
props
|
||||
});
|
||||
|
||||
@@ -0,0 +1,204 @@
|
||||
/* eslint react/require-default-props: 0 */
|
||||
/* eslint no-return-assign: 0 */
|
||||
/* eslint prefer-template: 0 */
|
||||
import React, { Component } from 'react';
|
||||
import { PropTypes } from 'prop-types';
|
||||
|
||||
import * as Comparator from '../comparison';
|
||||
import { FILTER_TYPE } from '../const';
|
||||
|
||||
const legalComparators = [
|
||||
Comparator.EQ,
|
||||
Comparator.NE,
|
||||
Comparator.GT,
|
||||
Comparator.GE,
|
||||
Comparator.LT,
|
||||
Comparator.LE
|
||||
];
|
||||
|
||||
function dateParser(d) {
|
||||
return `${d.getFullYear()}-${('0' + (d.getMonth() + 1)).slice(-2)}-${('0' + d.getDate()).slice(-2)}`;
|
||||
}
|
||||
|
||||
class DateFilter extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.timeout = null;
|
||||
this.comparators = props.comparators || legalComparators;
|
||||
this.applyFilter = this.applyFilter.bind(this);
|
||||
this.onChangeDate = this.onChangeDate.bind(this);
|
||||
this.onChangeComparator = this.onChangeComparator.bind(this);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const { getFilter } = this.props;
|
||||
const comparator = this.dateFilterComparator.value;
|
||||
const date = this.inputDate.value;
|
||||
if (comparator && date) {
|
||||
this.applyFilter(date, comparator);
|
||||
}
|
||||
|
||||
// export onFilter function to allow users to access
|
||||
if (getFilter) {
|
||||
getFilter((filterVal) => {
|
||||
this.dateFilterComparator.value = filterVal.comparator;
|
||||
this.inputDate.value = dateParser(filterVal.date);
|
||||
|
||||
this.applyFilter(filterVal.date, filterVal.comparator);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
if (this.timeout) clearTimeout(this.timeout);
|
||||
}
|
||||
|
||||
onChangeDate(e) {
|
||||
const comparator = this.dateFilterComparator.value;
|
||||
const filterValue = e.target.value;
|
||||
this.applyFilter(filterValue, comparator);
|
||||
}
|
||||
|
||||
onChangeComparator(e) {
|
||||
const value = this.inputDate.value;
|
||||
const comparator = e.target.value;
|
||||
this.applyFilter(value, comparator);
|
||||
}
|
||||
|
||||
getComparatorOptions() {
|
||||
const optionTags = [];
|
||||
const { withoutEmptyComparatorOption } = this.props;
|
||||
if (!withoutEmptyComparatorOption) {
|
||||
optionTags.push(<option key="-1" />);
|
||||
}
|
||||
for (let i = 0; i < this.comparators.length; i += 1) {
|
||||
optionTags.push(
|
||||
<option key={ i } value={ this.comparators[i] }>
|
||||
{ this.comparators[i] }
|
||||
</option>
|
||||
);
|
||||
}
|
||||
return optionTags;
|
||||
}
|
||||
|
||||
getDefaultDate() {
|
||||
let defaultDate = '';
|
||||
const { defaultValue } = this.props;
|
||||
if (defaultValue && defaultValue.date) {
|
||||
// Set the appropriate format for the input type=date, i.e. "YYYY-MM-DD"
|
||||
defaultDate = dateParser(new Date(defaultValue.date));
|
||||
}
|
||||
return defaultDate;
|
||||
}
|
||||
|
||||
applyFilter(value, comparator) {
|
||||
if (!comparator || !value) {
|
||||
return;
|
||||
}
|
||||
const { column, onFilter, delay } = this.props;
|
||||
const execute = () => {
|
||||
const date = typeof value !== 'object' ? new Date(value) : value;
|
||||
onFilter(column, FILTER_TYPE.DATE)({ date, comparator });
|
||||
};
|
||||
if (delay) {
|
||||
this.timeout = setTimeout(() => { execute(); }, delay);
|
||||
} else {
|
||||
execute();
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
placeholder,
|
||||
column: { text },
|
||||
style,
|
||||
comparatorStyle,
|
||||
dateStyle,
|
||||
className,
|
||||
comparatorClassName,
|
||||
dateClassName,
|
||||
defaultValue
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
<div className={ `filter date-filter ${className}` } style={ style }>
|
||||
<select
|
||||
ref={ n => this.dateFilterComparator = n }
|
||||
style={ comparatorStyle }
|
||||
className={ `date-filter-comparator form-control ${comparatorClassName}` }
|
||||
onChange={ this.onChangeComparator }
|
||||
defaultValue={ defaultValue ? defaultValue.comparator : '' }
|
||||
>
|
||||
{ this.getComparatorOptions() }
|
||||
</select>
|
||||
<input
|
||||
ref={ n => this.inputDate = n }
|
||||
className={ `filter date-filter-input form-control ${dateClassName}` }
|
||||
style={ dateStyle }
|
||||
type="date"
|
||||
onChange={ this.onChangeDate }
|
||||
placeholder={ placeholder || `Enter ${text}...` }
|
||||
defaultValue={ this.getDefaultDate() }
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
DateFilter.propTypes = {
|
||||
onFilter: PropTypes.func.isRequired,
|
||||
column: PropTypes.object.isRequired,
|
||||
delay: PropTypes.number,
|
||||
defaultValue: PropTypes.shape({
|
||||
date: PropTypes.oneOfType([PropTypes.object]),
|
||||
comparator: PropTypes.oneOf([...legalComparators, ''])
|
||||
}),
|
||||
/* eslint consistent-return: 0 */
|
||||
comparators: (props, propName) => {
|
||||
if (!props[propName]) {
|
||||
return;
|
||||
}
|
||||
for (let i = 0; i < props[propName].length; i += 1) {
|
||||
let comparatorIsValid = false;
|
||||
for (let j = 0; j < legalComparators.length; j += 1) {
|
||||
if (legalComparators[j] === props[propName][i] || props[propName][i] === '') {
|
||||
comparatorIsValid = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!comparatorIsValid) {
|
||||
return new Error(`Date comparator provided is not supported.
|
||||
Use only ${legalComparators}`);
|
||||
}
|
||||
}
|
||||
},
|
||||
placeholder: PropTypes.string,
|
||||
withoutEmptyComparatorOption: PropTypes.bool,
|
||||
style: PropTypes.object,
|
||||
comparatorStyle: PropTypes.object,
|
||||
dateStyle: PropTypes.object,
|
||||
className: PropTypes.string,
|
||||
comparatorClassName: PropTypes.string,
|
||||
dateClassName: PropTypes.string,
|
||||
getFilter: PropTypes.func
|
||||
};
|
||||
|
||||
DateFilter.defaultProps = {
|
||||
delay: 0,
|
||||
defaultValue: {
|
||||
date: undefined,
|
||||
comparator: ''
|
||||
},
|
||||
withoutEmptyComparatorOption: false,
|
||||
comparators: legalComparators,
|
||||
placeholder: undefined,
|
||||
style: undefined,
|
||||
className: '',
|
||||
comparatorStyle: undefined,
|
||||
comparatorClassName: '',
|
||||
dateStyle: undefined,
|
||||
dateClassName: ''
|
||||
};
|
||||
|
||||
|
||||
export default DateFilter;
|
||||
+2
-1
@@ -1,7 +1,8 @@
|
||||
export const FILTER_TYPE = {
|
||||
TEXT: 'TEXT',
|
||||
SELECT: 'SELECT',
|
||||
NUMBER: 'NUMBER'
|
||||
NUMBER: 'NUMBER',
|
||||
DATE: 'DATE'
|
||||
};
|
||||
|
||||
export const FILTER_DELAY = 500;
|
||||
|
||||
@@ -91,6 +91,102 @@ export const filterByNumber = _ => (
|
||||
})
|
||||
);
|
||||
|
||||
export const filterByDate = _ => (
|
||||
data,
|
||||
dataField,
|
||||
{ filterVal: { comparator, date } },
|
||||
customFilterValue
|
||||
) => {
|
||||
if (!date || !comparator) return data;
|
||||
const filterDate = date.getDate();
|
||||
const filterMonth = date.getMonth();
|
||||
const filterYear = date.getFullYear();
|
||||
|
||||
return data.filter((row) => {
|
||||
let valid = true;
|
||||
let cell = _.get(row, dataField);
|
||||
|
||||
if (customFilterValue) {
|
||||
cell = customFilterValue(cell, row);
|
||||
}
|
||||
|
||||
if (typeof cell !== 'object') {
|
||||
cell = new Date(cell);
|
||||
}
|
||||
|
||||
const targetDate = cell.getDate();
|
||||
const targetMonth = cell.getMonth();
|
||||
const targetYear = cell.getFullYear();
|
||||
|
||||
|
||||
switch (comparator) {
|
||||
case EQ: {
|
||||
if (
|
||||
filterDate !== targetDate ||
|
||||
filterMonth !== targetMonth ||
|
||||
filterYear !== targetYear
|
||||
) {
|
||||
valid = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case GT: {
|
||||
if (cell <= date) {
|
||||
valid = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case GE: {
|
||||
if (targetYear < filterYear) {
|
||||
valid = false;
|
||||
} else if (targetYear === filterYear &&
|
||||
targetMonth < filterMonth) {
|
||||
valid = false;
|
||||
} else if (targetYear === filterYear &&
|
||||
targetMonth === filterMonth &&
|
||||
targetDate < filterDate) {
|
||||
valid = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case LT: {
|
||||
if (cell >= date) {
|
||||
valid = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case LE: {
|
||||
if (targetYear > filterYear) {
|
||||
valid = false;
|
||||
} else if (targetYear === filterYear &&
|
||||
targetMonth > filterMonth) {
|
||||
valid = false;
|
||||
} else if (targetYear === filterYear &&
|
||||
targetMonth === filterMonth &&
|
||||
targetDate > filterDate) {
|
||||
valid = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case NE: {
|
||||
if (
|
||||
filterDate === targetDate &&
|
||||
filterMonth === targetMonth &&
|
||||
filterYear === targetYear
|
||||
) {
|
||||
valid = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
console.error('Date comparator provided is not supported');
|
||||
break;
|
||||
}
|
||||
}
|
||||
return valid;
|
||||
});
|
||||
};
|
||||
|
||||
export const filterFactory = _ => (filterType) => {
|
||||
let filterFn;
|
||||
switch (filterType) {
|
||||
@@ -101,6 +197,9 @@ export const filterFactory = _ => (filterType) => {
|
||||
case FILTER_TYPE.NUMBER:
|
||||
filterFn = filterByNumber(_);
|
||||
break;
|
||||
case FILTER_TYPE.DATE:
|
||||
filterFn = filterByDate(_);
|
||||
break;
|
||||
default:
|
||||
filterFn = filterByText(_);
|
||||
}
|
||||
|
||||
@@ -5,7 +5,8 @@
|
||||
.react-bootstrap-table > table > thead > tr > th .select-filter option[value=''],
|
||||
.react-bootstrap-table > table > thead > tr > th .select-filter.placeholder-selected,
|
||||
.react-bootstrap-table > table > thead > tr > th .filter::-webkit-input-placeholder,
|
||||
.react-bootstrap-table > table > thead > tr > th .number-filter-input::-webkit-input-placeholder {
|
||||
.react-bootstrap-table > table > thead > tr > th .number-filter-input::-webkit-input-placeholder,
|
||||
.react-bootstrap-table > table > thead > tr > th .date-filter-input::-webkit-input-placeholder {
|
||||
color: lightgrey;
|
||||
font-style: italic;
|
||||
}
|
||||
@@ -15,17 +16,20 @@
|
||||
font-style: initial;
|
||||
}
|
||||
|
||||
.react-bootstrap-table > table > thead > tr > th .number-filter {
|
||||
.react-bootstrap-table > table > thead > tr > th .number-filter,
|
||||
.react-bootstrap-table > table > thead > tr > th .date-filter {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.react-bootstrap-table > table > thead > tr > th .number-filter-input {
|
||||
.react-bootstrap-table > table > thead > tr > th .number-filter-input,
|
||||
.react-bootstrap-table > table > thead > tr > th .date-filter-input {
|
||||
margin-left: 5px;
|
||||
float: left;
|
||||
width: calc(100% - 67px - 5px);
|
||||
}
|
||||
|
||||
.react-bootstrap-table > table > thead > tr > th .number-filter-comparator {
|
||||
.react-bootstrap-table > table > thead > tr > th .number-filter-comparator,
|
||||
.react-bootstrap-table > table > thead > tr > th .date-filter-comparator {
|
||||
width: 67px;
|
||||
float: left;
|
||||
}
|
||||
Reference in New Issue
Block a user