UNPKG

carbon-components-angular

Version:
1 lines 160 kB
{"version":3,"file":"carbon-components-angular-tabs.mjs","sources":["../../src/tabs/base-tab-header.component.ts","../../src/tabs/tab-header.directive.ts","../../src/tabs/tab-header-group.component.ts","../../src/tabs/tab-header-group-vertical.component.ts","../../src/tabs/tab-header.component.ts","../../src/tabs/tab.component.ts","../../src/tabs/tab-headers.component.ts","../../src/tabs/tab-headers-vertical.component.ts","../../src/tabs/icon-tab.component.ts","../../src/tabs/tab-skeleton.component.ts","../../src/tabs/tabs.component.ts","../../src/tabs/tabs-vertical.component.ts","../../src/tabs/tabs-vertical-grouped.component.ts","../../src/tabs/tabs.module.ts","../../src/tabs/carbon-components-angular-tabs.ts"],"sourcesContent":["import {\n\tComponent,\n\tInput,\n\tViewChild,\n\tElementRef,\n\tTemplateRef,\n\tChangeDetectorRef,\n\tHostBinding,\n\tRenderer2\n} from \"@angular/core\";\nimport { EventService } from \"carbon-components-angular/utils\";\n\n/**\n * There are two ways to create a tab, this class is a collection of features\n * & metadata required by both.\n */\n@Component({\n\ttemplate: \"\"\n})\nexport class BaseTabHeader {\n\t/**\n\t * Set to `true` to have `Tab` items cached and not reloaded on tab switching.\n\t * Duplicated from `cds-tabs` to support standalone headers.\n\t */\n\t@Input() cacheActive = false;\n\t/**\n\t * Set to 'true' to have tabs automatically activated and have their content displayed when they receive focus.\n\t */\n\t@Input() followFocus: boolean;\n\t/**\n\t * Sets the aria label on the nav element.\n\t */\n\t@Input() ariaLabel: string;\n\t/**\n\t * Sets the aria labelledby on the nav element.\n\t */\n\t@Input() ariaLabelledby: string;\n\n\t/**\n\t * Template projected before tab items inside the tab list.\n\t */\n\t@Input() contentBefore: TemplateRef<any>;\n\t/**\n\t * Template projected after tab items inside the tab list.\n\t */\n\t@Input() contentAfter: TemplateRef<any>;\n\n\t/**\n\t * Visual style of the tab list: `line` or `contained`.\n\t */\n\t@Input() type: \"line\" | \"contained\" = \"line\";\n\t/**\n\t * Theme for contained tabs: `dark` or `light`.\n\t */\n\t@Input() theme: \"dark\" | \"light\" = \"dark\";\n\n\t/**\n\t * When using icon-only tabs, icon size: `default` (16px) or `lg` (20px).\n\t */\n\t@Input() iconSize: \"default\" | \"lg\";\n\n\t/**\n\t * **Contained only**: Evenly sized tabs across the row (**must** have fewer than 9 tabs).\n\t */\n\t@Input() fullWidth = false;\n\n\t/**\n\t * Show a close control on each tab.\n\t */\n\t@Input() dismissable = false;\n\n\t/**\n\t * Scroll the active tab into view on focus/select.\n\t */\n\t@Input() scrollIntoView = false;\n\n\t/**\n\t * Debounce (ms) for tab list scroll events; affects overflow chevron updates.\n\t */\n\t@Input() scrollDebounceWait = 200;\n\n\t@HostBinding(\"class.cds--tabs\") tabsClass = true;\n\t@HostBinding(\"class.cds--tabs--contained\") get containedClass() {\n\t\treturn this.type === \"contained\";\n\t}\n\t@HostBinding(\"class.cds--tabs--light\") get themeClass() {\n\t\treturn this.theme === \"light\";\n\t}\n\t@HostBinding(\"class.cds--tabs--dismissable\") get dismissableClass() {\n\t\treturn this.dismissable;\n\t}\n\t@HostBinding(\"class.cds--tabs__icon--default\") get iconSizeDefaultClass() {\n\t\treturn this.iconSize === \"default\";\n\t}\n\t@HostBinding(\"class.cds--tabs__icon--lg\") get iconSizeLgClass() {\n\t\treturn this.iconSize === \"lg\";\n\t}\n\t@HostBinding(\"class.cds--layout--size-lg\") get layoutSizeLgClass() {\n\t\treturn this.iconSize === \"lg\";\n\t}\n\n\t/**\n\t * Gets the Unordered List element that holds the `Tab` headings from the view DOM.\n\t */\n\t@ViewChild(\"tabList\", { static: true }) headerContainer;\n\n\t/**\n\t * Controls the manual focusing done by tabbing through headings.\n\t */\n\tcurrentSelectedTab: number;\n\t// width of the overflow buttons\n\treadonly OVERFLOW_BUTTON_OFFSET = 44;\n\treadonly longPressMultiplier = 3;\n\treadonly clickMultiplier = 1.5;\n\n\tprotected longPressInterval = null;\n\tprotected tickInterval = null;\n\tprotected scrollDebounceTimer: any = null;\n\n\tget hasHorizontalOverflow() {\n\t\tconst tabList = this.headerContainer.nativeElement;\n\t\treturn tabList.scrollWidth > tabList.clientWidth;\n\t}\n\n\tget leftOverflowNavButtonHidden() {\n\t\tconst tabList = this.headerContainer.nativeElement;\n\t\treturn !this.hasHorizontalOverflow || !tabList.scrollLeft;\n\t}\n\n\tget rightOverflowNavButtonHidden() {\n\t\tconst tabList = this.headerContainer.nativeElement;\n\t\treturn !this.hasHorizontalOverflow ||\n\t\t\t(tabList.scrollLeft + tabList.clientWidth) === tabList.scrollWidth;\n\t}\n\n\tconstructor(\n\t\tprotected elementRef: ElementRef,\n\t\tprotected changeDetectorRef: ChangeDetectorRef,\n\t\tprotected eventService: EventService,\n\t\tprotected renderer: Renderer2\n\t) { }\n\n\thandleScroll() {\n\t\t// Debounce the change detection trigger so the scroll arrow visibility\n\t\t// updates do not fire on every scroll tick.\n\t\tif (this.scrollDebounceWait <= 0) {\n\t\t\tthis.changeDetectorRef.markForCheck();\n\t\t\treturn;\n\t\t}\n\t\tclearTimeout(this.scrollDebounceTimer);\n\t\tthis.scrollDebounceTimer = setTimeout(() => {\n\t\t\tthis.changeDetectorRef.markForCheck();\n\t\t}, this.scrollDebounceWait);\n\t}\n\n\thandleOverflowNavClick(direction: number, numOftabs = 0) {\n\t\tconst tabList = this.headerContainer.nativeElement;\n\n\t\tconst { clientWidth, scrollLeft, scrollWidth } = tabList;\n\t\tif (direction > 0) {\n\t\t\ttabList.scrollLeft = Math.min(scrollLeft + (scrollWidth / numOftabs) * this.clickMultiplier,\n\t\t\t\tscrollWidth - clientWidth);\n\t\t} else if (direction < 0) {\n\t\t\ttabList.scrollLeft = Math.max(scrollLeft - (scrollWidth / numOftabs) * this.clickMultiplier, 0);\n\t\t}\n\t}\n\n\thandleOverflowNavMouseDown(direction: number) {\n\t\tconst tabList = this.headerContainer.nativeElement;\n\n\t\tthis.longPressInterval = setTimeout(() => {\n\t\t\t// Manually overriding scroll behvior to `auto` to make animation work correctly\n\t\t\tthis.renderer.setStyle(tabList, \"scroll-behavior\", \"auto\");\n\n\t\t\tthis.tickInterval = setInterval(() => {\n\t\t\t\ttabList.scrollLeft += (direction * this.longPressMultiplier);\n\t\t\t\t// clear interval if scroll reaches left or right edge\n\t\t\t\tif (this.leftOverflowNavButtonHidden || this.rightOverflowNavButtonHidden) {\n\t\t\t\t\treturn () => {\n\t\t\t\t\t\tclearInterval(this.tickInterval);\n\t\t\t\t\t\tthis.handleOverflowNavMouseUp();\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t});\n\n\t\t\treturn () => clearInterval(this.longPressInterval);\n\t\t}, 500);\n\t}\n\n\t/**\n\t * Clear intervals/Timeout & reset scroll behavior\n\t */\n\thandleOverflowNavMouseUp() {\n\t\tclearInterval(this.tickInterval);\n\t\tclearTimeout(this.longPressInterval);\n\n\t\t// Reset scroll behavior\n\t\tthis.renderer.setStyle(this.headerContainer.nativeElement, \"scroll-behavior\", \"smooth\");\n\t}\n}\n","import {\n\tDirective,\n\tInput,\n\tOutput,\n\tEventEmitter,\n\tElementRef,\n\tAfterViewInit,\n\tHostBinding,\n\tHostListener,\n\tTemplateRef,\n\tforwardRef\n} from \"@angular/core\";\n\nimport { Tab } from \"./tab.component\";\n\n/**\n * Shared inputs, outputs, and selection logic for `[cdsTabHeader]`\n * and `cds-tab-header` as we prepare for deprecation.\n * Groups use `@ContentChildren(TabHeaderBase)` so both forms appear in DOM order,\n * subclasses supply the template and host behavior.\n */\n@Directive()\n// eslint-disable-next-line @angular-eslint/directive-class-suffix -- abstract base class, not a directive instance\nexport abstract class TabHeaderBase {\n\t/**\n\t * Set to 'true' to have pane reference cached and not reloaded on tab switching.\n\t */\n\t@Input() set cacheActive(shouldCache: boolean) {\n\t\tthis._cacheActive = shouldCache;\n\n\t\t// Updates the pane references associated with the tab header when cache active is changed.\n\t\tif (this.paneReference) {\n\t\t\tthis.paneReference.cacheActive = this.cacheActive;\n\t\t}\n\t}\n\n\tget cacheActive() {\n\t\treturn this._cacheActive;\n\t}\n\n\t/**\n\t * Sets `tabIndex` on the linked `Tab` pane when the pane reference is set.\n\t */\n\t@Input() set paneTabIndex(tabIndex: number | null) {\n\t\tif (this.paneReference) {\n\t\t\tthis.paneReference.tabIndex = tabIndex;\n\t\t}\n\t}\n\n\t/**\n\t * Selected tab; controls whether the linked pane content is shown.\n\t */\n\t@Input() active = false;\n\n\t/**\n\t * Indicates whether or not the `Tab` item is disabled.\n\t */\n\t@Input() disabled = false;\n\n\t/**\n\t * Icon template; used with `cds-tab-header` / `cds-tab-header-group`.\n\t */\n\t@Input() icon: TemplateRef<any>;\n\n\t/**\n\t * Optional secondary label rendered below the primary tab label.\n\t * Only displayed when the parent group is using `type=\"contained\"`.\n\t */\n\t@Input() secondaryLabel: string;\n\n\t/**\n\t * Set to `true` to render this tab header as dismissable.\n\t */\n\t@Input() dismissable = false;\n\n\t/**\n\t * Reference to the corresponding tab pane.\n\t */\n\t@Input() paneReference: Tab;\n\n\t/**\n\t * Title attribute used as the tooltip for the tab item. Falls back to the tab item's text content if not provided.\n\t */\n\t@Input() title: string;\n\n\t/**\n\t * Emits when this header becomes selected.\n\t */\n\t@Output() selected = new EventEmitter<any>();\n\n\t/**\n\t * Emits when this tabs's close button is pressed.\n\t */\n\t@Output() tabClose = new EventEmitter<void>();\n\n\tprotected _cacheActive = false;\n\n\t/**\n\t * Move keyboard focus to the tab item.\n\t */\n\tabstract focus(): void;\n\n\t/**\n\t * Activates the linked pane and emits `selected`.\n\t */\n\tselectTab() {\n\t\tthis.focus();\n\t\tif (!this.disabled) {\n\t\t\tthis.selected.emit();\n\t\t\tthis.active = true;\n\t\t\tif (this.paneReference) {\n\t\t\t\tthis.paneReference.active = true;\n\t\t\t}\n\t\t}\n\t}\n}\n\n/**\n * Tab header as an attribute on a focusable host inside `cds-tab-header-group`.\n *\n * @deprecated as of v5.\n * Prefer `cds-tab-header` for icons, secondary labels, dismissable close, and icon-only tabs.\n */\n@Directive({\n\tselector: \"[cdsTabHeader], [ibmTabHeader]\",\n\tproviders: [\n\t\t// tslint:disable-next-line:no-forward-ref\n\t\t{ provide: TabHeaderBase, useExisting: forwardRef(() => TabHeader) }\n\t]\n})\nexport class TabHeader extends TabHeaderBase implements AfterViewInit {\n\t@HostBinding(\"attr.tabIndex\") get tabIndex() {\n\t\treturn this.active ? 0 : -1;\n\t}\n\n\t@HostBinding(\"class.cds--tabs__nav-item--selected\") get isSelected() {\n\t\treturn this.active;\n\t}\n\n\t@HostBinding(\"class.cds--tabs__nav-item--disabled\") get isDisabled() {\n\t\treturn this.disabled;\n\t}\n\n\t@HostBinding(\"attr.type\") type = \"button\";\n\t@HostBinding(\"attr.aria-selected\") get ariaSelected() {\n\t\treturn this.active;\n\t}\n\t@HostBinding(\"attr.aria-disabled\") get ariaDisabled() {\n\t\treturn this.disabled;\n\t}\n\t@HostBinding(\"class.cds--tabs__nav-item\") navItem = true;\n\t@HostBinding(\"class.cds--tabs__nav-link\") navLink = true;\n\t@HostBinding(\"attr.title\") get hostTitle() {\n\t\treturn this.title ?? null;\n\t}\n\n\tconstructor(private host: ElementRef) {\n\t\tsuper();\n\t}\n\n\t@HostListener(\"click\")\n\tonClick() {\n\t\tthis.selectTab();\n\t}\n\n\t@HostListener(\"keydown\", [\"$event\"])\n\tonKeyDown(event: KeyboardEvent) {\n\t\tif (this.dismissable && event.key === \"Delete\") {\n\t\t\tevent.stopPropagation();\n\t\t\tthis.tabClose.emit();\n\t\t}\n\t}\n\n\tngAfterViewInit() {\n\t\tsetTimeout(() => {\n\t\t\tthis.title = this.title ? this.title : this.host.nativeElement.textContent;\n\t\t});\n\t}\n\n\tfocus() {\n\t\tthis.host.nativeElement.focus();\n\t}\n}\n","import {\n\tComponent,\n\tQueryList,\n\tInput,\n\tOutput,\n\tEventEmitter,\n\tHostBinding,\n\tHostListener,\n\tContentChildren,\n\tAfterContentInit,\n\tElementRef,\n\tOnChanges,\n\tSimpleChanges,\n\tChangeDetectorRef,\n\tViewChild,\n\tOnInit,\n\tOnDestroy,\n\tRenderer2\n} from \"@angular/core\";\n\nimport { Subscription } from \"rxjs\";\nimport { EventService } from \"carbon-components-angular/utils\";\nimport { I18n } from \"carbon-components-angular/i18n\";\n\nimport { TabHeaderBase } from \"./tab-header.directive\";\nimport { BaseTabHeader } from \"./base-tab-header.component\";\n\n@Component({\n\tselector: \"cds-tab-header-group, ibm-tab-header-group\",\n\ttemplate: `\n\t\t<button\n\t\t\ttype=\"button\"\n\t\t\tclass=\"cds--tab--overflow-nav-button cds--tab--overflow-nav-button--previous\"\n\t\t\t[ngClass]=\"{\n\t\t\t\t'cds--tab--overflow-nav-button--hidden': leftOverflowNavButtonHidden\n\t\t\t}\"\n\t\t\t[attr.aria-hidden]=\"leftOverflowNavButtonHidden\"\n\t\t\t[attr.tabindex]=\"-1\"\n\t\t\t[attr.aria-label]=\"translations.BUTTON_ARIA_LEFT\"\n\t\t\t[attr.title]=\"translations.BUTTON_ARIA_LEFT\"\n\t\t\t(click)=\"handleOverflowNavClick(-1, tabHeaderQuery.length)\"\n\t\t\t(pointerdown)=\"handleOverflowNavMouseDown(-1)\"\n\t\t\t(pointerup)=\"handleOverflowNavMouseUp()\"\n\t\t\t(pointerleave)=\"handleOverflowNavMouseUp()\"\n\t\t\t(pointerout)=\"handleOverflowNavMouseUp()\"\n\t\t\t(pointercancel)=\"handleOverflowNavMouseUp()\">\n\t\t\t<svg\n\t\t\t\tfocusable=\"false\"\n\t\t\t\tpreserveAspectRatio=\"xMidYMid meet\"\n\t\t\t\txmlns=\"http://www.w3.org/2000/svg\"\n\t\t\t\tfill=\"currentColor\"\n\t\t\t\twidth=\"16\"\n\t\t\t\theight=\"16\"\n\t\t\t\tviewBox=\"0 0 16 16\"\n\t\t\t\taria-hidden=\"true\">\n\t\t\t\t<path d=\"M5 8L10 3 10.7 3.7 6.4 8 10.7 12.3 10 13z\"></path>\n\t\t\t</svg>\n\t\t</button>\n\t\t<div\n\t\t\tclass=\"cds--tab--list\"\n\t\t\trole=\"tablist\"\n\t\t\t[attr.aria-label]=\"ariaLabel || translations.HEADER_ARIA_LABEL\"\n\t\t\t[attr.aria-labelledby]=\"ariaLabelledby || null\"\n\t\t\t(scroll)=\"handleScroll()\"\n\t\t\t#tabList>\n\t\t\t<ng-container [ngTemplateOutlet]=\"contentBefore\"></ng-container>\n\t\t\t<ng-content></ng-content>\n\t\t\t<ng-container [ngTemplateOutlet]=\"contentAfter\"></ng-container>\n\t\t</div>\n\t\t<button\n\t\t\ttype=\"button\"\n\t\t\tclass=\"cds--tab--overflow-nav-button cds--tab--overflow-nav-button--next\"\n\t\t\t[ngClass]=\"{\n\t\t\t\t'cds--tab--overflow-nav-button--hidden': rightOverflowNavButtonHidden\n\t\t\t}\"\n\t\t\t[attr.aria-hidden]=\"rightOverflowNavButtonHidden\"\n\t\t\t[attr.tabindex]=\"-1\"\n\t\t\t[attr.aria-label]=\"translations.BUTTON_ARIA_RIGHT\"\n\t\t\t[attr.title]=\"translations.BUTTON_ARIA_RIGHT\"\n\t\t\t(click)=\"handleOverflowNavClick(1, tabHeaderQuery.length)\"\n\t\t\t(pointerdown)=\"handleOverflowNavMouseDown(1)\"\n\t\t\t(pointerup)=\"handleOverflowNavMouseUp()\"\n\t\t\t(pointerleave)=\"handleOverflowNavMouseUp()\"\n\t\t\t(pointerout)=\"handleOverflowNavMouseUp()\"\n\t\t\t(pointercancel)=\"handleOverflowNavMouseUp()\">\n\t\t\t<svg\n\t\t\t\tfocusable=\"false\"\n\t\t\t\tpreserveAspectRatio=\"xMidYMid meet\"\n\t\t\t\txmlns=\"http://www.w3.org/2000/svg\"\n\t\t\t\tfill=\"currentColor\"\n\t\t\t\twidth=\"16\"\n\t\t\t\theight=\"16\"\n\t\t\t\tviewBox=\"0 0 16 16\"\n\t\t\t\taria-hidden=\"true\">\n\t\t\t\t<path d=\"M11 8L6 13 5.3 12.3 9.6 8 5.3 3.7 6 3z\"></path>\n\t\t\t</svg>\n\t\t</button>\n\t`\n})\nexport class TabHeaderGroup extends BaseTabHeader implements AfterContentInit, OnChanges, OnInit, OnDestroy {\n\n\t@HostBinding(\"class.cds--tabs--full-width\") get fullWidthClass() {\n\t\treturn this.distributeWidth;\n\t}\n\n\t/**\n\t * We use taller rows when any header has a secondary label.\n\t */\n\t@HostBinding(\"class.cds--tabs--tall\") get tallClass(): boolean {\n\t\treturn this.hasSecondaryLabelTabs;\n\t}\n\n\tget hasSecondaryLabelTabs(): boolean {\n\t\tif (!this.tabHeaderQuery || this.type !== \"contained\") {\n\t\t\treturn false;\n\t\t}\n\t\treturn this.tabHeaderQuery.toArray().some(\n\t\t\th =>\n\t\t\t\th.secondaryLabel != null &&\n\t\t\t\tString(h.secondaryLabel).trim() !== \"\"\n\t\t);\n\t}\n\n\t/**\n\t * True when `fullWidth` applies (contained, fewer than 9 headers).\n\t */\n\tget distributeWidth(): boolean {\n\t\treturn (\n\t\t\tthis.fullWidth &&\n\t\t\tthis.type === \"contained\" &&\n\t\t\t(this.tabHeaderQuery ? this.tabHeaderQuery.length < 9 : false)\n\t\t);\n\t}\n\t/**\n\t * i18n strings for overflow controls and the tab list `aria-label` fallback.\n\t */\n\t@Input() translations = this.i18n.get().TABS;\n\n\t/**\n\t * When `true`, sets each tab panel `tabindex` to `-1` for navigation-style usage.\n\t */\n\t@Input() isNavigation = false;\n\n\t/**\n\t * Emits when a tab close control is used (with `dismissable`).\n\t * The emitted value is the tab index.\n\t */\n\t@Output() tabClose: EventEmitter<number> = new EventEmitter<number>();\n\n\t/**\n\t * Projected tab headers (`TabHeaderBase`: directive or `cds-tab-header`).\n\t */\n\t@ContentChildren(TabHeaderBase) tabHeaderQuery: QueryList<TabHeaderBase>;\n\n\t@ViewChild(\"tabList\", { static: true }) headerContainer;\n\tselectedSubscriptionTracker = new Subscription();\n\tcloseSubscriptionTracker = new Subscription();\n\n\t/**\n\t * Index of the selected tab for keyboard logic.\n\t */\n\tcurrentSelectedTab = 0;\n\n\t/**\n\t * Focused tab index when `followFocus` is false (manual activation).\n\t */\n\tactiveIndex: number | null = null;\n\n\tconstructor(\n\t\tprotected elementRef: ElementRef,\n\t\tprotected changeDetectorRef: ChangeDetectorRef,\n\t\tprotected eventService: EventService,\n\t\tprotected renderer: Renderer2,\n\t\tprotected i18n: I18n\n\t) {\n\t\tsuper(elementRef, changeDetectorRef, eventService, renderer);\n\t}\n\n\t@HostListener(\"keydown\", [\"$event\"])\n\tkeyboardInput(event) {\n\t\tconst tabHeadersArray = this.tabHeaderQuery.toArray();\n\n\t\tif (event.key === \"ArrowRight\") {\n\t\t\tif (this.currentSelectedTab < tabHeadersArray.length - 1) {\n\t\t\t\tevent.preventDefault();\n\t\t\t\tif (this.followFocus && !tabHeadersArray[this.currentSelectedTab + 1].disabled) {\n\t\t\t\t\ttabHeadersArray[this.currentSelectedTab + 1].selectTab();\n\t\t\t\t} else {\n\t\t\t\t\ttabHeadersArray[this.currentSelectedTab + 1].focus();\n\t\t\t\t\tthis.currentSelectedTab++;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tevent.preventDefault();\n\t\t\t\tif (this.followFocus && !tabHeadersArray[0].disabled) {\n\t\t\t\t\ttabHeadersArray[0].selectTab();\n\t\t\t\t} else {\n\t\t\t\t\ttabHeadersArray[0].focus();\n\t\t\t\t\tthis.currentSelectedTab = 0;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (event.key === \"ArrowLeft\") {\n\t\t\tif (this.currentSelectedTab > 0) {\n\t\t\t\tevent.preventDefault();\n\t\t\t\tif (this.followFocus && !tabHeadersArray[this.currentSelectedTab - 1].disabled) {\n\t\t\t\t\ttabHeadersArray[this.currentSelectedTab - 1].selectTab();\n\t\t\t\t} else {\n\t\t\t\t\ttabHeadersArray[this.currentSelectedTab - 1].focus();\n\t\t\t\t\tthis.currentSelectedTab--;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tevent.preventDefault();\n\t\t\t\tif (this.followFocus && !tabHeadersArray[tabHeadersArray.length - 1].disabled) {\n\t\t\t\t\ttabHeadersArray[tabHeadersArray.length - 1].selectTab();\n\t\t\t\t} else {\n\t\t\t\t\ttabHeadersArray[tabHeadersArray.length - 1].focus();\n\t\t\t\t\tthis.currentSelectedTab = tabHeadersArray.length - 1;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (event.key === \"Home\") {\n\t\t\tevent.preventDefault();\n\t\t\tif (this.followFocus && !tabHeadersArray[0].disabled) {\n\t\t\t\ttabHeadersArray[0].selectTab();\n\t\t\t} else {\n\t\t\t\ttabHeadersArray[0].focus();\n\t\t\t\tthis.currentSelectedTab = 0;\n\t\t\t}\n\t\t}\n\n\t\tif (event.key === \"End\") {\n\t\t\tevent.preventDefault();\n\t\t\tif (this.followFocus && !tabHeadersArray[tabHeadersArray.length - 1].disabled) {\n\t\t\t\ttabHeadersArray[tabHeadersArray.length - 1].selectTab();\n\t\t\t} else {\n\t\t\t\ttabHeadersArray[tabHeadersArray.length - 1].focus();\n\t\t\t\tthis.currentSelectedTab = tabHeadersArray.length - 1;\n\t\t\t}\n\t\t}\n\n\t\tif ((event.key === \" \") && !this.followFocus) {\n\t\t\ttabHeadersArray[this.currentSelectedTab].selectTab();\n\t\t}\n\t}\n\n\tngOnInit() {\n\t\tthis.eventService.on(window as any, \"resize\", () => this.handleScroll());\n\t}\n\n\tngAfterContentInit() {\n\t\t// Reallocate trackers because subscriptions are permanently closed after unsubscribe\n\t\tthis.selectedSubscriptionTracker.unsubscribe();\n\t\tthis.closeSubscriptionTracker.unsubscribe();\n\t\tthis.selectedSubscriptionTracker = new Subscription();\n\t\tthis.closeSubscriptionTracker = new Subscription();\n\n\t\tif (this.tabHeaderQuery) {\n\t\t\tthis.tabHeaderQuery.toArray()\n\t\t\t\t.forEach(tabHeader => {\n\t\t\t\t\ttabHeader.cacheActive = this.cacheActive;\n\t\t\t\t\ttabHeader.dismissable = this.dismissable;\n\t\t\t\t\ttabHeader.paneTabIndex = this.isNavigation ? null : 0;\n\t\t\t\t});\n\t\t}\n\n\t\tconst headersArray = this.tabHeaderQuery.toArray();\n\n\t\theadersArray.forEach(tabHeader => {\n\t\t\tthis.selectedSubscriptionTracker.add(\n\t\t\t\ttabHeader.selected.subscribe(() => {\n\t\t\t\t\tthis.currentSelectedTab = this.tabHeaderQuery.toArray().indexOf(tabHeader);\n\t\t\t\t\t// The Filter takes the current selected tab out, then all other headers are\n\t\t\t\t\t// deactivated and their associated pane references are also deactivated.\n\t\t\t\t\tthis.tabHeaderQuery.toArray().filter(header => header !== tabHeader)\n\t\t\t\t\t\t.forEach(filteredHeader => {\n\t\t\t\t\t\t\tfilteredHeader.active = false;\n\t\t\t\t\t\t\tif (filteredHeader.paneReference) {\n\t\t\t\t\t\t\t\tfilteredHeader.paneReference.active = false;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t});\n\t\t\t\t})\n\t\t\t);\n\n\t\t\tthis.closeSubscriptionTracker.add(\n\t\t\t\ttabHeader.tabClose.subscribe(() => {\n\t\t\t\t\tconst index = this.tabHeaderQuery.toArray().indexOf(tabHeader);\n\t\t\t\t\tthis.tabClose.emit(index);\n\t\t\t\t})\n\t\t\t);\n\t\t});\n\n\t\tthis.setFirstTab();\n\t}\n\n\tngOnDestroy() {\n\t\tthis.selectedSubscriptionTracker.unsubscribe();\n\t\tthis.closeSubscriptionTracker.unsubscribe();\n\t\tclearTimeout(this.scrollDebounceTimer);\n\t}\n\n\tngOnChanges(changes: SimpleChanges) {\n\t\tif (this.tabHeaderQuery) {\n\t\t\tif (changes.cacheActive) {\n\t\t\t\tthis.tabHeaderQuery.toArray().forEach(tabHeader => tabHeader.cacheActive = this.cacheActive);\n\t\t\t}\n\n\t\t\tif (changes.dismissable) {\n\t\t\t\tthis.tabHeaderQuery.toArray().forEach(tabHeader => tabHeader.dismissable = this.dismissable);\n\t\t\t}\n\n\t\t\tif (changes.isNavigation) {\n\t\t\t\tthis.tabHeaderQuery.toArray()\n\t\t\t\t\t.forEach(tabHeader => tabHeader.paneTabIndex = this.isNavigation ? null : 0);\n\t\t\t}\n\t\t}\n\t}\n\n\tgetSelectedTab(): any {\n\t\tconst selected = this.tabHeaderQuery.toArray()[this.currentSelectedTab];\n\t\tif (selected) {\n\t\t\treturn selected;\n\t\t}\n\t\treturn {\n\t\t\theadingIsTemplate: false,\n\t\t\theading: \"\"\n\t\t};\n\t}\n\n\t/**\n\t * Determines which `Tab` is initially selected.\n\t */\n\tprotected setFirstTab() {\n\t\tsetTimeout(() => {\n\t\t\tconst headers = this.tabHeaderQuery.toArray();\n\t\t\tlet selectedHeader = headers.find(h => h.active || h.paneReference?.active);\n\t\t\tif (!selectedHeader && headers.length > 0) {\n\t\t\t\tselectedHeader = headers[0];\n\t\t\t}\n\t\t\tif (selectedHeader) {\n\t\t\t\tselectedHeader.selectTab();\n\t\t\t\tthis.activeIndex = this.currentSelectedTab;\n\t\t\t\tthis.changeDetectorRef.markForCheck();\n\t\t\t}\n\t\t});\n\t}\n}\n","import {\r\n\tAfterContentInit,\r\n\tChangeDetectorRef,\r\n\tComponent,\r\n\tContentChildren,\r\n\tElementRef,\r\n\tEventEmitter,\r\n\tHostBinding,\r\n\tHostListener,\r\n\tInput,\r\n\tOnChanges,\r\n\tOnDestroy,\r\n\tOnInit,\r\n\tOutput,\r\n\tQueryList,\r\n\tRenderer2,\r\n\tSimpleChanges,\r\n\tViewChild\r\n} from \"@angular/core\";\r\nimport { Subscription } from \"rxjs\";\r\nimport { EventService } from \"carbon-components-angular/utils\";\r\nimport { I18n } from \"carbon-components-angular/i18n\";\r\n\r\nimport { BaseTabHeader } from \"./base-tab-header.component\";\r\nimport { TabHeaderBase } from \"./tab-header.directive\";\r\n\r\nconst VERTICAL_TAB_HEIGHT = 64;\r\n\r\n/**\r\n * Vertical tab header group: same children as `cds-tab-header-group`, with\r\n * up/down (and Home/End) keys, gradient overflow, and always-contained type.\r\n *\r\n *\r\n * ```html\r\n * <cds-tabs-vertical-grouped height=\"400px\">\r\n * <cds-tab-header-group-vertical>\r\n * <cds-tab-header [paneReference]=\"a\">A</cds-tab-header>\r\n * <cds-tab-header [paneReference]=\"b\">B</cds-tab-header>\r\n * </cds-tab-header-group-vertical>\r\n * <cds-tab #a>...</cds-tab>\r\n * <cds-tab #b>...</cds-tab>\r\n * </cds-tabs-vertical-grouped>\r\n * ```\r\n */\r\n@Component({\r\n\tselector: \"cds-tab-header-group-vertical, ibm-tab-header-group-vertical\",\r\n\ttemplate: `\r\n\t\t<div *ngIf=\"isOverflowingTop\" class=\"cds--tab--list-gradient_top\"></div>\r\n\t\t<div\r\n\t\t\t#tabList\r\n\t\t\tclass=\"cds--tab--list\"\r\n\t\t\trole=\"tablist\"\r\n\t\t\t[attr.aria-label]=\"ariaLabel || translations.HEADER_ARIA_LABEL\"\r\n\t\t\t[attr.aria-labelledby]=\"ariaLabelledby || null\">\r\n\t\t\t<ng-container [ngTemplateOutlet]=\"contentBefore\"></ng-container>\r\n\t\t\t<ng-content></ng-content>\r\n\t\t\t<ng-container [ngTemplateOutlet]=\"contentAfter\"></ng-container>\r\n\t\t</div>\r\n\t\t<div *ngIf=\"isOverflowingBottom\" class=\"cds--tab--list-gradient_bottom\"></div>\r\n\t`\r\n})\r\nexport class TabHeaderGroupVertical\r\n\textends BaseTabHeader\r\n\timplements AfterContentInit, OnChanges, OnInit, OnDestroy {\r\n\t/**\r\n\t * i18n strings for the tab list `aria-label` fallback.\r\n\t */\r\n\t@Input() translations = this.i18n.get().TABS;\r\n\r\n\t/**\r\n\t * When `true`, sets each tab panel `tabindex` to `-1` for navigation-style usage.\r\n\t */\r\n\t@Input() isNavigation = false;\r\n\r\n\t/**\r\n\t * Fires with tab index when a close control is used (with `dismissable`).\r\n\t */\r\n\t@Output() tabClose = new EventEmitter<number>();\r\n\r\n\t/**\r\n\t * Set to 'true' to have tabs automatically activated and have their content displayed when they receive focus.\r\n\t */\r\n\t@Input() followFocus = true;\r\n\r\n\t/**\r\n\t * ContentChildren of all the tab headers (both directive and component\r\n\t * forms — see `TabHeaderBase`).\r\n\t */\r\n\t@ContentChildren(TabHeaderBase) tabHeaderQuery: QueryList<TabHeaderBase>;\r\n\r\n\t@ViewChild(\"tabList\", { static: true }) headerContainer: ElementRef<HTMLElement>;\r\n\r\n\t@HostBinding(\"class.cds--tabs--vertical\") verticalClass = true;\r\n\r\n\t/**\r\n\t * Index of the selected tab for keyboard logic\r\n\t */\r\n\tcurrentSelectedTab = 0;\r\n\r\n\t/**\r\n\t * Focused tab index when `followFocus` is false (manual activation).\r\n\t */\r\n\tactiveIndex: number | null = null;\r\n\r\n\tisOverflowingTop = false;\r\n\tisOverflowingBottom = false;\r\n\r\n\t/**\r\n\t * We use taller rows when any header has a secondary label.\r\n\t */\r\n\t@HostBinding(\"class.cds--tabs--tall\") get tallClass(): boolean {\r\n\t\treturn this.hasSecondaryLabelTabs;\r\n\t}\r\n\r\n\tget hasSecondaryLabelTabs(): boolean {\r\n\t\tif (!this.tabHeaderQuery) {\r\n\t\t\treturn false;\r\n\t\t}\r\n\t\treturn this.tabHeaderQuery\r\n\t\t\t.toArray()\r\n\t\t\t.some((h) => h.secondaryLabel != null && h.secondaryLabel !== \"\");\r\n\t}\r\n\r\n\tprivate selectedSubscriptionTracker = new Subscription();\r\n\tprivate closeSubscriptionTracker = new Subscription();\r\n\r\n\tprivate resizeObserver: ResizeObserver | null = null;\r\n\tprivate boundListScrollHandler: () => void;\r\n\r\n\tconstructor(\r\n\t\tprotected elementRef: ElementRef,\r\n\t\tprotected changeDetectorRef: ChangeDetectorRef,\r\n\t\tprotected eventService: EventService,\r\n\t\tprotected renderer: Renderer2,\r\n\t\tprotected i18n: I18n\r\n\t) {\r\n\t\tsuper(elementRef, changeDetectorRef, eventService, renderer);\r\n\t\tthis.type = \"contained\";\r\n\t\t// Cache a stable reference for add/removeEventListener.\r\n\t\tthis.boundListScrollHandler = () => this.updateOverflowState();\r\n\t}\r\n\r\n\t@HostListener(\"keydown\", [\"$event\"])\r\n\tkeyboardInput(event: KeyboardEvent) {\r\n\t\tif (!this.tabHeaderQuery) {\r\n\t\t\treturn;\r\n\t\t}\r\n\t\tconst tabHeadersArray = this.tabHeaderQuery.toArray();\r\n\t\tconst enabledHeaders = tabHeadersArray.filter((h) => !h.disabled);\r\n\t\tif (enabledHeaders.length === 0) {\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\tconst referenceIndex = this.followFocus\r\n\t\t\t? this.currentSelectedTab\r\n\t\t\t: (this.activeIndex !== null ? this.activeIndex : this.currentSelectedTab);\r\n\t\tconst currentEnabledIndex = Math.max(0, enabledHeaders.indexOf(tabHeadersArray[referenceIndex]));\r\n\r\n\t\tlet nextEnabledIndex = currentEnabledIndex;\r\n\t\tlet handled = false;\r\n\r\n\t\tif (event.key === \"ArrowDown\") {\r\n\t\t\tnextEnabledIndex = (currentEnabledIndex + 1) % enabledHeaders.length;\r\n\t\t\thandled = true;\r\n\t\t} else if (event.key === \"ArrowUp\") {\r\n\t\t\tnextEnabledIndex = (enabledHeaders.length + currentEnabledIndex - 1) % enabledHeaders.length;\r\n\t\t\thandled = true;\r\n\t\t} else if (event.key === \"Home\") {\r\n\t\t\tnextEnabledIndex = 0;\r\n\t\t\thandled = true;\r\n\t\t} else if (event.key === \"End\") {\r\n\t\t\tnextEnabledIndex = enabledHeaders.length - 1;\r\n\t\t\thandled = true;\r\n\t\t}\r\n\r\n\t\tif (handled) {\r\n\t\t\tevent.preventDefault();\r\n\t\t\tconst nextHeader = enabledHeaders[nextEnabledIndex];\r\n\t\t\tconst nextIndex = tabHeadersArray.indexOf(nextHeader);\r\n\r\n\t\t\tif (this.followFocus) {\r\n\t\t\t\tnextHeader.selectTab();\r\n\t\t\t\tthis.currentSelectedTab = nextIndex;\r\n\t\t\t} else {\r\n\t\t\t\tnextHeader.focus();\r\n\t\t\t\tthis.activeIndex = nextIndex;\r\n\t\t\t}\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\tif ((event.key === \" \" || event.key === \"Spacebar\") && !this.followFocus) {\r\n\t\t\tconst focusIndex = this.activeIndex !== null ? this.activeIndex : this.currentSelectedTab;\r\n\t\t\ttabHeadersArray[focusIndex].selectTab();\r\n\t\t\tthis.currentSelectedTab = focusIndex;\r\n\t\t}\r\n\t}\r\n\r\n\t@HostListener(\"blur\", [\"$event\"])\r\n\thandleBlur(event: FocusEvent) {\r\n\t\tconst relatedTarget = event.relatedTarget as Node | null;\r\n\t\tconst container = this.headerContainer?.nativeElement;\r\n\t\tif (container && relatedTarget && container.contains(relatedTarget)) {\r\n\t\t\treturn;\r\n\t\t}\r\n\t\tif (!this.followFocus) {\r\n\t\t\tthis.activeIndex = this.currentSelectedTab;\r\n\t\t}\r\n\t}\r\n\r\n\tngOnInit() {\r\n\t\tthis.resizeObserver = new ResizeObserver(() => {\r\n\t\t\tthis.updateOverflowState();\r\n\t\t\tthis.changeDetectorRef.detectChanges();\r\n\t\t});\r\n\t\tthis.resizeObserver.observe(this.headerContainer.nativeElement);\r\n\t\tthis.headerContainer.nativeElement.addEventListener(\r\n\t\t\t\"scroll\",\r\n\t\t\tthis.boundListScrollHandler\r\n\t\t);\r\n\t}\r\n\r\n\tngOnDestroy() {\r\n\t\tthis.selectedSubscriptionTracker.unsubscribe();\r\n\t\tthis.closeSubscriptionTracker.unsubscribe();\r\n\t\tthis.resizeObserver?.unobserve(this.headerContainer.nativeElement);\r\n\t\tthis.resizeObserver = null;\r\n\t\tthis.headerContainer.nativeElement.removeEventListener(\r\n\t\t\t\"scroll\",\r\n\t\t\tthis.boundListScrollHandler\r\n\t\t);\r\n\t}\r\n\r\n\tngAfterContentInit() {\r\n\t\t// Reallocate trackers because subscriptions are permanently closed after unsubscribe\r\n\t\tthis.selectedSubscriptionTracker.unsubscribe();\r\n\t\tthis.closeSubscriptionTracker.unsubscribe();\r\n\t\tthis.selectedSubscriptionTracker = new Subscription();\r\n\t\tthis.closeSubscriptionTracker = new Subscription();\r\n\r\n\t\tthis.applyHeaderInputs();\r\n\t\tthis.wireSubscriptions();\r\n\r\n\t\tthis.tabHeaderQuery.changes.subscribe(() => {\r\n\t\t\t// Re-wire when the projected list changes.\r\n\t\t\tthis.selectedSubscriptionTracker.unsubscribe();\r\n\t\t\tthis.closeSubscriptionTracker.unsubscribe();\r\n\t\t\tthis.selectedSubscriptionTracker = new Subscription();\r\n\t\t\tthis.closeSubscriptionTracker = new Subscription();\r\n\t\t\tthis.applyHeaderInputs();\r\n\t\t\tthis.wireSubscriptions();\r\n\t\t\tthis.changeDetectorRef.markForCheck();\r\n\t\t});\r\n\r\n\t\tsetTimeout(() => {\r\n\t\t\tconst headers = this.tabHeaderQuery.toArray();\r\n\t\t\tconst activeIdx = headers.findIndex(h => h.active || h.paneReference?.active);\r\n\t\t\tconst initialIndex = activeIdx >= 0 ? activeIdx : 0;\r\n\t\t\tthis.currentSelectedTab = initialIndex;\r\n\t\t\tthis.activeIndex = initialIndex;\r\n\t\t\theaders[initialIndex]?.selectTab();\r\n\t\t\tthis.updateOverflowState();\r\n\t\t});\r\n\t}\r\n\r\n\tngOnChanges(changes: SimpleChanges) {\r\n\t\tif (this.tabHeaderQuery) {\r\n\t\t\tif (changes.cacheActive) {\r\n\t\t\t\tthis.tabHeaderQuery.toArray().forEach(h => h.cacheActive = this.cacheActive);\r\n\t\t\t}\r\n\t\t\tif (changes.dismissable) {\r\n\t\t\t\tthis.tabHeaderQuery.toArray().forEach(h => h.dismissable = this.dismissable);\r\n\t\t\t}\r\n\t\t\tif (changes.isNavigation) {\r\n\t\t\t\tthis.tabHeaderQuery.toArray()\r\n\t\t\t\t\t.forEach(h => h.paneTabIndex = this.isNavigation ? null : 0);\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\tprotected updateOverflowState() {\r\n\t\tconst element = this.headerContainer?.nativeElement;\r\n\t\tif (!element) {\r\n\t\t\treturn;\r\n\t\t}\r\n\t\tconst halfTabHeight = VERTICAL_TAB_HEIGHT / 2;\r\n\t\tthis.isOverflowingBottom =\r\n\t\t\telement.scrollTop + element.clientHeight + halfTabHeight <= element.scrollHeight;\r\n\t\tthis.isOverflowingTop = element.scrollTop > halfTabHeight;\r\n\t\tthis.changeDetectorRef.markForCheck();\r\n\t}\r\n\r\n\tprotected scrollSelectedTabIntoView() {\r\n\t\tif (!this.scrollIntoView) {\r\n\t\t\treturn;\r\n\t\t}\r\n\t\tconst container = this.headerContainer?.nativeElement;\r\n\t\tif (!container) {\r\n\t\t\treturn;\r\n\t\t}\r\n\t\tcontainer.scrollTo({\r\n\t\t\ttop: Math.max(0, (this.currentSelectedTab - 1) * VERTICAL_TAB_HEIGHT),\r\n\t\t\tbehavior: \"smooth\"\r\n\t\t});\r\n\t}\r\n\r\n\tprivate applyHeaderInputs() {\r\n\t\tthis.tabHeaderQuery.toArray().forEach((header) => {\r\n\t\t\theader.cacheActive = this.cacheActive;\r\n\t\t\theader.dismissable = this.dismissable;\r\n\t\t\theader.paneTabIndex = this.isNavigation ? null : 0;\r\n\t\t});\r\n\t}\r\n\r\n\tprivate wireSubscriptions() {\r\n\t\tthis.tabHeaderQuery.toArray().forEach((header) => {\r\n\t\t\tthis.selectedSubscriptionTracker.add(\r\n\t\t\t\theader.selected.subscribe(() => {\r\n\t\t\t\t\tthis.currentSelectedTab = this.tabHeaderQuery\r\n\t\t\t\t\t\t.toArray()\r\n\t\t\t\t\t\t.indexOf(header);\r\n\t\t\t\t\tthis.tabHeaderQuery\r\n\t\t\t\t\t\t.toArray()\r\n\t\t\t\t\t\t.filter((h) => h !== header)\r\n\t\t\t\t\t\t.forEach((other) => {\r\n\t\t\t\t\t\t\tother.active = false;\r\n\t\t\t\t\t\t\tif (other.paneReference) {\r\n\t\t\t\t\t\t\t\tother.paneReference.active = false;\r\n\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t});\r\n\t\t\t\t\tthis.scrollSelectedTabIntoView();\r\n\t\t\t\t})\r\n\t\t\t);\r\n\r\n\t\t\tthis.closeSubscriptionTracker.add(\r\n\t\t\t\theader.tabClose.subscribe(() => {\r\n\t\t\t\t\tconst index = this.tabHeaderQuery.toArray().indexOf(header);\r\n\t\t\t\t\tthis.tabClose.emit(index);\r\n\t\t\t\t})\r\n\t\t\t);\r\n\t\t});\r\n\t}\r\n}\r\n","import {\r\n\tAfterViewInit,\r\n\tComponent,\r\n\tElementRef,\r\n\tforwardRef,\r\n\tHostBinding,\r\n\tInput,\r\n\tViewChild\r\n} from \"@angular/core\";\r\n\r\nimport { TabHeaderBase } from \"./tab-header.directive\";\r\n\r\n/**\r\n * Tab header with template for label, optional icon, secondary label, badge, and dismissable close.\r\n *\r\n * ```html\r\n * <cds-tab-header-group>\r\n * <cds-tab-header [paneReference]=\"c1\">Dashboard</cds-tab-header>\r\n * <cds-tab-header [paneReference]=\"c2\" [icon]=\"iconTpl\" secondaryLabel=\"(1/4)\">\r\n * Monitoring\r\n * </cds-tab-header>\r\n * </cds-tab-header-group>\r\n * <cds-tab #c1>...</cds-tab>\r\n * <cds-tab #c2>...</cds-tab>\r\n * ```\r\n */\r\n@Component({\r\n\tselector: \"cds-tab-header, ibm-tab-header\",\r\n\tproviders: [\r\n\t\t// tslint:disable-next-line:no-forward-ref\r\n\t\t{ provide: TabHeaderBase, useExisting: forwardRef(() => TabHeaderComponent) }\r\n\t],\r\n\ttemplate: `\r\n\t\t<cds-tooltip\r\n\t\t\t*ngIf=\"iconOnly; else plainButton\"\r\n\t\t\talign=\"bottom\"\r\n\t\t\t[autoAlign]=\"true\"\r\n\t\t\tclass=\"cds--icon-tooltip\"\r\n\t\t\t[description]=\"iconLabel\"\r\n\t\t\t[enterDelayMs]=\"enterDelayMs\"\r\n\t\t\t[leaveDelayMs]=\"leaveDelayMs\"\r\n\t\t\t[isOpen]=\"isTooltipOpen\"\r\n\t\t\t[disabled]=\"disabled\">\r\n\t\t\t<ng-container *ngTemplateOutlet=\"tabButtonTemplate\"></ng-container>\r\n\t\t</cds-tooltip>\r\n\t\t<ng-template #plainButton>\r\n\t\t\t<ng-container *ngTemplateOutlet=\"tabButtonTemplate\"></ng-container>\r\n\t\t</ng-template>\r\n\t\t<ng-template #tabButtonTemplate>\r\n\t\t\t<button\r\n\t\t\t\t#tabButton\r\n\t\t\t\ttype=\"button\"\r\n\t\t\t\trole=\"tab\"\r\n\t\t\t\tclass=\"cds--tabs__nav-item cds--tabs__nav-link\"\r\n\t\t\t\t[ngClass]=\"{\r\n\t\t\t\t\t'cds--tabs__nav-item--selected': active,\r\n\t\t\t\t\t'cds--tabs__nav-item--disabled': disabled,\r\n\t\t\t\t\t'cds--tabs__nav-item--icon-only': iconOnly,\r\n\t\t\t\t\t'cds--tabs__nav-item--icon-only__20': iconOnly && iconSize === 'lg'\r\n\t\t\t\t}\"\r\n\t\t\t\t[attr.aria-selected]=\"active\"\r\n\t\t\t\t[attr.aria-disabled]=\"disabled\"\r\n\t\t\t\t[attr.aria-controls]=\"paneReference?.id || null\"\r\n\t\t\t\t[attr.aria-label]=\"iconOnly ? iconLabel : null\"\r\n\t\t\t\t[attr.tabindex]=\"active ? 0 : -1\"\r\n\t\t\t\t[attr.title]=\"resolvedTitle\"\r\n\t\t\t\t[disabled]=\"disabled\"\r\n\t\t\t\t(click)=\"onTabButtonClick()\"\r\n\t\t\t\t(keydown)=\"onTabButtonKeyDown($event)\">\r\n\t\t\t\t<ng-container *ngIf=\"iconOnly; else labeledTab\">\r\n\t\t\t\t\t<ng-container [ngTemplateOutlet]=\"icon\"></ng-container>\r\n\t\t\t\t\t<span\r\n\t\t\t\t\t\t*ngIf=\"!disabled && badgeIndicator\"\r\n\t\t\t\t\t\tclass=\"cds--badge-indicator\"\r\n\t\t\t\t\t\taria-hidden=\"true\">\r\n\t\t\t\t\t</span>\r\n\t\t\t\t</ng-container>\r\n\t\t\t\t<ng-template #labeledTab>\r\n\t\t\t\t\t<div class=\"cds--tabs__nav-item-label-wrapper\">\r\n\t\t\t\t\t\t<div *ngIf=\"dismissable && icon\" class=\"cds--tabs__nav-item--icon-left\">\r\n\t\t\t\t\t\t\t<ng-container [ngTemplateOutlet]=\"icon\"></ng-container>\r\n\t\t\t\t\t\t</div>\r\n\t\t\t\t\t\t<span class=\"cds--tabs__nav-item-label\">\r\n\t\t\t\t\t\t\t<ng-content></ng-content>\r\n\t\t\t\t\t\t</span>\r\n\t\t\t\t\t\t<div *ngIf=\"!dismissable && icon\" class=\"cds--tabs__nav-item--icon\">\r\n\t\t\t\t\t\t\t<ng-container [ngTemplateOutlet]=\"icon\"></ng-container>\r\n\t\t\t\t\t\t</div>\r\n\t\t\t\t\t</div>\r\n\t\t\t\t\t<div\r\n\t\t\t\t\t\t*ngIf=\"secondaryLabel\"\r\n\t\t\t\t\t\tclass=\"cds--tabs__nav-item-secondary-label\"\r\n\t\t\t\t\t\t[attr.title]=\"secondaryLabel\">\r\n\t\t\t\t\t\t{{ secondaryLabel }}\r\n\t\t\t\t\t</div>\r\n\t\t\t\t</ng-template>\r\n\t\t\t</button>\r\n\t\t</ng-template>\r\n\t\t<div *ngIf=\"dismissable\" class=\"cds--tabs__nav-item--close\">\r\n\t\t\t<button\r\n\t\t\t\ttype=\"button\"\r\n\t\t\t\tclass=\"cds--tabs__nav-item--close-icon\"\r\n\t\t\t\t[attr.tabindex]=\"-1\"\r\n\t\t\t\t[attr.aria-disabled]=\"disabled\"\r\n\t\t\t\t[attr.aria-hidden]=\"!(active && !disabled)\"\r\n\t\t\t\t[ngClass]=\"{\r\n\t\t\t\t\t'cds--tabs__nav-item--close-icon--selected': active,\r\n\t\t\t\t\t'cds--tabs__nav-item--close-icon--disabled': disabled\r\n\t\t\t\t}\"\r\n\t\t\t\t[disabled]=\"disabled\"\r\n\t\t\t\t[attr.title]=\"closeButtonTitle\"\r\n\t\t\t\t(click)=\"onClose($event)\">\r\n\t\t\t\t<svg\r\n\t\t\t\t\tfocusable=\"false\"\r\n\t\t\t\t\tpreserveAspectRatio=\"xMidYMid meet\"\r\n\t\t\t\t\txmlns=\"http://www.w3.org/2000/svg\"\r\n\t\t\t\t\tfill=\"currentColor\"\r\n\t\t\t\t\twidth=\"16\"\r\n\t\t\t\t\theight=\"16\"\r\n\t\t\t\t\tviewBox=\"0 0 32 32\"\r\n\t\t\t\t\t[attr.aria-label]=\"closeButtonAriaLabel\"\r\n\t\t\t\t\t[attr.aria-hidden]=\"!(active && !disabled)\">\r\n\t\t\t\t\t<path d=\"M17.4141 16L24 9.4141 22.5859 8 16 14.5859 9.4143 8 8 9.4141 14.5859 16 8 22.5859 9.4143 24 16 17.4141 22.5859 24 24 22.5859 17.4141 16z\"></path>\r\n\t\t\t\t</svg>\r\n\t\t\t</button>\r\n\t\t</div>\r\n\t`\r\n})\r\nexport class TabHeaderComponent extends TabHeaderBase implements AfterViewInit {\r\n\t/**\r\n\t * Icon-only tab: set `icon` and `iconLabel`.\r\n\t */\r\n\t@Input() iconOnly = false;\r\n\t/**\r\n\t * Icon-only tabs: accessible name (`aria-label` / `title`).\r\n\t */\r\n\t@Input() iconLabel: string;\r\n\t/**\r\n\t * **Preview**: Icon-only tabs — show a notification dot on the icon.\r\n\t */\r\n\t@Input() badgeIndicator = false;\r\n\t/**\r\n\t * Icon-only tabs: icon size `default` (16px) or `lg` (20px); usually set on the parent group.\r\n\t */\r\n\t@Input() iconSize: \"default\" | \"lg\" = \"default\";\r\n\t/**\r\n\t * `aria-label` for the dismissable close button.\r\n\t */\r\n\t@Input() closeButtonAriaLabel = \"Press delete to remove tab\";\r\n\t/**\r\n\t * Icon-only tabs: tooltip show delay (ms).\r\n\t */\r\n\t@Input() enterDelayMs: number;\r\n\t/**\r\n\t * Icon-only tabs: tooltip hide delay (ms).\r\n\t */\r\n\t@Input() leaveDelayMs: number;\r\n\t/**\r\n\t * Icon-only tabs: open the tooltip on first render.\r\n\t */\r\n\t@Input() isTooltipOpen = false;\r\n\r\n\t@HostBinding(\"style.display\") displayContents = \"contents\";\r\n\r\n\t@ViewChild(\"tabButton\") tabButton: ElementRef<HTMLButtonElement>;\r\n\r\n\tngAfterViewInit() {\r\n\t\t// Mirror the deprecated directive's title-fallback behavior, but read\r\n\t\t// from the inner rendered button rather than the `display: contents` host.\r\n\t\tsetTimeout(() => {\r\n\t\t\tif (!this.title && this.tabButton?.nativeElement) {\r\n\t\t\t\tconst text = this.tabButton.nativeElement.textContent?.trim();\r\n\t\t\t\tif (text) {\r\n\t\t\t\t\tthis.title = text;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t});\r\n\t}\r\n\r\n\t/**\r\n\t * Focus the rendered tab button (not the host).\r\n\t */\r\n\tfocus() {\r\n\t\tthis.tabButton?.nativeElement?.focus();\r\n\t}\r\n\r\n\tonTabButtonClick() {\r\n\t\tthis.selectTab();\r\n\t}\r\n\r\n\t/**\r\n\t * `Delete` closes dismissable tabs when focus is on the tab.\r\n\t */\r\n\tonTabButtonKeyDown(event: KeyboardEvent) {\r\n\t\tif (this.dismissable && event.key === \"Delete\") {\r\n\t\t\tevent.stopPropagation();\r\n\t\t\tthis.tabClose.emit();\r\n\t\t}\r\n\t}\r\n\r\n\t/**\r\n\t * Close button click; stops propagation so the tab does not activate.\r\n\t */\r\n\tonClose(event: Event) {\r\n\t\tevent.stopPropagation();\r\n\t\tif (this.disabled) {\r\n\t\t\treturn;\r\n\t\t}\r\n\t\tthis.tabClose.emit();\r\n\t}\r\n\r\n\tget resolvedTitle(): string | null {\r\n\t\tif (this.iconOnly) {\r\n\t\t\treturn this.iconLabel || null;\r\n\t\t}\r\n\t\treturn this.title || null;\r\n\t}\r\n\r\n\tget closeButtonTitle(): string {\r\n\t\tconst label = this.tabButton?.nativeElement?.textContent?.trim();\r\n\t\treturn label ? `Remove ${label} tab` : \"Remove tab\";\r\n\t}\r\n}\r\n","import {\n\tComponent,\n\tOnInit,\n\tInput,\n\tOutput,\n\tEventEmitter,\n\tHostBinding,\n\tTemplateRef\n} from \"@angular/core\";\n\n/**\n* The `Tab` component is a child of the `Tabs` component.\n* It represents one `Tab` item and its content within a panel of other `Tab` items.\n*\n* `Tab` takes a string or `TemplateRef` for the header, and any content for the body of the tab.\n* Disabled states should be handled by the application (ie. switch to the tab, but display some\n* indication as to _why_ the tab is disabled).\n*\n* When the tab is selected the `select` output will be triggered.\n* The `select` output will also be triggered for the active tab when the tabs are loaded or updated.\n*\n*\n* Tab with string header:\n *\n * ```html\n* <cds-tab heading='tab1'>\n* \ttab 1 content\n* </cds-tab>\n* ```\n*\n* Tab with custom header:\n*\n* ```html\n* <ng-template #tabHeading>\n* \t<svg cdsIcon=\"facebook\"\n* \t\tsize=\"sm\"\n* \t\tstyle=\"margin-right: 7px;\">\n* \t</svg>\n* \tHello Tab 1\n* </ng-template>\n* <cds-tabs>\n* \t<cds-tab [heading]=\"tabHeading\">\n* \t\tTab 1 content <svg cdsIcon=\"alert\" size=\"lg\"></svg>\n* \t</cds-tab>\n* \t<cds-tab heading='Tab2'>\n* \t\tTab 2 content\n* \t</cds-tab>\n* \t<cds-tab heading='Tab3'>\n* \t\tTab 3 content\n* \t</cds-tab>\n* </cds-tabs>\n * ```\n */\n@Component({\n\tselector: \"cds-tab, ibm-tab\",\n\ttemplate: `\n\t\t<ng-container *ngIf=\"shouldRender()\">\n\t\t\t<ng-template\n\t\t\t\t*ngIf=\"isTemplate(tabContent)\"\n\t\t\t\t[ngTemplateOutlet]=\"tabContent\"\n\t\t\t\t[ngTemplateOutletContext]=\"{ $implicit: templateContext }\">\n\t\t\t</ng-template>\n\t\t\t<ng-content></ng-content>\n\t\t</ng-container>\n\t`\n})\nexport class Tab implements OnInit {\n\t@HostBinding(\"attr.id\") get hostId() {\n\t\treturn this.id;\n\t}\n\t@HostBinding(\"attr.aria-labelledby\") get hostAriaLabelledby() {\n\t\treturn `${this.id}-header`;\n\t}\n\t@HostBinding(\"attr.tabindex\") get hostTabIndex() {\n\t\treturn this.tabIndex;\n\t}\n\t/**\n\t * `hidden` + display keep inactive panels out of layout; `null` display when active preserves grid/flex.\n\t */\n\t@HostBinding(\"attr.hidden\") get hostHidden() {\n\t\treturn this.active ? null : \"\";\n\t}\n\t@HostBinding(\"style.display\") get hostDisplay() {\n\t\treturn this.active ? \"block\" : \"none\";\n\t}\n\t/**\n\t * Set to `true` to have `Tab` items cached and not reloaded on tab switching.\n\t */\n\t@Input() set cacheActive(shouldCache: boolean) {\n\t\tthis._cacheActive = shouldCache;\n\t}\n\n\tget cacheActive() {\n\t\treturn this._cacheActive;\n\t}\n\tprivate static counter = 0;\n\t@HostBinding(\"class.cds--tab-content\") tabContentClass = true;\n\t@HostBinding(\"attr.role\") panelRole = \"tabpanel\";\n\t@HostBinding(\"attr.aria-live\") panelAriaLive = \"polite\";\n\t/**\n\t * Boolean value reflects if the `Tab` is using a custom template for the heading.\n\t * Default value is false.\n\t */\n\tpublic headingIsTemplate = false;\n\n\t/**\n\t * The `Tab`'s title to be displayed or custom template for the `Tab` heading.\n\t */\n\t@Input() heading: string | TemplateRef<any>;\n\t/**\n\t * Optional override for the `tabItem's`'s title attribute which is set in `TabHeaders`.\n\t * `tabItem`'s title attribute is automatically set to `heading`.\n\t *\n\t * You might want to use this if you set `heading` to a `TemplateRef`.\n\t */\n\t@Input() title: string;\n\t/**\n\t * Allows the user to pass data to the custom template for the `Tab` heading.\n\t */\n\t@Input() context: any;\n\t/**\n\t * Indicates whether the `Tab` is active/selected.\n\t * Determines whether its tab panel content is rendered.\n\t */\n\t@Input() active = false;\n\t/**\n\t * Indicates whether or not the `Tab` item is disabled.\n\t */\n\t@Input() disabled = false;\n\n\t/**\n\t * `tabindex` on the tab panel, the parent may set this to `null` when `isNavigation` is `true`.\n\t */\n\t@Input() tabIndex = 0;\n\t/**\n\t * Sets the id of the `Tab`. Will be uniquely generated if not provided.\n\t */\n\t@Input() id = `n-tab-${Tab.counter++}`;\n\t/**\n\t * Allows lifecycle hooks to be called on the rendered content.\n\t */\n\t@Input() tabContent: TemplateRef<any>;\n\t/**\n\t * Optional data for templates passed as implicit context.\n\t */\n\t@Input() templateContext: any;\n\t/**\n\t * Optional template that renders an icon inside the `Tab` header.\n\t * Useful for rendering a `cdsIcon` or any other icon next to the tab label.\n\t */\n\t@Input() icon: TemplateRef<any>;\n\t/**\n\t * Optional secondary label rendered below the primary tab label.\n\t * Only displayed when the parent `Tabs` is using `type=\"contained\"`.\n\t */\n\t@Input() secondaryLabel: string;\n\t/**\n\t * Sets the aria-label of the close button when the parent `Tabs` uses `dismissable`.\n\t */\n\t@Input() closeButtonAriaLabel = \"Press delete to remove tab\";\n\t/**\n\t * Icon-only tab: pair with `icon` and `iconLabel`.\n\t */\n\t@Input() iconOnly = false;\n\t/**\n\t * Icon-only tabs: accessible name and tooltip text.\n\t */\n\t@Input() iconLabel: string;\n\t/**\n\t * **Preview**: Icon-only tabs — show a notification dot on the icon.\n\t */\n\t@Input() badgeIndicator = false;\n\t/**\n\t * Icon-only tabs: tooltip show delay (ms).\n\t */\n\t@Input() enterDelayMs: number;\n\t/**\n\t * Icon-only tabs: tooltip hide delay (ms).\n\t */\n\t@Input() leaveDelayMs: number;\n\t/**\n\t * Icon-only tabs: open the tooltip on first render.\n\t */\n\t@Input() isTooltipOpen = false;\n\t/**\n\t * Emits when this tab becomes selected.\n\t */\n\t@Output() selected: EventEmitter<void> = new EventEmitter<void>();\n\t/**\n\t * Emits when this tab's close button is pressed.\n\t */\n\t@Output() tabClose: EventEmitter<void> = new EventEmitter<void>();\n\n\tprotected _cacheActive = false;\n\n\t/**\n\t * Checks for custom heading template on initialization and updates the value\n\t * of the boolean 'headingIsTemplate'.\n\t */\n\tngOnInit() {\n\t\tif (this.heading instanceof TemplateRef) {\n\t\t\tthis.headingIsTemplate = true;\n\t\t}\n\t}\n\n\t/**\n\t * Emit the status of the `Tab`, specifically 'select' and 'selected' properties.\n\t */\n\tdoSelect() {\n\t\tthis.selected.emit();\n\t}\n\n\t/**\n\t* Returns value indicating whether this `Tab` should be rendered in a tab panel.\n\t */\n\tshouldRender() {\n\t\treturn this.active || this.cacheActive;\n\t}\n\n\tpublic isTemplate(value) {\n\t\treturn value instanceof TemplateRef;\n\t}\n}\n","import {\n\tComponent,\n\tQueryList,\n\tInput,\n\tOutput,\n\tEventEmitter,\n\tHostListener,\n\tHostBinding,\n\tViewChild,\n\tContentChildren,\n\tAfterContentInit,\n\tViewChildren,\n\tElementRef,\n\tOnChanges,\n\tSimpleChanges,\n\tOnDestroy,\n\tOnInit,\n\tChangeDetectorRef,\n\tRenderer2\n} from \"@angular/core\";\nimport { EventService } from \"carbon-components-angular/utils\";\nimport { I18n } from \"carbon-components-angular/i18n\";\n\nimport { BaseTabHeader } from \"./base-tab-header.component\";\nimport { Tab } from \"./tab.component\";\n\n/**\n * The `TabHeaders` component contains the `Tab` items and controls scroll functionality\n * if content has overflow.\n */\n@Component({\n\tselector: \"cds-tab-headers, ibm-tab-headers\",\n\ttemplate: `\n\t\t<button\n\t\t\ttype=\"button\"\n\t\t\t(click)=\"handleOverflowNavClick(-1, tabs.length)\"\n\t\t\t(pointerdown)=\"handleOverflowNavMouseDown(-1)\"\n\t\t\t(pointerup)=\"handleOverflowNavMouseUp()\"\n\t\t\t(pointerleave)=\"handleOverflowNavMouseUp()\"\n\t\t\t(pointerout)=\"handleOverflowNavMouseU