@progress/kendo-angular-layout
Version:
Kendo UI for Angular Layout Package - a collection of components to create professional application layoyts
177 lines (176 loc) • 8.11 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 { DEFAULT_SCROLL_BEHAVIOR } from './constants';
import { Injectable, NgZone } from '@angular/core';
import { isDocumentAvailable } from '@progress/kendo-angular-common';
import { Subject } from 'rxjs';
import { getActiveTab, isTablistHorizontal } from './util';
import { LocalizationService } from '@progress/kendo-angular-l10n';
import * as i0 from "@angular/core";
import * as i1 from "@progress/kendo-angular-l10n";
/**
* @hidden
*/
export class ScrollService {
ngZone;
localization;
owner;
position = 0;
scrollButtonActiveStateChange = new Subject();
get tablistElement() {
return this.owner.tablist.nativeElement;
}
get tabstripSize() {
const hostElement = this.owner.wrapper.nativeElement;
const wrapperWidth = parseFloat(getComputedStyle(hostElement).width);
const wrapperHeight = parseFloat(getComputedStyle(hostElement).height);
return isTablistHorizontal(this.owner.tabPosition) ? wrapperWidth : wrapperHeight;
}
get tablistOverflowSize() {
if (!isDocumentAvailable()) {
return 0;
}
const isHorizontal = isTablistHorizontal(this.owner.tabPosition);
const overflowSize = Math.floor(this.tablistElement[isHorizontal ? 'scrollWidth' : 'scrollHeight']
- this.tablistElement.getBoundingClientRect()[isHorizontal ? 'width' : 'height']);
return overflowSize < 0 ? 0 : overflowSize;
}
get tabsOverflow() {
return this.tablistOverflowSize > 0;
}
constructor(ngZone, localization) {
this.ngZone = ngZone;
this.localization = localization;
}
toggleScrollButtonsState() {
const tabStrip = this.owner;
if (!tabStrip.hasScrollButtons) {
return;
}
const currentPrevButtonActive = !this.isDisabled('prev');
const currentNextButtonActive = !this.isDisabled('next');
const isHorizontal = isTablistHorizontal(this.owner.tabPosition);
const rtlDelta = this.localization.rtl && isHorizontal ? -1 : 1;
const calculatedPrevButtonActive = (this.position * rtlDelta) > 0 && this.tablistOverflowSize > 0;
const calculatedNextButtonActive = (this.position * rtlDelta) < this.tablistOverflowSize && this.tablistOverflowSize > 0;
if (calculatedPrevButtonActive !== currentPrevButtonActive) {
this.ngZone.run(() => this.toggleButtonActiveState('prev', calculatedPrevButtonActive));
}
if (calculatedNextButtonActive !== currentNextButtonActive) {
this.ngZone.run(() => this.toggleButtonActiveState('next', calculatedNextButtonActive));
}
}
scrollToSelectedTab() {
if (!this.tabsOverflow) {
return;
}
const { index: activeIndex } = getActiveTab(this.owner.tabs);
if (activeIndex === -1) {
return;
}
this.position += this.getScrollOffset(activeIndex);
if (isTablistHorizontal(this.owner.tabPosition)) {
this.tablistElement.scrollLeft = this.position;
}
else {
this.tablistElement.scrollTop = this.position;
}
this.toggleScrollButtonsState();
const tabStrip = this.owner;
if (!tabStrip.hasScrollButtons) {
return;
}
const isFirstTabActive = activeIndex === 0;
const isLastTabActive = activeIndex === this.owner.tabs.length - 1;
if (isFirstTabActive && !this.isDisabled('prev')) {
this.ngZone.run(() => this.toggleButtonActiveState('prev', false));
}
if (isLastTabActive && !this.isDisabled('next')) {
this.ngZone.run(() => this.toggleButtonActiveState('next', false));
}
}
getScrollOffset(activeIndex) {
if (!isDocumentAvailable()) {
return 0;
}
const isHorizontal = isTablistHorizontal(this.owner.tabPosition);
this.tablistElement[`scroll${isHorizontal ? 'Left' : 'Top'}`] = this.position;
const activeTabRect = this.tablistElement.children[activeIndex].getBoundingClientRect();
const tablistRect = this.tablistElement.getBoundingClientRect();
const end = isHorizontal ? 'right' : 'bottom';
const start = isHorizontal ? 'left' : 'top';
const activeTabStart = activeTabRect[start];
const activeTabEnd = activeTabRect[end];
const tablistStart = tablistRect[start];
const tablistEnd = tablistRect[end];
const tabEndIsInVisibleRange = activeTabEnd <= tablistEnd;
const tabStartIsInVisibleRange = activeTabStart >= tablistStart;
const isWholeTabVisible = tabEndIsInVisibleRange && tabStartIsInVisibleRange;
if (isWholeTabVisible) {
return 0;
}
if (!tabEndIsInVisibleRange) {
return activeTabEnd - tablistEnd;
}
if (!tabStartIsInVisibleRange) {
return activeTabStart - tablistStart;
}
}
onScroll(e) {
this.position = isTablistHorizontal(this.owner.tabPosition) ? e.target.scrollLeft : e.target.scrollTop;
this.toggleScrollButtonsState();
}
scrollTabs(direction) {
this.calculateListPosition(direction, this.owner.scrollable.buttonScrollSpeed);
if (isTablistHorizontal(this.owner.tabPosition) && this.tablistElement) {
this.tablistElement.scrollTo({ left: this.position, behavior: DEFAULT_SCROLL_BEHAVIOR });
}
else {
this.tablistElement.scrollTo({ top: this.position, behavior: DEFAULT_SCROLL_BEHAVIOR });
}
this.toggleScrollButtonsState();
}
calculateListPosition(direction, scrollSpeed) {
const isHorizontal = isTablistHorizontal(this.owner.tabPosition);
if (direction === 'prev') {
if (this.localization.rtl && isHorizontal) {
this.position = this.position + scrollSpeed >= 0 ? 0 : this.position + scrollSpeed;
}
else {
this.position = this.position - scrollSpeed <= 0 ? 0 : this.position - scrollSpeed;
}
}
else if (direction === 'next' && this.position < this.tablistOverflowSize) {
if (this.position + scrollSpeed > this.tablistOverflowSize) {
this.position = this.tablistOverflowSize;
return;
}
if (this.localization.rtl && isHorizontal) {
this.position -= scrollSpeed;
}
else {
this.position += scrollSpeed;
}
}
}
restoreScrollPosition() {
if (isTablistHorizontal(this.owner.tabPosition)) {
this.tablistElement.scrollLeft = this.position;
}
else {
this.tablistElement.scrollTop = this.position;
}
this.toggleScrollButtonsState();
}
toggleButtonActiveState(buttonType, active) {
this.scrollButtonActiveStateChange.next({ buttonType, active });
}
isDisabled = (buttonType) => this.owner[`${buttonType}ScrollButton`]?.host.nativeElement.classList.contains('k-disabled');
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: ScrollService, deps: [{ token: i0.NgZone }, { token: i1.LocalizationService }], target: i0.ɵɵFactoryTarget.Injectable });
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: ScrollService });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: ScrollService, decorators: [{
type: Injectable
}], ctorParameters: function () { return [{ type: i0.NgZone }, { type: i1.LocalizationService }]; } });