UNPKG

@progress/kendo-angular-pager

Version:
1,105 lines (1,082 loc) 48.2 kB
/**----------------------------------------------------------------------------------------- * Copyright © 2025 Progress Software Corporation. All rights reserved. * Licensed under commercial license. See LICENSE.md in the project root for more information *-------------------------------------------------------------------------------------------*/ import { Component, Input, EventEmitter, Output, HostBinding, ElementRef, Renderer2, NgZone, HostListener, ContentChildren, QueryList, Optional, SkipSelf, inject, ViewChild, ChangeDetectorRef } from '@angular/core'; import { PagerTemplateDirective } from "./pager-template.directive"; import { anyChanged, isChanged, isDocumentAvailable, Keys, normalizeKeys, replaceMessagePlaceholder, ResizeSensorComponent } from "@progress/kendo-angular-common"; import { PagerContextService } from "./pager-context.service"; import { Subscription } from "rxjs"; import { DEFAULT_PAGE_SIZE_VALUES, getStylingClasses, DEFAULT_SIZE, calculatePadding, calculateGap, createMeasurementSpan, copyComputedStyles, positionOffScreen, getAllFocusableChildren } from '../util'; import { L10N_PREFIX, LocalizationService } from '@progress/kendo-angular-l10n'; import { validatePackage } from '@progress/kendo-licensing'; import { packageMetadata } from '../package-metadata'; import { PagerNavigationService } from './navigation.service'; import { PagerNumericButtonsComponent } from './pager-numeric-buttons.component'; import { PagerInfoComponent } from './pager-info.component'; import { PagerPageSizesComponent } from './pager-page-sizes.component'; import { PagerNextButtonsComponent } from './pager-next-buttons.component'; import { PagerInputComponent } from './pager-input.component'; import { PagerPrevButtonsComponent } from './pager-prev-buttons.component'; import { NgStyle, NgTemplateOutlet } from '@angular/common'; import { LocalizedMessagesDirective } from './localization/localized-messages.directive'; import { take } from 'rxjs/operators'; import * as i0 from "@angular/core"; import * as i1 from "./pager-context.service"; import * as i2 from "@progress/kendo-angular-l10n"; import * as i3 from "./navigation.service"; /** * Represents the Kendo UI Pager component for Angular. * Enables you to split a set of data into pages, providing a flexible and intuitive UI. * * @example * ```ts * @Component({ * selector: 'my-app', * template: ` * <kendo-pager * [total]="total" * [skip]="skip" * [pageSize]="pageSize" * (pageChange)="onPageChange($event)"> * </kendo-pager> * ` * }) * export class AppComponent { * public total = 200; * public skip = 0; * public pageSize = 10; * * public onPageChange(event: PageChangeEvent) { * this.skip = event.skip; * } * } * ``` * * @remarks * Supported children components are: * {@link PagerNumericButtonsComponent}, * {@link PagerInputComponent}, * {@link PagerPageSizesComponent}, * {@link PagerSpacerComponent}, * {@link PagerPrevButtonsComponent}, * {@link PagerNextButtonsComponent}, * {@link PagerInfoComponent}, * {@link CustomMessagesComponent} */ export class PagerComponent { pagerContext; element; localization; renderer; ngZone; cdr; navigationService; /** * Represents the collection of pager template directives. */ template; set numericButtons(buttons) { const newWidth = buttons ? buttons.nativeElement?.offsetWidth : 0; if (buttons && newWidth !== this.pagerDimensions.numericButtonsWidth) { this.pagerDimensions.numericButtonsWidth = newWidth; } } set pagerInput(input) { const newWidth = input ? input.nativeElement?.offsetWidth : 0; if (input && newWidth !== this.pagerDimensions.inputWidth) { this.pagerDimensions.inputWidth = newWidth; } if (input?.nativeElement && !this.pagerDimensions.gapPageText) { const innerStyledElement = input.nativeElement.querySelector('.k-pager-input'); this.pagerDimensions.gapPageText = calculateGap(innerStyledElement); } } pagerInputComponent; set pageSizes(sizes) { const newWidth = sizes ? sizes.nativeElement?.offsetWidth : 0; if (sizes && newWidth !== this.pagerDimensions.pageSizesWidth) { this.pagerDimensions.pageSizesWidth = newWidth; } } pageSizesComponent; /** * @hidden */ externalTemplate; /** * Specifies the total number of data items in the collection. * * @default 0 */ total = 0; /** * Specifies the number of data items to skip. * * @default 0 */ skip = 0; /** * Specifies the number of data items per page. */ pageSize; /** * Specifies the maximum number of numeric buttons before the buttons are collapsed. * * @default 10 */ buttonCount = 10; /** * Determines whether to display information about the current page and the total number of records. * * @default true */ info = true; /** * Specifies the type of the Pager. * * @default 'numeric' */ type = 'numeric'; /** * Displays a dropdown for selecting the page size. * When set to `true`, the dropdown contains the default list of options - 5, 10, 20. * To customize the list of options, set `pageSizeValues` to an array of the desired values. * The array can contain numbers and [PageSizeItem]({% slug api_pager_pagesizeitem %}) objects. */ set pageSizeValues(value) { if (typeof value === 'boolean') { this._pageSizeValues = value ? DEFAULT_PAGE_SIZE_VALUES : []; } else { this._pageSizeValues = value; } } get pageSizeValues() { return this._pageSizeValues; } /** * Determines whether to display the **Previous** and **Next** buttons. * * @default true */ previousNext = true; /** * Determines whether users can use dedicated shortcuts to interact with the Pager ([see example]({% slug keyboard_navigation_pager %})). * By default, navigation is enabled. To disable it and make the Pager content accessible in the normal tab sequence, set the property to `false`. * @default true */ set navigable(value) { this._navigable = value; this.navigationService.isNavigable = value; } get navigable() { return this._navigable; } /** * Specifies the padding of all Pager elements. * * @default 'medium' */ set size(size) { const newSize = size ? size : DEFAULT_SIZE; this.handleClasses(newSize, 'size'); this._size = newSize; } get size() { return this._size; } /** * Determines whether the Pager responsive functionality is enabled ([see example](slug:responsive_pager)). * * @default true */ responsive = true; /** * Determines whether the Pager adaptiveness functionality is enabled ([see example](slug:adaptive_mode_pager)). * * @default 'none' */ adaptiveMode = 'none'; /** * Fires when the current page of the Pager changes ([see example](slug:overview_pager)). * You have to handle the event and page the data. */ pageChange = new EventEmitter(); /** * Fires when the page size of the Pager changes. * You have to handle the event and page the data. * If the event is prevented, the page size remains unchanged ([see example]({% slug pager_events %})). */ pageSizeChange = new EventEmitter(); /** * @hidden */ pagerInputVisibilityChange = new EventEmitter(); /** * @hidden */ pageTextVisibilityChange = new EventEmitter(); /** * @hidden */ itemsTextVisibilityChange = new EventEmitter(); pagerClass = true; get responsiveClass() { return this.responsive; } widgetRole = 'application'; roleDescription = 'pager'; keyShortcuts = 'Enter ArrowRight ArrowLeft'; get hostTabindex() { return this.navigable ? '0' : '-1'; } get dir() { return this.direction; } /** * @hidden */ focusHandler(ev) { const isInnerNavigationEnabled = ev.target !== this.element.nativeElement; this.navigationService.toggleInnerNavigation(isInnerNavigationEnabled); } get totalPages() { return Math.ceil((this.total || 0) / this.pageSize); } get currentPage() { return Math.floor((this.skip || 0) / this.pageSize) + 1; } get templateContext() { const context = this._templateContext; context.totalPages = this.totalPages; context.total = this.total; context.skip = this.skip; context.pageSize = this.pageSize; context.currentPage = this.currentPage; return context; } /** * @hidden */ get showPageText() { return this._showPageText; } set showPageText(value) { this._showPageText = value; if (this.pagerInputComponent) { this.pagerInputComponent.showPageText = value; } this.pageTextVisibilityChange.emit(value); } /** * @hidden */ get showItemsText() { return this._showItemsText; } set showItemsText(value) { this._showItemsText = value; if (this.pageSizesComponent) { this.pageSizesComponent.showItemsText = value; } this.itemsTextVisibilityChange.emit(value); } /** * @hidden */ get showInput() { return this._showInput; } set showInput(value) { this._showInput = value; this.pagerInputVisibilityChange.emit(value); } /** * @hidden */ initialized = false; subscriptions = new Subscription(); _templateContext = {}; _pageSizeValues = DEFAULT_PAGE_SIZE_VALUES; direction; isInnerNavigationEnabled = false; _navigable = true; _size = DEFAULT_SIZE; _showInput = true; _showPageText = true; _showItemsText = true; _isAllSelected = false; /** * Stores the measurements of various Pager elements. * These dimensions are used for responsive layout calculations. * @hidden */ pagerDimensions = { padding: 0, numericButtonsWidth: 0, inputWidth: 0, pageSizesWidth: 0, sizesTextWidth: 0, pageTextWidth: 0, infoTextWidth: 0, gapNumbersSizes: 0, gapSizesInfo: 0, gapPageText: 0, width: 0 }; constructor(pagerContext, element, localization, renderer, ngZone, cdr, navigationService) { this.pagerContext = pagerContext; this.element = element; this.localization = localization; this.renderer = renderer; this.ngZone = ngZone; this.cdr = cdr; this.navigationService = navigationService; validatePackage(packageMetadata); this.direction = localization.rtl ? 'rtl' : 'ltr'; if (!navigationService) { this.navigationService = inject(PagerNavigationService); } if (!pagerContext) { this.pagerContext = inject(PagerContextService); } this.pagerContext.localization = localization; } ngOnInit() { this.subscriptions.add(this.pagerContext.pageChange.subscribe(this.changePage.bind(this))); this.subscriptions.add(this.pagerContext.pageSizeChange.subscribe(this.changePageSize.bind(this))); this.subscriptions.add(this.localization.changes.subscribe(({ rtl }) => { this.direction = rtl ? 'rtl' : 'ltr'; this.measureAllTextWidths(); if (this.responsive) { this.resizeHandler(); } })); this.subscriptions.add(this.navigationService.innerNavigationChange.subscribe(this.innerNavigationChange.bind(this))); if (this.navigable) { const wrapper = this.element.nativeElement; this.ngZone.runOutsideAngular(() => { this.subscriptions.add(this.renderer.listen(wrapper, 'keydown', this.keyDownHandler.bind(this))); }); } } /** * Gets the maximum number of items displayed on the current page. */ get maxItems() { return Math.min(this.currentPage * this.pageSize, this.total); } ngAfterViewInit() { this.renderer.setAttribute(this.element.nativeElement, 'aria-label', this.ariaLabel); this.subscriptions.add(this.template.changes.subscribe(() => { this.measureAllTextWidths(); if (this.responsive) { this.resizeHandler(false); } })); this.handleClasses(this.size, 'size'); this.setPagerDimensions(); this.ngZone.onStable.pipe(take(1)).subscribe(() => { if (this.type !== 'input') { this.showInput = false; } this.responsive && this.resizeHandler(); }); if (!isDocumentAvailable()) { this.initialized = true; return; } this.ngZone.runOutsideAngular(() => { setTimeout(() => { this.initialized = true; this.cdr.markForCheck(); }, 0); }); } ngOnChanges(changes) { if (anyChanged(["pageSize", "skip", "total"], changes, false)) { const previousTotal = changes['total']?.previousValue; const currentTotal = this.total; let pageSizeAdjusted = false; if (this._isAllSelected && previousTotal && currentTotal !== previousTotal && this.pageSize === previousTotal) { this.pageSize = currentTotal; pageSizeAdjusted = true; } const previousButtonCount = Math.min(this.buttonCount, (changes['total']?.previousValue || this.total) / (changes['pageSize']?.previousValue || this.pageSize)); this.pagerContext.notifyChanges({ pageSize: this.pageSize, skip: this.skip, total: this.total, isAllSelected: this._isAllSelected }); this.pagerDimensions.numericButtonsWidth = (this.pagerDimensions.numericButtonsWidth * Math.min(this.buttonCount, this.total / this.pageSize)) / previousButtonCount; this.renderer.setAttribute(this.element.nativeElement, 'aria-label', this.ariaLabel); if (this.responsive) { this.resizeHandler(false); } if (pageSizeAdjusted) { Promise.resolve().then(() => { this.pageChange.emit({ skip: this.skip, take: currentTotal }); this.cdr.detectChanges(); }); } } if (anyChanged(["pageSizeValues", "previousNext", "buttonCount"], changes, true)) { if (this.responsive) { this.resizeHandler(false); } } if (isChanged('responsive', changes, true)) { if (changes['responsive'].currentValue && !changes['responsive'].previousValue) { this.measureAllTextWidths(); this.resizeHandler(false); } if (!this.responsive) { this.showInput = this.type === 'input'; this.showElements(this.element.nativeElement.offsetWidth, this.pagerDimensions.width); } } if (isChanged('type', changes, true)) { this.showNumericButtonsResponsive(); if (this.responsive) { this.resizeHandler(false); } } } ngOnDestroy() { this.subscriptions.unsubscribe(); } /** * @hidden */ changePage(event) { this.pageChange.emit(event); } /** * @hidden */ changePageSize(event) { this.pageSizeChange.emit(event); if (!event.isDefaultPrevented()) { if (event.newPageSize === 'all') { this._isAllSelected = true; this.pageChange.emit({ skip: 0, take: this.total }); } else { this._isAllSelected = false; this.pageChange.emit({ skip: 0, take: event.newPageSize }); } } } /** * @hidden */ onPageSizeChange(event) { this.pageSizeChange.emit(event); if (!event.isDefaultPrevented()) { this.pageChange.emit({ skip: this.skip, take: event.newPageSize }); } } /** * @hidden */ resizeHandler = (compareWidth = true) => { if (this.template?.first && !this.responsive) { return; } if (!isDocumentAvailable() || !this.element?.nativeElement) { this.initialized = true; return; } let pagerWidth = this.element.nativeElement.offsetWidth; if (pagerWidth <= 0) { return; } if (compareWidth && pagerWidth === this.pagerDimensions.width) { return; } else { this.pagerDimensions.width = pagerWidth; } this.ngZone.runOutsideAngular(() => { setTimeout(() => { if (this.template?.first && !this.responsive) { return; } const numericButtonsWrapperElement = this.element.nativeElement.querySelector('.k-pager-numbers-wrap'); const pagerInfoElement = this.pagerInfoElement(); const pagerPageSizes = this.sizesDropDownElement(); let elementsWidths = numericButtonsWrapperElement?.offsetWidth + (pagerPageSizes?.offsetWidth || 0) + (pagerInfoElement?.offsetWidth > 0 ? Math.min(this.pagerDimensions.infoTextWidth) : 0); if (this.isElementVisible(pagerInfoElement)) { elementsWidths += this.pagerDimensions.gapSizesInfo; } pagerWidth -= this.pagerDimensions.padding; if (this.isElementVisible(pagerPageSizes)) { pagerWidth -= this.pagerDimensions.gapNumbersSizes; } if (pagerWidth < 0) { return; } this.showElements(pagerWidth, elementsWidths); if (!this.initialized) { this.ngZone.onStable.pipe(take(1)).subscribe(() => this.initialized = true); } }); }); }; // use selectors to get the element even when used in a template sizesDropDownElement = () => this.element.nativeElement.querySelector('.k-pager-sizes'); pagerInputElement = () => this.element.nativeElement.querySelector('.k-pager-input'); pagerInfoElement = () => this.element.nativeElement.querySelector('.k-pager-info'); numericButtonsElement = () => this.element.nativeElement.querySelector('kendo-datapager-numeric-buttons, kendo-pager-numeric-buttons'); responsiveDropDownElement = { name: 'sizeDropDown', isEnabled: () => this.showPageSizes, isVisible: () => this.isElementVisible(this.sizesDropDownElement()), width: () => this.pagerDimensions.pageSizesWidth + this.pagerDimensions.gapNumbersSizes - this.pagerDimensions.sizesTextWidth, show: () => { this.ngZone.run(() => this.showItemsText = false); this.showElement(this.sizesDropDownElement()); }, hide: () => { this.hideElement(this.sizesDropDownElement()); } }; responsiveDropDownTextElement = { name: 'itemsDropDownText', isEnabled: () => this.showPageSizes, isVisible: () => this.isElementVisible(this.sizesDropDownElement()) && this.showItemsText, width: () => this.pagerDimensions.sizesTextWidth + this.pagerDimensions.gapPageText, show: () => this.ngZone.run(() => this.showItemsText = true), hide: () => this.ngZone.run(() => this.showItemsText = false) }; responsivePageTextElement = { name: 'pageText', isEnabled: () => this.isElementVisible(this.pagerInputElement()), isVisible: () => this.showPageText, width: () => this.pagerDimensions.pageTextWidth + this.pagerDimensions.gapPageText, show: () => this.ngZone.run(() => this.showPageText = true), hide: () => this.ngZone.run(() => this.showPageText = false) }; responsiveInfoTextElement = { name: 'infoText', isEnabled: () => this.info, isVisible: () => this.isElementVisible(this.pagerInfoElement()), width: () => this.pagerDimensions.infoTextWidth + this.pagerDimensions.gapSizesInfo, show: () => { this.ngZone.run(() => { this.showElement(this.pagerInfoElement()); }); }, hide: () => { this.ngZone.run(() => { this.hideElement(this.pagerInfoElement()); }); } }; responsiveNumericButtonsElement = { name: 'numericButtons', isEnabled: () => this.type === 'numeric', isVisible: () => this.isElementVisible(this.numericButtonsElement()), width: () => this.pagerDimensions.numericButtonsWidth - this.pagerDimensions.inputWidth, show: () => { this.showElement(this.numericButtonsElement()); this.ngZone.run(() => { this.showInput = false; this.cdr.markForCheck(); }); }, hide: () => { this.hideElement(this.numericButtonsElement()); this.ngZone.run(() => { this.showInput = true; this.showPageText = true; this.cdr.markForCheck(); }); } }; /** * Contains all elements that are subject to responsive toggling. * These elements will be shown or hidden based on the available space. * The order of elements in the array defines the priority of visibility. * This array allows us to work with the elements without performing element-specific logic in other methods. */ responsiveElements = [ this.responsiveDropDownElement, this.responsiveDropDownTextElement, this.responsivePageTextElement, this.responsiveInfoTextElement, this.responsiveNumericButtonsElement ]; get ariaLabel() { const localizationMsg = this.localization.get('ariaLabel') || ''; return replaceMessagePlaceholder(replaceMessagePlaceholder(localizationMsg, 'currentPage', this.currentPage.toString()), 'totalPages', this.totalPages.toString()); } keyDownHandler(e) { const target = e.target; const wrapper = this.element.nativeElement; // on some keyboards arrow keys, PageUp/Down, and Home/End are mapped to Numpad keys const code = normalizeKeys(e); const isArrowLeftOrPageUp = code === Keys.ArrowLeft || code === Keys.PageUp; const isArrowRightOrPageDown = code === Keys.ArrowRight || code === Keys.PageDown; const isEnter = code === Keys.Enter || code === Keys.NumpadEnter; const isHome = code === Keys.Home; const isEnd = code === Keys.End; const isEsc = code === Keys.Escape; const isTab = code === Keys.Tab; const isFirstPage = this.currentPage === 1; const isLastPage = this.currentPage === this.totalPages; this.ngZone.run(() => { if (isHome) { if (e.target !== wrapper) { return; } e.preventDefault(); if (!isFirstPage) { this.pagerContext.changePage(0); } } else if (isEnd) { e.preventDefault(); if (e.target !== wrapper) { return; } if (!isLastPage) { this.pagerContext.changePage(this.totalPages - 1); } } else if (this.isInnerNavigationEnabled) { if (isEsc) { this.navigationService.toggleInnerNavigation(false); wrapper.focus(); } else if (isTab) { this.navigationService.keepFocusWithinComponent(wrapper, target, e); } } else { if (e.target !== wrapper) { return; } if (isArrowLeftOrPageUp) { e.preventDefault(); if (!isFirstPage) { this.pagerContext.prevPage(); } } else if (isArrowRightOrPageDown) { e.preventDefault(); if (!isLastPage) { this.pagerContext.nextPage(); } } else if (isEnter) { e.preventDefault(); let [firstFocusable] = this.navigationService.getFirstAndLastFocusable(wrapper); if (firstFocusable.getAttribute('aria-disabled') === 'true') { firstFocusable = Array.from(getAllFocusableChildren(wrapper)).find((el) => !el.getAttribute('aria-disabled')); const input = firstFocusable.querySelector('input'); if (input) { firstFocusable = input; } } this.navigationService.toggleInnerNavigation(true); firstFocusable?.focus(); } } }); } innerNavigationChange(value) { this.isInnerNavigationEnabled = value; } handleClasses(value, input) { const elem = this.element.nativeElement; const classes = getStylingClasses('pager', input, this[input], value); if (classes.toRemove) { this.renderer.removeClass(elem, classes.toRemove); } if (classes.toAdd) { this.renderer.addClass(elem, classes.toAdd); } } showElements(availableWidth, currentWidth) { let index = 0; while (index < this.responsiveElements.length) { const element = this.responsiveElements[index]; if (!element.isEnabled() || element.isVisible()) { index++; continue; } const elementWidth = element.width(); // If not responsive, show all elements regardless of available width. If responsive, check if the current element fits if (this.responsive && (currentWidth + elementWidth) > availableWidth) { index++; break; } element.show(); currentWidth += elementWidth; index++; } // Do not hide elements if not responsive. If all elements fit, do not hide any. if (!this.responsive || currentWidth <= availableWidth) { return; } let hideIndex = Math.min(index - 1, this.responsiveElements.length - 1); while (hideIndex >= 0 && currentWidth > availableWidth) { const element = this.responsiveElements[hideIndex]; if (!element.isEnabled() || !element.isVisible()) { hideIndex--; continue; } const elementWidth = element.width(); element.hide(); currentWidth -= elementWidth; hideIndex--; } } isElementVisible(element) { return element && !element?.classList.contains('k-hidden'); } hideElement(element) { if (element) { this.renderer.addClass(element, 'k-hidden'); } } showElement(element) { if (element) { this.renderer.removeClass(element, 'k-hidden'); } } measureAllTextWidths() { if (!isDocumentAvailable()) { return; } const existingInfo = this.pagerInfoElement(); const existingInput = this.pagerInputElement(); const existingSizes = this.sizesDropDownElement(); // create a single measurement container const measureContainer = this.renderer.createElement('div'); positionOffScreen(this.renderer, measureContainer); this.renderer.appendChild(this.element.nativeElement, measureContainer); const infoSpan = createMeasurementSpan(this.renderer, measureContainer, 'k-pager-info'); const pageSpan = createMeasurementSpan(this.renderer, measureContainer, 'k-pager-input'); const sizesSpan = createMeasurementSpan(this.renderer, measureContainer, 'k-pager-sizes'); const infoText = `${this.currentPage} - ${this.maxItems} ${this.localization.get('of')} ${this.total} ${this.localization.get('items')}`; this.renderer.setProperty(infoSpan, 'textContent', infoText); this.renderer.setProperty(pageSpan, 'textContent', this.localization.get('page')); this.renderer.setProperty(sizesSpan, 'textContent', this.localization.get('itemsPerPage')); // copy computed styles if available if (existingInfo) copyComputedStyles(this.renderer, existingInfo, infoSpan); if (existingInput) copyComputedStyles(this.renderer, existingInput, pageSpan); if (existingSizes) copyComputedStyles(this.renderer, existingSizes, sizesSpan); // force a reflow to ensure measurements are accurate measureContainer.getBoundingClientRect(); this.pagerDimensions.infoTextWidth = infoSpan?.offsetWidth; if (this.pagerDimensions.inputWidth && this.pagerDimensions.pageTextWidth) { this.pagerDimensions.inputWidth = this.pagerDimensions.inputWidth - this.pagerDimensions.pageTextWidth + pageSpan.offsetWidth; } this.pagerDimensions.pageTextWidth = pageSpan?.offsetWidth; if (this.pagerDimensions.pageSizesWidth && this.pagerDimensions.sizesTextWidth) { this.pagerDimensions.pageSizesWidth = this.pagerDimensions.pageSizesWidth - this.pagerDimensions.sizesTextWidth + sizesSpan.offsetWidth; } this.pagerDimensions.sizesTextWidth = sizesSpan?.offsetWidth; this.renderer.removeChild(this.element.nativeElement, measureContainer); } showNumericButtonsResponsive() { if (!isDocumentAvailable() || !this.element?.nativeElement) { return; } const numericButtonsElement = this.numericButtonsElement(); const hasNumericButtons = this.numericButtons || numericButtonsElement; const hasInput = this.pagerInput || this.pagerInputElement(); if (!this.responsive || (!hasNumericButtons && !hasInput)) { this.showInput = this.type === 'input'; return; } const pagerInfoElement = this.pagerInfoElement(); if (this.type === 'input' || !this.isElementVisible(pagerInfoElement)) { this.showInput = true; return; } if (this.isElementVisible(numericButtonsElement)) { this.showInput = false; return; } const pagerWidth = this.element.nativeElement?.offsetWidth; const numericButtonsWrapperElement = this.element.nativeElement.querySelector('.k-pager-numbers-wrap'); const elementsWidths = numericButtonsWrapperElement?.offsetWidth + this.pagerDimensions.pageSizesWidth + this.pagerDimensions.infoTextWidth + this.pagerDimensions.gapSizesInfo; const hasAvailableWidth = pagerWidth > elementsWidths - this.pagerDimensions.inputWidth + this.pagerDimensions.numericButtonsWidth; this.showInput = !hasAvailableWidth; } get showPageSizes() { if (typeof this.pageSizeValues === 'boolean') { return this.pageSizeValues; } return this.pageSizeValues?.length > 0; } setPagerDimensions() { this.measureAllTextWidths(); !this.numericButtons && (this.pagerDimensions.numericButtonsWidth = this.element.nativeElement.querySelector('.k-pager-numbers')?.offsetWidth ?? 0); !this.pagerInput && (this.pagerDimensions.inputWidth = this.element.nativeElement.querySelector('kendo-pager-input')?.offsetWidth ?? 0); !this.pageSizes && (this.pagerDimensions.pageSizesWidth = this.sizesDropDownElement()?.offsetWidth ?? 0); const padding = calculatePadding(this.element.nativeElement); this.pagerDimensions.padding = padding.padding; this.pagerDimensions.gapNumbersSizes = padding.gapNumbersSizes; this.pagerDimensions.gapSizesInfo = padding.gapSizesInfo; const innerStyledElement = this.pagerInputElement(); !this.pagerDimensions.gapPageText && (this.pagerDimensions.gapPageText = calculateGap(innerStyledElement)); } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: PagerComponent, deps: [{ token: i1.PagerContextService, optional: true, skipSelf: true }, { token: i0.ElementRef }, { token: i2.LocalizationService }, { token: i0.Renderer2 }, { token: i0.NgZone }, { token: i0.ChangeDetectorRef }, { token: i3.PagerNavigationService, optional: true, skipSelf: true }], target: i0.ɵɵFactoryTarget.Component }); static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: PagerComponent, isStandalone: true, selector: "kendo-datapager, kendo-pager", inputs: { externalTemplate: "externalTemplate", total: "total", skip: "skip", pageSize: "pageSize", buttonCount: "buttonCount", info: "info", type: "type", pageSizeValues: "pageSizeValues", previousNext: "previousNext", navigable: "navigable", size: "size", responsive: "responsive", adaptiveMode: "adaptiveMode" }, outputs: { pageChange: "pageChange", pageSizeChange: "pageSizeChange", pagerInputVisibilityChange: "pagerInputVisibilityChange", pageTextVisibilityChange: "pageTextVisibilityChange", itemsTextVisibilityChange: "itemsTextVisibilityChange" }, host: { listeners: { "focusin": "focusHandler($event)" }, properties: { "class.k-pager": "this.pagerClass", "class.k-pager-responsive": "this.responsiveClass", "attr.role": "this.widgetRole", "attr.aria-roledescription": "this.roleDescription", "attr.aria-keyshortcuts": "this.keyShortcuts", "attr.tabindex": "this.hostTabindex", "attr.dir": "this.dir" } }, providers: [ LocalizationService, PagerContextService, PagerNavigationService, { provide: L10N_PREFIX, useValue: 'kendo.pager' } ], queries: [{ propertyName: "template", predicate: PagerTemplateDirective }], viewQueries: [{ propertyName: "numericButtons", first: true, predicate: PagerNumericButtonsComponent, descendants: true, read: ElementRef }, { propertyName: "pagerInput", first: true, predicate: PagerInputComponent, descendants: true, read: ElementRef }, { propertyName: "pagerInputComponent", first: true, predicate: PagerInputComponent, descendants: true }, { propertyName: "pageSizes", first: true, predicate: PagerPageSizesComponent, descendants: true, read: ElementRef }, { propertyName: "pageSizesComponent", first: true, predicate: PagerPageSizesComponent, descendants: true }], exportAs: ["kendoDataPager", "kendoPager"], usesOnChanges: true, ngImport: i0, template: ` <ng-container kendoPagerLocalizedMessages i18n-ariaLabel="kendo.pager.ariaLabel|The value of the aria-label attribute of the Pager" ariaLabel="{{ 'Page navigation, page {currentPage} of {totalPages}' }}" i18n-firstPage="kendo.pager.firstPage|The label for the first page button in the Pager" firstPage="Go to the first page" i18n-previousPage="kendo.pager.previousPage|The label for the previous page button in the Pager" previousPage="Go to the previous page" i18n-nextPage="kendo.pager.nextPage|The label for the next page button in the Pager" nextPage="Go to the next page" i18n-lastPage="kendo.pager.lastPage|The label for the last page button in the Pager" lastPage="Go to the last page" i18n-page="kendo.pager.page|The label before the current page number in the Pager" page="Page" i18n-of="kendo.pager.of|The label before the total pages number in the Pager" of="of" i18n-pageNumberInputTitle="kendo.pager.pageNumberInputTitle|The label for the pager input in the Pager" pageNumberInputTitle="Page Number" i18n-items="kendo.pager.items|The label after the total pages number in the Pager" items="items" i18n-itemsPerPage="kendo.pager.itemsPerPage|The label for the page size chooser in the Pager" itemsPerPage="items per page" i18n-selectPage="kendo.pager.selectPage|The text of the title and aria-label attributes applied to the page chooser in the Pager" selectPage="Select page" i18n-inputLabel="kendo.pager.inputLabel|The text of the aria-label attribute applied to the input element for entering the page number." inputLabel="Type a page number" > </ng-container> @if (template.first?.templateRef) { <ng-container [ngTemplateOutlet]="template.first?.templateRef" [ngTemplateOutletContext]="templateContext"> </ng-container> } @else { <div class="k-pager-numbers-wrap" [ngStyle]="{opacity: initialized ? null : '0'}"> @if (previousNext) { <kendo-pager-prev-buttons [size]="size"></kendo-pager-prev-buttons> } @if (type === 'numeric' && buttonCount > 0) { <kendo-pager-numeric-buttons [size]="size" [buttonCount]="buttonCount"> </kendo-pager-numeric-buttons> } @if (showInput) { <kendo-pager-input [size]="size"></kendo-pager-input> } @if (previousNext) { <kendo-pager-next-buttons [size]="size"></kendo-pager-next-buttons> } </div> @if (_pageSizeValues.length) { <kendo-pager-page-sizes [ngStyle]="{opacity: initialized ? null : '0'}" [size]="size" [pageSizes]="_pageSizeValues" [adaptiveMode]="adaptiveMode"> </kendo-pager-page-sizes> } @if (info) { <kendo-pager-info [ngStyle]="{opacity: initialized ? null : '0'}"> </kendo-pager-info> } } @if (responsive) { <kendo-resize-sensor (resize)="resizeHandler()"></kendo-resize-sensor> } `, isInline: true, dependencies: [{ kind: "directive", type: LocalizedMessagesDirective, selector: "[kendoPagerLocalizedMessages]" }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: PagerPrevButtonsComponent, selector: "kendo-datapager-prev-buttons, kendo-pager-prev-buttons", inputs: ["size"] }, { kind: "component", type: PagerNumericButtonsComponent, selector: "kendo-datapager-numeric-buttons, kendo-pager-numeric-buttons", inputs: ["buttonCount", "size"] }, { kind: "component", type: PagerInputComponent, selector: "kendo-datapager-input, kendo-pager-input", inputs: ["showPageText", "size"] }, { kind: "component", type: PagerNextButtonsComponent, selector: "kendo-datapager-next-buttons, kendo-pager-next-buttons", inputs: ["size"] }, { kind: "component", type: PagerPageSizesComponent, selector: "kendo-datapager-page-sizes, kendo-pager-page-sizes", inputs: ["showItemsText", "pageSizes", "size", "adaptiveMode"] }, { kind: "component", type: PagerInfoComponent, selector: "kendo-datapager-info, kendo-pager-info" }, { kind: "component", type: ResizeSensorComponent, selector: "kendo-resize-sensor", inputs: ["rateLimit"], outputs: ["resize"] }, { kind: "directive", type: NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }] }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: PagerComponent, decorators: [{ type: Component, args: [{ selector: 'kendo-datapager, kendo-pager', exportAs: 'kendoDataPager, kendoPager', providers: [ LocalizationService, PagerContextService, PagerNavigationService, { provide: L10N_PREFIX, useValue: 'kendo.pager' } ], template: ` <ng-container kendoPagerLocalizedMessages i18n-ariaLabel="kendo.pager.ariaLabel|The value of the aria-label attribute of the Pager" ariaLabel="{{ 'Page navigation, page {currentPage} of {totalPages}' }}" i18n-firstPage="kendo.pager.firstPage|The label for the first page button in the Pager" firstPage="Go to the first page" i18n-previousPage="kendo.pager.previousPage|The label for the previous page button in the Pager" previousPage="Go to the previous page" i18n-nextPage="kendo.pager.nextPage|The label for the next page button in the Pager" nextPage="Go to the next page" i18n-lastPage="kendo.pager.lastPage|The label for the last page button in the Pager" lastPage="Go to the last page" i18n-page="kendo.pager.page|The label before the current page number in the Pager" page="Page" i18n-of="kendo.pager.of|The label before the total pages number in the Pager" of="of" i18n-pageNumberInputTitle="kendo.pager.pageNumberInputTitle|The label for the pager input in the Pager" pageNumberInputTitle="Page Number" i18n-items="kendo.pager.items|The label after the total pages number in the Pager" items="items" i18n-itemsPerPage="kendo.pager.itemsPerPage|The label for the page size chooser in the Pager" itemsPerPage="items per page" i18n-selectPage="kendo.pager.selectPage|The text of the title and aria-label attributes applied to the page chooser in the Pager" selectPage="Select page" i18n-inputLabel="kendo.pager.inputLabel|The text of the aria-label attribute applied to the input element for entering the page number." inputLabel="Type a page number" > </ng-container> @if (template.first?.templateRef) { <ng-container [ngTemplateOutlet]="template.first?.templateRef" [ngTemplateOutletContext]="templateContext"> </ng-container> } @else { <div class="k-pager-numbers-wrap" [ngStyle]="{opacity: initialized ? null : '0'}"> @if (previousNext) { <kendo-pager-prev-buttons [size]="size"></kendo-pager-prev-buttons> } @if (type === 'numeric' && buttonCount > 0) { <kendo-pager-numeric-buttons [size]="size" [buttonCount]="buttonCount"> </kendo-pager-numeric-buttons> } @if (showInput) { <kendo-pager-input [size]="size"></kendo-pager-input> } @if (previousNext) { <kendo-pager-next-buttons [size]="size"></kendo-pager-next-buttons> } </div> @if (_pageSizeValues.length) { <kendo-pager-page-sizes [ngStyle]="{opacity: initialized ? null : '0'}" [size]="size" [pageSizes]="_pageSizeValues" [adaptiveMode]="adaptiveMode"> </kendo-pager-page-sizes> } @if (info) { <kendo-pager-info [ngStyle]="{opacity: initialized ? null : '0'}"> </kendo-pager-info> } } @if (responsive) { <kendo-resize-sensor (resize)="resizeHandler()"></kendo-resize-sensor> } `, standalone: true, imports: [LocalizedMessagesDirective, NgTemplateOutlet, PagerPrevButtonsComponent, PagerNumericButtonsComponent, PagerInputComponent, PagerNextButtonsComponent, PagerPageSizesComponent, PagerInfoComponent, ResizeSensorComponent, NgStyle] }] }], ctorParameters: () => [{ type: i1.PagerContextService, decorators: [{ type: Optional }, { type: SkipSelf }] }, { type: i0.ElementRef }, { type: i2.LocalizationService }, { type: i0.Renderer2 }, { type: i0.NgZone }, { type: i0.ChangeDetectorRef }, { type: i3.PagerNavigationService, decorators: [{ type: Optional }, { type: SkipSelf }] }], propDecorators: { template: [{ type: ContentChildren, args: [PagerTemplateDirective] }], numericButtons: [{ type: ViewChild, args: [PagerNumericButtonsComponent, { read: ElementRef }] }], pagerInput: [{ type: ViewChild, args: [PagerInputComponent, { read: ElementRef }] }], pagerInputComponent: [{ type: ViewChild, args: [PagerInputComponent] }], pageSizes: [{ type: ViewChild, args: [PagerPageSizesComponent, { read: ElementRef }] }], pageSizesComponent: [{ type: ViewChild, args: [PagerPageSizesComponent] }], externalTemplate: [{ type: Input }], total: [{ type: Input }], skip: [{ type: Input }], pageSize: [{ type: Input }], buttonCount: [{ type: Input }], info: [{ type: Input }], type: [{ type: Input }], pageSizeValues: [{ type: Input }], previousNext: [{ type: Input }], navigable: [{ type: Input }], size: [{ type: Input }], responsive: [{ type: Input }], adaptiveMode: [{ type: Input }], pageChange: [{ type: Output }], pageSizeChange: [{ type: Output }], pagerInputVisibilityChange: [{ type: Output }], pageTextVisibilityChange: [{ type: Output }], itemsTextVisibilityChange: [{ type: Output }], pagerClass: [{ type: HostBinding, args: ['class.k-pager'] }], responsiveClass: [{ type: HostBinding, args: ['class.k-pager-responsive'] }], widgetRole: [{ type: HostBinding, args: ['attr.role'] }], roleDescription: [{ type: HostBinding, args: ['attr.aria-roledescription'] }], keyShortcuts: [{ type: HostBinding, args: ['attr.aria-keyshortcuts'] }], hostTabindex: [{ type: HostBinding, args: ['attr.tabindex'] }], dir: [{ type: HostBinding, args: ['attr.dir'] }], focusHandler: [{ type: HostListener, args: ['focusin', ['$event']] }] } });