@progress/kendo-angular-pager
Version:
Kendo UI Angular Pager
1,105 lines (1,082 loc) • 48.2 kB
JavaScript
/**-----------------------------------------------------------------------------------------
* 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>
(template.first?.templateRef) {
<ng-container
[ngTemplateOutlet]="template.first?.templateRef"
[ngTemplateOutletContext]="templateContext">
</ng-container>
} {
<div class="k-pager-numbers-wrap" [ngStyle]="{opacity: initialized ? null : '0'}">
(previousNext) {
<kendo-pager-prev-buttons [size]="size"></kendo-pager-prev-buttons>
}
(type === 'numeric' && buttonCount > 0) {
<kendo-pager-numeric-buttons
[size]="size"
[buttonCount]="buttonCount">
</kendo-pager-numeric-buttons>
}
(showInput) {
<kendo-pager-input [size]="size"></kendo-pager-input>
}
(previousNext) {
<kendo-pager-next-buttons [size]="size"></kendo-pager-next-buttons>
}
</div>
(_pageSizeValues.length) {
<kendo-pager-page-sizes
[ngStyle]="{opacity: initialized ? null : '0'}"
[size]="size"
[pageSizes]="_pageSizeValues"
[adaptiveMode]="adaptiveMode">
</kendo-pager-page-sizes>
}
(info) {
<kendo-pager-info [ngStyle]="{opacity: initialized ? null : '0'}">
</kendo-pager-info>
}
}
(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>
(template.first?.templateRef) {
<ng-container
[ngTemplateOutlet]="template.first?.templateRef"
[ngTemplateOutletContext]="templateContext">
</ng-container>
} {
<div class="k-pager-numbers-wrap" [ngStyle]="{opacity: initialized ? null : '0'}">
(previousNext) {
<kendo-pager-prev-buttons [size]="size"></kendo-pager-prev-buttons>
}
(type === 'numeric' && buttonCount > 0) {
<kendo-pager-numeric-buttons
[size]="size"
[buttonCount]="buttonCount">
</kendo-pager-numeric-buttons>
}
(showInput) {
<kendo-pager-input [size]="size"></kendo-pager-input>
}
(previousNext) {
<kendo-pager-next-buttons [size]="size"></kendo-pager-next-buttons>
}
</div>
(_pageSizeValues.length) {
<kendo-pager-page-sizes
[ngStyle]="{opacity: initialized ? null : '0'}"
[size]="size"
[pageSizes]="_pageSizeValues"
[adaptiveMode]="adaptiveMode">
</kendo-pager-page-sizes>
}
(info) {
<kendo-pager-info [ngStyle]="{opacity: initialized ? null : '0'}">
</kendo-pager-info>
}
}
(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']]
}] } });