feat(filter): Filter now supports complex queries for Number columns

More advanced filter support for Number columns

For e.g
  >5000 filters rows with the cell value greater than 5000
  <30 filters rows with cell value less than 30
  20:340 filters rows with cell value in the range of 20 and 340

You can also provide a custom filter function by providing a
`filterRows` function in the options during initialization.
This commit is contained in:
Faris Ansari 2018-08-09 13:29:52 +05:30
parent ae6415c950
commit cdb276abfd
7 changed files with 157 additions and 32 deletions

View File

@ -99,6 +99,11 @@
data,
inlineFilters: true,
dynamicRowHeight: true,
// filterRows(keyword, cells, colIndex) {
// return cells
// .filter(cell => cell.content.includes(keyword))
// .map(cell => cell.rowIndex);
// },
getEditor(colIndex, rowIndex, value, parent) {
// editing obj only for date field
if (colIndex != 6) return;

View File

@ -13,6 +13,7 @@ export default class CellManager {
'wrapper',
'options',
'style',
'header',
'bodyScrollable',
'columnmanager',
'rowmanager',
@ -121,6 +122,10 @@ export default class CellManager {
this.activateFilter(colIndex);
return true;
});
$.on(this.header, 'focusin', '.dt-filter', () => {
this.unfocusCell(this.$focusedCell);
});
}
}
@ -246,6 +251,19 @@ export default class CellManager {
this.highlightRowColumnHeader($cell);
}
unfocusCell($cell) {
if (!$cell) return;
// remove cell border
$cell.classList.remove('dt-cell--focus');
this.$focusedCell = null;
// reset header background
if (this.lastHeaders) {
this.lastHeaders.forEach(header => header.classList.remove('dt-cell--highlight'));
}
}
highlightRowColumnHeader($cell) {
const {
colIndex,

View File

@ -269,24 +269,26 @@ export default class ColumnManager {
bindFilter() {
if (!this.options.inlineFilters) return;
const handler = e => {
const $filterCell = $.closest('.dt-cell', e.target);
const {
colIndex
} = $.data($filterCell);
this.$filterCell = $.closest('.dt-cell', e.target);
const { colIndex } = $.data(this.$filterCell);
const keyword = e.target.value;
this.datamanager.filterRows(keyword, colIndex)
.then(({
rowsToHide,
rowsToShow
}) => {
this.rowmanager.hideRows(rowsToHide);
this.rowmanager.showRows(rowsToShow);
});
this.applyFilter(keyword, colIndex);
};
$.on(this.header, 'keydown', '.dt-filter', debounce(handler, 300));
}
applyFilter(keyword, colIndex) {
this.datamanager.filterRows(keyword, colIndex)
.then(({
rowsToHide,
rowsToShow
}) => {
this.rowmanager.hideRows(rowsToHide);
this.rowmanager.showRows(rowsToShow);
});
}
applyDefaultSortOrder() {
// sort rows if any 1 column has a default sortOrder set
const columnsToSort = this.getColumns().filter(col => col.sortOrder !== 'none');

View File

@ -11,7 +11,6 @@ export default class DataManager {
this.sortRows = nextTick(this.sortRows, this);
this.switchColumn = nextTick(this.switchColumn, this);
this.removeColumn = nextTick(this.removeColumn, this);
this.filterRows = nextTick(this.filterRows, this);
}
init(data, columns) {
@ -423,31 +422,36 @@ export default class DataManager {
}
filterRows(keyword, colIndex) {
let rowsToHide = [];
let rowsToShow = [];
const cells = this.rows.map(row => row[colIndex]);
let result = this.options.filterRows(keyword, cells, colIndex);
cells.forEach(cell => {
const hay = String(cell.content || '').toLowerCase();
const needle = (keyword || '').toLowerCase();
if (!result) {
result = this.getAllRowIndices();
}
if (!needle || hay.includes(needle)) {
rowsToShow.push(cell.rowIndex);
} else {
rowsToHide.push(cell.rowIndex);
}
if (!result.then) {
result = Promise.resolve(result);
}
return result.then(rowsToShow => {
this._filteredRows = rowsToShow;
const rowsToHide = this.getAllRowIndices()
.filter(index => !rowsToShow.includes(index));
return {
rowsToHide,
rowsToShow
};
});
this._filteredRows = rowsToShow;
return {
rowsToHide,
rowsToShow
};
}
getFilteredRowIndices() {
return this._filteredRows || this.rows.map(row => row.meta.rowIndex);
return this._filteredRows || this.getAllRowIndices();
}
getAllRowIndices() {
return this.rows.map(row => row.meta.rowIndex);
}
getRowCount() {

View File

@ -1,3 +1,5 @@
import filterRows from './filterRows';
export default {
columns: [],
data: [],
@ -40,6 +42,7 @@ export default {
desc: '↓',
none: ''
},
filterRows: filterRows,
freezeMessage: '',
getEditor: null,
serialNoColumn: true,

93
src/filterRows.js Normal file
View File

@ -0,0 +1,93 @@
import { isNumber } from './utils';
export default function filterRows(keyword, cells, colIndex) {
let filter = guessFilter(keyword);
let filterMethod = getFilterMethod(filter);
if (filterMethod) {
return filterMethod(filter.text, cells);
}
return cells.map(cell => cell.rowIndex);
};
function getFilterMethod(filter) {
let filterMethodMap = {
contains(keyword, cells) {
return cells
.filter(cell => {
const hay = String(cell.content || '').toLowerCase();
const needle = (keyword || '').toLowerCase();
return !needle || hay.includes(needle);
})
.map(cell => cell.rowIndex);
},
greaterThan(keyword, cells) {
return cells
.filter(cell => {
const value = Number(cell.content);
return value > keyword;
})
.map(cell => cell.rowIndex);
},
lessThan(keyword, cells) {
return cells
.filter(cell => {
const value = Number(cell.content);
return value < keyword;
})
.map(cell => cell.rowIndex);
},
range(rangeValues, cells) {
return cells
.filter(cell => {
const value = Number(cell.content);
return value >= rangeValues[0] && value <= rangeValues[1];
})
.map(cell => cell.rowIndex);
}
};
return filterMethodMap[filter.type];
}
function guessFilter(keyword) {
if (keyword.length === 1) return {};
if (keyword.startsWith('>')) {
if (isNumber(keyword.slice(1))) {
return {
type: 'greaterThan',
text: Number(keyword.slice(1).trim())
};
}
keyword = keyword.slice(1);
}
if (keyword.startsWith('<')) {
if (isNumber(keyword.slice(1))) {
return {
type: 'lessThan',
text: Number(keyword.slice(1).trim())
};
}
keyword = keyword.slice(1);
}
if (keyword.split(':').length === 2 && keyword.split(':').every(isNumber)) {
return {
type: 'range',
text: keyword.split(':').map(v => v.trim()).map(Number)
};
}
return {
type: 'contains',
text: keyword.toLowerCase()
};
}

View File

@ -277,7 +277,7 @@ export default class RowManager {
getFilterInput(props) {
const dataAttr = makeDataAttributeString(props);
return `<input class="dt-filter dt-input" type="text" ${dataAttr} />`;
return `<input class="dt-filter dt-input" type="text" ${dataAttr} tabindex="1" />`;
}
selector(rowIndex) {