UNPKG

xenos

Version:

Xenos is a data grid built upon angular2 and bootstrap.

198 lines (165 loc) 6.27 kB
import { SortDescriptor } from "./sort-descriptor"; import { SortDirection } from "./sort-direction"; import { FilterDescriptor } from "./filter-descriptor"; import { ObservableArray } from "./observable-array"; import { Subject } from "rxjs/Subject"; import { async } from "rxjs/scheduler/async"; import { Observable } from "rxjs/Observable"; import "rxjs/add/Observable/from"; import "rxjs/add/operator/do"; import "rxjs/add/operator/map"; import "rxjs/add/operator/observeOn"; export interface ItemsPreview { items?: any[]; processedItems?: any[]; viewItems?: any[]; } export class ViewSource { private static empty: any[] = []; private itemsSourceInternal: any[]; private pageSizeInternal: number; private pageIndexInternal: number; private pageCountInternal: number = 1; private itemsPreviewer: Subject<ItemsPreview> = new Subject<ItemsPreview>(); private itemsLoader: Subject<any[]> = new Subject<any[]>(); constructor() { this.pageIndex = 0; this.pageSize = Number.MAX_SAFE_INTEGER; this.itemsViewChanging = this.itemsPreviewer .asObservable(); this.itemsViewChanged = this.itemsLoader .asObservable() .observeOn(async) .do(x => this.working = true) .map(x => { let container: ItemsPreview = { items: x }; return container; }) .map(x => { x.processedItems = this.filterItems(x.items); return x; }) .do(x => this.calculatePageCount(x.processedItems)) .map(x => { x.processedItems = this.sortItems(x.processedItems); return x; }) .map(x => { x.viewItems = this.applyPaging(x.processedItems.slice(0)); return x; }) .map(x => { this.itemsPreviewer.next(x); return x.viewItems; }) .do(x => this.working = false); } public working: boolean = false; public itemsViewChanging: Observable<ItemsPreview>; public itemsViewChanged: Observable<any[]>; public sortDescriptors: ObservableArray<SortDescriptor> = new ObservableArray<SortDescriptor>(); public filterDescriptors: ObservableArray<FilterDescriptor> = new ObservableArray<FilterDescriptor>(); private calculatePageCount(items: any[]): void { this.pageCountInternal = Math.ceil(items.length / this.pageSize) } public set pageIndex(value: number) { if (value > this.pageCount - 1) { throw new RangeError("Page index must be larger than -1 and smaller than the page count plus 1."); } this.pageIndexInternal = value; } public get pageIndex(): number { return this.pageIndexInternal; } public set itemsSource(value: any[]) { this.itemsSourceInternal = value; } public get itemsSource(): any[] { return this.itemsSourceInternal; } private applyPaging(items: any[]): any[] { var page: any; if (this.pageSize > items.length) { page = items; } else { page = this.getPage(items); } return page; } private groupFilterDescriptors(descriptors: FilterDescriptor[]): Map<any, FilterDescriptor[]> { var placeboId = 0; var groups = new Map<any, FilterDescriptor[]>(); descriptors.forEach(x => { let groupId = x.groupId; if (groupId == null) { groupId = placeboId++; } if (!groups.has(groupId)) { groups.set(groupId, [x]); } else { groups.get(groupId).push(x); } }); return groups; } private filterItems(items: any[]): any[] { return items.filter((x, i) => { if (this.filterDescriptors.length === 0) { return true; } // We construct a CNF formula using the provided filter predicates. // All filters with the same groupId are concatenated using the "OR" operator // while all of those groups are then combined using an "AND" -- (A||B)&&(C||D) let expression = true; var groups = this.groupFilterDescriptors(this.filterDescriptors); groups.forEach((v, i) => { let group = v.map(y => y.predicate(x)).reduce((a, b) => a || b, false); expression = expression && group; }); return expression; }); } private sortItems(items: any[]): any[] { return items.sort((a, b) => { for (let i = 0; i < this.sortDescriptors.length; i++) { let descriptor = this.sortDescriptors[i]; let v1 = descriptor.valueAccessor(a); let v2 = descriptor.valueAccessor(b); if (descriptor.direction === SortDirection.descending) { let v = v1; v1 = v2; v2 = v } let c = v1 === v2 ? 0 : v1 < v2 ? -1 : 1; if (c != 0) { return c; } } return 0; }); } public set pageSize(value: number) { if (value < 1) { throw new RangeError("Page size must be larger than 0."); } this.pageSizeInternal = value; } public get pageSize(): number { return this.pageSizeInternal; } public get pageCount(): number { return this.pageCountInternal; } private getPage(items: any[]): any[] { let index = 0 + this.pageIndex * this.pageSize; return items.slice(index, index + this.pageSize); } public refresh(): void { if (this.itemsSourceInternal == null) { return; } this.itemsLoader.next(this.itemsSourceInternal); } }