@synergy-design-system/components
Version:
381 lines (371 loc) • 12.8 kB
JavaScript
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}
-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
=${(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
=${(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}
-change=${this.pageChangedViaUserInput}
type="number"
value=${this.currentPage}
></syn-input>
<span>${this.localize.term("paginationOfTotalPages", totalPages)}</span>
</section>
<section>
<syn-icon-button
=${(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
=${(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