Skip to content

Commit

Permalink
fix(primeng/p-tabview): show the `forward' button when necessary
Browse files Browse the repository at this point in the history
Fixes #11684.
  • Loading branch information
volvachev committed May 23, 2023
1 parent 2e57322 commit 8e4c3aa
Show file tree
Hide file tree
Showing 4 changed files with 108 additions and 18 deletions.
4 changes: 2 additions & 2 deletions src/app/components/contextmenu/contextmenu.ts
Original file line number Diff line number Diff line change
Expand Up @@ -342,15 +342,15 @@ export class ContextMenu implements AfterViewInit, OnDestroy {
if (this.global) {
const documentTarget: any = this.el ? this.el.nativeElement.ownerDocument : 'document';
this.triggerEventListener = this.renderer.listen(documentTarget, this.triggerEvent, (event) => {
if(this.containerViewChild && this.containerViewChild.nativeElement.style.display !== 'none') {
if (this.containerViewChild && this.containerViewChild.nativeElement.style.display !== 'none') {
this.hide();
}
this.show(event);
event.preventDefault();
});
} else if (this.target) {
this.triggerEventListener = this.renderer.listen(this.target, this.triggerEvent, (event) => {
if(this.containerViewChild && this.containerViewChild.nativeElement.style.display !== 'none') {
if (this.containerViewChild && this.containerViewChild.nativeElement.style.display !== 'none') {
this.hide();
}
this.show(event);
Expand Down
68 changes: 52 additions & 16 deletions src/app/components/tabview/tabview.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { CommonModule, isPlatformBrowser } from '@angular/common';
import { CommonModule, isPlatformBrowser, DOCUMENT } from '@angular/common';
import {
AfterContentInit,
AfterViewChecked,
Expand All @@ -20,7 +20,10 @@ import {
ViewChild,
ViewContainerRef,
ViewEncapsulation,
forwardRef
forwardRef,
AfterViewInit,
NgZone,
Self
} from '@angular/core';
import { BlockableUI, PrimeTemplate, SharedModule } from 'primeng/api';
import { DomHandler } from 'primeng/dom';
Expand All @@ -29,7 +32,10 @@ import { ChevronRightIcon } from 'primeng/icons/chevronright';
import { TimesIcon } from 'primeng/icons/times';
import { RippleModule } from 'primeng/ripple';
import { TooltipModule } from 'primeng/tooltip';
import { Subscription } from 'rxjs';
import { filter, fromEvent } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { OnDestroyService } from '../utils/on-destroy.service';
import { outsideZone } from '../utils/outside-zone-operator';
import { TabViewChangeEvent, TabViewCloseEvent } from './tabview.interface';

let idx: number = 0;
Expand Down Expand Up @@ -248,7 +254,6 @@ export class TabPanel implements AfterContentInit, OnDestroy {
role="tab"
class="p-tabview-nav-link"
[attr.id]="tab.id + '-label'"
[attr.aria-selected]="tab.selected"
[attr.aria-controls]="tab.id"
[pTooltip]="tab.tooltip"
[tooltipPosition]="tab.tooltipPosition"
Expand Down Expand Up @@ -298,9 +303,10 @@ export class TabPanel implements AfterContentInit, OnDestroy {
styleUrls: ['./tabview.css'],
host: {
class: 'p-element'
}
},
providers: [OnDestroyService]
})
export class TabView implements AfterContentInit, AfterViewChecked, OnDestroy, BlockableUI {
export class TabView implements AfterContentInit, AfterViewInit, AfterViewChecked, BlockableUI {
/**
* Inline style of the component.
* @group Props
Expand Down Expand Up @@ -392,18 +398,16 @@ export class TabView implements AfterContentInit, AfterViewChecked, OnDestroy, B

forwardIsDisabled: boolean = false;

private tabChangesSubscription!: Subscription;

nextIconTemplate: TemplateRef<any> | undefined;

previousIconTemplate: TemplateRef<any> | undefined;

constructor(@Inject(PLATFORM_ID) private platformId: any, public el: ElementRef, public cd: ChangeDetectorRef) {}
constructor(@Inject(PLATFORM_ID) private platformId: any, public el: ElementRef, public cd: ChangeDetectorRef, private zone: NgZone, @Self() private destroy$: OnDestroyService, @Inject(DOCUMENT) private documentRef: Document) {}

ngAfterContentInit() {
this.initTabs();

this.tabChangesSubscription = (this.tabPanels as QueryList<TabPanel>).changes.subscribe((_) => {
(this.tabPanels as QueryList<TabPanel>).changes.pipe(takeUntil(this.destroy$)).subscribe((_) => {
this.initTabs();
});

Expand All @@ -420,6 +424,11 @@ export class TabView implements AfterContentInit, AfterViewChecked, OnDestroy, B
});
}

ngAfterViewInit(): void {
this.initButtonState();
this.listenWindowResize();
}

ngAfterViewChecked() {
if (isPlatformBrowser(this.platformId)) {
if (this.tabChanged) {
Expand All @@ -429,12 +438,6 @@ export class TabView implements AfterContentInit, AfterViewChecked, OnDestroy, B
}
}

ngOnDestroy(): void {
if (this.tabChangesSubscription) {
this.tabChangesSubscription.unsubscribe();
}
}

initTabs(): void {
this.tabs = (this.tabPanels as QueryList<TabPanel>).toArray();
let selectedTab: TabPanel = this.findSelectedTab() as TabPanel;
Expand Down Expand Up @@ -590,6 +593,39 @@ export class TabView implements AfterContentInit, AfterViewChecked, OnDestroy, B

content.scrollLeft = pos >= lastPos ? lastPos : pos;
}

private initButtonState(): void {
if (this.scrollable) {
// We have to wait for the rendering and then retrieve the actual size element from the DOM.
// in future `Promise.resolve` can be changed to `queueMicrotask` (if ie11 support will be dropped)
Promise.resolve().then(() => {
this.updateButtonState();
this.cd.markForCheck();
});
}
}

private listenWindowResize(): void {
if (!isPlatformBrowser(this.platformId)) {
return;
}

fromEvent(this.documentRef.defaultView, 'resize', { passive: true })
.pipe(
outsideZone(this.zone),
filter(() => this.scrollable),
takeUntil(this.destroy$)
)
.subscribe(() => {
const prevBackwardIsDisabled = this.backwardIsDisabled;
const prevForwardIsDisabled = this.forwardIsDisabled;
this.updateButtonState();

if (this.forwardIsDisabled !== prevForwardIsDisabled || this.backwardIsDisabled !== prevBackwardIsDisabled) {
this.cd.detectChanges();
}
});
}
}

@NgModule({
Expand Down
33 changes: 33 additions & 0 deletions src/app/components/utils/on-destroy.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { Injectable, OnDestroy } from '@angular/core';
import { Subject } from 'rxjs';

/**
* Observable abstraction over ngOnDestroy to use with takeUntil
* @example
*@Component({
* selector: 'app-example',
* templateUrl: './example.component.html',
* styleUrls: [ './example.component.scss' ],
* providers: [ OnDestroyService ] <=== add to the element providers section
*})
*export class ExampleComponent implements OnInit, OnDestroy {
* private stream$ = interval(200);
*
* constructor(@Self() private readonly destroy$: OnDestroyService) { <=== Inject, using @Self()
* }
*
* public ngOnInit(): void {
* this.stream$.pipe(
* map((i: number) => i * 2),
* takeUntil(this.destroy$) <=== Use in takeUntil() operator
* ).subscribe();
* }
*}
*/
@Injectable()
export class OnDestroyService extends Subject<null> implements OnDestroy {
public ngOnDestroy(): void {
this.next(null);
this.complete();
}
}
21 changes: 21 additions & 0 deletions src/app/components/utils/outside-zone-operator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { NgZone } from '@angular/core';
import { Observable } from 'rxjs';

/**
* RxJs operator, that run subscription function outside NgZone (This operator should be first in the pipe() method
*
* @param {NgZone} zone - injected ngZone reference
*
* @returns {Observable<T>} Input Observable
*
* @example
* fromEvent<MouseEvent>(this.elementRef.nativeElement, 'click')
* .pipe(
* outsideZone(this.zone),
* ...other operators,
* takeUntil(this.destroy$)
* )
*/
export function outsideZone<T>(zone: NgZone): (source: Observable<T>) => Observable<T> {
return (source) => new Observable((subscriber) => zone.runOutsideAngular(() => source.subscribe(subscriber)));
}

0 comments on commit 8e4c3aa

Please sign in to comment.