From 9f36400f46788ca4c13f912c9db3fbe5e3176a46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mehmet=20=C3=87etin?= <92744169+mehmetcetin01140@users.noreply.github.com> Date: Mon, 8 Jul 2024 17:51:56 +0300 Subject: [PATCH 1/3] Fixed #15973 - TabMenu doesn't scroll to active item when it's set programatically --- src/app/components/tabmenu/tabmenu.ts | 53 ++++++++++----------------- 1 file changed, 20 insertions(+), 33 deletions(-) diff --git a/src/app/components/tabmenu/tabmenu.ts b/src/app/components/tabmenu/tabmenu.ts index 77aa1165c53..c3bcd5f0bb8 100644 --- a/src/app/components/tabmenu/tabmenu.ts +++ b/src/app/components/tabmenu/tabmenu.ts @@ -12,10 +12,10 @@ import { Inject, Input, NgModule, - OnDestroy, Output, PLATFORM_ID, QueryList, + SimpleChanges, TemplateRef, ViewChild, ViewChildren, @@ -132,7 +132,7 @@ import { filter } from 'rxjs/operators'; class: 'p-element' } }) -export class TabMenu implements AfterContentInit, AfterViewInit, AfterViewChecked, OnDestroy { +export class TabMenu implements AfterContentInit, AfterViewInit, AfterViewChecked { /** * An array of menuitems. * @group Props @@ -225,8 +225,6 @@ export class TabMenu implements AfterContentInit, AfterViewInit, AfterViewChecke forwardIsDisabled: boolean = false; - private timerIdForInitialAutoScroll: any = null; - _focusableItems: MenuItem[] | undefined | any; _model: MenuItem[] | undefined; @@ -257,6 +255,19 @@ export class TabMenu implements AfterContentInit, AfterViewInit, AfterViewChecke }); } + ngOnChanges(simpleChange: SimpleChanges) { + if (simpleChange.activeItem) { + if (!this.scrollable) { + return; + } + const activeItem = (this.model as MenuItem[]).findIndex((menuItem) => this.isActive(menuItem)); + + if (activeItem !== -1) { + this.updateScrollBar(activeItem); + } + } + } + ngAfterContentInit() { this.templates?.forEach((item) => { switch (item.getType()) { @@ -282,7 +293,7 @@ export class TabMenu implements AfterContentInit, AfterViewInit, AfterViewChecke ngAfterViewInit(): void { if (isPlatformBrowser(this.platformId)) { this.updateInkBar(); - this.initAutoScrollForActiveItem(); + this.initButtonState(); } } @@ -294,10 +305,6 @@ export class TabMenu implements AfterContentInit, AfterViewInit, AfterViewChecke } } - ngOnDestroy(): void { - this.clearAutoScrollHandler(); - } - isActive(item: MenuItem) { if (item.routerLink) { const routerLink = Array.isArray(item.routerLink) ? item.routerLink : [item.routerLink]; @@ -342,6 +349,7 @@ export class TabMenu implements AfterContentInit, AfterViewInit, AfterViewChecke } this.activeItem = item; + this.activeItemChange.emit(item); this.tabChanged = true; this.cd.markForCheck(); @@ -460,7 +468,9 @@ export class TabMenu implements AfterContentInit, AfterViewInit, AfterViewChecke return; } - tabHeader.scrollIntoView({ block: 'nearest', inline: 'center' }); + if (tabHeader && typeof tabHeader.scrollIntoView === 'function') { + tabHeader.scrollIntoView({ block: 'nearest', inline: 'center' }); + } } onScroll(event: Event) { @@ -484,29 +494,6 @@ export class TabMenu implements AfterContentInit, AfterViewInit, AfterViewChecke content.scrollLeft = pos >= lastPos ? lastPos : pos; } - private initAutoScrollForActiveItem(): void { - if (!this.scrollable) { - return; - } - - this.clearAutoScrollHandler(); - // We have to wait for the rendering and then can scroll to element. - this.timerIdForInitialAutoScroll = setTimeout(() => { - const activeItem = (this.model as MenuItem[]).findIndex((menuItem) => this.isActive(menuItem)); - - if (activeItem !== -1) { - this.updateScrollBar(activeItem); - } - }); - } - - private clearAutoScrollHandler(): void { - if (this.timerIdForInitialAutoScroll) { - clearTimeout(this.timerIdForInitialAutoScroll); - this.timerIdForInitialAutoScroll = null; - } - } - private initButtonState(): void { if (this.scrollable) { // We have to wait for the rendering and then retrieve the actual size element from the DOM. From f7f061a74de6f749b078400617ff1a7787992185 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mehmet=20=C3=87etin?= <92744169+mehmetcetin01140@users.noreply.github.com> Date: Wed, 24 Jul 2024 15:14:41 +0300 Subject: [PATCH 2/3] refactor --- src/app/components/tabmenu/tabmenu.ts | 51 ++++++++++++------- src/app/showcase/doc/tabmenu/basicdoc.ts | 24 ++++++--- src/app/showcase/pages/tabmenu/tabmenudemo.ts | 37 +------------- 3 files changed, 52 insertions(+), 60 deletions(-) diff --git a/src/app/components/tabmenu/tabmenu.ts b/src/app/components/tabmenu/tabmenu.ts index c3bcd5f0bb8..1a38d3ba7ed 100644 --- a/src/app/components/tabmenu/tabmenu.ts +++ b/src/app/components/tabmenu/tabmenu.ts @@ -12,6 +12,7 @@ import { Inject, Input, NgModule, + OnDestroy, Output, PLATFORM_ID, QueryList, @@ -132,7 +133,7 @@ import { filter } from 'rxjs/operators'; class: 'p-element' } }) -export class TabMenu implements AfterContentInit, AfterViewInit, AfterViewChecked { +export class TabMenu implements AfterContentInit, AfterViewInit, AfterViewChecked, OnDestroy { /** * An array of menuitems. * @group Props @@ -225,6 +226,8 @@ export class TabMenu implements AfterContentInit, AfterViewInit, AfterViewChecke forwardIsDisabled: boolean = false; + private timerIdForAutoScroll: any = null; + _focusableItems: MenuItem[] | undefined | any; _model: MenuItem[] | undefined; @@ -244,12 +247,7 @@ export class TabMenu implements AfterContentInit, AfterViewInit, AfterViewChecke return this._focusableItems; } - constructor( - @Inject(PLATFORM_ID) private platformId: any, - private router: Router, - private route: ActivatedRoute, - private cd: ChangeDetectorRef - ) { + constructor(@Inject(PLATFORM_ID) private platformId: any, private router: Router, private route: ActivatedRoute, private cd: ChangeDetectorRef) { this.router.events.pipe(filter((event) => event instanceof NavigationEnd)).subscribe((event: NavigationEnd) => { this.cd.markForCheck(); }); @@ -257,14 +255,7 @@ export class TabMenu implements AfterContentInit, AfterViewInit, AfterViewChecke ngOnChanges(simpleChange: SimpleChanges) { if (simpleChange.activeItem) { - if (!this.scrollable) { - return; - } - const activeItem = (this.model as MenuItem[]).findIndex((menuItem) => this.isActive(menuItem)); - - if (activeItem !== -1) { - this.updateScrollBar(activeItem); - } + this.autoScrollForActiveItem(); } } @@ -293,7 +284,7 @@ export class TabMenu implements AfterContentInit, AfterViewInit, AfterViewChecke ngAfterViewInit(): void { if (isPlatformBrowser(this.platformId)) { this.updateInkBar(); - + this.autoScrollForActiveItem(); this.initButtonState(); } } @@ -305,6 +296,10 @@ export class TabMenu implements AfterContentInit, AfterViewInit, AfterViewChecke } } + ngOnDestroy(): void { + this.clearAutoScrollHandler(); + } + isActive(item: MenuItem) { if (item.routerLink) { const routerLink = Array.isArray(item.routerLink) ? item.routerLink : [item.routerLink]; @@ -349,7 +344,6 @@ export class TabMenu implements AfterContentInit, AfterViewInit, AfterViewChecke } this.activeItem = item; - this.activeItemChange.emit(item); this.tabChanged = true; this.cd.markForCheck(); @@ -494,6 +488,29 @@ export class TabMenu implements AfterContentInit, AfterViewInit, AfterViewChecke content.scrollLeft = pos >= lastPos ? lastPos : pos; } + private autoScrollForActiveItem(): void { + if (!this.scrollable) { + return; + } + + this.clearAutoScrollHandler(); + // We have to wait for the rendering and then can scroll to element. + this.timerIdForAutoScroll = setTimeout(() => { + const activeItem = (this.model as MenuItem[]).findIndex((menuItem) => this.isActive(menuItem)); + + if (activeItem !== -1) { + this.updateScrollBar(activeItem); + } + }); + } + + private clearAutoScrollHandler(): void { + if (this.timerIdForAutoScroll) { + clearTimeout(this.timerIdForAutoScroll); + this.timerIdForAutoScroll = null; + } + } + private initButtonState(): void { if (this.scrollable) { // We have to wait for the rendering and then retrieve the actual size element from the DOM. diff --git a/src/app/showcase/doc/tabmenu/basicdoc.ts b/src/app/showcase/doc/tabmenu/basicdoc.ts index 02c254f0589..f44b6f99713 100644 --- a/src/app/showcase/doc/tabmenu/basicdoc.ts +++ b/src/app/showcase/doc/tabmenu/basicdoc.ts @@ -1,4 +1,4 @@ -import { Component, OnInit } from '@angular/core'; +import { ChangeDetectorRef, Component, OnInit } from '@angular/core'; import { MenuItem } from 'primeng/api'; import { Code } from '@domain/code'; @@ -9,7 +9,7 @@ import { Code } from '@domain/code';

TabMenu requires a collection of menuitems as its model.

- +
` @@ -17,13 +17,21 @@ import { Code } from '@domain/code'; export class BasicDoc implements OnInit { items: MenuItem[] | undefined; + activeItem: MenuItem | undefined; + + constructor(private cd:ChangeDetectorRef){ + + } + ngOnInit() { - this.items = [ - { label: 'Dashboard', icon: 'pi pi-home' }, - { label: 'Transactions', icon: 'pi pi-chart-line' }, - { label: 'Products', icon: 'pi pi-list' }, - { label: 'Messages', icon: 'pi pi-inbox' } - ]; + this.items = Array.from({ length: 50 }, (_, i) => ({ + label: `Tab ${i + 1}`, + })); + + setTimeout((_) => { + this.activeItem = this.items[40]; + this.cd.markForCheck() + }, 2000); } code: Code = { diff --git a/src/app/showcase/pages/tabmenu/tabmenudemo.ts b/src/app/showcase/pages/tabmenu/tabmenudemo.ts index 32918522f9a..1fd5ce417e1 100755 --- a/src/app/showcase/pages/tabmenu/tabmenudemo.ts +++ b/src/app/showcase/pages/tabmenu/tabmenudemo.ts @@ -13,45 +13,12 @@ import { RouterDoc } from '@doc/tabmenu/routerdoc'; }) export class TabMenuDemo { docs = [ - { - id: 'import', - label: 'Import', - component: ImportDoc - }, + { id: 'basic', label: 'Basic', component: BasicDoc }, - { - id: 'controlled', - label: 'Controlled', - component: ControlledDoc - }, - { - id: 'template', - label: 'Template', - component: TemplateDoc - }, - { - id: 'command', - label: 'Command', - component: CommandDoc - }, - { - id: 'router', - label: 'Router', - component: RouterDoc - }, - { - id: 'style', - label: 'Style', - component: StyleDoc - }, - { - id: 'accessibility', - label: 'Accessibility', - component: AccessibilityDoc - } + ]; } From 96fc148488e5edfb49e542b88d0cd7d35c4798ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mehmet=20=C3=87etin?= <92744169+mehmetcetin01140@users.noreply.github.com> Date: Wed, 24 Jul 2024 15:19:35 +0300 Subject: [PATCH 3/3] revert demo changes --- src/app/showcase/doc/tabmenu/basicdoc.ts | 24 ++++-------- src/app/showcase/pages/tabmenu/tabmenudemo.ts | 39 +++++++++++++++++-- 2 files changed, 44 insertions(+), 19 deletions(-) diff --git a/src/app/showcase/doc/tabmenu/basicdoc.ts b/src/app/showcase/doc/tabmenu/basicdoc.ts index f44b6f99713..02c254f0589 100644 --- a/src/app/showcase/doc/tabmenu/basicdoc.ts +++ b/src/app/showcase/doc/tabmenu/basicdoc.ts @@ -1,4 +1,4 @@ -import { ChangeDetectorRef, Component, OnInit } from '@angular/core'; +import { Component, OnInit } from '@angular/core'; import { MenuItem } from 'primeng/api'; import { Code } from '@domain/code'; @@ -9,7 +9,7 @@ import { Code } from '@domain/code';

TabMenu requires a collection of menuitems as its model.

- +
` @@ -17,21 +17,13 @@ import { Code } from '@domain/code'; export class BasicDoc implements OnInit { items: MenuItem[] | undefined; - activeItem: MenuItem | undefined; - - constructor(private cd:ChangeDetectorRef){ - - } - ngOnInit() { - this.items = Array.from({ length: 50 }, (_, i) => ({ - label: `Tab ${i + 1}`, - })); - - setTimeout((_) => { - this.activeItem = this.items[40]; - this.cd.markForCheck() - }, 2000); + this.items = [ + { label: 'Dashboard', icon: 'pi pi-home' }, + { label: 'Transactions', icon: 'pi pi-chart-line' }, + { label: 'Products', icon: 'pi pi-list' }, + { label: 'Messages', icon: 'pi pi-inbox' } + ]; } code: Code = { diff --git a/src/app/showcase/pages/tabmenu/tabmenudemo.ts b/src/app/showcase/pages/tabmenu/tabmenudemo.ts index 1fd5ce417e1..24b8427b437 100755 --- a/src/app/showcase/pages/tabmenu/tabmenudemo.ts +++ b/src/app/showcase/pages/tabmenu/tabmenudemo.ts @@ -13,12 +13,45 @@ import { RouterDoc } from '@doc/tabmenu/routerdoc'; }) export class TabMenuDemo { docs = [ - + { + id: 'import', + label: 'Import', + component: ImportDoc + }, { id: 'basic', label: 'Basic', component: BasicDoc }, - + { + id: 'controlled', + label: 'Controlled', + component: ControlledDoc + }, + { + id: 'template', + label: 'Template', + component: TemplateDoc + }, + { + id: 'command', + label: 'Command', + component: CommandDoc + }, + { + id: 'router', + label: 'Router', + component: RouterDoc + }, + { + id: 'style', + label: 'Style', + component: StyleDoc + }, + { + id: 'accessibility', + label: 'Accessibility', + component: AccessibilityDoc + } ]; -} +} \ No newline at end of file