| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456 | /*! * All material copyright ESRI, All Rights Reserved, unless otherwise specified. * See https://github.com/Esri/calcite-components/blob/master/LICENSE.md for details. * v1.0.0-beta.97 */import { h, Fragment } from "@stencil/core";import { connectLocalized, disconnectLocalized, numberStringFormatter } from "../../utils/locale";import { CSS, TEXT } from "./resources";const maxPagesDisplayed = 5;export class Pagination {  constructor() {    //--------------------------------------------------------------------------    //    //  Public Properties    //    //--------------------------------------------------------------------------    /**     * When `true`, number values are displayed with a group separator corresponding to the language and country format.     */    this.groupSeparator = false;    /** Specifies the number of items per page. */    this.num = 20;    /** Specifies the starting item number. */    this.start = 1;    /** Specifies the total number of items. */    this.total = 0;    /**     * Accessible name for the component's next button.     *     * @default "Next"     */    this.textLabelNext = TEXT.nextLabel;    /**     * Accessible name for the component's previous button.     *     * @default "Previous"     */    this.textLabelPrevious = TEXT.previousLabel;    /** Specifies the size of the component. */    this.scale = "m";    //--------------------------------------------------------------------------    //    //  State    //    //--------------------------------------------------------------------------    this.effectiveLocale = "";    this.previousClicked = () => {      this.previousPage().then();      this.emitUpdate();    };    this.nextClicked = () => {      this.nextPage();      this.emitUpdate();    };    /**     * Returns a string representing the localized label value based on groupSeparator prop being on or off.     *     * @param value     */    this.determineGroupSeparator = (value) => {      numberStringFormatter.numberFormatOptions = {        locale: this.effectiveLocale,        numberingSystem: this.numberingSystem,        useGrouping: this.groupSeparator      };      return this.groupSeparator        ? numberStringFormatter.localize(value.toString())        : value.toString();    };  }  // --------------------------------------------------------------------------  //  //  Lifecycle  //  // --------------------------------------------------------------------------  connectedCallback() {    connectLocalized(this);  }  disconnectedCallback() {    disconnectLocalized(this);  }  // --------------------------------------------------------------------------  //  //  Public Methods  //  // --------------------------------------------------------------------------  /** Go to the next page of results. */  async nextPage() {    this.start = Math.min(this.getLastStart(), this.start + this.num);  }  /** Go to the previous page of results. */  async previousPage() {    this.start = Math.max(1, this.start - this.num);  }  // --------------------------------------------------------------------------  //  //  Private Methods  //  // --------------------------------------------------------------------------  getLastStart() {    const { total, num } = this;    const lastStart = total % num === 0 ? total - num : Math.floor(total / num) * num;    return lastStart + 1;  }  showLeftEllipsis() {    return Math.floor(this.start / this.num) > 3;  }  showRightEllipsis() {    return (this.total - this.start) / this.num > 3;  }  emitUpdate() {    const changePayload = {      start: this.start,      total: this.total,      num: this.num    };    this.calcitePaginationChange.emit(changePayload);    this.calcitePaginationUpdate.emit(changePayload);  }  //--------------------------------------------------------------------------  //  //  Render Methods  //  //--------------------------------------------------------------------------  renderPages() {    const lastStart = this.getLastStart();    let end;    let nextStart;    // if we don't need ellipses render the whole set    if (this.total / this.num <= maxPagesDisplayed) {      nextStart = 1 + this.num;      end = lastStart - this.num;    }    else {      // if we're within max pages of page 1      if (this.start / this.num < maxPagesDisplayed - 1) {        nextStart = 1 + this.num;        end = 1 + 4 * this.num;      }      else {        // if we're within max pages of last page        if (this.start + 3 * this.num >= this.total) {          nextStart = lastStart - 4 * this.num;          end = lastStart - this.num;        }        else {          nextStart = this.start - this.num;          end = this.start + this.num;        }      }    }    const pages = [];    while (nextStart <= end) {      pages.push(nextStart);      nextStart = nextStart + this.num;    }    return pages.map((page) => this.renderPage(page));  }  renderPage(start) {    const page = Math.floor(start / this.num) + (this.num === 1 ? 0 : 1);    const displayedPage = this.determineGroupSeparator(page);    return (h("button", { class: {        [CSS.page]: true,        [CSS.selected]: start === this.start      }, onClick: () => {        this.start = start;        this.emitUpdate();      } }, displayedPage));  }  renderLeftEllipsis() {    if (this.total / this.num > maxPagesDisplayed && this.showLeftEllipsis()) {      return h("span", { class: `${CSS.ellipsis} ${CSS.ellipsisStart}` }, "\u2026");    }  }  renderRightEllipsis() {    if (this.total / this.num > maxPagesDisplayed && this.showRightEllipsis()) {      return h("span", { class: `${CSS.ellipsis} ${CSS.ellipsisEnd}` }, "\u2026");    }  }  render() {    const { total, num, start } = this;    const prevDisabled = num === 1 ? start <= num : start < num;    const nextDisabled = num === 1 ? start + num > total : start + num > total;    return (h(Fragment, null, h("button", { "aria-label": this.textLabelPrevious, class: {        [CSS.previous]: true,        [CSS.disabled]: prevDisabled      }, disabled: prevDisabled, onClick: this.previousClicked }, h("calcite-icon", { flipRtl: true, icon: "chevronLeft", scale: "s" })), total > num ? this.renderPage(1) : null, this.renderLeftEllipsis(), this.renderPages(), this.renderRightEllipsis(), this.renderPage(this.getLastStart()), h("button", { "aria-label": this.textLabelNext, class: {        [CSS.next]: true,        [CSS.disabled]: nextDisabled      }, disabled: nextDisabled, onClick: this.nextClicked }, h("calcite-icon", { flipRtl: true, icon: "chevronRight", scale: "s" }))));  }  static get is() { return "calcite-pagination"; }  static get encapsulation() { return "shadow"; }  static get originalStyleUrls() {    return {      "$": ["pagination.scss"]    };  }  static get styleUrls() {    return {      "$": ["pagination.css"]    };  }  static get properties() {    return {      "groupSeparator": {        "type": "boolean",        "mutable": false,        "complexType": {          "original": "boolean",          "resolved": "boolean",          "references": {}        },        "required": false,        "optional": false,        "docs": {          "tags": [],          "text": "When `true`, number values are displayed with a group separator corresponding to the language and country format."        },        "attribute": "group-separator",        "reflect": true,        "defaultValue": "false"      },      "num": {        "type": "number",        "mutable": false,        "complexType": {          "original": "number",          "resolved": "number",          "references": {}        },        "required": false,        "optional": false,        "docs": {          "tags": [],          "text": "Specifies the number of items per page."        },        "attribute": "num",        "reflect": true,        "defaultValue": "20"      },      "numberingSystem": {        "type": "string",        "mutable": false,        "complexType": {          "original": "NumberingSystem",          "resolved": "\"arab\" | \"arabext\" | \"bali\" | \"beng\" | \"deva\" | \"fullwide\" | \"gujr\" | \"guru\" | \"hanidec\" | \"khmr\" | \"knda\" | \"laoo\" | \"latn\" | \"limb\" | \"mlym\" | \"mong\" | \"mymr\" | \"orya\" | \"tamldec\" | \"telu\" | \"thai\" | \"tibt\"",          "references": {            "NumberingSystem": {              "location": "import",              "path": "../../utils/locale"            }          }        },        "required": false,        "optional": true,        "docs": {          "tags": [],          "text": "Specifies the Unicode numeral system used by the component for localization."        },        "attribute": "numbering-system",        "reflect": false      },      "start": {        "type": "number",        "mutable": true,        "complexType": {          "original": "number",          "resolved": "number",          "references": {}        },        "required": false,        "optional": false,        "docs": {          "tags": [],          "text": "Specifies the starting item number."        },        "attribute": "start",        "reflect": true,        "defaultValue": "1"      },      "total": {        "type": "number",        "mutable": false,        "complexType": {          "original": "number",          "resolved": "number",          "references": {}        },        "required": false,        "optional": false,        "docs": {          "tags": [],          "text": "Specifies the total number of items."        },        "attribute": "total",        "reflect": true,        "defaultValue": "0"      },      "textLabelNext": {        "type": "string",        "mutable": false,        "complexType": {          "original": "string",          "resolved": "string",          "references": {}        },        "required": false,        "optional": false,        "docs": {          "tags": [{              "name": "default",              "text": "\"Next\""            }],          "text": "Accessible name for the component's next button."        },        "attribute": "text-label-next",        "reflect": false,        "defaultValue": "TEXT.nextLabel"      },      "textLabelPrevious": {        "type": "string",        "mutable": false,        "complexType": {          "original": "string",          "resolved": "string",          "references": {}        },        "required": false,        "optional": false,        "docs": {          "tags": [{              "name": "default",              "text": "\"Previous\""            }],          "text": "Accessible name for the component's previous button."        },        "attribute": "text-label-previous",        "reflect": false,        "defaultValue": "TEXT.previousLabel"      },      "scale": {        "type": "string",        "mutable": false,        "complexType": {          "original": "Scale",          "resolved": "\"l\" | \"m\" | \"s\"",          "references": {            "Scale": {              "location": "import",              "path": "../interfaces"            }          }        },        "required": false,        "optional": false,        "docs": {          "tags": [],          "text": "Specifies the size of the component."        },        "attribute": "scale",        "reflect": true,        "defaultValue": "\"m\""      }    };  }  static get states() {    return {      "effectiveLocale": {}    };  }  static get events() {    return [{        "method": "calcitePaginationUpdate",        "name": "calcitePaginationUpdate",        "bubbles": true,        "cancelable": false,        "composed": true,        "docs": {          "tags": [{              "name": "deprecated",              "text": "use calcitePaginationChange instead"            }],          "text": "Emits when the selected page changes."        },        "complexType": {          "original": "PaginationDetail",          "resolved": "PaginationDetail",          "references": {            "PaginationDetail": {              "location": "local"            }          }        }      }, {        "method": "calcitePaginationChange",        "name": "calcitePaginationChange",        "bubbles": true,        "cancelable": false,        "composed": true,        "docs": {          "tags": [{              "name": "see",              "text": "[PaginationDetail](https://github.com/Esri/calcite-components/blob/master/src/components/pagination/pagination.tsx#L23)"            }],          "text": "Emits when the selected page changes."        },        "complexType": {          "original": "PaginationDetail",          "resolved": "PaginationDetail",          "references": {            "PaginationDetail": {              "location": "local"            }          }        }      }];  }  static get methods() {    return {      "nextPage": {        "complexType": {          "signature": "() => Promise<void>",          "parameters": [],          "references": {            "Promise": {              "location": "global"            }          },          "return": "Promise<void>"        },        "docs": {          "text": "Go to the next page of results.",          "tags": []        }      },      "previousPage": {        "complexType": {          "signature": "() => Promise<void>",          "parameters": [],          "references": {            "Promise": {              "location": "global"            }          },          "return": "Promise<void>"        },        "docs": {          "text": "Go to the previous page of results.",          "tags": []        }      }    };  }  static get elementRef() { return "el"; }}
 |