import * as i1 from '@angular/common'; import { isPlatformBrowser, DOCUMENT, CommonModule } from '@angular/common'; import * as i0 from '@angular/core'; import { PLATFORM_ID, numberAttribute, Component, ChangeDetectionStrategy, ViewEncapsulation, Inject, Input, ViewChild, ContentChildren, NgModule } from '@angular/core'; import { PrimeTemplate } from 'primeng/api'; import { DomHandler } from 'primeng/dom'; import { UniqueComponentId } from 'primeng/utils'; /** * ScrollPanel is a cross browser, lightweight and themable alternative to native browser scrollbar. * @group Components */ class ScrollPanel { platformId; el; zone; cd; document; renderer; /** * Inline style of the component. * @group Props */ style; /** * Style class of the component. * @group Props */ styleClass; /** * Step factor to scroll the content while pressing the arrow keys. * @group Props */ step = 5; containerViewChild; contentViewChild; xBarViewChild; yBarViewChild; templates; scrollYRatio; scrollXRatio; timeoutFrame = (fn) => setTimeout(fn, 0); initialized = false; lastPageY; lastPageX; isXBarClicked = false; isYBarClicked = false; contentTemplate; lastScrollLeft = 0; lastScrollTop = 0; orientation = 'vertical'; timer; contentId; windowResizeListener; contentScrollListener; mouseEnterListener; xBarMouseDownListener; yBarMouseDownListener; documentMouseMoveListener; documentMouseUpListener; constructor(platformId, el, zone, cd, document, renderer) { this.platformId = platformId; this.el = el; this.zone = zone; this.cd = cd; this.document = document; this.renderer = renderer; this.contentId = UniqueComponentId() + '_content'; } ngAfterViewInit() { if (isPlatformBrowser(this.platformId)) { this.zone.runOutsideAngular(() => { this.moveBar(); this.moveBar = this.moveBar.bind(this); this.onXBarMouseDown = this.onXBarMouseDown.bind(this); this.onYBarMouseDown = this.onYBarMouseDown.bind(this); this.onDocumentMouseMove = this.onDocumentMouseMove.bind(this); this.onDocumentMouseUp = this.onDocumentMouseUp.bind(this); this.windowResizeListener = this.renderer.listen(window, 'resize', this.moveBar); this.contentScrollListener = this.renderer.listen(this.contentViewChild.nativeElement, 'scroll', this.moveBar); this.mouseEnterListener = this.renderer.listen(this.contentViewChild.nativeElement, 'mouseenter', this.moveBar); this.xBarMouseDownListener = this.renderer.listen(this.xBarViewChild.nativeElement, 'mousedown', this.onXBarMouseDown); this.yBarMouseDownListener = this.renderer.listen(this.yBarViewChild.nativeElement, 'mousedown', this.onYBarMouseDown); this.calculateContainerHeight(); this.initialized = true; }); } } ngAfterContentInit() { this.templates.forEach((item) => { switch (item.getType()) { case 'content': this.contentTemplate = item.template; break; default: this.contentTemplate = item.template; break; } }); } calculateContainerHeight() { let container = this.containerViewChild.nativeElement; let content = this.contentViewChild.nativeElement; let xBar = this.xBarViewChild.nativeElement; const window = this.document.defaultView; let containerStyles = window.getComputedStyle(container), xBarStyles = window.getComputedStyle(xBar), pureContainerHeight = DomHandler.getHeight(container) - parseInt(xBarStyles['height'], 10); if (containerStyles['max-height'] != 'none' && pureContainerHeight == 0) { if (content.offsetHeight + parseInt(xBarStyles['height'], 10) > parseInt(containerStyles['max-height'], 10)) { container.style.height = containerStyles['max-height']; } else { container.style.height = content.offsetHeight + parseFloat(containerStyles.paddingTop) + parseFloat(containerStyles.paddingBottom) + parseFloat(containerStyles.borderTopWidth) + parseFloat(containerStyles.borderBottomWidth) + 'px'; } } } moveBar() { let container = this.containerViewChild.nativeElement; let content = this.contentViewChild.nativeElement; /* horizontal scroll */ let xBar = this.xBarViewChild.nativeElement; let totalWidth = content.scrollWidth; let ownWidth = content.clientWidth; let bottom = (container.clientHeight - xBar.clientHeight) * -1; this.scrollXRatio = ownWidth / totalWidth; /* vertical scroll */ let yBar = this.yBarViewChild.nativeElement; let totalHeight = content.scrollHeight; let ownHeight = content.clientHeight; let right = (container.clientWidth - yBar.clientWidth) * -1; this.scrollYRatio = ownHeight / totalHeight; this.requestAnimationFrame(() => { if (this.scrollXRatio >= 1) { xBar.setAttribute('data-p-scrollpanel-hidden', 'true'); DomHandler.addClass(xBar, 'p-scrollpanel-hidden'); } else { xBar.setAttribute('data-p-scrollpanel-hidden', 'false'); DomHandler.removeClass(xBar, 'p-scrollpanel-hidden'); const xBarWidth = Math.max(this.scrollXRatio * 100, 10); const xBarLeft = (content.scrollLeft * (100 - xBarWidth)) / (totalWidth - ownWidth); xBar.style.cssText = 'width:' + xBarWidth + '%; left:' + xBarLeft + '%;bottom:' + bottom + 'px;'; } if (this.scrollYRatio >= 1) { yBar.setAttribute('data-p-scrollpanel-hidden', 'true'); DomHandler.addClass(yBar, 'p-scrollpanel-hidden'); } else { yBar.setAttribute('data-p-scrollpanel-hidden', 'false'); DomHandler.removeClass(yBar, 'p-scrollpanel-hidden'); const yBarHeight = Math.max(this.scrollYRatio * 100, 10); const yBarTop = (content.scrollTop * (100 - yBarHeight)) / (totalHeight - ownHeight); yBar.style.cssText = 'height:' + yBarHeight + '%; top: calc(' + yBarTop + '% - ' + xBar.clientHeight + 'px);right:' + right + 'px;'; } }); this.cd.markForCheck(); } onScroll(event) { if (this.lastScrollLeft !== event.target.scrollLeft) { this.lastScrollLeft = event.target.scrollLeft; this.orientation = 'horizontal'; } else if (this.lastScrollTop !== event.target.scrollTop) { this.lastScrollTop = event.target.scrollTop; this.orientation = 'vertical'; } this.moveBar(); } onKeyDown(event) { if (this.orientation === 'vertical') { switch (event.code) { case 'ArrowDown': { this.setTimer('scrollTop', this.step); event.preventDefault(); break; } case 'ArrowUp': { this.setTimer('scrollTop', this.step * -1); event.preventDefault(); break; } case 'ArrowLeft': case 'ArrowRight': { event.preventDefault(); break; } default: //no op break; } } else if (this.orientation === 'horizontal') { switch (event.code) { case 'ArrowRight': { this.setTimer('scrollLeft', this.step); event.preventDefault(); break; } case 'ArrowLeft': { this.setTimer('scrollLeft', this.step * -1); event.preventDefault(); break; } case 'ArrowDown': case 'ArrowUp': { event.preventDefault(); break; } default: //no op break; } } } onKeyUp() { this.clearTimer(); } repeat(bar, step) { this.contentViewChild.nativeElement[bar] += step; this.moveBar(); } setTimer(bar, step) { this.clearTimer(); this.timer = setTimeout(() => { this.repeat(bar, step); }, 40); } clearTimer() { if (this.timer) { clearTimeout(this.timer); } } bindDocumentMouseListeners() { if (!this.documentMouseMoveListener) { this.documentMouseMoveListener = (e) => { this.onDocumentMouseMove(e); }; this.document.addEventListener('mousemove', this.documentMouseMoveListener); } if (!this.documentMouseUpListener) { this.documentMouseUpListener = (e) => { this.onDocumentMouseUp(e); }; this.document.addEventListener('mouseup', this.documentMouseUpListener); } } unbindDocumentMouseListeners() { if (this.documentMouseMoveListener) { this.document.removeEventListener('mousemove', this.documentMouseMoveListener); this.documentMouseMoveListener = null; } if (this.documentMouseUpListener) { document.removeEventListener('mouseup', this.documentMouseUpListener); this.documentMouseUpListener = null; } } onYBarMouseDown(e) { this.isYBarClicked = true; this.yBarViewChild.nativeElement.focus(); this.lastPageY = e.pageY; this.yBarViewChild.nativeElement.setAttribute('data-p-scrollpanel-grabbed', 'true'); DomHandler.addClass(this.yBarViewChild.nativeElement, 'p-scrollpanel-grabbed'); this.document.body.setAttribute('data-p-scrollpanel-grabbed', 'true'); DomHandler.addClass(this.document.body, 'p-scrollpanel-grabbed'); this.bindDocumentMouseListeners(); e.preventDefault(); } onXBarMouseDown(e) { this.isXBarClicked = true; this.xBarViewChild.nativeElement.focus(); this.lastPageX = e.pageX; this.xBarViewChild.nativeElement.setAttribute('data-p-scrollpanel-grabbed', 'false'); DomHandler.addClass(this.xBarViewChild.nativeElement, 'p-scrollpanel-grabbed'); this.document.body.setAttribute('data-p-scrollpanel-grabbed', 'false'); DomHandler.addClass(this.document.body, 'p-scrollpanel-grabbed'); this.bindDocumentMouseListeners(); e.preventDefault(); } onDocumentMouseMove(e) { if (this.isXBarClicked) { this.onMouseMoveForXBar(e); } else if (this.isYBarClicked) { this.onMouseMoveForYBar(e); } else { this.onMouseMoveForXBar(e); this.onMouseMoveForYBar(e); } } onMouseMoveForXBar(e) { let deltaX = e.pageX - this.lastPageX; this.lastPageX = e.pageX; this.requestAnimationFrame(() => { this.contentViewChild.nativeElement.scrollLeft += deltaX / this.scrollXRatio; }); } onMouseMoveForYBar(e) { let deltaY = e.pageY - this.lastPageY; this.lastPageY = e.pageY; this.requestAnimationFrame(() => { this.contentViewChild.nativeElement.scrollTop += deltaY / this.scrollYRatio; }); } /** * Scrolls the top location to the given value. * @param scrollTop * @group Method */ scrollTop(scrollTop) { let scrollableHeight = this.contentViewChild.nativeElement.scrollHeight - this.contentViewChild.nativeElement.clientHeight; scrollTop = scrollTop > scrollableHeight ? scrollableHeight : scrollTop > 0 ? scrollTop : 0; this.contentViewChild.nativeElement.scrollTop = scrollTop; } onFocus(event) { if (this.xBarViewChild.nativeElement.isSameNode(event.target)) { this.orientation = 'horizontal'; } else if (this.yBarViewChild.nativeElement.isSameNode(event.target)) { this.orientation = 'vertical'; } } onBlur() { if (this.orientation === 'horizontal') { this.orientation = 'vertical'; } } onDocumentMouseUp(e) { this.yBarViewChild.nativeElement.setAttribute('data-p-scrollpanel-grabbed', 'false'); DomHandler.removeClass(this.yBarViewChild.nativeElement, 'p-scrollpanel-grabbed'); this.xBarViewChild.nativeElement.setAttribute('data-p-scrollpanel-grabbed', 'false'); DomHandler.removeClass(this.xBarViewChild.nativeElement, 'p-scrollpanel-grabbed'); this.document.body.setAttribute('data-p-scrollpanel-grabbed', 'false'); DomHandler.removeClass(this.document.body, 'p-scrollpanel-grabbed'); this.unbindDocumentMouseListeners(); this.isXBarClicked = false; this.isYBarClicked = false; } requestAnimationFrame(f) { let frame = window.requestAnimationFrame || this.timeoutFrame; frame(f); } unbindListeners() { if (this.windowResizeListener) { this.windowResizeListener(); this.windowResizeListener = null; } if (this.contentScrollListener) { this.contentScrollListener(); this.contentScrollListener = null; } if (this.mouseEnterListener) { this.mouseEnterListener(); this.mouseEnterListener = null; } if (this.xBarMouseDownListener) { this.xBarMouseDownListener(); this.xBarMouseDownListener = null; } if (this.yBarMouseDownListener) { this.yBarMouseDownListener(); this.yBarMouseDownListener = null; } } ngOnDestroy() { if (this.initialized) { this.unbindListeners(); } } /** * Refreshes the position and size of the scrollbar. * @group Method */ refresh() { this.moveBar(); } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.7", ngImport: i0, type: ScrollPanel, deps: [{ token: PLATFORM_ID }, { token: i0.ElementRef }, { token: i0.NgZone }, { token: i0.ChangeDetectorRef }, { token: DOCUMENT }, { token: i0.Renderer2 }], target: i0.ɵɵFactoryTarget.Component }); static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "16.1.0", version: "17.3.7", type: ScrollPanel, selector: "p-scrollPanel", inputs: { style: "style", styleClass: "styleClass", step: ["step", "step", numberAttribute] }, host: { classAttribute: "p-element" }, queries: [{ propertyName: "templates", predicate: PrimeTemplate }], viewQueries: [{ propertyName: "containerViewChild", first: true, predicate: ["container"], descendants: true }, { propertyName: "contentViewChild", first: true, predicate: ["content"], descendants: true }, { propertyName: "xBarViewChild", first: true, predicate: ["xBar"], descendants: true }, { propertyName: "yBarViewChild", first: true, predicate: ["yBar"], descendants: true }], ngImport: i0, template: `