UNPKG

ng-zorro-antd

Version:

An enterprise-class UI components based on Ant Design and Angular

1 lines 104 kB
{"version":3,"file":"ng-zorro-antd-tabs.mjs","sources":["../../components/tabs/tab-add-button.component.ts","../../components/tabs/tabs-ink-bar.directive.ts","../../components/tabs/tab-nav-item.directive.ts","../../components/tabs/tab-nav-operation.component.ts","../../components/tabs/tab-scroll-list.directive.ts","../../components/tabs/tab-nav-bar.component.ts","../../components/tabs/tab-body.component.ts","../../components/tabs/tab-close-button.component.ts","../../components/tabs/tab-link.directive.ts","../../components/tabs/tab.directive.ts","../../components/tabs/tab.component.ts","../../components/tabs/interfaces.ts","../../components/tabs/tabset.component.ts","../../components/tabs/tabs.module.ts","../../components/tabs/public-api.ts","../../components/tabs/ng-zorro-antd-tabs.ts"],"sourcesContent":["/**\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE\n */\n\nimport { Component, ElementRef, Input, TemplateRef } from '@angular/core';\n\nimport { NzSafeAny } from 'ng-zorro-antd/core/types';\n\n@Component({\n selector: 'nz-tab-add-button, button[nz-tab-add-button]',\n template: `\n <ng-container *nzStringTemplateOutlet=\"addIcon; let icon\">\n <i nz-icon [nzType]=\"icon\" nzTheme=\"outline\"></i>\n </ng-container>\n `,\n host: {\n class: 'ant-tabs-nav-add',\n 'aria-label': 'Add tab',\n type: 'button'\n }\n})\nexport class NzTabAddButtonComponent {\n @Input() addIcon: string | TemplateRef<NzSafeAny> = 'plus';\n\n private readonly element: HTMLElement;\n\n constructor(private elementRef: ElementRef<HTMLElement>) {\n this.element = this.elementRef.nativeElement;\n }\n\n getElementWidth(): number {\n return this.element?.offsetWidth || 0;\n }\n\n getElementHeight(): number {\n return this.element?.offsetHeight || 0;\n }\n}\n","/**\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE\n */\n\nimport { Directive, ElementRef, Inject, Input, NgZone, Optional } from '@angular/core';\nimport { ANIMATION_MODULE_TYPE } from '@angular/platform-browser/animations';\n\nimport { reqAnimFrame } from 'ng-zorro-antd/core/polyfill';\n\nimport { NzTabPositionMode } from './interfaces';\n\n@Directive({\n selector: 'nz-tabs-ink-bar, [nz-tabs-ink-bar]',\n host: {\n class: 'ant-tabs-ink-bar',\n '[class.ant-tabs-ink-bar-animated]': '_animated'\n }\n})\nexport class NzTabsInkBarDirective {\n @Input() position: NzTabPositionMode = 'horizontal';\n @Input() animated = true;\n\n get _animated(): boolean {\n return this.animationMode !== 'NoopAnimations' && this.animated;\n }\n\n constructor(\n private elementRef: ElementRef<HTMLElement>,\n private ngZone: NgZone,\n @Optional() @Inject(ANIMATION_MODULE_TYPE) public animationMode?: string\n ) {}\n\n alignToElement(element: HTMLElement): void {\n this.ngZone.runOutsideAngular(() => {\n reqAnimFrame(() => this.setStyles(element));\n });\n }\n\n setStyles(element: HTMLElement): void {\n const inkBar: HTMLElement = this.elementRef.nativeElement;\n\n if (this.position === 'horizontal') {\n inkBar.style.top = '';\n inkBar.style.height = '';\n inkBar.style.left = this.getLeftPosition(element);\n inkBar.style.width = this.getElementWidth(element);\n } else {\n inkBar.style.left = '';\n inkBar.style.width = '';\n inkBar.style.top = this.getTopPosition(element);\n inkBar.style.height = this.getElementHeight(element);\n }\n }\n\n getLeftPosition(element: HTMLElement): string {\n return element ? `${element.offsetLeft || 0}px` : '0';\n }\n\n getElementWidth(element: HTMLElement): string {\n return element ? `${element.offsetWidth || 0}px` : '0';\n }\n\n getTopPosition(element: HTMLElement): string {\n return element ? `${element.offsetTop || 0}px` : '0';\n }\n\n getElementHeight(element: HTMLElement): string {\n return element ? `${element.offsetHeight || 0}px` : '0';\n }\n}\n","/**\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE\n */\n\nimport { FocusableOption } from '@angular/cdk/a11y';\nimport { Directive, ElementRef, Input } from '@angular/core';\n\nimport { NzTabComponent } from './tab.component';\n\n@Directive({\n selector: '[nzTabNavItem]'\n})\nexport class NzTabNavItemDirective implements FocusableOption {\n @Input() disabled: boolean = false;\n @Input() tab!: NzTabComponent;\n @Input() active: boolean = false;\n private el!: HTMLElement;\n private parentElement!: HTMLElement;\n\n constructor(public elementRef: ElementRef<HTMLElement>) {\n this.el = elementRef.nativeElement;\n this.parentElement = this.el.parentElement!;\n }\n\n focus(): void {\n this.el.focus();\n }\n\n get width(): number {\n return this.parentElement.offsetWidth;\n }\n\n get height(): number {\n return this.parentElement.offsetHeight;\n }\n\n get left(): number {\n return this.parentElement.offsetLeft;\n }\n\n get top(): number {\n return this.parentElement.offsetTop;\n }\n}\n","/**\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE\n */\n\nimport {\n ChangeDetectionStrategy,\n ChangeDetectorRef,\n Component,\n ElementRef,\n EventEmitter,\n Input,\n OnDestroy,\n Output,\n TemplateRef,\n ViewEncapsulation\n} from '@angular/core';\n\nimport { NzSafeAny } from 'ng-zorro-antd/core/types';\n\nimport { NzTabNavItemDirective } from './tab-nav-item.directive';\n\n@Component({\n selector: 'nz-tab-nav-operation',\n exportAs: 'nzTabNavOperation',\n preserveWhitespaces: false,\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n template: `\n <button\n nz-dropdown\n class=\"ant-tabs-nav-more\"\n type=\"button\"\n tabindex=\"-1\"\n aria-hidden=\"true\"\n nzOverlayClassName=\"nz-tabs-dropdown\"\n #dropdownTrigger=\"nzDropdown\"\n [nzDropdownMenu]=\"menu\"\n [nzOverlayStyle]=\"{ minWidth: '46px' }\"\n [nzMatchWidthElement]=\"null\"\n (nzVisibleChange)=\"menuVisChange($event)\"\n (mouseenter)=\"showItems()\"\n >\n <i nz-icon nzType=\"ellipsis\"></i>\n </button>\n <nz-dropdown-menu #menu=\"nzDropdownMenu\">\n <ul nz-menu *ngIf=\"menuOpened\">\n <li\n nz-menu-item\n *ngFor=\"let item of items\"\n class=\"ant-tabs-dropdown-menu-item\"\n [class.ant-tabs-dropdown-menu-item-disabled]=\"item.disabled\"\n [nzSelected]=\"item.active\"\n [nzDisabled]=\"item.disabled\"\n (click)=\"onSelect(item)\"\n (contextmenu)=\"onContextmenu(item, $event)\"\n >\n <ng-container *nzStringTemplateOutlet=\"item.tab.label; context: { visible: false }\">\n {{ item.tab.label }}\n </ng-container>\n </li>\n </ul>\n </nz-dropdown-menu>\n <button *ngIf=\"addable\" nz-tab-add-button [addIcon]=\"addIcon\" (click)=\"addClicked.emit()\"></button>\n `,\n host: {\n class: 'ant-tabs-nav-operations',\n '[class.ant-tabs-nav-operations-hidden]': 'items.length === 0'\n }\n})\nexport class NzTabNavOperationComponent implements OnDestroy {\n @Input() items: NzTabNavItemDirective[] = [];\n @Input() addable: boolean = false;\n @Input() addIcon: string | TemplateRef<NzSafeAny> = 'plus';\n\n @Output() readonly addClicked = new EventEmitter<void>();\n @Output() readonly selected = new EventEmitter<NzTabNavItemDirective>();\n closeAnimationWaitTimeoutId = -1;\n menuOpened = false;\n\n private readonly element: HTMLElement;\n constructor(public cdr: ChangeDetectorRef, private elementRef: ElementRef<HTMLElement>) {\n this.element = this.elementRef.nativeElement;\n }\n\n onSelect(item: NzTabNavItemDirective): void {\n if (!item.disabled) {\n // ignore nzCanDeactivate\n item.tab.nzClick.emit();\n this.selected.emit(item);\n }\n }\n\n onContextmenu(item: NzTabNavItemDirective, e: MouseEvent): void {\n if (!item.disabled) {\n item.tab.nzContextmenu.emit(e);\n }\n }\n showItems(): void {\n clearTimeout(this.closeAnimationWaitTimeoutId);\n this.menuOpened = true;\n this.cdr.markForCheck();\n }\n\n menuVisChange(visible: boolean): void {\n if (!visible) {\n this.closeAnimationWaitTimeoutId = setTimeout(() => {\n this.menuOpened = false;\n this.cdr.markForCheck();\n }, 150);\n }\n }\n\n getElementWidth(): number {\n return this.element?.offsetWidth || 0;\n }\n\n getElementHeight(): number {\n return this.element?.offsetHeight || 0;\n }\n\n ngOnDestroy(): void {\n clearTimeout(this.closeAnimationWaitTimeoutId);\n }\n}\n","/**\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE\n */\n\nimport { Directive, ElementRef, EventEmitter, NgZone, OnDestroy, OnInit, Output } from '@angular/core';\nimport { fromEvent, Observable, Subscription } from 'rxjs';\n\nimport {\n NzTabScrollEvent,\n NzTabScrollEventHandlerFun,\n NzTabScrollListOffset,\n NzTabScrollListOffsetEvent\n} from './interfaces';\n\nconst MIN_SWIPE_DISTANCE = 0.1;\nconst STOP_SWIPE_DISTANCE = 0.01;\nconst REFRESH_INTERVAL = 20;\nconst SPEED_OFF_MULTIPLE = 0.995 ** REFRESH_INTERVAL;\n\n@Directive({\n selector: '[nzTabScrollList]'\n})\nexport class NzTabScrollListDirective implements OnInit, OnDestroy {\n lastWheelDirection: 'x' | 'y' | null = null;\n lastWheelTimestamp = 0;\n lastTimestamp = 0;\n lastTimeDiff = 0;\n lastMixedWheel = 0;\n lastWheelPrevent = false;\n touchPosition: NzTabScrollListOffset | null = null;\n lastOffset: NzTabScrollListOffset | null = null;\n motion = -1;\n\n unsubscribe: () => void = () => void 0;\n\n @Output() readonly offsetChange = new EventEmitter<NzTabScrollListOffsetEvent>();\n @Output() readonly tabScroll = new EventEmitter<NzTabScrollEvent>();\n\n constructor(private ngZone: NgZone, private elementRef: ElementRef<HTMLElement>) {}\n\n ngOnInit(): void {\n this.unsubscribe = this.ngZone.runOutsideAngular(() => {\n const el = this.elementRef.nativeElement;\n\n const wheel$ = fromEvent<WheelEvent>(el, 'wheel');\n const touchstart$ = fromEvent<TouchEvent>(el, 'touchstart');\n const touchmove$ = fromEvent<TouchEvent>(el, 'touchmove');\n const touchend$ = fromEvent<TouchEvent>(el, 'touchend');\n\n const subscription = new Subscription();\n subscription.add(this.subscribeWrap('wheel', wheel$, this.onWheel));\n subscription.add(this.subscribeWrap('touchstart', touchstart$, this.onTouchStart));\n subscription.add(this.subscribeWrap('touchmove', touchmove$, this.onTouchMove));\n subscription.add(this.subscribeWrap('touchend', touchend$, this.onTouchEnd));\n\n return () => {\n subscription.unsubscribe();\n };\n });\n }\n\n subscribeWrap<T extends NzTabScrollEvent['event']>(\n type: NzTabScrollEvent['type'],\n observable: Observable<T>,\n handler: NzTabScrollEventHandlerFun<T>\n ): Subscription {\n return observable.subscribe(event => {\n this.tabScroll.emit({\n type,\n event\n } as NzTabScrollEvent);\n if (!event.defaultPrevented) {\n handler(event);\n }\n });\n }\n\n onTouchEnd = (e: TouchEvent): void => {\n if (!this.touchPosition) {\n return;\n }\n const lastOffset = this.lastOffset;\n const lastTimeDiff = this.lastTimeDiff;\n\n this.lastOffset = this.touchPosition = null;\n\n if (lastOffset) {\n const distanceX = lastOffset.x / lastTimeDiff;\n const distanceY = lastOffset.y / lastTimeDiff;\n const absX = Math.abs(distanceX);\n const absY = Math.abs(distanceY);\n\n // Skip swipe if low distance\n if (Math.max(absX, absY) < MIN_SWIPE_DISTANCE) {\n return;\n }\n\n let currentX = distanceX;\n let currentY = distanceY;\n\n this.motion = window.setInterval(() => {\n if (Math.abs(currentX) < STOP_SWIPE_DISTANCE && Math.abs(currentY) < STOP_SWIPE_DISTANCE) {\n window.clearInterval(this.motion);\n return;\n }\n\n currentX *= SPEED_OFF_MULTIPLE;\n currentY *= SPEED_OFF_MULTIPLE;\n this.onOffset(currentX * REFRESH_INTERVAL, currentY * REFRESH_INTERVAL, e);\n }, REFRESH_INTERVAL);\n }\n };\n\n onTouchMove = (e: TouchEvent): void => {\n if (!this.touchPosition) {\n return;\n }\n\n e.preventDefault();\n const { screenX, screenY } = e.touches[0];\n\n const offsetX = screenX - this.touchPosition.x;\n const offsetY = screenY - this.touchPosition.y;\n this.onOffset(offsetX, offsetY, e);\n const now = Date.now();\n\n this.lastTimeDiff = now - this.lastTimestamp;\n this.lastTimestamp = now;\n this.lastOffset = { x: offsetX, y: offsetY };\n this.touchPosition = { x: screenX, y: screenY };\n };\n\n onTouchStart = (e: TouchEvent): void => {\n const { screenX, screenY } = e.touches[0];\n this.touchPosition = { x: screenX, y: screenY };\n window.clearInterval(this.motion);\n };\n\n onWheel = (e: WheelEvent): void => {\n const { deltaX, deltaY } = e;\n let mixed: number;\n const absX = Math.abs(deltaX);\n const absY = Math.abs(deltaY);\n\n if (absX === absY) {\n mixed = this.lastWheelDirection === 'x' ? deltaX : deltaY;\n } else if (absX > absY) {\n mixed = deltaX;\n this.lastWheelDirection = 'x';\n } else {\n mixed = deltaY;\n this.lastWheelDirection = 'y';\n }\n\n // Optimize mac touch scroll\n const now = Date.now();\n const absMixed = Math.abs(mixed);\n\n if (now - this.lastWheelTimestamp > 100 || absMixed - this.lastMixedWheel > 10) {\n this.lastWheelPrevent = false;\n }\n this.onOffset(-mixed, -mixed, e);\n if (e.defaultPrevented || this.lastWheelPrevent) {\n this.lastWheelPrevent = true;\n }\n\n this.lastWheelTimestamp = now;\n this.lastMixedWheel = absMixed;\n };\n\n onOffset(x: number, y: number, event: Event): void {\n this.ngZone.run(() => {\n this.offsetChange.emit({\n x,\n y,\n event\n });\n });\n }\n\n ngOnDestroy(): void {\n this.unsubscribe();\n }\n}\n","/**\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE\n */\n\nimport { FocusKeyManager } from '@angular/cdk/a11y';\nimport { Direction, Directionality } from '@angular/cdk/bidi';\nimport { coerceNumberProperty } from '@angular/cdk/coercion';\nimport { DOWN_ARROW, ENTER, hasModifierKey, LEFT_ARROW, RIGHT_ARROW, SPACE, UP_ARROW } from '@angular/cdk/keycodes';\nimport { ViewportRuler } from '@angular/cdk/overlay';\nimport {\n AfterContentChecked,\n AfterViewInit,\n ChangeDetectionStrategy,\n ChangeDetectorRef,\n Component,\n ContentChildren,\n ElementRef,\n EventEmitter,\n Input,\n NgZone,\n OnChanges,\n OnDestroy,\n Optional,\n Output,\n QueryList,\n SimpleChanges,\n TemplateRef,\n ViewChild,\n ViewEncapsulation\n} from '@angular/core';\nimport { animationFrameScheduler, asapScheduler, merge, of, Subject } from 'rxjs';\nimport { auditTime, takeUntil } from 'rxjs/operators';\n\nimport { NzResizeObserver } from 'ng-zorro-antd/cdk/resize-observer';\nimport { reqAnimFrame } from 'ng-zorro-antd/core/polyfill';\nimport { NumberInput, NzSafeAny } from 'ng-zorro-antd/core/types';\n\nimport { NzTabPositionMode, NzTabScrollEvent, NzTabScrollListOffsetEvent } from './interfaces';\nimport { NzTabAddButtonComponent } from './tab-add-button.component';\nimport { NzTabNavItemDirective } from './tab-nav-item.directive';\nimport { NzTabNavOperationComponent } from './tab-nav-operation.component';\nimport { NzTabsInkBarDirective } from './tabs-ink-bar.directive';\n\nconst RESIZE_SCHEDULER = typeof requestAnimationFrame !== 'undefined' ? animationFrameScheduler : asapScheduler;\nconst CSS_TRANSFORM_TIME = 150;\n\n@Component({\n selector: 'nz-tabs-nav',\n exportAs: 'nzTabsNav',\n preserveWhitespaces: false,\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n template: `\n <div\n class=\"ant-tabs-nav-wrap\"\n [class.ant-tabs-nav-wrap-ping-left]=\"pingLeft\"\n [class.ant-tabs-nav-wrap-ping-right]=\"pingRight\"\n [class.ant-tabs-nav-wrap-ping-top]=\"pingTop\"\n [class.ant-tabs-nav-wrap-ping-bottom]=\"pingBottom\"\n #navWarp\n >\n <div\n class=\"ant-tabs-nav-list\"\n #navList\n nzTabScrollList\n (offsetChange)=\"onOffsetChange($event)\"\n (tabScroll)=\"tabScroll.emit($event)\"\n >\n <ng-content></ng-content>\n <button *ngIf=\"showAddButton\" nz-tab-add-button [addIcon]=\"addIcon\" (click)=\"addClicked.emit()\"></button>\n <div nz-tabs-ink-bar [hidden]=\"hideBar\" [position]=\"position\" [animated]=\"inkBarAnimated\"></div>\n </div>\n </div>\n <nz-tab-nav-operation\n (addClicked)=\"addClicked.emit()\"\n (selected)=\"onSelectedFromMenu($event)\"\n [addIcon]=\"addIcon\"\n [addable]=\"addable\"\n [items]=\"hiddenItems\"\n ></nz-tab-nav-operation>\n <div class=\"ant-tabs-extra-content\" *ngIf=\"extraTemplate\">\n <ng-template [ngTemplateOutlet]=\"extraTemplate\"></ng-template>\n </div>\n `,\n host: {\n role: 'tablist',\n class: 'ant-tabs-nav',\n '(keydown)': 'handleKeydown($event)'\n }\n})\nexport class NzTabNavBarComponent implements AfterViewInit, AfterContentChecked, OnDestroy, OnChanges {\n static ngAcceptInputType_selectedIndex: NumberInput;\n\n @Output() readonly indexFocused: EventEmitter<number> = new EventEmitter<number>();\n @Output() readonly selectFocusedIndex: EventEmitter<number> = new EventEmitter<number>();\n @Output() readonly addClicked = new EventEmitter<void>();\n @Output() readonly tabScroll = new EventEmitter<NzTabScrollEvent>();\n\n @Input() position: NzTabPositionMode = 'horizontal';\n @Input() addable: boolean = false;\n @Input() hideBar: boolean = false;\n @Input() addIcon: string | TemplateRef<NzSafeAny> = 'plus';\n @Input() inkBarAnimated = true;\n @Input() extraTemplate?: TemplateRef<void>;\n\n @Input()\n get selectedIndex(): number {\n return this._selectedIndex;\n }\n set selectedIndex(value: number) {\n const newValue = coerceNumberProperty(value);\n if (this._selectedIndex !== newValue) {\n this._selectedIndex = value;\n this.selectedIndexChanged = true;\n if (this.keyManager) {\n this.keyManager.updateActiveItem(value);\n }\n }\n }\n\n @ViewChild('navWarp', { static: true }) navWarpRef!: ElementRef<HTMLElement>;\n @ViewChild('navList', { static: true }) navListRef!: ElementRef<HTMLElement>;\n @ViewChild(NzTabNavOperationComponent, { static: true }) operationRef!: NzTabNavOperationComponent;\n @ViewChild(NzTabAddButtonComponent, { static: false }) addBtnRef!: NzTabAddButtonComponent;\n @ViewChild(NzTabsInkBarDirective, { static: true }) inkBar!: NzTabsInkBarDirective;\n @ContentChildren(NzTabNavItemDirective, { descendants: true }) items!: QueryList<NzTabNavItemDirective>;\n\n /** Tracks which element has focus; used for keyboard navigation */\n get focusIndex(): number {\n return this.keyManager ? this.keyManager.activeItemIndex! : 0;\n }\n\n /** When the focus index is set, we must manually send focus to the correct label */\n set focusIndex(value: number) {\n if (!this.isValidIndex(value) || this.focusIndex === value || !this.keyManager) {\n return;\n }\n\n this.keyManager.setActiveItem(value);\n }\n\n get showAddButton(): boolean {\n return this.hiddenItems.length === 0 && this.addable;\n }\n\n translate: null | string = null;\n transformX = 0;\n transformY = 0;\n pingLeft = false;\n pingRight = false;\n pingTop = false;\n pingBottom = false;\n hiddenItems: NzTabNavItemDirective[] = [];\n\n private keyManager!: FocusKeyManager<NzTabNavItemDirective>;\n private destroy$ = new Subject<void>();\n private _selectedIndex = 0;\n private wrapperWidth = 0;\n private wrapperHeight = 0;\n private scrollListWidth = 0;\n private scrollListHeight = 0;\n private operationWidth = 0;\n private operationHeight = 0;\n private addButtonWidth = 0;\n private addButtonHeight = 0;\n private selectedIndexChanged = false;\n private lockAnimationTimeoutId = -1;\n private cssTransformTimeWaitingId = -1;\n\n constructor(\n private cdr: ChangeDetectorRef,\n private ngZone: NgZone,\n private viewportRuler: ViewportRuler,\n private nzResizeObserver: NzResizeObserver,\n @Optional() private dir: Directionality\n ) {}\n\n ngAfterViewInit(): void {\n const dirChange = this.dir ? this.dir.change : of(null);\n const resize = this.viewportRuler.change(150);\n\n const realign = (): void => {\n this.updateScrollListPosition();\n this.alignInkBarToSelectedTab();\n };\n this.keyManager = new FocusKeyManager<NzTabNavItemDirective>(this.items)\n .withHorizontalOrientation(this.getLayoutDirection())\n .withWrap();\n this.keyManager.updateActiveItem(this.selectedIndex);\n\n reqAnimFrame(realign);\n\n merge(this.nzResizeObserver.observe(this.navWarpRef), this.nzResizeObserver.observe(this.navListRef))\n .pipe(takeUntil(this.destroy$), auditTime(16, RESIZE_SCHEDULER))\n .subscribe(() => {\n realign();\n });\n merge(dirChange, resize, this.items.changes)\n .pipe(takeUntil(this.destroy$))\n .subscribe(() => {\n Promise.resolve().then(realign);\n this.keyManager.withHorizontalOrientation(this.getLayoutDirection());\n });\n\n this.keyManager.change.pipe(takeUntil(this.destroy$)).subscribe(newFocusIndex => {\n this.indexFocused.emit(newFocusIndex);\n this.setTabFocus(newFocusIndex);\n this.scrollToTab(this.keyManager.activeItem!);\n });\n }\n\n ngAfterContentChecked(): void {\n if (this.selectedIndexChanged) {\n this.updateScrollListPosition();\n this.alignInkBarToSelectedTab();\n this.selectedIndexChanged = false;\n this.cdr.markForCheck();\n }\n }\n\n ngOnDestroy(): void {\n clearTimeout(this.lockAnimationTimeoutId);\n clearTimeout(this.cssTransformTimeWaitingId);\n this.destroy$.next();\n this.destroy$.complete();\n }\n\n onSelectedFromMenu(tab: NzTabNavItemDirective): void {\n const tabIndex = this.items.toArray().findIndex(e => e === tab);\n if (tabIndex !== -1) {\n this.keyManager.updateActiveItem(tabIndex);\n if (this.focusIndex !== this.selectedIndex) {\n this.selectFocusedIndex.emit(this.focusIndex);\n this.scrollToTab(tab);\n }\n }\n }\n\n onOffsetChange(e: NzTabScrollListOffsetEvent): void {\n if (this.position === 'horizontal') {\n if (this.lockAnimationTimeoutId === -1) {\n if (this.transformX >= 0 && e.x > 0) {\n return;\n }\n if (this.transformX <= this.wrapperWidth - this.scrollListWidth && e.x < 0) {\n return;\n }\n }\n e.event.preventDefault();\n this.transformX = this.clampTransformX(this.transformX + e.x);\n this.setTransform(this.transformX, 0);\n } else {\n if (this.lockAnimationTimeoutId === -1) {\n if (this.transformY >= 0 && e.y > 0) {\n return;\n }\n if (this.transformY <= this.wrapperHeight - this.scrollListHeight && e.y < 0) {\n return;\n }\n }\n e.event.preventDefault();\n this.transformY = this.clampTransformY(this.transformY + e.y);\n this.setTransform(0, this.transformY);\n }\n\n this.lockAnimation();\n this.setVisibleRange();\n this.setPingStatus();\n }\n\n handleKeydown(event: KeyboardEvent): void {\n const inNavigationList = this.navWarpRef.nativeElement.contains(event.target as HTMLElement);\n if (hasModifierKey(event) || !inNavigationList) {\n return;\n }\n\n switch (event.keyCode) {\n case LEFT_ARROW:\n case UP_ARROW:\n case RIGHT_ARROW:\n case DOWN_ARROW:\n this.lockAnimation();\n this.keyManager.onKeydown(event);\n break;\n case ENTER:\n case SPACE:\n if (this.focusIndex !== this.selectedIndex) {\n this.selectFocusedIndex.emit(this.focusIndex);\n }\n break;\n default:\n this.keyManager.onKeydown(event);\n }\n }\n\n private isValidIndex(index: number): boolean {\n if (!this.items) {\n return true;\n }\n\n const tab = this.items ? this.items.toArray()[index] : null;\n return !!tab && !tab.disabled;\n }\n\n private scrollToTab(tab: NzTabNavItemDirective): void {\n if (!this.items.find(e => e === tab)) {\n return;\n }\n const tabs = this.items.toArray();\n\n if (this.position === 'horizontal') {\n let newTransform = this.transformX;\n if (this.getLayoutDirection() === 'rtl') {\n const right = tabs[0].left + tabs[0].width - tab.left - tab.width;\n\n if (right < this.transformX) {\n newTransform = right;\n } else if (right + tab.width > this.transformX + this.wrapperWidth) {\n newTransform = right + tab.width - this.wrapperWidth;\n }\n } else if (tab.left < -this.transformX) {\n newTransform = -tab.left;\n } else if (tab.left + tab.width > -this.transformX + this.wrapperWidth) {\n newTransform = -(tab.left + tab.width - this.wrapperWidth);\n }\n this.transformX = newTransform;\n this.transformY = 0;\n this.setTransform(newTransform, 0);\n } else {\n let newTransform = this.transformY;\n\n if (tab.top < -this.transformY) {\n newTransform = -tab.top;\n } else if (tab.top + tab.height > -this.transformY + this.wrapperHeight) {\n newTransform = -(tab.top + tab.height - this.wrapperHeight);\n }\n this.transformY = newTransform;\n this.transformX = 0;\n this.setTransform(0, newTransform);\n }\n\n clearTimeout(this.cssTransformTimeWaitingId);\n this.cssTransformTimeWaitingId = setTimeout(() => {\n this.setVisibleRange();\n }, CSS_TRANSFORM_TIME);\n }\n\n private lockAnimation(): void {\n if (this.lockAnimationTimeoutId === -1) {\n this.ngZone.runOutsideAngular(() => {\n this.navListRef.nativeElement.style.transition = 'none';\n this.lockAnimationTimeoutId = setTimeout(() => {\n this.navListRef.nativeElement.style.transition = '';\n this.lockAnimationTimeoutId = -1;\n }, CSS_TRANSFORM_TIME);\n });\n }\n }\n\n private setTransform(x: number, y: number): void {\n this.navListRef.nativeElement.style.transform = `translate(${x}px, ${y}px)`;\n }\n\n private clampTransformX(transform: number): number {\n const scrollWidth = this.wrapperWidth - this.scrollListWidth;\n if (this.getLayoutDirection() === 'rtl') {\n return Math.max(Math.min(scrollWidth, transform), 0);\n } else {\n return Math.min(Math.max(scrollWidth, transform), 0);\n }\n }\n\n private clampTransformY(transform: number): number {\n return Math.min(Math.max(this.wrapperHeight - this.scrollListHeight, transform), 0);\n }\n\n private updateScrollListPosition(): void {\n this.resetSizes();\n this.transformX = this.clampTransformX(this.transformX);\n this.transformY = this.clampTransformY(this.transformY);\n this.setVisibleRange();\n this.setPingStatus();\n if (this.keyManager) {\n this.keyManager.updateActiveItem(this.keyManager.activeItemIndex!);\n if (this.keyManager.activeItem) {\n this.scrollToTab(this.keyManager.activeItem);\n }\n }\n }\n\n private resetSizes(): void {\n this.addButtonWidth = this.addBtnRef ? this.addBtnRef.getElementWidth() : 0;\n this.addButtonHeight = this.addBtnRef ? this.addBtnRef.getElementHeight() : 0;\n this.operationWidth = this.operationRef.getElementWidth();\n this.operationHeight = this.operationRef.getElementHeight();\n this.wrapperWidth = this.navWarpRef.nativeElement.offsetWidth || 0;\n this.wrapperHeight = this.navWarpRef.nativeElement.offsetHeight || 0;\n this.scrollListHeight = this.navListRef.nativeElement.offsetHeight || 0;\n this.scrollListWidth = this.navListRef.nativeElement.offsetWidth || 0;\n }\n\n private alignInkBarToSelectedTab(): void {\n const selectedItem = this.items && this.items.length ? this.items.toArray()[this.selectedIndex] : null;\n const selectedItemElement = selectedItem ? selectedItem.elementRef.nativeElement : null;\n\n if (selectedItemElement) {\n /**\n * .ant-tabs-nav-list - Target offset parent element\n * └──.ant-tabs-tab\n * └──.ant-tabs-tab-btn - Currently focused element\n */\n this.inkBar.alignToElement(selectedItemElement.parentElement!);\n }\n }\n\n private setPingStatus(): void {\n const ping = {\n top: false,\n right: false,\n bottom: false,\n left: false\n };\n const navWarp = this.navWarpRef.nativeElement;\n if (this.position === 'horizontal') {\n if (this.getLayoutDirection() === 'rtl') {\n ping.right = this.transformX > 0;\n ping.left = this.transformX + this.wrapperWidth < this.scrollListWidth;\n } else {\n ping.left = this.transformX < 0;\n ping.right = -this.transformX + this.wrapperWidth < this.scrollListWidth;\n }\n } else {\n ping.top = this.transformY < 0;\n ping.bottom = -this.transformY + this.wrapperHeight < this.scrollListHeight;\n }\n\n (Object.keys(ping) as Array<'top' | 'right' | 'bottom' | 'left'>).forEach(pos => {\n const className = `ant-tabs-nav-wrap-ping-${pos}`;\n if (ping[pos]) {\n navWarp.classList.add(className);\n } else {\n navWarp.classList.remove(className);\n }\n });\n }\n\n private setVisibleRange(): void {\n let unit: 'width' | 'height';\n let position: 'left' | 'top' | 'right';\n let transformSize: number;\n let basicSize: number;\n let tabContentSize: number;\n let addSize: number;\n const tabs = this.items.toArray();\n const DEFAULT_SIZE = { width: 0, height: 0, left: 0, top: 0, right: 0 };\n\n const getOffset = (index: number): number => {\n let offset: number;\n const size = tabs[index] || DEFAULT_SIZE;\n if (position === 'right') {\n offset = tabs[0].left + tabs[0].width - tabs[index].left - tabs[index].width;\n } else {\n offset = size[position];\n }\n return offset;\n };\n\n if (this.position === 'horizontal') {\n unit = 'width';\n basicSize = this.wrapperWidth;\n tabContentSize = this.scrollListWidth - (this.hiddenItems.length ? this.operationWidth : 0);\n addSize = this.addButtonWidth;\n transformSize = Math.abs(this.transformX);\n if (this.getLayoutDirection() === 'rtl') {\n position = 'right';\n this.pingRight = this.transformX > 0;\n this.pingLeft = this.transformX + this.wrapperWidth < this.scrollListWidth;\n } else {\n this.pingLeft = this.transformX < 0;\n this.pingRight = -this.transformX + this.wrapperWidth < this.scrollListWidth;\n position = 'left';\n }\n } else {\n unit = 'height';\n basicSize = this.wrapperHeight;\n tabContentSize = this.scrollListHeight - (this.hiddenItems.length ? this.operationHeight : 0);\n addSize = this.addButtonHeight;\n position = 'top';\n transformSize = -this.transformY;\n this.pingTop = this.transformY < 0;\n this.pingBottom = -this.transformY + this.wrapperHeight < this.scrollListHeight;\n }\n\n let mergedBasicSize = basicSize;\n if (tabContentSize + addSize > basicSize) {\n mergedBasicSize = basicSize - addSize;\n }\n\n if (!tabs.length) {\n this.hiddenItems = [];\n this.cdr.markForCheck();\n return;\n }\n\n const len = tabs.length;\n let endIndex = len;\n for (let i = 0; i < len; i += 1) {\n const offset = getOffset(i);\n const size = tabs[i] || DEFAULT_SIZE;\n if (offset + size[unit] > transformSize + mergedBasicSize) {\n endIndex = i - 1;\n break;\n }\n }\n\n let startIndex = 0;\n for (let i = len - 1; i >= 0; i -= 1) {\n const offset = getOffset(i);\n if (offset < transformSize) {\n startIndex = i + 1;\n break;\n }\n }\n\n const startHiddenTabs = tabs.slice(0, startIndex);\n const endHiddenTabs = tabs.slice(endIndex + 1);\n this.hiddenItems = [...startHiddenTabs, ...endHiddenTabs];\n this.cdr.markForCheck();\n }\n\n private getLayoutDirection(): Direction {\n return this.dir && this.dir.value === 'rtl' ? 'rtl' : 'ltr';\n }\n\n private setTabFocus(_tabIndex: number): void {}\n\n ngOnChanges(changes: SimpleChanges): void {\n const { position } = changes;\n // The first will be aligning in ngAfterViewInit\n if (position && !position.isFirstChange()) {\n this.alignInkBarToSelectedTab();\n this.lockAnimation();\n this.updateScrollListPosition();\n }\n }\n}\n","/**\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE\n */\n\nimport { ChangeDetectionStrategy, Component, Input, TemplateRef, ViewEncapsulation } from '@angular/core';\n\n@Component({\n selector: '[nz-tab-body]',\n exportAs: 'nzTabBody',\n preserveWhitespaces: false,\n encapsulation: ViewEncapsulation.None,\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `\n <ng-container *ngIf=\"active || forceRender\">\n <ng-template [ngTemplateOutlet]=\"content\"></ng-template>\n </ng-container>\n `,\n host: {\n class: 'ant-tabs-tabpane',\n '[class.ant-tabs-tabpane-active]': 'active',\n '[attr.tabindex]': 'active ? 0 : -1',\n '[attr.aria-hidden]': '!active',\n '[style.visibility]': 'tabPaneAnimated ? active ? null : \"hidden\" : null',\n '[style.height]': 'tabPaneAnimated ? active ? null : 0 : null',\n '[style.overflow-y]': 'tabPaneAnimated ? active ? null : \"none\" : null',\n '[style.display]': '!tabPaneAnimated ? active ? null : \"none\" : null'\n }\n})\nexport class NzTabBodyComponent {\n @Input() content: TemplateRef<void> | null = null;\n @Input() active = false;\n @Input() tabPaneAnimated = true;\n @Input() forceRender = false;\n}\n","/**\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE\n */\n\nimport { Component, Input, TemplateRef } from '@angular/core';\n\nimport { NzSafeAny } from 'ng-zorro-antd/core/types';\n\n@Component({\n selector: 'nz-tab-close-button, button[nz-tab-close-button]',\n template: `\n <ng-container *nzStringTemplateOutlet=\"closeIcon; let icon\">\n <i nz-icon [nzType]=\"icon\" nzTheme=\"outline\"></i>\n </ng-container>\n `,\n host: {\n class: 'ant-tabs-tab-remove',\n 'aria-label': 'Close tab',\n type: 'button'\n }\n})\nexport class NzTabCloseButtonComponent {\n @Input() closeIcon: string | TemplateRef<NzSafeAny> = 'close';\n\n constructor() {}\n}\n","/**\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE\n */\n\nimport { Directive, ElementRef, Host, Optional, Self, TemplateRef } from '@angular/core';\nimport { RouterLink, RouterLinkWithHref } from '@angular/router';\n\nimport { TabTemplateContext } from './interfaces';\n\n/**\n * Fix https://github.com/angular/angular/issues/8563\n */\n@Directive({\n selector: 'ng-template[nzTabLink]',\n exportAs: 'nzTabLinkTemplate'\n})\nexport class NzTabLinkTemplateDirective {\n constructor(@Host() public templateRef: TemplateRef<TabTemplateContext>) {}\n}\n\n/**\n * This component is for catching `routerLink` directive.\n */\n@Directive({\n selector: 'a[nz-tab-link]',\n exportAs: 'nzTabLink'\n})\nexport class NzTabLinkDirective {\n constructor(\n public elementRef: ElementRef<HTMLAnchorElement>,\n @Optional() @Self() public routerLink?: RouterLink,\n @Optional() @Self() public routerLinkWithHref?: RouterLinkWithHref\n ) {}\n}\n","/**\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE\n */\n\nimport { Directive } from '@angular/core';\n\n/** Decorates the `ng-template` tags and reads out the template from it. */\n@Directive({\n selector: '[nz-tab]',\n exportAs: 'nzTab'\n})\nexport class NzTabDirective {}\n","/**\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE\n */\n\nimport {\n ChangeDetectionStrategy,\n Component,\n ContentChild,\n EventEmitter,\n Inject,\n InjectionToken,\n Input,\n OnChanges,\n OnDestroy,\n Output,\n SimpleChanges,\n TemplateRef,\n ViewChild,\n ViewEncapsulation\n} from '@angular/core';\nimport { Subject } from 'rxjs';\n\nimport { BooleanInput, NzSafeAny } from 'ng-zorro-antd/core/types';\nimport { InputBoolean } from 'ng-zorro-antd/core/util';\n\nimport { TabTemplateContext } from './interfaces';\nimport { NzTabLinkDirective, NzTabLinkTemplateDirective } from './tab-link.directive';\nimport { NzTabDirective } from './tab.directive';\n\n/**\n * Used to provide a tab set to a tab without causing a circular dependency.\n */\nexport const NZ_TAB_SET = new InjectionToken<NzSafeAny>('NZ_TAB_SET');\n\n@Component({\n selector: 'nz-tab',\n exportAs: 'nzTab',\n preserveWhitespaces: false,\n encapsulation: ViewEncapsulation.None,\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `\n <ng-template #tabLinkTemplate>\n <ng-content select=\"[nz-tab-link]\"></ng-content>\n </ng-template>\n <ng-template #contentTemplate><ng-content></ng-content></ng-template>\n `\n})\nexport class NzTabComponent implements OnChanges, OnDestroy {\n static ngAcceptInputType_nzDisabled: BooleanInput;\n static ngAcceptInputType_nzClosable: BooleanInput;\n static ngAcceptInputType_nzForceRender: BooleanInput;\n\n @Input() nzTitle: string | TemplateRef<TabTemplateContext> = '';\n @Input() @InputBoolean() nzClosable = false;\n @Input() nzCloseIcon: string | TemplateRef<NzSafeAny> = 'close';\n @Input() @InputBoolean() nzDisabled = false;\n @Input() @InputBoolean() nzForceRender = false;\n @Output() readonly nzSelect = new EventEmitter<void>();\n @Output() readonly nzDeselect = new EventEmitter<void>();\n @Output() readonly nzClick = new EventEmitter<void>();\n @Output() readonly nzContextmenu = new EventEmitter<MouseEvent>();\n\n @ContentChild(NzTabLinkTemplateDirective, { static: false }) nzTabLinkTemplateDirective!: NzTabLinkTemplateDirective;\n @ContentChild(NzTabDirective, { static: false, read: TemplateRef }) template: TemplateRef<void> | null = null;\n @ContentChild(NzTabLinkDirective, { static: false }) linkDirective!: NzTabLinkDirective;\n @ViewChild('contentTemplate', { static: true }) contentTemplate!: TemplateRef<NzSafeAny>;\n\n isActive: boolean = false;\n position: number | null = null;\n origin: number | null = null;\n readonly stateChanges = new Subject<void>();\n\n get content(): TemplateRef<NzSafeAny> {\n return this.template || this.contentTemplate;\n }\n\n get label(): string | TemplateRef<NzSafeAny> {\n return this.nzTitle || this.nzTabLinkTemplateDirective?.templateRef;\n }\n\n constructor(@Inject(NZ_TAB_SET) public closestTabSet: NzSafeAny) {}\n\n ngOnChanges(changes: SimpleChanges): void {\n const { nzTitle, nzDisabled, nzForceRender } = changes;\n if (nzTitle || nzDisabled || nzForceRender) {\n this.stateChanges.next();\n }\n }\n\n ngOnDestroy(): void {\n this.stateChanges.complete();\n }\n}\n","/**\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE\n */\n\nimport { Observable } from 'rxjs';\n\nimport { NzSafeAny } from 'ng-zorro-antd/core/types';\n\nexport type NzTabPosition = 'top' | 'bottom' | 'left' | 'right';\nexport type NzTabType = 'line' | 'card' | 'editable-card';\nexport type NzTabsCanDeactivateFn = (\n fromIndex: number,\n toIndex: number\n) => Observable<boolean> | Promise<boolean> | boolean;\nexport type NzTabPositionMode = 'horizontal' | 'vertical';\n\nexport interface NzAnimatedInterface {\n inkBar: boolean;\n tabPane: boolean;\n}\n\nexport class NzTabChangeEvent {\n index?: number;\n tab: NzSafeAny;\n}\n\nexport interface NzTabScrollListOffset {\n x: number;\n y: number;\n}\n\nexport type NzTabScrollListOffsetEvent = NzTabScrollListOffset & { event: Event };\n\ninterface NzTabWheelScrollEvent {\n type: 'wheel';\n event: WheelEvent;\n}\n\ninterface NzTabTouchScrollEvent {\n type: 'touchstart' | 'touchmove' | 'touchend';\n event: TouchEvent;\n}\n\nexport type NzTabScrollEvent = NzTabTouchScrollEvent | NzTabWheelScrollEvent;\nexport type NzTabScrollEventHandlerFun<T extends NzTabScrollEvent['event']> = (event: T) => void;\n\nexport interface TabTemplateContext {\n visible: boolean;\n}\n","/**\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE\n */\n\n/** get some code from https://github.com/angular/material2 */\n\nimport { Direction, Directionality } from '@angular/cdk/bidi';\nimport { coerceNumberProperty } from '@angular/cdk/coercion';\nimport {\n AfterContentChecked,\n AfterContentInit,\n ChangeDetectionStrategy,\n ChangeDetectorRef,\n Component,\n ContentChildren,\n EventEmitter,\n Input,\n NgZone,\n OnDestroy,\n OnInit,\n Optional,\n Output,\n QueryList,\n TemplateRef,\n ViewChild,\n ViewEncapsulation\n} from '@angular/core';\nimport { NavigationEnd, Router, RouterLink, RouterLinkWithHref } from '@angular/router';\nimport { merge, Observable, of, Subject, Subscription } from 'rxjs';\nimport { delay, filter, first, startWith, takeUntil } from 'rxjs/operators';\n\nimport { NzConfigKey, NzConfigService, WithConfig } from 'ng-zorro-antd/core/config';\nimport { PREFIX } from 'ng-zorro-antd/core/logger';\nimport { BooleanInput, NumberInput, NzSafeAny, NzSizeLDSType } from 'ng-zorro-antd/core/types';\nimport { InputBoolean, wrapIntoObservable } from 'ng-zorro-antd/core/util';\n\nimport {\n NzAnimatedInterface,\n NzTabChangeEvent,\n NzTabPosition,\n NzTabPositionMode,\n NzTabsCanDeactivateFn,\n NzTabScrollEvent,\n NzTabType\n} from './interfaces';\nimport { NzTabNavBarComponent } from './tab-nav-bar.component';\nimport { NzTabComponent, NZ_TAB_SET } from './tab.component';\n\nconst NZ_CONFIG_MODULE_NAME: NzConfigKey = 'tabs';\n\nlet nextId = 0;\n\n@Component({\n selector: 'nz-tabset',\n exportAs: 'nzTabset',\n preserveWhitespaces: false,\n encapsulation: ViewEncapsulation.None,\n changeDetection: ChangeDetectionStrategy.Default,\n providers: [\n {\n provide: NZ_TAB_SET,\n useExisting: NzTabSetComponent\n }\n ],\n template: `\n <nz-tabs-nav\n *ngIf=\"tabs.length || addable\"\n [ngStyle]=\"nzTabBarStyle\"\n [selectedIndex]=\"nzSelectedIndex || 0\"\n [inkBarAnimated]=\"inkBarAnimated\"\n [addable]=\"addable\"\n [addIcon]=\"nzAddIcon\"\n [hideBar]=\"nzHideAll\"\n [position]=\"position\"\n [extraTemplate]=\"nzTabBarExtraContent\"\n (tabScroll)=\"nzTabListScroll.emit($event)\"\n (selectFocusedIndex)=\"setSelectedIndex($event)\"\n (addClicked)=\"onAdd()\"\n >\n <div\n class=\"ant-tabs-tab\"\n [style.margin-right.px]=\"position === 'horizontal' ? nzTabBarGutter : null\"\n [style.margin-bottom.px]=\"position === 'vertical' ? nzTabBarGutter : null\"\n [class.ant-tabs-tab-active]=\"nzSelectedIndex === i\"\n [class.ant-tabs-tab-disabled]=\"tab.nzDisabled\"\n (click)=\"clickNavItem(tab, i, $event)\"\n (contextmenu)=\"contextmenuNavItem(tab, $event)\"\n *ngFor=\"let tab of tabs; let i = index\"\n >\n <div\n role=\"tab\"\n [attr.tabIndex]=\"getTabIndex(tab, i)\"\n [attr.aria-disabled]=\"tab.nzDisabled\"\n [attr.aria-selected]=\"nzSelectedIndex === i && !nzHideAll\"\n [attr.aria-controls]=\"getTabContentId(i)\"\n [disabled]=\"tab.nzDisabled\"\n [tab]=\"tab\"\n [active]=\"nzSelectedIndex === i\"\n class=\"ant-tabs-tab-btn\"\n nzTabNavItem\n cdkMonitorElementFocus\n >\n <ng-container *nzStringTemplateOutlet=\"tab.label; context: { visible: true }\">{{ tab.label }}</ng-container>\n <button\n nz-tab-close-button\n *ngIf=\"tab.nzClosable && closable && !tab.nzDisabled\"\n [closeIcon]=\"tab.nzCloseIcon\"\n (click)=\"onClose(i, $event)\"\n ></button>\n </div>\n </div>\n </nz-tabs-nav>\n <div class=\"ant-tabs-content-holder\">\n <div\n class=\"ant-tabs-content\"\n [class.ant-tabs-content-top]=\"nzTabPosition === 'top'\"\n [class.ant-tabs-content-bottom]=\"nzTabPosition === 'bottom'\"\n [class.ant-tabs-content-left]=\"nzTabPosition === 'left'\"\n [class.ant-tabs-content-right]=\"nzTabPosition === 'right'\"\n [class.ant-tabs-content-animated]=\"tabPaneAnimated\"\n [style.margin-left]=\"getTabContentMarginLeft()\"\n [style.margin-right]=\"getTabContentMarginRight()\"\n >\n <div\n nz-tab-body\n *ngFor=\"let tab of tabs; let i = index\"\n [active]=\"nzSelectedIndex === i && !nzHideAll\"\n [content]=\"tab.content\"\n [forceRender]=\"tab.nzForceRender\"\n [tabPaneAnimated]=\"tabPaneAnimated\"\n ></div>\n </div>\n </div>\n `,\n host: {\n class: 'ant-tabs',\n '[class.ant-tabs-card]': `nzType === 'card' || nzType === 'editable-card'`,\n '[class.ant-tabs-editable]': `nzType === 'editable-card'`,\n '[class.ant-tabs-editable-card]': `nzType === 'editable-card'`,\n '[class.ant-tabs-centered]': `nzCentered`,\n '[class.ant-tabs-rtl]': `dir === 'rtl'`,\n '[class.ant-tabs-top]': `nzTabPosition === 'top'`,\n '[class.ant-tabs-bottom]': `nzTabPosition === 'bottom'`,\n '[class.ant-tabs-left]': `nzTabPosition === 'left'`,\n '[class.ant-tabs-right]': `nzTabPosition === 'right'`,\n '[class.ant-tabs-default]': `nzSize === 'default'`,\n '[class.ant-tabs-small]': `nzSize === 'small'`,\n '[class.ant-tabs-large]': `nzSize === 'large'`\n }\n})\nexport class NzTabSetComponent implements OnInit, AfterContentChecked, OnDestroy, AfterContentInit {\n readonly _nzModuleName: NzConfigKey = NZ_CONFIG_MODULE_NAME;\n\n static ngAcceptInputType_nzHideAdd: BooleanInput;\n static ngAcceptInputType_nzHideAll: BooleanInput;\n static ngAcceptInputType_nzCentered: BooleanInput;\n static ngAcceptInputType_nzLinkRouter: BooleanInput;\n static ngAcceptInputType_nzLinkExact: BooleanInput;\n static ngAcceptInputType_nzSelectedIndex: NumberInput;\n\n @Input()\n get nzSelectedIndex(): number | null {\n return this.selectedIndex;\n }\n set nzSelectedIndex(value: null | number) {\n this.indexToSelect = coerceNumberProperty(value, null);\n }\n @Input() nzTabPosition: NzTabPosition = 'top';\n @Input() nzTabBarExtraContent?: TemplateRef<void>;\n @Input() nzCanDeactivate: NzTabsCanDeactivateFn | null = null;\n @Input() nzAddIcon: string | TemplateRef<NzSafeAny> = 'plus';\n @Input() nzTabBarStyle: { [key: string]: string } | null = null;\n @Input() @WithConfig() nzType: NzTabType = 'line';\n @Input() @WithConfig() nzSize: NzSizeLDSType = 'default';\n @Input() @WithConfig() nzAnimated: NzAnimatedInterface | boolean = true;\n @Input() @WithConfig() nzTabBarGutter?: number = undefined;\n @Input() @InputBoolean() nzHideAdd: boolean = false;\n @Input() @InputBoolean() nzCentered: boolean = false;\n @Input() @InputBoolean() nzHideAll = false;\n @Input() @InputBoolean() nzLinkRouter = false;\n @Input() @InputBoolean() nzLinkExact = true;\n\n @Output() readonly nzSelectChange: EventEmitter<NzTabChangeEvent> = new EventEmitter<NzTabChangeEvent>(true);\n @Output() readonly nzSelectedIndexChange: EventEmitter<number> = new EventEmitter<number>();\n @Output() readonly nzTabListScroll = new EventEmitter<NzTabScrollEvent>();\n @Output() readonly nzClose = new EventEmitter<{ index: number }>();\n @Output() readonly nzAdd = new EventEmitter<void>();\n\n get position(): NzTabPositionMode {\n return ['top', 'bottom'].indexOf(this.nzTabPosition) === -1 ? 'vertical' : 'horizontal';\n }\n\n get addable(): boolean {\n return this.nzType === 'editable-card' && !this.nzHideAdd;\n }\n\n get closable(): boolean {\n return this.nzType === 'editable-card';\n }\n\n get line(): boolean {\n return this.nzType === 'line';\n }\n\n get inkBarAnimated(): boolean {\n return this.line && (typeof this.nzAnimated === 'boolean' ? this.nzAnimated : this.nzAnimated.inkBar);\n }\n\n get tabPaneAnimated(): boolean {\n return (\n this.position === 'horizontal' &&\n this.line &&\n (typeof this.nzAnimated === 'boolean' ? this.nzAnimated : this