UNPKG

@mhmdaljefri/revogrid

Version:

Virtual reactive data grid component - RevoGrid.

201 lines (200 loc) 6.9 kB
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; } }