import { Component, Input, ContentChildren, QueryList, Directive, TemplateRef, ContentChild, AfterContentChecked, Output, EventEmitter } from '@angular/core'; import {NgbTabsetConfig} from './tabset-config'; let nextId = 0; /** * This directive should be used to wrap tab titles that need to contain HTML markup or other directives. */ @Directive({selector: 'ng-template[ngbTabTitle]'}) export class NgbTabTitle { constructor(public templateRef: TemplateRef) {} } /** * This directive must be used to wrap content to be displayed in a tab. */ @Directive({selector: 'ng-template[ngbTabContent]'}) export class NgbTabContent { constructor(public templateRef: TemplateRef) {} } /** * A directive representing an individual tab. */ @Directive({selector: 'ngb-tab'}) export class NgbTab { /** * Unique tab identifier. Must be unique for the entire document for proper accessibility support. */ @Input() id = `ngb-tab-${nextId++}`; /** * Simple (string only) title. Use the "NgbTabTitle" directive for more complex use-cases. */ @Input() title: string; /** * Allows toggling disabled state of a given state. Disabled tabs can't be selected. */ @Input() disabled = false; titleTpl: NgbTabTitle | null; contentTpl: NgbTabContent | null; @ContentChildren(NgbTabTitle, {descendants: false}) titleTpls: QueryList; @ContentChildren(NgbTabContent, {descendants: false}) contentTpls: QueryList; ngAfterContentChecked() { // We are using @ContentChildren instead of @ContantChild as in the Angular version being used // only @ContentChildren allows us to specify the {descendants: false} option. // Without {descendants: false} we are hitting bugs described in: // https://github.com/ng-bootstrap/ng-bootstrap/issues/2240 this.titleTpl = this.titleTpls.first; this.contentTpl = this.contentTpls.first; } } /** * The payload of the change event fired right before the tab change */ export interface NgbTabChangeEvent { /** * Id of the currently active tab */ activeId: string; /** * Id of the newly selected tab */ nextId: string; /** * Function that will prevent tab switch if called */ preventDefault: () => void; } /** * A component that makes it easy to create tabbed interface. */ @Component({ selector: 'ngb-tabset', exportAs: 'ngbTabset', template: `
` }) export class NgbTabset implements AfterContentChecked { justifyClass: string; @ContentChildren(NgbTab) tabs: QueryList; /** * An identifier of an initially selected (active) tab. Use the "select" method to switch a tab programmatically. */ @Input() activeId: string; /** * Whether the closed tabs should be hidden without destroying them */ @Input() destroyOnHide = true; /** * The horizontal alignment of the nav with flexbox utilities. Can be one of 'start', 'center', 'end', 'fill' or * 'justified' * The default value is 'start'. */ @Input() set justify(className: 'start' | 'center' | 'end' | 'fill' | 'justified') { if (className === 'fill' || className === 'justified') { this.justifyClass = `nav-${className}`; } else { this.justifyClass = `justify-content-${className}`; } } /** * The orientation of the nav (horizontal or vertical). * The default value is 'horizontal'. */ @Input() orientation: 'horizontal' | 'vertical'; /** * Type of navigation to be used for tabs. Can be one of 'tabs' or 'pills'. */ @Input() type: 'tabs' | 'pills'; /** * A tab change event fired right before the tab selection happens. See NgbTabChangeEvent for payload details */ @Output() tabChange = new EventEmitter(); constructor(config: NgbTabsetConfig) { this.type = config.type; this.justify = config.justify; this.orientation = config.orientation; } /** * Selects the tab with the given id and shows its associated pane. * Any other tab that was previously selected becomes unselected and its associated pane is hidden. */ select(tabId: string) { let selectedTab = this._getTabById(tabId); if (selectedTab && !selectedTab.disabled && this.activeId !== selectedTab.id) { let defaultPrevented = false; this.tabChange.emit( {activeId: this.activeId, nextId: selectedTab.id, preventDefault: () => { defaultPrevented = true; }}); if (!defaultPrevented) { this.activeId = selectedTab.id; } } } ngAfterContentChecked() { // auto-correct activeId that might have been set incorrectly as input let activeTab = this._getTabById(this.activeId); this.activeId = activeTab ? activeTab.id : (this.tabs.length ? this.tabs.first.id : null); } private _getTabById(id: string): NgbTab { let tabsWithId: NgbTab[] = this.tabs.filter(tab => tab.id === id); return tabsWithId.length ? tabsWithId[0] : null; } }