diff --git a/index.html b/index.html index 1a3588e..7a33202 100644 --- a/index.html +++ b/index.html @@ -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; diff --git a/src/cellmanager.js b/src/cellmanager.js index c5b81bd..68260d2 100644 --- a/src/cellmanager.js +++ b/src/cellmanager.js @@ -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, diff --git a/src/columnmanager.js b/src/columnmanager.js index 8214795..58b4e47 100644 --- a/src/columnmanager.js +++ b/src/columnmanager.js @@ -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'); diff --git a/src/datamanager.js b/src/datamanager.js index 6d38fee..d73cbaa 100644 --- a/src/datamanager.js +++ b/src/datamanager.js @@ -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() { diff --git a/src/defaults.js b/src/defaults.js index 6742c98..ca10dd6 100644 --- a/src/defaults.js +++ b/src/defaults.js @@ -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, diff --git a/src/filterRows.js b/src/filterRows.js new file mode 100644 index 0000000..fec0831 --- /dev/null +++ b/src/filterRows.js @@ -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() + }; +} diff --git a/src/rowmanager.js b/src/rowmanager.js index 881f32d..c161aa5 100644 --- a/src/rowmanager.js +++ b/src/rowmanager.js @@ -277,7 +277,7 @@ export default class RowManager { getFilterInput(props) { const dataAttr = makeDataAttributeString(props); - return ``; + return ``; } selector(rowIndex) {