/// /// Copyright 2015-2026 Micro Focus or one of its affiliates. /// /// Licensed under the Apache License, Version 2.0 (the "License"); /// you may not use this file except in compliance with the License. /// You may obtain a copy of the License at /// /// http://www.apache.org/licenses/LICENSE-2.0 /// /// Unless required by applicable law or agreed to in writing, software /// distributed under the License is distributed on an "AS IS" BASIS, /// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. /// See the License for the specific language governing permissions and /// limitations under the License. /// import { SPACE } from '@angular/cdk/keycodes'; import { NgClass, NgTemplateOutlet, AsyncPipe } from '@angular/common'; import { AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, ContentChildren, forwardRef, inject, Input, OnDestroy, QueryList, } from '@angular/core'; import { RouterLink } from '@angular/router'; import { Subject } from 'rxjs'; import { takeUntil } from 'rxjs/operators'; import { FocusIndicatorDirective } from '../../directives/accessibility/focus-indicator/focus-indicator.directive'; import { TabbableListItemDirective } from '../../directives/accessibility/tabbable-list/tabbable-list-item.directive'; import { TabbableListDirective } from '../../directives/accessibility/tabbable-list/tabbable-list.directive'; import { TabComponent } from './tab/tab.component'; import { TabsetService } from './tabset.service'; import { TabsetToken } from './tabset.token'; @Component({ selector: 'ux-tabset', templateUrl: './tabset.component.html', changeDetection: ChangeDetectionStrategy.OnPush, providers: [ TabsetService, { provide: TabsetToken, useExisting: forwardRef(() => TabsetComponent), }, ], host: { '[class.tabs-left]': 'stacked === "left"', '[class.tabs-right]': 'stacked === "right"', }, imports: [ TabbableListDirective, NgClass, NgTemplateOutlet, TabbableListItemDirective, FocusIndicatorDirective, RouterLink, AsyncPipe, ], }) export class TabsetComponent implements AfterViewInit, OnDestroy { readonly _tabset = inject(TabsetService); private readonly _changeDetector = inject(ChangeDetectorRef); /** Determine if the appearance of the tabset */ @Input() minimal: boolean = true; /** Determine if the tabset should appear stacked */ @Input() stacked: 'left' | 'right' | 'none' = 'none'; /** Determine if we want to manually update the active state */ @Input() set manual(manual: boolean) { this._tabset.manual = manual; } /** Provide am aria label for the tabset */ @Input('aria-label') ariaLabel: string; /** Access all the children */ @ContentChildren(TabComponent) _tabs: QueryList; /** Remove subscriptions on destroy */ private readonly _onDestroy$ = new Subject(); ngAfterViewInit(): void { // provide the service with the initial array of items this._tabset.update(this._tabs.toArray()); // Make sure a tab is selected if (!this._tabset.isTabActive()) { this._tabset.selectFirstTab(); } // run change detection once we have setup the tabs this._changeDetector.detectChanges(); // watch for any future changes this._tabs.changes.pipe(takeUntil(this._onDestroy$)).subscribe(tabs => { // update the internal list of tabs this._tabset.update(tabs); // run change detection this._changeDetector.detectChanges(); }); } ngOnDestroy(): void { this._onDestroy$.next(); this._onDestroy$.complete(); } selectTab(tab: number | TabComponent): void { // pass tab to select method this._tabset.select(tab instanceof TabComponent ? tab : this._tabs.toArray()[tab]); // run change detection this._changeDetector.detectChanges(); } handleKeyDown(event: KeyboardEvent, tab: HTMLElement): void { if (event.keyCode === SPACE) { event.preventDefault(); tab.click(); } } markForCheck(): void { this._changeDetector.detectChanges(); } }