projects/wms-framework/src/lib/baseframework/PagedCollectionView.ts
Workaround for the generation of metadata.
Methods |
()
|
Returns :
Iterator<T>
|
import { INotifyPropertyChanged } from '../basecomponentmodel/INotifyPropertyChanged';
import { ClassInfo } from '../decorators';
import { Debugger } from '../diagnostics/Debugger';
import { SubscriptionEvent } from '../utils/SubscriptionEvent';
import {
SimpleList,
INotifyCollectionChanged,
CollectionChangeInfo,
SortDescriptionCollection,
IComparer,
iuWhere,
CollectionChangeAction,
iuCount,
IPagedCollectionView,
PageChangingEventArgs,
} from './collections';
import { SortDescription } from './SortDescription';
/**
* Class representing a view of another collection.
*
* Supporting filtered and sorted views
*
* @export
* @class PagedCollectionView
* @extends {SimpleList<any>}
* @implements {INotifyCollectionChanged}
* @wType System.Windows.Data.PagedCollectionView
*/
@ClassInfo({
classId: 'PagedCollectionView',
implements: ['System.Collections.Specialized.INotifyCollectionChanged'],
})
export class PagedCollectionView
extends SimpleList<any>
implements
INotifyCollectionChanged,
IPagedCollectionView,
INotifyPropertyChanged
{
/**
* Event for notification of collection changes
*
* @memberof PagedCollectionView
*/
CollectionChanged: SubscriptionEvent<
(e: any, args: CollectionChangeInfo) => void
> = new SubscriptionEvent();
/**
* Event for notification on changes of the sort description collection
*
* @private
* @memberof PagedCollectionView
*/
private sortHandler: (e: any, args: CollectionChangeInfo) => void;
/**
* Current collection item
*
* @type {*}
* @memberof PagedCollectionView
*/
CurrentItem: any;
/**
* Internal page size
*
* @private
* @memberof PagedCollectionView
*/
private pageSize = 0;
/**
* Gets or sets the size of the page
*
* @memberof PagedCollectionView
*/
public get PageSize() {
return this.pageSize;
}
public set PageSize(value) {
if (value !== this.pageSize) {
this.pageSize = value;
this.RefreshContents();
this.PropertyChanged.fire([this, { PropertyName: 'PageSize' }]);
}
}
/**
* Internal page index
*
* @private
* @memberof PagedCollectionView
*/
private innerPageIndex = 0;
/**
* Gets or sets the current page index
*
* @memberof PagedCollectionView
*/
public get PageIndex() {
return this.innerPageIndex;
}
public set PageIndex(value) {
if (this.innerPageIndex !== value) {
this.innerPageIndex = value;
this.updateCurrentPage();
this.PropertyChanged.fire([this, { PropertyName: 'PageIndex' }]);
}
}
/**
* Current item position
*
* @memberof PagedCollectionView
*/
CurrentPosition = -1;
/**
* Event for page changes
*
* @memberof PagedCollectionView
*/
PageChanged: SubscriptionEvent<(e: any, args: CollectionChangeInfo) => void> =
new SubscriptionEvent();
/**
* Event for page changing
*
* @memberof PagedCollectionView
*/
PageChanging: SubscriptionEvent<
(e: any, args: PageChangingEventArgs) => void
> = new SubscriptionEvent();
/**
* Sort descriptiors
*
* @private
* @type {SortDescriptionCollection}
* @memberof PagedCollectionView
*/
private _sortDescriptions: SortDescriptionCollection = null;
/**
* Function used for filtering
*
* @private
* @memberof PagedCollectionView
*/
private innerFilter: (e: unknown) => boolean = null;
/**
* Original data without modification
*
* @private
* @type {Iterable<any>}
* @memberof PagedCollectionView
*/
private originalData: Iterable<any>;
/**
* Array data that may be filtered
*
* @private
* @type {Array<any>}
* @memberof PagedCollectionView
*/
private effectiveData: Array<any>;
/**
* Current element count
*
* @readonly
* @type {number}
* @memberof PagedCollectionView
* @wProperty Count
*/
get count(): number {
return iuCount(this.effectiveData);
}
/**
* Gets the collection view filter
*
* @memberof PagedCollectionView
*/
public get Filter(): (e: unknown) => boolean {
return this.innerFilter;
}
/**
* Sets the collection view filter
*
* @memberof PagedCollectionView
*/
public set Filter(filter: (e: unknown) => boolean) {
this.innerFilter = filter;
this.RefreshContents();
}
/**
* Creates an instance of PagedCollectionView.
* @param {Iterable<any>} [data]
* @memberof PagedCollectionView
*/
constructor(data?: Iterable<any>) {
super(data);
this.originalData = typeof data !== 'undefined' ? data : [];
this.effectiveData = [...this.originalData];
this._internalArray = this.effectiveData;
}
/**
* Event for notification of property changes
*
* @memberof PagedCollectionView
*/
PropertyChanged: SubscriptionEvent<
(o: any, args: { PropertyName: string }) => void
> = new SubscriptionEvent();
/**
* Verifies if the page can be changed
*
* @type {boolean}
* @memberof PagedCollectionView
*/
CanChangePage: boolean = true;
/**
* Verifies if the page is changing
*
* @type {boolean}
* @memberof PagedCollectionView
*/
IsPageChanging: boolean = false;
/**
* Returns current item count (considering filtered data)
*
* @readonly
* @type {number}
* @memberof PagedCollectionView
*/
get ItemCount(): number {
return this.effectiveData.length;
}
get TotalItemCount(): number {
return this.effectiveData.length;
}
/**
* Notification of events on item count change
*
* @private
* @memberof PagedCollectionView
*/
private OnItemCountChanged() {
this.PropertyChanged.fire([this, { PropertyName: 'TotalItemCount' }]);
this.PropertyChanged.fire([this, { PropertyName: 'ItemCount' }]);
}
/**
* Moves to the last page
*
* @return {*} {boolean}
* @memberof PagedCollectionView
* @wNoMap
*/
MoveToLastPage(): boolean {
Debugger.Throw('Move to last page not implemented');
return false;
}
/**
* The source to page
*
* @type {Array<any>}
* @memberof PagedCollectionView
*/
public paged: Array<any>;
/**
* Move current element to
*
* @param {*} item
* @return {*} {boolean}
* @memberof PagedCollectionView
*/
MoveCurrentTo(item: any): boolean {
const idx = this.internalArray.indexOf(item);
if (idx > -1) {
this.CurrentItem = item;
this.CurrentPosition = idx;
return true;
}
return false;
}
/**
* Move current element to first position
*
* @return {*} {boolean}
* @memberof PagedCollectionView
*/
MoveCurrentToFirst(): boolean {
return this.InternalMoveFirst();
}
/**
* Move current element to next position
*
* @return {*} {boolean}
* @memberof PagedCollectionView
*/
MoveCurrentToNext(): boolean {
const next = this.CurrentPosition > -1 ? this.CurrentPosition + 1 : -1;
if (next > -1 && next < this.internalArray.length - 1) {
this.CurrentItem = this.internalArray[next];
this.CurrentPosition = next;
return true;
}
return false;
}
/**
* Move current element to last position
*
* @return {*} {boolean}
* @memberof PagedCollectionView
*/
MoveCurrentToLast(): boolean {
if (this.internalArray.length > 0) {
this.CurrentItem = this.internalArray[this.internalArray.length - 1];
this.CurrentPosition = this.internalArray.length - 1;
return true;
}
return false;
}
/**
* Move current to position
*
* @param {number} position
* @return {*} {boolean}
* @memberof PagedCollectionView
*/
MoveCurrentToPosition(position: number): boolean {
if (position > -1 && position < this.internalArray.length) {
this.CurrentItem = this.internalArray[position];
this.CurrentPosition = position;
return true;
}
}
/**
* Move current to previous position
*
* @return {*} {boolean}
* @memberof PagedCollectionView
*/
MoveCurrentToPrevious(): boolean {
const next = this.CurrentPosition > 0 ? this.CurrentPosition - 1 : -1;
if (next > -1 && next < this.internalArray.length - 1) {
this.CurrentItem = this.internalArray[next];
this.CurrentPosition = next;
return true;
}
return false;
}
/**
* Move to the first page
*
* @return {*} {boolean}
* @memberof PagedCollectionView
*/
MoveToFirstPage(): boolean {
return this.InternalMoveFirst();
}
/**
* Move to Next page
*
* @return {*} {boolean}
* @memberof PagedCollectionView
*/
MoveToNextPage(): boolean {
const npi =
Math.floor(this.CurrentPosition / this.PageSize) + this.PageSize;
if (npi >= this.effectiveData.length || npi < 0) {
return false;
}
this.CurrentItem = this.effectiveData[npi];
this.CurrentPosition = npi;
this.PageIndex = this.PageIndex + 1;
return true;
}
/**
* Move to page
*
* @param {number} pageIndex
* @return {*} {boolean}
* @memberof PagedCollectionView
*/
MoveToPage(pageIndex: number): boolean {
const fep = (pageIndex + 1) * this.PageSize - this.PageSize;
if (fep >= this.effectiveData.length || fep < 0) {
return false;
}
this.CurrentItem = this.effectiveData[fep];
this.CurrentPosition = fep;
this.PageIndex = pageIndex;
return true;
}
/**
* Move to previous page
*
* @return {*} {boolean}
* @memberof PagedCollectionView
*/
MoveToPreviousPage(): boolean {
return this.MoveToPage(this.PageIndex - 1);
}
/**
* Method bring the page from the source
*
* @param {number} [currentPage=1]
* @param {number} [pageSize=1]
* @param {number} [maxPages=5]
* @return {*} {Record<string, any>}
* @memberof PagedCollectionView
*/
paginate(currentPage = 1, pageSize = 1, maxPages = 5): Record<string, any> {
if (pageSize === 0) {
this.paged = this.effectiveData;
this._internalArray = this.effectiveData;
return;
}
const totalItems = this.count;
// calculate total pages
const totalPages = pageSize > 0 ? Math.ceil(totalItems / pageSize) : 1;
// ensure current page isn't out of range
if (currentPage < 1) {
currentPage = 1;
} else if (currentPage > totalPages) {
currentPage = totalPages;
}
let startPage: number;
let endPage: number;
if (totalPages <= maxPages) {
// total pages less than max so show all pages
startPage = 1;
endPage = totalPages;
} else {
// total pages more than max so calculate start and end pages
const maxPagesBeforeCurrentPage = Math.floor(maxPages / 2);
const maxPagesAfterCurrentPage = Math.ceil(maxPages / 2) - 1;
if (currentPage <= maxPagesBeforeCurrentPage) {
// current page near the start
startPage = 1;
endPage = maxPages;
} else if (currentPage + maxPagesAfterCurrentPage >= totalPages) {
// current page near the end
startPage = totalPages - maxPages + 1;
endPage = totalPages;
} else {
// current page somewhere in the middle
startPage = currentPage - maxPagesBeforeCurrentPage;
endPage = currentPage + maxPagesAfterCurrentPage;
}
}
// calculate start and end item indexes
const startIndex = (currentPage - 1) * pageSize;
const endIndex = Math.min(startIndex + pageSize - 1, totalItems - 1);
// create an array of pages to ng-repeat in the pager control
const pages = Array.from(Array(endPage + 1 - startPage).keys()).map(
(i) => startPage + i
);
const pager = {
totalItems,
currentPage,
pageSize,
totalPages,
startPage,
endPage,
startIndex,
endIndex,
pages,
};
this.paged = this.effectiveData.slice(pager.startIndex, pager.endIndex + 1);
// return object with all pager properties required by the view
return pager;
}
/**
* Get item
*
* @param {number} index
* @return {*} {*}
* @memberof PagedCollectionView
* @wMethod GetItemAt
*/
GetItem(index: number): any {
return this.getItem(index);
}
/**
* Set item
*
* @param {number} index
* @param {*} value
* @memberof PagedCollectionView
* @wIgnore
*/
SetItem(index: number, value: any) {
this.setItem(index, value);
}
/**
* Index of
*
* @param {*} value
* @return {*}
* @memberof PagedCollectionView
*/
IndexOf(value: any) {
return this.indexOf(value);
}
/**
* Insert
*
* @param {number} index
* @param {*} value
* @return {*}
* @memberof PagedCollectionView
* @wIgnore
*/
Insert(index: number, value: any) {
return this.insert(index, value);
}
/**
* Remove at
*
* @param {number} index
* @memberof PagedCollectionView
*/
RemoveAt(index: number) {
this.removeAt(index);
}
/**
* Count property
*
* @readonly
* @type {number}
* @memberof PagedCollectionView
*/
get Count(): number {
return this.internalArray.length;
}
/**
* Adds an item
*
* @param {*} value
* @memberof PagedCollectionView
* @wIgnore
*/
Add(value: any): void {
this.add(value);
}
/**
* Clear the collection
*
* @memberof PagedCollectionView
* @wIgnore
*/
Clear(): void {
this.clear();
}
/**
* Contains an item
*
* @param {*} value
* @return {*} {boolean}
* @memberof PagedCollectionView
*/
Contains(value: any): boolean {
return this.contains(value);
}
/**
* Remove element
*
* @param {*} value
* @return {*} {boolean}
* @memberof PagedCollectionView
*/
Remove(value: any): boolean {
return this.remove(value);
}
/**
* Add range
*
* @param {Iterable<any>} range
* @memberof PagedCollectionView
* @wIgnore
*/
AddRange(range: Iterable<any>): void {
this.addRange(range);
}
/**
* Insert range
*
* @param {number} position
* @param {Iterable<any>} range
* @memberof PagedCollectionView
* @wIgnore
*/
InsertRange(position: number, range: Iterable<any>): void {
this.insertRange(position, range);
}
/**
* Sorting methods
*
* @memberof PagedCollectionView
* @wIgnore
*/
Sort();
Sort(comparer: IComparer<any>);
Sort(comparison: (x: any, y: any) => number);
Sort(p?: any) {
this.sort(p);
}
/**
* Copy to
*
* @param {any[]} target
* @param {number} index
* @memberof PagedCollectionView
* @wIgnore
*/
CopyTo(target: any[], index: number): void {
this.copyTo(target, index);
}
/**
* Remove all
*
* @param {(e: any) => boolean} predicate
* @return {*}
* @memberof PagedCollectionView
* @wIgnore
*/
RemoveAll(predicate: (e: any) => boolean) {
return this.removeAll(predicate);
}
/**
* Convert to array
*
* @return {*}
* @memberof PagedCollectionView'
* @wIgnore
*/
public ToArray() {
return [...this.internalArray];
}
/**
* Returns the SortDescriptions collection
*
* @readonly
* @type {SortDescriptionCollection}
* @memberof PagedCollectionView
*/
public get SortDescriptions(): SortDescriptionCollection {
if (!this._sortDescriptions) {
this._sortDescriptions = new SortDescriptionCollection();
this.sortHandler = this._sortDescriptions.CollectionChanged.addHandler(
(sender, args) => this.RefreshContents()
);
}
return this._sortDescriptions;
}
/**
* Refresh the contents of the collection view
*
* @private
* @memberof PagedCollectionView
*/
private RefreshContents() {
if (this.Filter) {
this.effectiveData = [...iuWhere(this.Filter, this.originalData)];
} else {
this.effectiveData = [...this.originalData];
}
this.effectiveData.sort((x: any, y: any) =>
SortDescription.sortFunction(this.SortDescriptions, x, y)
);
if (this.pageSize === 0) {
this._internalArray = this.effectiveData;
} else {
this.PageIndex = 0;
}
this.OnItemCountChanged();
this.updateCurrentPage();
}
private updateCurrentPage() {
this.paginate(this.PageIndex + 1, this.pageSize);
this._internalArray = this.paged;
this.CollectionChanged.fire([
this,
new CollectionChangeInfo(CollectionChangeAction.Reset),
]);
}
/**
* Move to first position
*
* @private
* @return {*} {boolean}
* @memberof PagedCollectionView
*/
private InternalMoveFirst(): boolean {
if (this.internalArray.length > 0) {
this.CurrentItem = this.internalArray[0];
this.CurrentPosition = 0;
return true;
}
return false;
}
}
/**
* Workaround for the generation of metadata.
*/
interface Iterable<T> {
[Symbol.iterator](): Iterator<T>;
}