UNPKG

cheetah-grid

Version:

Cheetah Grid is a high performance grid engine that works on canvas

221 lines (214 loc) 5.96 kB
import type { FieldDef, MaybePromise, MaybePromiseOrUndef } from "../ts-types"; import { each, isPromise } from "../internal/utils"; import { DataSource } from "./DataSource"; import { EventHandler } from "../internal/EventHandler"; /** @private */ type Filter<T> = (record: T | undefined) => boolean; /** @private */ class DataSourceIterator<T> { _dataSource: DataSource<T>; _curIndex: number; _data: MaybePromiseOrUndef<T>[]; constructor(dataSource: DataSource<T>) { this._dataSource = dataSource; this._curIndex = -1; this._data = []; } hasNext(): boolean { const next = this._curIndex + 1; return this._dataSource.length > next; } next(): MaybePromiseOrUndef<T> { const next = this._curIndex + 1; const data = this._getIndexData(next); this._curIndex = next; return data; } movePrev(): void { this._curIndex--; } _getIndexData(index: number, nest?: boolean): MaybePromiseOrUndef<T> { const dataSource = this._dataSource; const data = this._data; if (index < data.length) { return data[index]; } if (dataSource.length <= index) { return undefined; } const record = this._dataSource.get(index); data[index] = record; if (isPromise(record)) { record.then((val) => { data[index] = val; }); if (!nest) { for (let i = 1; i <= 100; i++) { this._getIndexData(index + i, true); } } } return record; } } /** @private */ class FilterData<T> { _owner: FilterDataSource<T>; _dataSourceItr: DataSourceIterator<T>; _filter: Filter<T>; _filteredList: (T | undefined)[]; _queues: (Promise<T | undefined> | null)[]; _cancel = false; constructor( dc: FilterDataSource<T>, original: DataSource<T>, filter: Filter<T> ) { this._owner = dc; this._dataSourceItr = new DataSourceIterator(original); this._filter = filter; this._filteredList = []; this._queues = []; } get(index: number): MaybePromiseOrUndef<T> { if (this._cancel) { return undefined; } const filteredList = this._filteredList; if (index < filteredList.length) { return filteredList[index]; } const queues = this._queues; const indexQueue = queues[index]; if (indexQueue) { return indexQueue; } return queues[index] || this._findIndex(index); } cancel(): void { this._cancel = true; } _findIndex(index: number): MaybePromiseOrUndef<T> { if (window.Promise) { const timeout = Date.now() + 100; let count = 0; return this._findIndexWithTimeout(index, () => { count++; if (count >= 100) { count = 0; return timeout < Date.now(); } return false; }); } return this._findIndexWithTimeout(index, () => false); } _findIndexWithTimeout( index: number, testTimeout: () => boolean ): MaybePromiseOrUndef<T> { const filteredList = this._filteredList; const filter = this._filter; const dataSourceItr = this._dataSourceItr; const queues = this._queues; while (dataSourceItr.hasNext()) { if (this._cancel) { return undefined; } const record = dataSourceItr.next(); if (isPromise(record)) { dataSourceItr.movePrev(); const queue = record.then((_value) => { queues[index] = null; return this.get(index); }); queues[index] = queue; return queue; } if (filter(record)) { filteredList.push(record); if (index < filteredList.length) { return filteredList[index]; } } if (testTimeout()) { const promise = new Promise<void>((resolve) => { setTimeout(() => { resolve(); }, 300); }); const queue = promise.then(() => { queues[index] = null; return this.get(index); }); queues[index] = queue; return queue; } } const dc = this._owner; dc.length = filteredList.length; return undefined; } } /** * grid data source for filter * * @classdesc cheetahGrid.data.FilterDataSource * @memberof cheetahGrid.data */ export class FilterDataSource<T> extends DataSource<T> { private _dataSource: DataSource<T>; private _handler: EventHandler; private _filterData: FilterData<T> | null = null; static get EVENT_TYPE(): typeof DataSource.EVENT_TYPE { return DataSource.EVENT_TYPE; } constructor(dataSource: DataSource<T>, filter: Filter<T>) { super(dataSource); this._dataSource = dataSource; this.filter = filter; const handler = (this._handler = new EventHandler()); handler.on(dataSource, DataSource.EVENT_TYPE.UPDATED_ORDER, () => { // reset // eslint-disable-next-line no-self-assign this.filter = this.filter; }); each(DataSource.EVENT_TYPE, (type) => { handler.on(dataSource, type, (...args) => this.fireListeners(type, ...args) ); }); } get filter(): Filter<T> | null { return this._filterData?._filter || null; } set filter(filter: Filter<T> | null) { if (this._filterData) { this._filterData.cancel(); } this._filterData = filter ? new FilterData(this, this._dataSource, filter) : null; this.length = this._dataSource.length; } protected getOriginal(index: number): MaybePromiseOrUndef<T> { if (!this._filterData) { return super.getOriginal(index); } return this._filterData.get(index); } sort(field: FieldDef<T>, order: "desc" | "asc"): MaybePromise<void> { return this._dataSource.sort(field, order); } // eslint-disable-next-line @typescript-eslint/no-explicit-any get source(): any { return this._dataSource.source; } get dataSource(): DataSource<T> { return this._dataSource; } dispose(): void { this._handler.dispose(); super.dispose(); } }