UNPKG

@synergy-design-system/components

Version:
381 lines (371 loc) 12.8 kB
import { SynInput } from "./chunk.ZR6W3V2W.js"; import { calculatePageItemIndices, calculateTotalPages, clampPage, getMaxOptionCharCount, getPreviousOrDefault, getTotalPages, getTotalPagesCharCount, isValidNonNegativeInteger, isValidPositiveInteger, sanitizePageSizeOptions } from "./chunk.GXWM6IF5.js"; import { SynOption } from "./chunk.ZQGIIVB5.js"; import { pagination_styles_default } from "./chunk.DG2TSAVZ.js"; import { SynSelect } from "./chunk.SR6T6DFN.js"; import { SynDivider } from "./chunk.JDCOIHSN.js"; import { SynIconButton } from "./chunk.5XHXH7QA.js"; import { enableDefaultSettings } from "./chunk.WYMKPLYK.js"; import { LocalizeController } from "./chunk.ZXMSJF2M.js"; import { component_styles_default } from "./chunk.2NT3B5WJ.js"; import { SynergyElement } from "./chunk.L2IYPRPF.js"; import { __decorateClass } from "./chunk.VK2FVWOF.js"; // src/components/pagination/pagination.component.ts import { html, nothing } from "lit"; import { property } from "lit/decorators.js"; import { createRef, ref } from "lit/directives/ref.js"; var SynPagination = class extends SynergyElement { constructor() { super(...arguments); this.baseRef = createRef(); this.localize = new LocalizeController(this); this.divider = false; this.disabled = false; this.size = "medium"; this.currentPage = 1; this.pageSize = 25; this.pageSizeOptions = [10, 25, 50, 100]; this.totalItems = 0; this.variant = "full"; this.ariaLabel = "Pagination"; } pageChangedViaUserInput(e) { const newPage = e.target.valueAsNumber; if (!isValidPositiveInteger(newPage)) { return; } this.updateCurrentPage(newPage); } navigationClicked(event, newPage) { var _a; (_a = event.currentTarget) == null ? void 0 : _a.blur(); this.updateCurrentPage(newPage); } sanitizeInvalidPropertyValues(changed) { if (changed.has("pageSize") && !isValidPositiveInteger(this.pageSize)) { this.pageSize = getPreviousOrDefault( changed, "pageSize", SynPagination.DEFAULT_PAGE_SIZE, isValidPositiveInteger ); } if (changed.has("currentPage") && !isValidPositiveInteger(this.currentPage)) { this.currentPage = getPreviousOrDefault( changed, "currentPage", SynPagination.DEFAULT_CURRENT_PAGE, isValidPositiveInteger ); } if (changed.has("totalItems") && !isValidNonNegativeInteger(this.totalItems)) { this.totalItems = getPreviousOrDefault( changed, "totalItems", SynPagination.DEFAULT_TOTAL_ITEMS, isValidNonNegativeInteger ); } if (changed.has("pageSizeOptions")) { const sanitizedPageSizeOptions = sanitizePageSizeOptions(this.pageSizeOptions); const hasChanged = this.pageSizeOptions.length !== sanitizedPageSizeOptions.length || this.pageSizeOptions.some((option, index) => option !== sanitizedPageSizeOptions[index]); if (hasChanged) { this.pageSizeOptions = sanitizedPageSizeOptions; } } } /** * Updates the current page number and emits a "syn-pagination-page-changed" event with the new and previous page numbers. * This method is called when the page number is changed via the page input or the navigation buttons. */ updateCurrentPage(newPage) { const totalPages = getTotalPages(this.pageSize, this.totalItems); const safeNewPage = clampPage(newPage, totalPages); const { currentPage } = this; if (safeNewPage === currentPage) { return; } this.emit("syn-pagination-page-changed", { detail: { currentPage: safeNewPage, previousPage: currentPage } }); this.currentPage = safeNewPage; } /** * Called when the page size is changed via the page size select. * Emits a `syn-pagination-page-size-changed` event with the new and previous page sizes. */ pageSizeChanged(e) { const { currentPage, pageSize } = this; const currentTarget = e.target; const { value } = currentTarget; const nextPageSize = parseInt(value, 10); if (!Number.isSafeInteger(nextPageSize) || nextPageSize <= 0) return; const oldStartIndex = (currentPage - 1) * pageSize + 1; const nextTotalPages = getTotalPages(nextPageSize, this.totalItems); const nextPage = clampPage( Math.floor((oldStartIndex - 1) / nextPageSize) + 1, nextTotalPages ); this.pageSize = nextPageSize; this.currentPage = nextPage; this.emit("syn-pagination-page-size-changed", { detail: { currentPageSize: nextPageSize, previousPageSize: pageSize } }); if (nextPage !== currentPage) { this.emit("syn-pagination-page-changed", { detail: { currentPage: nextPage, previousPage: currentPage } }); } } willUpdate(changed) { super.willUpdate(changed); this.sanitizeInvalidPropertyValues(changed); const previousPageSizeOptions = changed.get("pageSizeOptions"); const hasPreviousPageSizeOptions = Array.isArray(previousPageSizeOptions); if (hasPreviousPageSizeOptions && !this.pageSizeOptions.includes(this.pageSize)) { const nextPageSize = this.pageSizeOptions[0]; const oldStartIndex = (this.currentPage - 1) * this.pageSize + 1; const nextTotalPages = getTotalPages(nextPageSize, this.totalItems); const nextPage = clampPage( Math.floor((oldStartIndex - 1) / nextPageSize) + 1, nextTotalPages ); this.pageSize = nextPageSize; this.currentPage = nextPage; } if (changed.has("currentPage") || changed.has("pageSize") || changed.has("totalItems")) { const totalPages = getTotalPages(this.pageSize, this.totalItems); const clamped = clampPage(this.currentPage, totalPages); if (clamped !== this.currentPage) { this.currentPage = clamped; } } } updated(changed) { super.updated(changed); if (changed.has("pageSizeOptions") || changed.has("pageSize") || changed.has("totalItems")) { const maxOptionCharCount = getMaxOptionCharCount(this.pageSizeOptions); const totalPagesCharCount = getTotalPagesCharCount(this.pageSize, this.totalItems); const baseElement = this.baseRef.value; if (baseElement) { baseElement.style.setProperty("--pagination-page-size-option-char-count", String(maxOptionCharCount)); baseElement.style.setProperty("--pagination-total-pages-char-count", String(totalPagesCharCount)); } } } render() { const totalPages = calculateTotalPages(this.totalItems, this.pageSize); const isCompact = this.variant === "compact"; const isDisabled = this.disabled || totalPages === 0; const pageItemIndices = calculatePageItemIndices(this.totalItems, this.pageSize, this.currentPage); const selectedPageSizeOption = this.pageSizeOptions.includes(this.pageSize) ? this.pageSize : this.pageSizeOptions[0]; const isFirstPage = this.currentPage === 1; const isLastPage = this.currentPage === totalPages; return html` ${this.divider ? html`<syn-divider part="divider"></syn-divider>` : nothing} <nav aria-label=${this.ariaLabel} class="pagination" part="base" ${ref(this.baseRef)} > ${!isCompact ? html` <div class="pagination__page-size-select-wrapper" part="page-size-select-wrapper"> <syn-select class="pagination__page-size-select" ?disabled=${isDisabled} label=${this.localize.term("paginationItemsPerPage")} part="page-size-select" value=${selectedPageSizeOption} size=${this.size} @syn-change=${this.pageSizeChanged} > ${this.pageSizeOptions.map((option) => html` <syn-option value="${option}"> ${option} </syn-option> `)} </syn-select> <!-- /.pagination__page-size-select --> <span part="page-item-summary"> ${this.localize.term( "paginationItemSummary", pageItemIndices.startIndex, pageItemIndices.endIndex, this.totalItems )} </span> <!-- /.pagination__page-item-summary --> </div> <!-- /.pagination__page-size-select-wrapper --> ` : nothing} <div class="pagination__navigation" part="navigation"> <section> <syn-icon-button @click=${(e) => this.navigationClicked(e, 1)} color="primary" ?disabled=${isFirstPage || isDisabled} label=${this.localize.term("paginationFirstPage")} library="system" name="first-page" part="navigation-action" size=${this.size} ></syn-icon-button> <syn-icon-button @click=${(e) => this.navigationClicked(e, this.currentPage - 1)} color="primary" ?disabled=${isFirstPage || isDisabled} label=${this.localize.term("paginationPreviousPage")} library="system" name="previous-page" part="navigation-action" size=${this.size} ></syn-icon-button> </section> <section part="page-input-section"> <syn-input class="pagination__page-input" ?disabled=${isDisabled} label=${this.localize.term("paginationInputLabel")} max=${totalPages} min="1" no-spin-buttons numeric-strategy="modern" part="page-input" size=${this.size} @syn-change=${this.pageChangedViaUserInput} type="number" value=${this.currentPage} ></syn-input> <span>${this.localize.term("paginationOfTotalPages", totalPages)}</span> </section> <section> <syn-icon-button @click=${(e) => this.navigationClicked(e, this.currentPage + 1)} color="primary" ?disabled=${isLastPage || isDisabled} label=${this.localize.term("paginationNextPage")} library="system" name="next-page" part="navigation-action" size=${this.size} ></syn-icon-button> <syn-icon-button @click=${(e) => this.navigationClicked(e, totalPages)} color="primary" ?disabled=${isLastPage || isDisabled} label=${this.localize.term("paginationLastPage")} library="system" name="last-page" part="navigation-action" size=${this.size} ></syn-icon-button> </section> </div> <!-- /.pagination__navigation --> </nav> `; } }; SynPagination.DEFAULT_PAGE_SIZE = 25; SynPagination.DEFAULT_CURRENT_PAGE = 1; SynPagination.DEFAULT_TOTAL_ITEMS = 0; SynPagination.styles = [ component_styles_default, pagination_styles_default ]; SynPagination.dependencies = { "syn-divider": SynDivider, "syn-icon-button": SynIconButton, "syn-input": SynInput, "syn-option": SynOption, "syn-select": SynSelect }; __decorateClass([ property({ type: Boolean }) ], SynPagination.prototype, "divider", 2); __decorateClass([ property({ reflect: true, type: Boolean }) ], SynPagination.prototype, "disabled", 2); __decorateClass([ property({ reflect: true }) ], SynPagination.prototype, "size", 2); __decorateClass([ property({ attribute: "current-page", reflect: true, type: Number }) ], SynPagination.prototype, "currentPage", 2); __decorateClass([ property({ attribute: "page-size", reflect: true, type: Number }) ], SynPagination.prototype, "pageSize", 2); __decorateClass([ property({ attribute: "page-size-options", converter: { fromAttribute: (value) => value.split(",").map((val) => { const num = parseInt(val.trim(), 10); return Number.isSafeInteger(num) ? num : null; }).filter(Boolean) }, type: Array }) ], SynPagination.prototype, "pageSizeOptions", 2); __decorateClass([ property({ attribute: "total-items", reflect: true, type: Number }) ], SynPagination.prototype, "totalItems", 2); __decorateClass([ property({ attribute: "variant", reflect: true }) ], SynPagination.prototype, "variant", 2); __decorateClass([ property({ attribute: "aria-label" }) ], SynPagination.prototype, "ariaLabel", 2); SynPagination = __decorateClass([ enableDefaultSettings("SynPagination") ], SynPagination); export { SynPagination }; //# sourceMappingURL=chunk.5FCLI2A7.js.map