@mhmdaljefri/revogrid
Version:
Virtual reactive data grid component - RevoGrid.
201 lines (200 loc) • 6.9 kB
JavaScript
import { h } from '@stencil/core';
import BasePlugin from '../basePlugin';
import { FILTER_PROP, isFilterBtn } from './filter.button';
import { filterEntities, filterNames, filterTypes } from './filter.service';
export const FILTER_TRIMMED_TYPE = 'filter';
export default class FilterPlugin extends BasePlugin {
constructor(revogrid, uiid, config) {
super(revogrid);
this.revogrid = revogrid;
this.filterCollection = {};
this.possibleFilters = Object.assign({}, filterTypes);
this.possibleFilterNames = Object.assign({}, filterNames);
this.possibleFilterEntities = Object.assign({}, filterEntities);
if (config) {
this.initConfig(config);
}
const headerclick = (e) => this.headerclick(e);
const aftersourceset = () => {
if (Object.keys(this.filterCollection).length) {
this.filterByProps(this.filterCollection);
}
};
this.addEventListener('headerclick', headerclick);
this.addEventListener('aftersourceset', aftersourceset);
this.revogrid.registerVNode([
h("revogr-filter-panel", { uuid: `filter-${uiid}`, filterNames: this.possibleFilterNames, filterEntities: this.possibleFilterEntities, onFilterChange: e => this.onFilterChange(e.detail), ref: e => (this.pop = e) }),
]);
}
initConfig(config) {
if (config.collection) {
this.filterCollection = Object.assign({}, config.collection);
}
if (config.customFilters) {
for (let cType in config.customFilters) {
const cFilter = config.customFilters[cType];
if (!this.possibleFilters[cFilter.columnFilterType]) {
this.possibleFilters[cFilter.columnFilterType] = [];
}
this.possibleFilters[cFilter.columnFilterType].push(cType);
this.possibleFilterEntities[cType] = cFilter.func;
this.possibleFilterNames[cType] = cFilter.name;
}
}
/**
* which filters has to be included/excluded
* convinient way to exclude system filters
*/
if (config.include) {
const filters = {};
for (let t in this.possibleFilters) {
// validate filters, if appropriate function present
const newTypes = this.possibleFilters[t].filter(f => config.include.indexOf(f) > -1);
if (newTypes.length) {
filters[t] = newTypes;
}
}
// if any valid filters provided show them
if (Object.keys(filters).length > 0) {
this.possibleFilters = filters;
}
}
}
async headerclick(e) {
var _a;
const el = (_a = e.detail.originalEvent) === null || _a === void 0 ? void 0 : _a.target;
if (!isFilterBtn(el)) {
return;
}
e.preventDefault();
// close if same
const changes = await this.pop.getChanges();
if (changes && (changes === null || changes === void 0 ? void 0 : changes.prop) === e.detail.prop) {
this.pop.show();
return;
}
// filter button clicked, open filter dialog
const gridPos = this.revogrid.getBoundingClientRect();
const buttonPos = el.getBoundingClientRect();
const prop = e.detail.prop;
this.pop.filterTypes = this.getColumnFilter(e.detail.filter);
this.pop.show(Object.assign(Object.assign({}, this.filterCollection[prop]), { x: buttonPos.x - gridPos.x, y: buttonPos.y - gridPos.y + buttonPos.height, prop }));
}
getColumnFilter(type) {
let filterType = 'string';
if (!type) {
return { [filterType]: this.possibleFilters[filterType] };
}
// if custom column filter
if (this.isValidType(type)) {
filterType = type;
// if multiple filters applied
}
else if (typeof type === 'object' && type.length) {
return type.reduce((r, multiType) => {
if (this.isValidType(multiType)) {
r[multiType] = this.possibleFilters[multiType];
}
return r;
}, {});
}
return { [filterType]: this.possibleFilters[filterType] };
}
isValidType(type) {
return !!(typeof type === 'string' && this.possibleFilters[type]);
}
// called on internal component change
async onFilterChange(filterItem) {
this.filterByProps({ [filterItem.prop]: filterItem });
}
/**
* Apply filters collection to extend existing one or override
* @method
* @param conditions - list of filters to apply
*/
async filterByProps(conditions, override = false) {
if (override) {
this.filterCollection = {};
}
for (const prop in conditions) {
const { type, value } = conditions[prop];
if (type === 'none') {
delete this.filterCollection[prop];
}
else {
const filter = this.possibleFilterEntities[type];
this.filterCollection[prop] = {
filter,
value,
type,
};
}
}
await this.runFiltering();
}
/**
* Triggers grid filtering
*/
async doFiltering(collection, items, columns) {
const columnsToUpdate = [];
// todo improvement: loop through collection of props
columns.forEach(rgCol => {
const column = Object.assign({}, rgCol);
const hasFilter = collection[column.prop];
if (column[FILTER_PROP] && !hasFilter) {
delete column[FILTER_PROP];
columnsToUpdate.push(column);
}
if (!column[FILTER_PROP] && hasFilter) {
columnsToUpdate.push(column);
column[FILTER_PROP] = true;
}
});
const itemsToFilter = this.getRowFilter(items, collection);
// check is filter event prevented
const { defaultPrevented, detail } = this.emit('beforefiltertrimmed', { collection, itemsToFilter, source: items });
if (defaultPrevented) {
return;
}
// check is trimmed event prevented
const isAddedEvent = await this.revogrid.addTrimmed(detail.itemsToFilter, FILTER_TRIMMED_TYPE);
if (isAddedEvent.defaultPrevented) {
return;
}
await this.revogrid.updateColumns(columnsToUpdate);
this.emit('afterFilterApply');
}
async clearFiltering() {
this.filterCollection = {};
await this.runFiltering();
}
async runFiltering() {
const { source, columns } = await this.getData();
const { defaultPrevented, detail } = this.emit('beforefilterapply', { collection: this.filterCollection, source, columns });
if (defaultPrevented) {
return;
}
this.doFiltering(detail.collection, detail.source, detail.columns);
}
async getData() {
const source = await this.revogrid.getSource();
const columns = await this.revogrid.getColumns();
return {
source,
columns
};
}
getRowFilter(rows, collection) {
const trimmed = {};
rows.forEach((model, rowIndex) => {
for (const prop in collection) {
const filterItem = collection[prop];
const filter = filterItem.filter;
if (!filter(model[prop], filterItem.value)) {
trimmed[rowIndex] = true;
}
}
});
return trimmed;
}
}