File

projects/wms-framework/src/lib/baseframework/PagedCollectionView.ts

Description

Workaround for the generation of metadata.

Index

Methods

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>;
}

result-matching ""

    No results matching ""