diff --git a/.storybook/stories/tabs/tabs-actions.stories.ts b/.storybook/stories/tabs/tabs-actions.stories.ts new file mode 100644 index 0000000000..339c2e935d --- /dev/null +++ b/.storybook/stories/tabs/tabs-actions.stories.ts @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2016-2024 Broadcom. All Rights Reserved. + * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. + * This software is released under MIT license. + * The full license information can be found in LICENSE in the root directory of this project. + */ + +import { ClrTabs, ClrTabsModule } from '@clr/angular'; +import { moduleMetadata, StoryFn, StoryObj } from '@storybook/angular'; + +import { TabsLayout } from '../../../projects/angular/src/layout/tabs/enums/tabs-layout.enum'; + +export default { + title: 'Tabs/Tabs Actions', + decorators: [ + moduleMetadata({ + imports: [ClrTabsModule], + }), + ], + component: ClrTabs, + argTypes: { + // inputs + clrLayout: { control: 'inline-radio', options: TabsLayout }, + tabsActionsPosition: { control: 'inline-radio', options: ['left', 'right'] }, + // methods + closeOnEscapeKey: { control: { disable: true }, table: { disable: true } }, + closeOnFocusOut: { control: { disable: true }, table: { disable: true } }, + closeOnOutsideClick: { control: { disable: true }, table: { disable: true } }, + openOverflowOnFocus: { control: { disable: true }, table: { disable: true } }, + resetKeyFocusCurrentToActive: { control: { disable: true }, table: { disable: true } }, + toggleOverflowOnClick: { control: { disable: true }, table: { disable: true } }, + toggleOverflowOnPosition: { control: { disable: true }, table: { disable: true } }, + // story helpers + createArray: { control: { disable: true }, table: { disable: true } }, + clickTabAction: { control: { disable: true }, table: { disable: true } }, + tabCount: { control: { type: 'number', min: 1, max: 100 } }, + activeTab: { control: { type: 'number', min: 1, max: 100 } }, + }, + args: { + // inputs + clrLayout: TabsLayout.HORIZONTAL, + tabsActionsPosition: 'right', + // story helpers + createArray: n => new Array(n), + clickTabAction: () => alert('Tab action clicked!'), + tabCount: 4, + activeTab: 1, + title: 'Tab', + content: 'Tab Content', + }, +}; + +const tabsTemplate: StoryFn = args => ({ + template: ` + + + + + + + +

{{ content }} {{ i + 1 }}

+
+
+
+ `, + props: { ...args }, +}); + +export const Tabs: StoryObj = { + render: tabsTemplate, +}; + +export const VerticalTabs: StoryObj = { + render: tabsTemplate, + args: { + tabsActionsPosition: 'left', + clrLayout: TabsLayout.VERTICAL, + }, +}; + +export const TabsResponsive: StoryObj = { + render: tabsTemplate, + parameters: { + viewport: { + defaultViewport: 'large', + }, + }, +}; + +export const TabsActionsLeft: StoryObj = { + render: tabsTemplate, + args: { + tabsActionsPosition: 'left', + }, +}; diff --git a/projects/angular/clarity.api.md b/projects/angular/clarity.api.md index 90ebcdbe62..c4c1b358c9 100644 --- a/projects/angular/clarity.api.md +++ b/projects/angular/clarity.api.md @@ -96,9 +96,9 @@ export class ClarityModule { // Warning: (ae-forgotten-export) The symbol "i8_6" needs to be exported by the entry point index.d.ts // Warning: (ae-forgotten-export) The symbol "i9_2" needs to be exported by the entry point index.d.ts // Warning: (ae-forgotten-export) The symbol "i10_4" needs to be exported by the entry point index.d.ts - // Warning: (ae-forgotten-export) The symbol "i11_4" needs to be exported by the entry point index.d.ts + // Warning: (ae-forgotten-export) The symbol "i11_3" needs to be exported by the entry point index.d.ts // Warning: (ae-forgotten-export) The symbol "i12_3" needs to be exported by the entry point index.d.ts - // Warning: (ae-forgotten-export) The symbol "i13_3" needs to be exported by the entry point index.d.ts + // Warning: (ae-forgotten-export) The symbol "i13_4" needs to be exported by the entry point index.d.ts // Warning: (ae-forgotten-export) The symbol "i14_2" needs to be exported by the entry point index.d.ts // Warning: (ae-forgotten-export) The symbol "i15_2" needs to be exported by the entry point index.d.ts // Warning: (ae-forgotten-export) The symbol "i16_2" needs to be exported by the entry point index.d.ts @@ -106,7 +106,7 @@ export class ClarityModule { // Warning: (ae-forgotten-export) The symbol "i18_2" needs to be exported by the entry point index.d.ts // // (undocumented) - static ɵmod: i0.ɵɵNgModuleDeclaration; + static ɵmod: i0.ɵɵNgModuleDeclaration; } // @public (undocumented) @@ -3886,10 +3886,10 @@ export class ClrStepperModule { // Warning: (ae-forgotten-export) The symbol "i3_27" needs to be exported by the entry point index.d.ts // Warning: (ae-forgotten-export) The symbol "i4_18" needs to be exported by the entry point index.d.ts // Warning: (ae-forgotten-export) The symbol "i5_14" needs to be exported by the entry point index.d.ts - // Warning: (ae-forgotten-export) The symbol "i8_8" needs to be exported by the entry point index.d.ts + // Warning: (ae-forgotten-export) The symbol "i8_9" needs to be exported by the entry point index.d.ts // // (undocumented) - static ɵmod: i0.ɵɵNgModuleDeclaration; + static ɵmod: i0.ɵɵNgModuleDeclaration; } // @public (undocumented) @@ -3953,6 +3953,14 @@ export class ClrTab { static ɵfac: i0.ɵɵFactoryDeclaration; } +// @public (undocumented) +export class ClrTabAction { + // (undocumented) + static ɵdir: i0.ɵɵDirectiveDeclaration; + // (undocumented) + static ɵfac: i0.ɵɵFactoryDeclaration; +} + // @public (undocumented) export class ClrTabContent implements OnDestroy { constructor(ifActiveService: IfActiveService, id: number, tabsService: TabsService); @@ -4065,6 +4073,8 @@ export class ClrTabs implements AfterContentInit, OnDestroy { // (undocumented) set tabOverflowEl(value: ElementRef); // (undocumented) + tabsActions: QueryList; + // (undocumented) tabsId: number; // (undocumented) tabsService: TabsService; @@ -4075,11 +4085,24 @@ export class ClrTabs implements AfterContentInit, OnDestroy { // (undocumented) toggleService: ClrPopoverToggleService; // (undocumented) - static ɵcmp: i0.ɵɵComponentDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; // (undocumented) static ɵfac: i0.ɵɵFactoryDeclaration; } +// @public (undocumented) +export class ClrTabsActions { + // (undocumented) + position: ClrTabsActionsPosition; + // (undocumented) + static ɵcmp: i0.ɵɵComponentDeclaration; + // (undocumented) + static ɵfac: i0.ɵɵFactoryDeclaration; +} + +// @public (undocumented) +export type ClrTabsActionsPosition = 'left' | 'right'; + // @public (undocumented) export class ClrTabsModule { constructor(); @@ -4094,10 +4117,12 @@ export class ClrTabsModule { // Warning: (ae-forgotten-export) The symbol "i5_11" needs to be exported by the entry point index.d.ts // Warning: (ae-forgotten-export) The symbol "i6_8" needs to be exported by the entry point index.d.ts // Warning: (ae-forgotten-export) The symbol "i7_7" needs to be exported by the entry point index.d.ts - // Warning: (ae-forgotten-export) The symbol "i11_3" needs to be exported by the entry point index.d.ts + // Warning: (ae-forgotten-export) The symbol "i8_7" needs to be exported by the entry point index.d.ts + // Warning: (ae-forgotten-export) The symbol "i9_6" needs to be exported by the entry point index.d.ts + // Warning: (ae-forgotten-export) The symbol "i13_3" needs to be exported by the entry point index.d.ts // // (undocumented) - static ɵmod: i0.ɵɵNgModuleDeclaration; + static ɵmod: i0.ɵɵNgModuleDeclaration; } // @public (undocumented) @@ -4683,13 +4708,13 @@ export class ClrWizardModule { // Warning: (ae-forgotten-export) The symbol "i5_13" needs to be exported by the entry point index.d.ts // Warning: (ae-forgotten-export) The symbol "i6_9" needs to be exported by the entry point index.d.ts // Warning: (ae-forgotten-export) The symbol "i7_8" needs to be exported by the entry point index.d.ts - // Warning: (ae-forgotten-export) The symbol "i8_7" needs to be exported by the entry point index.d.ts - // Warning: (ae-forgotten-export) The symbol "i9_6" needs to be exported by the entry point index.d.ts + // Warning: (ae-forgotten-export) The symbol "i8_8" needs to be exported by the entry point index.d.ts + // Warning: (ae-forgotten-export) The symbol "i9_7" needs to be exported by the entry point index.d.ts // Warning: (ae-forgotten-export) The symbol "i10_5" needs to be exported by the entry point index.d.ts - // Warning: (ae-forgotten-export) The symbol "i11_5" needs to be exported by the entry point index.d.ts + // Warning: (ae-forgotten-export) The symbol "i11_4" needs to be exported by the entry point index.d.ts // // (undocumented) - static ɵmod: i0.ɵɵNgModuleDeclaration; + static ɵmod: i0.ɵɵNgModuleDeclaration; } // @public diff --git a/projects/angular/src/layout/tabs/_tabs.clarity.scss b/projects/angular/src/layout/tabs/_tabs.clarity.scss index 3ea34ea79f..c7b722ffcc 100644 --- a/projects/angular/src/layout/tabs/_tabs.clarity.scss +++ b/projects/angular/src/layout/tabs/_tabs.clarity.scss @@ -46,101 +46,112 @@ [data-hidden='true'] { display: none; } -} - -button.nav-link { - border-radius: 0; - text-transform: capitalize; - min-width: 0; -} + button.nav-link { + border-radius: 0; + text-transform: capitalize; + min-width: 0; + } -.tabs-overflow { - position: relative; + .tabs-overflow { + position: relative; - .nav-item { - margin-right: 0; + .nav-item { + margin-right: 0; + } } -} -.tab-content { - display: inline; -} - -@include mixins.fixForIE11AndUp { .tab-content { - display: inline-block; - width: 100%; + display: inline; } -} -.tabs-vertical { - display: flex; - - // Must be direct child, so horizontal tabs can be nested in vertical tabs - & > .nav { - height: auto; - box-shadow: none; - flex-direction: column; - align-items: stretch; - margin-right: variables.$clr_baselineRem_1; - overflow: auto; - flex-shrink: 0; - padding: variables.$clr_baselineRem_4px; - width: variables.$clr_baselineRem_10; - min-width: variables.$clr_baselineRem_2; - - @include mixins.css-var(gap, clr-nav-vertical-gap-size, 0, variables.$clr-use-custom-properties); - - // ATM - remove - .nav-item { - @include mixins.css-var( - margin-right, - clr-nav-item-margin-right, - variables.$clr_baselineRem_1, - variables.$clr-use-custom-properties - ); + @include mixins.fixForIE11AndUp { + .tab-content { + display: inline-block; + width: 100%; } + } - .nav-link { - text-align: left; - @include mixins.css-var( - padding, - clr-nav-vertical-link-padding, - 0 variables.$clr_baselineRem_0_5, - variables.$clr-use-custom-properties - ); - border: none; + .tabs-vertical { + display: flex; + + // Must be direct child, so horizontal tabs can be nested in vertical tabs + & > .nav { + height: auto; + box-shadow: none; + flex-direction: column; + align-items: stretch; + margin-right: variables.$clr_baselineRem_1; + overflow: auto; flex-shrink: 0; - margin-top: 0; - margin-left: 0; - width: 100%; + padding: variables.$clr_baselineRem_4px; + width: variables.$clr_baselineRem_10; + min-width: variables.$clr_baselineRem_2; - // ATM - remove - &.btn { - margin-bottom: variables.$clr_baselineRem_1px; - } + @include mixins.css-var(gap, clr-nav-vertical-gap-size, 0, variables.$clr-use-custom-properties); - &.active, - &:hover { - @include nav-link-border-appearence('left'); - } - &:not(:active).active { + // ATM - remove + .nav-item { @include mixins.css-var( - background-color, - clr-nav-selected-bg-color, - variables.$clr-global-selection-color, + margin-right, + clr-nav-item-margin-right, + variables.$clr_baselineRem_1, variables.$clr-use-custom-properties ); } - &:not(.active):hover { + .nav-link { + text-align: left; @include mixins.css-var( - background-color, - clr-nav-hover-bg-color, - tabs-variables.$clr-nav-hover-bg-color, + padding, + clr-nav-vertical-link-padding, + 0 variables.$clr_baselineRem_0_5, variables.$clr-use-custom-properties ); + border: none; + flex-shrink: 0; + margin-top: 0; + margin-left: 0; + width: 100%; + + // ATM - remove + &.btn { + margin-bottom: variables.$clr_baselineRem_1px; + } + + &.active, + &:hover { + @include nav-link-border-appearence('left'); + } + &:not(:active).active { + @include mixins.css-var( + background-color, + clr-nav-selected-bg-color, + variables.$clr-global-selection-color, + variables.$clr-use-custom-properties + ); + } + + &:not(.active):hover { + @include mixins.css-var( + background-color, + clr-nav-hover-bg-color, + tabs-variables.$clr-nav-hover-bg-color, + variables.$clr-use-custom-properties + ); + } } } } + + .tabs-actions { + display: inline-flex; + width: 100%; + + &[position~='left'] { + justify-content: start; + } + &[position~='right'] { + justify-content: end; + } + } } diff --git a/projects/angular/src/layout/tabs/index.ts b/projects/angular/src/layout/tabs/index.ts index 4ace03ce07..9dc6b5946f 100644 --- a/projects/angular/src/layout/tabs/index.ts +++ b/projects/angular/src/layout/tabs/index.ts @@ -6,10 +6,12 @@ */ export * from './tabs'; +export * from './tabs-actions'; export * from './tab'; export * from './tab-content'; export * from './tab-overflow-content'; export * from './tab-link.directive'; +export * from './tab-action.directive'; export * from './tabs.module'; export { TabsWillyWonka as ÇlrTabsWillyWonka } from './chocolate/tabs-willy-wonka'; diff --git a/projects/angular/src/layout/tabs/tab-action.directive.spec.ts b/projects/angular/src/layout/tabs/tab-action.directive.spec.ts new file mode 100644 index 0000000000..684b55980e --- /dev/null +++ b/projects/angular/src/layout/tabs/tab-action.directive.spec.ts @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2016-2024 Broadcom. All Rights Reserved. + * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. + * This software is released under MIT license. + * The full license information can be found in LICENSE in the root directory of this project. + */ + +import { Component, ElementRef, QueryList, ViewChildren } from '@angular/core'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ClrTabAction } from './tab-action.directive'; +import { ClrTabsModule } from './tabs.module'; + +@Component({ + template: ` + + + + + + + + + + + + `, +}) +class TestComponent { + @ViewChildren(ClrTabAction, { read: ElementRef }) tabsActions: QueryList; +} + +describe('TabAction Directive', () => { + let fixture: ComponentFixture; + let instance: any; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [ClrTabsModule], + declarations: [TestComponent], + providers: [], + }); + + fixture = TestBed.createComponent(TestComponent); + fixture.detectChanges(); + instance = fixture.componentInstance; + }); + + afterEach(() => { + fixture.destroy(); + }); + + it('has the correct tabIndex value', () => { + const tabsActions: QueryList = instance.tabsActions; + expect(tabsActions.length).toBe(1); + expect(tabsActions.get(0).nativeElement.tabIndex).toBe(0); + }); +}); diff --git a/projects/angular/src/layout/tabs/tab-action.directive.ts b/projects/angular/src/layout/tabs/tab-action.directive.ts new file mode 100644 index 0000000000..bd24511b15 --- /dev/null +++ b/projects/angular/src/layout/tabs/tab-action.directive.ts @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2016-2024 Broadcom. All Rights Reserved. + * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. + * This software is released under MIT license. + * The full license information can be found in LICENSE in the root directory of this project. + */ + +import { Directive } from '@angular/core'; + +@Directive({ + selector: '[clrTabAction]', + host: { + tabindex: '0', + }, +}) +export class ClrTabAction {} diff --git a/projects/angular/src/layout/tabs/tabs-actions.spec.ts b/projects/angular/src/layout/tabs/tabs-actions.spec.ts new file mode 100644 index 0000000000..e839e06621 --- /dev/null +++ b/projects/angular/src/layout/tabs/tabs-actions.spec.ts @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2016-2024 Broadcom. All Rights Reserved. + * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. + * This software is released under MIT license. + * The full license information can be found in LICENSE in the root directory of this project. + */ + +import { Component } from '@angular/core'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { By } from '@angular/platform-browser'; + +import { ClrTabsActions } from './tabs-actions'; +import { ClrTabsModule } from './tabs.module'; + +@Component({ + template: `Hello world`, +}) +class TestComponent {} + +describe('TabsActions', () => { + let fixture: ComponentFixture; + let compiled: any; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [ClrTabsModule], + declarations: [TestComponent], + providers: [], + }); + fixture = TestBed.createComponent(TestComponent); + compiled = fixture.nativeElement; + fixture.detectChanges(); + }); + + it('projects content', () => { + expect(compiled.textContent.trim()).toMatch('Hello world'); + }); + + it('adds a .tabs-actions class on the host element', () => { + const tabsActionsElement = fixture.debugElement.query(By.directive(ClrTabsActions)).nativeElement; + expect(tabsActionsElement.classList.contains('tabs-actions')).toBe(true); + }); +}); diff --git a/projects/angular/src/layout/tabs/tabs-actions.ts b/projects/angular/src/layout/tabs/tabs-actions.ts new file mode 100644 index 0000000000..431dc93c06 --- /dev/null +++ b/projects/angular/src/layout/tabs/tabs-actions.ts @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2016-2024 Broadcom. All Rights Reserved. + * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. + * This software is released under MIT license. + * The full license information can be found in LICENSE in the root directory of this project. + */ + +import { Component, HostBinding, Input } from '@angular/core'; + +export type ClrTabsActionsPosition = 'left' | 'right'; + +@Component({ + selector: 'clr-tabs-actions', + template: ` +
+ +
+ `, + host: { + '[class.tabs-actions]': 'true', + }, +}) +export class ClrTabsActions { + @Input() + @HostBinding('attr.position') + position: ClrTabsActionsPosition = 'right'; +} diff --git a/projects/angular/src/layout/tabs/tabs.module.ts b/projects/angular/src/layout/tabs/tabs.module.ts index ec9125c971..b95d8254fa 100644 --- a/projects/angular/src/layout/tabs/tabs.module.ts +++ b/projects/angular/src/layout/tabs/tabs.module.ts @@ -16,10 +16,12 @@ import { ClrTemplateRefModule } from '../../utils/template-ref/template-ref.modu import { ActiveOompaLoompa } from './chocolate/active-oompa-loompa'; import { TabsWillyWonka } from './chocolate/tabs-willy-wonka'; import { ClrTab } from './tab'; +import { ClrTabAction } from './tab-action.directive'; import { ClrTabContent } from './tab-content'; import { ClrTabLink } from './tab-link.directive'; import { ClrTabOverflowContent } from './tab-overflow-content'; import { ClrTabs } from './tabs'; +import { ClrTabsActions } from './tabs-actions'; export const CLR_TABS_DIRECTIVES: Type[] = [ ClrTabContent, @@ -27,6 +29,8 @@ export const CLR_TABS_DIRECTIVES: Type[] = [ ClrTabs, ClrTabOverflowContent, ClrTabLink, + ClrTabAction, + ClrTabsActions, TabsWillyWonka, ActiveOompaLoompa, ]; diff --git a/projects/angular/src/layout/tabs/tabs.ts b/projects/angular/src/layout/tabs/tabs.ts index d5bbc0a1bb..7f9195e243 100644 --- a/projects/angular/src/layout/tabs/tabs.ts +++ b/projects/angular/src/layout/tabs/tabs.ts @@ -29,6 +29,7 @@ import { ClrPopoverToggleService } from '../../utils/popover/providers/popover-t import { TabsLayout } from './enums/tabs-layout.enum'; import { TabsService } from './providers/tabs.service'; import { ClrTab } from './tab'; +import { ClrTabAction } from './tab-action.directive'; import { ClrTabLink } from './tab-link.directive'; import { ClrTabOverflowContent } from './tab-overflow-content'; import { TABS_ID, TABS_ID_PROVIDER } from './tabs-id.provider'; @@ -91,6 +92,7 @@ import { TABS_ID, TABS_ID_PROVIDER } from './tabs-id.provider'; + `, @@ -106,6 +108,7 @@ export class ClrTabs implements AfterContentInit, OnDestroy { @ViewChild(ClrKeyFocus, { static: true }) keyFocus: ClrKeyFocus; + @ContentChildren(ClrTabAction, { read: ElementRef, descendants: true }) tabsActions: QueryList; @ContentChildren(ClrTab) private tabs: QueryList; private subscriptions: Subscription[] = []; @@ -178,6 +181,7 @@ export class ClrTabs implements AfterContentInit, OnDestroy { ngAfterContentInit() { this.subscriptions.push(this.listenForTabLinkChanges()); + this.subscriptions.push(this.listedForTabsActionsChanges()); if (typeof this.ifActiveService.current === 'undefined' && this.tabLinkDirectives[0]) { this.tabLinkDirectives[0].activate(); @@ -252,7 +256,14 @@ export class ClrTabs implements AfterContentInit, OnDestroy { // This is because we have another handler on the tabOverflowTrigger element itself. // As this handler method is on the document level so the event bubbles up to it and conflicts // with the tabOverflowTrigger handler resulting in opening the tab overflow and closing it right away consecutively. - if (event.target === tabOverflowTrigger || tabOverflowTrigger.contains(event.target as HTMLElement)) { + const isTabsAction = this.tabsActions.some(action => + (action.nativeElement as HTMLElement).contains(event.target as HTMLElement) + ); + if ( + event.target === tabOverflowTrigger || + tabOverflowTrigger.contains(event.target as HTMLElement) || + isTabsAction + ) { return; } @@ -262,10 +273,21 @@ export class ClrTabs implements AfterContentInit, OnDestroy { } } + private setTabLinkElements() { + this._tabLinkDirectives = this.tabs.map(tab => tab.tabLink); + this.tabLinkElements = this._tabLinkDirectives.map(tab => tab.el.nativeElement); + if (this.tabsActions && this.tabsActions) { + this.tabLinkElements.push(...this.tabsActions.map(action => action.nativeElement)); + } + } + private listenForTabLinkChanges() { - return this.tabs.changes.pipe(startWith(this.tabs.map(tab => tab.tabLink))).subscribe(() => { - this._tabLinkDirectives = this.tabs.map(tab => tab.tabLink); - this.tabLinkElements = this._tabLinkDirectives.map(tab => tab.el.nativeElement); - }); + return this.tabs.changes + .pipe(startWith(this.tabs.map(tab => tab.tabLink))) + .subscribe(() => this.setTabLinkElements()); + } + + private listedForTabsActionsChanges() { + return this.tabsActions.changes.subscribe(() => this.setTabLinkElements()); } } diff --git a/projects/demo/src/app/tabs/tabs-actions-angular.demo.html b/projects/demo/src/app/tabs/tabs-actions-angular.demo.html new file mode 100644 index 0000000000..b7c28fa997 --- /dev/null +++ b/projects/demo/src/app/tabs/tabs-actions-angular.demo.html @@ -0,0 +1,85 @@ + + +
+

Action after the last tab

+ + + + + + + + +

{{ tab.content }}

+ +
+
+
+

Action at the end

+ + + + + + + +

{{ tab.content }}

+ +
+
+
+

Vertical Action at the end

+ + + + + + + +

{{ tab.content }}

+ +
+
+
+

With overflow tabs

+ + + + + + + +

{{ tab.content }}

+ +
+
+
+
diff --git a/projects/demo/src/app/tabs/tabs-actions-angular.ts b/projects/demo/src/app/tabs/tabs-actions-angular.ts new file mode 100644 index 0000000000..17c1c1efd8 --- /dev/null +++ b/projects/demo/src/app/tabs/tabs-actions-angular.ts @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2016-2024 Broadcom. All Rights Reserved. + * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. + * This software is released under MIT license. + * The full license information can be found in LICENSE in the root directory of this project. + */ + +import { Component } from '@angular/core'; + +@Component({ + selector: 'clr-modal-tabs-actions-angular', + styleUrls: ['./tabs.demo.scss'], + templateUrl: './tabs-actions-angular.demo.html', +}) +export class TabsActionsAngularDemo { + layout = 'vertical'; + inOverflow = false; + tabs = [ + { + title: 'Dashboard', + content: `Content for Dashboard tab. Here is a link that can be accessed via clicking or + through keyboard via tabbing.`, + }, + { + title: 'Management', + content: 'Content for Management tab.', + }, + ]; + + addTab() { + this.tabs.push({ + title: `Inserted Tab ${this.tabs.length + 1}`, + content: `Content for Inserted Tab ${this.tabs.length + 1}`, + }); + } + + removeTab(index) { + this.tabs.splice(index, 1); + } +} diff --git a/projects/demo/src/app/tabs/tabs.demo.html b/projects/demo/src/app/tabs/tabs.demo.html index 7f4ca29b61..b46dfb88f9 100644 --- a/projects/demo/src/app/tabs/tabs.demo.html +++ b/projects/demo/src/app/tabs/tabs.demo.html @@ -10,6 +10,7 @@

Tabs

diff --git a/projects/demo/src/app/tabs/tabs.demo.module.ts b/projects/demo/src/app/tabs/tabs.demo.module.ts index 866128ab52..cf7e2a2e86 100644 --- a/projects/demo/src/app/tabs/tabs.demo.module.ts +++ b/projects/demo/src/app/tabs/tabs.demo.module.ts @@ -10,6 +10,7 @@ import { NgModule } from '@angular/core'; import { ClarityModule } from '@clr/angular'; import { ClrKeyFocusModule } from '../../../../angular/src/utils/focus/key-focus/key-focus.module'; +import { TabsActionsAngularDemo } from './tabs-actions-angular'; import { TabsAngularDemo } from './tabs-angular'; import { TabsStaticDemo } from './tabs-static'; import { TabsDemo } from './tabs.demo'; @@ -17,7 +18,7 @@ import { ROUTING } from './tabs.demo.routing'; @NgModule({ imports: [CommonModule, ClarityModule, ROUTING, ClrKeyFocusModule], - declarations: [TabsDemo, TabsStaticDemo, TabsAngularDemo], - exports: [TabsDemo, TabsStaticDemo, TabsAngularDemo], + declarations: [TabsDemo, TabsStaticDemo, TabsAngularDemo, TabsActionsAngularDemo], + exports: [TabsDemo, TabsStaticDemo, TabsAngularDemo, TabsActionsAngularDemo], }) export class TabsDemoModule {} diff --git a/projects/demo/src/app/tabs/tabs.demo.routing.ts b/projects/demo/src/app/tabs/tabs.demo.routing.ts index df3a901737..cd22e8be90 100644 --- a/projects/demo/src/app/tabs/tabs.demo.routing.ts +++ b/projects/demo/src/app/tabs/tabs.demo.routing.ts @@ -8,6 +8,7 @@ import { ModuleWithProviders } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; +import { TabsActionsAngularDemo } from './tabs-actions-angular'; import { TabsAngularDemo } from './tabs-angular'; import { TabsStaticDemo } from './tabs-static'; import { TabsDemo } from './tabs.demo'; @@ -20,6 +21,7 @@ const ROUTES: Routes = [ { path: '', redirectTo: 'static', pathMatch: 'full' }, { path: 'static', component: TabsStaticDemo }, { path: 'angular', component: TabsAngularDemo }, + { path: 'actions-angular', component: TabsActionsAngularDemo }, ], }, ]; diff --git a/tests/snapshots/chromium/tabs/tabs-actions--tabs-actions-left-core-dark.png b/tests/snapshots/chromium/tabs/tabs-actions--tabs-actions-left-core-dark.png new file mode 100644 index 0000000000..28ab37ac1e Binary files /dev/null and b/tests/snapshots/chromium/tabs/tabs-actions--tabs-actions-left-core-dark.png differ diff --git a/tests/snapshots/chromium/tabs/tabs-actions--tabs-actions-left-core-light.png b/tests/snapshots/chromium/tabs/tabs-actions--tabs-actions-left-core-light.png new file mode 100644 index 0000000000..5a2e2d6329 Binary files /dev/null and b/tests/snapshots/chromium/tabs/tabs-actions--tabs-actions-left-core-light.png differ diff --git a/tests/snapshots/chromium/tabs/tabs-actions--tabs-actions-left-ng-dark.png b/tests/snapshots/chromium/tabs/tabs-actions--tabs-actions-left-ng-dark.png new file mode 100644 index 0000000000..a5f7745765 Binary files /dev/null and b/tests/snapshots/chromium/tabs/tabs-actions--tabs-actions-left-ng-dark.png differ diff --git a/tests/snapshots/chromium/tabs/tabs-actions--tabs-actions-left-ng-light.png b/tests/snapshots/chromium/tabs/tabs-actions--tabs-actions-left-ng-light.png new file mode 100644 index 0000000000..b31d465d05 Binary files /dev/null and b/tests/snapshots/chromium/tabs/tabs-actions--tabs-actions-left-ng-light.png differ diff --git a/tests/snapshots/chromium/tabs/tabs-actions--tabs-core-dark.png b/tests/snapshots/chromium/tabs/tabs-actions--tabs-core-dark.png new file mode 100644 index 0000000000..bc89985f33 Binary files /dev/null and b/tests/snapshots/chromium/tabs/tabs-actions--tabs-core-dark.png differ diff --git a/tests/snapshots/chromium/tabs/tabs-actions--tabs-core-light.png b/tests/snapshots/chromium/tabs/tabs-actions--tabs-core-light.png new file mode 100644 index 0000000000..4ec7c5c83e Binary files /dev/null and b/tests/snapshots/chromium/tabs/tabs-actions--tabs-core-light.png differ diff --git a/tests/snapshots/chromium/tabs/tabs-actions--tabs-ng-dark.png b/tests/snapshots/chromium/tabs/tabs-actions--tabs-ng-dark.png new file mode 100644 index 0000000000..a700750421 Binary files /dev/null and b/tests/snapshots/chromium/tabs/tabs-actions--tabs-ng-dark.png differ diff --git a/tests/snapshots/chromium/tabs/tabs-actions--tabs-ng-light.png b/tests/snapshots/chromium/tabs/tabs-actions--tabs-ng-light.png new file mode 100644 index 0000000000..13496ec012 Binary files /dev/null and b/tests/snapshots/chromium/tabs/tabs-actions--tabs-ng-light.png differ diff --git a/tests/snapshots/chromium/tabs/tabs-actions--tabs-responsive-core-dark.png b/tests/snapshots/chromium/tabs/tabs-actions--tabs-responsive-core-dark.png new file mode 100644 index 0000000000..bc89985f33 Binary files /dev/null and b/tests/snapshots/chromium/tabs/tabs-actions--tabs-responsive-core-dark.png differ diff --git a/tests/snapshots/chromium/tabs/tabs-actions--tabs-responsive-core-light.png b/tests/snapshots/chromium/tabs/tabs-actions--tabs-responsive-core-light.png new file mode 100644 index 0000000000..4ec7c5c83e Binary files /dev/null and b/tests/snapshots/chromium/tabs/tabs-actions--tabs-responsive-core-light.png differ diff --git a/tests/snapshots/chromium/tabs/tabs-actions--tabs-responsive-ng-dark.png b/tests/snapshots/chromium/tabs/tabs-actions--tabs-responsive-ng-dark.png new file mode 100644 index 0000000000..a700750421 Binary files /dev/null and b/tests/snapshots/chromium/tabs/tabs-actions--tabs-responsive-ng-dark.png differ diff --git a/tests/snapshots/chromium/tabs/tabs-actions--tabs-responsive-ng-light.png b/tests/snapshots/chromium/tabs/tabs-actions--tabs-responsive-ng-light.png new file mode 100644 index 0000000000..13496ec012 Binary files /dev/null and b/tests/snapshots/chromium/tabs/tabs-actions--tabs-responsive-ng-light.png differ diff --git a/tests/snapshots/chromium/tabs/tabs-actions--vertical-tabs-core-dark.png b/tests/snapshots/chromium/tabs/tabs-actions--vertical-tabs-core-dark.png new file mode 100644 index 0000000000..26fe376732 Binary files /dev/null and b/tests/snapshots/chromium/tabs/tabs-actions--vertical-tabs-core-dark.png differ diff --git a/tests/snapshots/chromium/tabs/tabs-actions--vertical-tabs-core-light.png b/tests/snapshots/chromium/tabs/tabs-actions--vertical-tabs-core-light.png new file mode 100644 index 0000000000..e4d7f20a97 Binary files /dev/null and b/tests/snapshots/chromium/tabs/tabs-actions--vertical-tabs-core-light.png differ diff --git a/tests/snapshots/chromium/tabs/tabs-actions--vertical-tabs-ng-dark.png b/tests/snapshots/chromium/tabs/tabs-actions--vertical-tabs-ng-dark.png new file mode 100644 index 0000000000..bfa2e0eae0 Binary files /dev/null and b/tests/snapshots/chromium/tabs/tabs-actions--vertical-tabs-ng-dark.png differ diff --git a/tests/snapshots/chromium/tabs/tabs-actions--vertical-tabs-ng-light.png b/tests/snapshots/chromium/tabs/tabs-actions--vertical-tabs-ng-light.png new file mode 100644 index 0000000000..1fab4fa616 Binary files /dev/null and b/tests/snapshots/chromium/tabs/tabs-actions--vertical-tabs-ng-light.png differ diff --git a/tests/snapshots/firefox/tabs/tabs-actions--tabs-actions-left-core-dark.png b/tests/snapshots/firefox/tabs/tabs-actions--tabs-actions-left-core-dark.png new file mode 100644 index 0000000000..9256ff0b1f Binary files /dev/null and b/tests/snapshots/firefox/tabs/tabs-actions--tabs-actions-left-core-dark.png differ diff --git a/tests/snapshots/firefox/tabs/tabs-actions--tabs-actions-left-core-light.png b/tests/snapshots/firefox/tabs/tabs-actions--tabs-actions-left-core-light.png new file mode 100644 index 0000000000..178003beb5 Binary files /dev/null and b/tests/snapshots/firefox/tabs/tabs-actions--tabs-actions-left-core-light.png differ diff --git a/tests/snapshots/firefox/tabs/tabs-actions--tabs-actions-left-ng-dark.png b/tests/snapshots/firefox/tabs/tabs-actions--tabs-actions-left-ng-dark.png new file mode 100644 index 0000000000..a93b989ec4 Binary files /dev/null and b/tests/snapshots/firefox/tabs/tabs-actions--tabs-actions-left-ng-dark.png differ diff --git a/tests/snapshots/firefox/tabs/tabs-actions--tabs-actions-left-ng-light.png b/tests/snapshots/firefox/tabs/tabs-actions--tabs-actions-left-ng-light.png new file mode 100644 index 0000000000..6d5a468737 Binary files /dev/null and b/tests/snapshots/firefox/tabs/tabs-actions--tabs-actions-left-ng-light.png differ diff --git a/tests/snapshots/firefox/tabs/tabs-actions--tabs-core-dark.png b/tests/snapshots/firefox/tabs/tabs-actions--tabs-core-dark.png new file mode 100644 index 0000000000..45b1610df9 Binary files /dev/null and b/tests/snapshots/firefox/tabs/tabs-actions--tabs-core-dark.png differ diff --git a/tests/snapshots/firefox/tabs/tabs-actions--tabs-core-light.png b/tests/snapshots/firefox/tabs/tabs-actions--tabs-core-light.png new file mode 100644 index 0000000000..bdfb5b9255 Binary files /dev/null and b/tests/snapshots/firefox/tabs/tabs-actions--tabs-core-light.png differ diff --git a/tests/snapshots/firefox/tabs/tabs-actions--tabs-ng-dark.png b/tests/snapshots/firefox/tabs/tabs-actions--tabs-ng-dark.png new file mode 100644 index 0000000000..7fdf6ba896 Binary files /dev/null and b/tests/snapshots/firefox/tabs/tabs-actions--tabs-ng-dark.png differ diff --git a/tests/snapshots/firefox/tabs/tabs-actions--tabs-ng-light.png b/tests/snapshots/firefox/tabs/tabs-actions--tabs-ng-light.png new file mode 100644 index 0000000000..6b4d67e4ac Binary files /dev/null and b/tests/snapshots/firefox/tabs/tabs-actions--tabs-ng-light.png differ diff --git a/tests/snapshots/firefox/tabs/tabs-actions--tabs-responsive-core-dark.png b/tests/snapshots/firefox/tabs/tabs-actions--tabs-responsive-core-dark.png new file mode 100644 index 0000000000..45b1610df9 Binary files /dev/null and b/tests/snapshots/firefox/tabs/tabs-actions--tabs-responsive-core-dark.png differ diff --git a/tests/snapshots/firefox/tabs/tabs-actions--tabs-responsive-core-light.png b/tests/snapshots/firefox/tabs/tabs-actions--tabs-responsive-core-light.png new file mode 100644 index 0000000000..bdfb5b9255 Binary files /dev/null and b/tests/snapshots/firefox/tabs/tabs-actions--tabs-responsive-core-light.png differ diff --git a/tests/snapshots/firefox/tabs/tabs-actions--tabs-responsive-ng-dark.png b/tests/snapshots/firefox/tabs/tabs-actions--tabs-responsive-ng-dark.png new file mode 100644 index 0000000000..7fdf6ba896 Binary files /dev/null and b/tests/snapshots/firefox/tabs/tabs-actions--tabs-responsive-ng-dark.png differ diff --git a/tests/snapshots/firefox/tabs/tabs-actions--tabs-responsive-ng-light.png b/tests/snapshots/firefox/tabs/tabs-actions--tabs-responsive-ng-light.png new file mode 100644 index 0000000000..6b4d67e4ac Binary files /dev/null and b/tests/snapshots/firefox/tabs/tabs-actions--tabs-responsive-ng-light.png differ diff --git a/tests/snapshots/firefox/tabs/tabs-actions--vertical-tabs-core-dark.png b/tests/snapshots/firefox/tabs/tabs-actions--vertical-tabs-core-dark.png new file mode 100644 index 0000000000..431dee8805 Binary files /dev/null and b/tests/snapshots/firefox/tabs/tabs-actions--vertical-tabs-core-dark.png differ diff --git a/tests/snapshots/firefox/tabs/tabs-actions--vertical-tabs-core-light.png b/tests/snapshots/firefox/tabs/tabs-actions--vertical-tabs-core-light.png new file mode 100644 index 0000000000..ce27aebaac Binary files /dev/null and b/tests/snapshots/firefox/tabs/tabs-actions--vertical-tabs-core-light.png differ diff --git a/tests/snapshots/firefox/tabs/tabs-actions--vertical-tabs-ng-dark.png b/tests/snapshots/firefox/tabs/tabs-actions--vertical-tabs-ng-dark.png new file mode 100644 index 0000000000..fc7bc4d111 Binary files /dev/null and b/tests/snapshots/firefox/tabs/tabs-actions--vertical-tabs-ng-dark.png differ diff --git a/tests/snapshots/firefox/tabs/tabs-actions--vertical-tabs-ng-light.png b/tests/snapshots/firefox/tabs/tabs-actions--vertical-tabs-ng-light.png new file mode 100644 index 0000000000..2fea8d3973 Binary files /dev/null and b/tests/snapshots/firefox/tabs/tabs-actions--vertical-tabs-ng-light.png differ