Skip to content

Commit 5414245

Browse files
authored
feat(tabs): new bordered tabs (#DS-2904) (#328)
1 parent 97721d6 commit 5414245

File tree

11 files changed

+127
-5
lines changed

11 files changed

+127
-5
lines changed

packages/components-dev/tabs/template.html

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,42 @@
11
<div>
2+
<h4>Underlined tabs</h4>
3+
<kbq-tab-group underlined>
4+
<kbq-tab [label]="'Spam Attack'">Spam Attack Content</kbq-tab>
5+
<kbq-tab [label]="'Miscellaneous'">Miscellaneous Content</kbq-tab>
6+
<kbq-tab [label]="'DDoS'">DDoS Content</kbq-tab>
7+
<kbq-tab [label]="'IDS/IPS Alert'">IDS/IPS Alert Content</kbq-tab>
8+
</kbq-tab-group>
9+
10+
<h4>Underlined tabs with icons</h4>
11+
<kbq-tab-group underlined>
12+
<kbq-tab>
13+
<ng-template kbq-tab-label>
14+
<i kbq-icon="kbq-play_16"></i>
15+
Spam Attack
16+
</ng-template>
17+
Spam Attack Content
18+
</kbq-tab>
19+
<kbq-tab>
20+
<ng-template kbq-tab-label>
21+
Miscellaneous
22+
<i kbq-icon="kbq-play_16"></i>
23+
</ng-template>
24+
Miscellaneous Content
25+
</kbq-tab>
26+
<kbq-tab>
27+
<ng-template kbq-tab-label>
28+
<i kbq-icon="kbq-play_16"></i>
29+
DDoS
30+
<i kbq-icon="kbq-play_16"></i>
31+
</ng-template>
32+
DDoS Content
33+
</kbq-tab>
34+
<kbq-tab>
35+
<ng-template kbq-tab-label>IDS/IPS Alert</ng-template>
36+
IDS/IPS Alert Content
37+
</kbq-tab>
38+
</kbq-tab-group>
39+
240
<h4>Default tabs (filled)</h4>
341
<kbq-tab-group>
442
<kbq-tab [label]="'label 3'">Content 3</kbq-tab>

packages/components/tabs/_tabs-common.scss

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,25 @@
3232

3333
outline: none;
3434

35+
&.kbq-tab-label_underlined:not(.kbq-tab-label_vertical) {
36+
padding-left: 14px;
37+
padding-right: 14px;
38+
}
39+
40+
&.kbq-tab-label_underlined:not(.kbq-tab-label_vertical).kbq-tab-label_with-icon-left {
41+
padding-left: kbq-difference-series-css-variables(
42+
[tabs-size-tab-item-padding-horizontal,
43+
tabs-size-tab-item-focus-outline-width]
44+
);
45+
}
46+
47+
&.kbq-tab-label_underlined:not(.kbq-tab-label_vertical).kbq-tab-label_with-icon-right {
48+
padding-right: kbq-difference-series-css-variables(
49+
[tabs-size-tab-item-padding-horizontal,
50+
tabs-size-tab-item-focus-outline-width]
51+
);
52+
}
53+
3554
&.kbq-selected {
3655
cursor: default;
3756
}
@@ -51,6 +70,26 @@
5170
gap: kbq-css-variable(tabs-size-tab-item-content-gap-horizontal);
5271
}
5372

73+
&.kbq-tab-label_underlined:not(.kbq-tab-label_vertical):before {
74+
content: '';
75+
position: absolute;
76+
left: 0;
77+
right: 0;
78+
top: -8px;
79+
bottom: -8px;
80+
}
81+
82+
&.kbq-tab-label_underlined:not(.kbq-tab-label_vertical).kbq-selected:after {
83+
content: '';
84+
position: absolute;
85+
left: 0;
86+
right: 0;
87+
bottom: -11px;
88+
height: 3px;
89+
border-radius: 2px 2px 0 0;
90+
background: rgba(33, 34, 44, 1);
91+
}
92+
5493
&.kbq-tab-label_vertical {
5594
justify-content: flex-start;
5695

packages/components/tabs/_tabs-theme.scss

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,16 @@
2222
&.kbq-selected {
2323
@include kbq-tab-item-state($type, $sub-type, states-selected);
2424

25+
&.kbq-tab-label_underlined:not(.kbq-tab-label_vertical) {
26+
background: none;
27+
}
28+
2529
&:hover {
2630
@include kbq-tab-item-state($type, $sub-type, states-selected-hover);
31+
32+
&.kbq-tab-label_underlined:not(.kbq-tab-label_vertical) {
33+
background: none;
34+
}
2735
}
2836
}
2937

packages/components/tabs/tab-group.component.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,12 @@ export class KbqStretchTabsCssStyler {}
5252
})
5353
export class KbqVerticalTabsCssStyler {}
5454

55+
@Directive({
56+
selector: 'kbq-tab-group[underlined], [kbq-tab-nav-bar][underlined]',
57+
host: { class: 'kbq-tab-group_underlined' }
58+
})
59+
export class KbqUnderlinedTabsCssStyler {}
60+
5561
/** Used to generate unique ID's for each tab component */
5662
let nextId = 0;
5763

@@ -117,6 +123,7 @@ export class KbqTabGroup
117123
readonly resizeStream = new Subject<Event>();
118124

119125
vertical: boolean;
126+
underlined: boolean;
120127

121128
@ContentChildren(KbqTab) tabs: QueryList<KbqTab>;
122129

@@ -215,11 +222,13 @@ export class KbqTabGroup
215222
elementRef: ElementRef,
216223
private readonly changeDetectorRef: ChangeDetectorRef,
217224
@Attribute('vertical') vertical: string,
225+
@Attribute('underlined') underlined: string,
218226
@Inject(KBQ_TABS_CONFIG) @Optional() defaultConfig?: IKbqTabsConfig
219227
) {
220228
super(elementRef);
221229

222230
this.vertical = coerceBooleanProperty(vertical);
231+
this.underlined = coerceBooleanProperty(underlined);
223232

224233
this.groupId = nextId++;
225234
this.animationDuration = defaultConfig?.animationDuration || '0ms';

packages/components/tabs/tab-group.html

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
<kbq-tab-header
22
#tabHeader
33
[selectedIndex]="selectedIndex"
4+
[underlined]="underlined"
45
[vertical]="vertical"
56
(indexFocused)="focusChanged($event)"
67
(selectFocusedIndex)="onSelectFocusedIndex($event)"
@@ -14,6 +15,7 @@
1415
[class.kbq-selected]="selectedIndex === i"
1516
[class.kbq-tab-label_empty]="tab.empty"
1617
[class.kbq-tab-label_horizontal]="!vertical"
18+
[class.kbq-tab-label_underlined]="underlined"
1719
[class.kbq-tab-label_vertical]="vertical"
1820
[disabled]="tab.disabled"
1921
[id]="getTabLabelId(i)"

packages/components/tabs/tab-header.component.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,13 +44,15 @@ export type ScrollDirection = 'after' | 'before';
4444
host: {
4545
class: 'kbq-tab-header',
4646
'[class.kbq-tab-header_vertical]': 'vertical',
47+
'[class.kbq-tab-header_underlined]': 'underlined',
4748
'[class.kbq-tab-header__pagination-controls_enabled]': 'showPaginationControls',
4849
'[class.kbq-tab-header_rtl]': "getLayoutDirection() == 'rtl'"
4950
}
5051
})
5152
export class KbqTabHeader extends KbqPaginatedTabHeader {
5253
/** The index of the active tab. */
5354
@Input() vertical: boolean = false;
55+
@Input() underlined: boolean = false;
5456

5557
@ContentChildren(KbqTabLabelWrapper, { descendants: false }) items: QueryList<KbqTabLabelWrapper>;
5658
@ViewChild('tabListContainer', { static: true }) tabListContainer: ElementRef;

packages/components/tabs/tab-header.scss

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,11 @@
2424
flex-direction: column;
2525
}
2626

27+
.kbq-tab-header_underlined:not(.kbq-tab-header_vertical) .kbq-tab-list__content {
28+
padding: 8px 0;
29+
border-bottom: 1px solid rgba(13, 14, 18, 0.12);
30+
}
31+
2732
.kbq-tab-header__pagination {
2833
@include vendor-prefixes.user-select(none);
2934

packages/components/tabs/tab-label-wrapper.directive.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,14 +72,18 @@ export class KbqTabLabelWrapper extends KbqTabLabelWrapperMixinBase implements C
7272

7373
if (firstIconElement.nextSibling && firstIconElement.nextSibling.nodeType !== COMMENT_NODE) {
7474
this.renderer.addClass(firstIconElement, 'kbq-icon_left');
75+
this.renderer.addClass(this.elementRef.nativeElement, 'kbq-tab-label_with-icon-left');
7576
}
7677

7778
if (firstIconElement.previousSibling && firstIconElement.previousSibling.nodeType !== COMMENT_NODE) {
7879
this.renderer.addClass(firstIconElement, 'kbq-icon_right');
80+
this.renderer.addClass(this.elementRef.nativeElement, 'kbq-tab-label_with-icon-right');
7981
}
8082
} else if (icons.length === twoIcons) {
8183
this.renderer.addClass(firstIconElement, 'kbq-icon_left');
8284
this.renderer.addClass(secondIconElement, 'kbq-icon_right');
85+
this.renderer.addClass(this.elementRef.nativeElement, 'kbq-tab-label_with-icon-left');
86+
this.renderer.addClass(this.elementRef.nativeElement, 'kbq-tab-label_with-icon-right');
8387
}
8488
}
8589
}

packages/components/tabs/tabs.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#### Default tabs
1+
### Default tabs
22

33
<!-- example(tabs-overview) -->
44

packages/components/tabs/tabs.module.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {
1212
KbqAlignTabsEndCssStyler,
1313
KbqStretchTabsCssStyler,
1414
KbqTabGroup,
15+
KbqUnderlinedTabsCssStyler,
1516
KbqVerticalTabsCssStyler
1617
} from './tab-group.component';
1718
import { KbqTabHeader } from './tab-header.component';
@@ -41,6 +42,7 @@ import { KbqTab } from './tab.component';
4142
KbqAlignTabsCenterCssStyler,
4243
KbqAlignTabsEndCssStyler,
4344
KbqStretchTabsCssStyler,
45+
KbqUnderlinedTabsCssStyler,
4446
KbqVerticalTabsCssStyler
4547
],
4648
declarations: [
@@ -57,6 +59,7 @@ import { KbqTab } from './tab.component';
5759
KbqAlignTabsCenterCssStyler,
5860
KbqAlignTabsEndCssStyler,
5961
KbqStretchTabsCssStyler,
62+
KbqUnderlinedTabsCssStyler,
6063
KbqVerticalTabsCssStyler
6164
]
6265
})

tools/public_api_guard/components/tabs.api.md

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@ export class KbqTabContent {
185185

186186
// @public
187187
export class KbqTabGroup extends KbqTabGroupMixinBase implements AfterContentInit, AfterViewInit, AfterContentChecked, OnDestroy {
188-
constructor(elementRef: ElementRef, changeDetectorRef: ChangeDetectorRef, vertical: string, defaultConfig?: IKbqTabsConfig);
188+
constructor(elementRef: ElementRef, changeDetectorRef: ChangeDetectorRef, vertical: string, underlined: string, defaultConfig?: IKbqTabsConfig);
189189
// (undocumented)
190190
get activeTab(): KbqTab | null;
191191
set activeTab(value: KbqTabSelectBy | null);
@@ -230,11 +230,13 @@ export class KbqTabGroup extends KbqTabGroupMixinBase implements AfterContentIni
230230
// (undocumented)
231231
transparent: boolean;
232232
// (undocumented)
233+
underlined: boolean;
234+
// (undocumented)
233235
vertical: boolean;
234236
// (undocumented)
235237
static ɵcmp: i0.ɵɵComponentDeclaration<KbqTabGroup, "kbq-tab-group", ["kbqTabGroup"], { "disabled": { "alias": "disabled"; "required": false; }; "transparent": { "alias": "transparent"; "required": false; }; "onSurface": { "alias": "onSurface"; "required": false; }; "dynamicHeight": { "alias": "dynamicHeight"; "required": false; }; "selectedIndex": { "alias": "selectedIndex"; "required": false; }; "activeTab": { "alias": "activeTab"; "required": false; }; "headerPosition": { "alias": "headerPosition"; "required": false; }; "animationDuration": { "alias": "animationDuration"; "required": false; }; }, { "selectedIndexChange": "selectedIndexChange"; "activeTabChange": "activeTabChange"; "focusChange": "focusChange"; "animationDone": "animationDone"; "selectedTabChange": "selectedTabChange"; }, ["tabs"], never, false, never>;
236238
// (undocumented)
237-
static ɵfac: i0.ɵɵFactoryDeclaration<KbqTabGroup, [null, null, { attribute: "vertical"; }, { optional: true; }]>;
239+
static ɵfac: i0.ɵɵFactoryDeclaration<KbqTabGroup, [null, null, { attribute: "vertical"; }, { attribute: "underlined"; }, { optional: true; }]>;
238240
}
239241

240242
// @public
@@ -268,9 +270,11 @@ export class KbqTabHeader extends KbqPaginatedTabHeader {
268270
tabList: ElementRef;
269271
// (undocumented)
270272
tabListContainer: ElementRef;
273+
// (undocumented)
274+
underlined: boolean;
271275
vertical: boolean;
272276
// (undocumented)
273-
static ɵcmp: i0.ɵɵComponentDeclaration<KbqTabHeader, "kbq-tab-header", never, { "selectedIndex": { "alias": "selectedIndex"; "required": false; }; "vertical": { "alias": "vertical"; "required": false; }; }, { "selectFocusedIndex": "selectFocusedIndex"; "indexFocused": "indexFocused"; }, ["items"], ["*"], false, never>;
277+
static ɵcmp: i0.ɵɵComponentDeclaration<KbqTabHeader, "kbq-tab-header", never, { "selectedIndex": { "alias": "selectedIndex"; "required": false; }; "vertical": { "alias": "vertical"; "required": false; }; "underlined": { "alias": "underlined"; "required": false; }; }, { "selectFocusedIndex": "selectFocusedIndex"; "indexFocused": "indexFocused"; }, ["items"], ["*"], false, never>;
274278
// (undocumented)
275279
static ɵfac: i0.ɵɵFactoryDeclaration<KbqTabHeader, [null, null, null, null, null, { optional: true; }, { optional: true; }]>;
276280
}
@@ -379,7 +383,15 @@ export class KbqTabsModule {
379383
// Warning: (ae-forgotten-export) The symbol "i8" needs to be exported by the entry point index.d.ts
380384
//
381385
// (undocumented)
382-
static ɵmod: i0.ɵɵNgModuleDeclaration<KbqTabsModule, [typeof i1.KbqTabGroup, typeof i2.KbqTabLabel, typeof i3.KbqTab, typeof i4.KbqTabLabelWrapper, typeof i5.KbqTabNav, typeof i5.KbqTabLink, typeof i6.KbqTabBody, typeof i6.KbqTabBodyPortal, typeof i7.KbqTabHeader, typeof i8.KbqTabContent, typeof i1.KbqAlignTabsCenterCssStyler, typeof i1.KbqAlignTabsEndCssStyler, typeof i1.KbqStretchTabsCssStyler, typeof i1.KbqVerticalTabsCssStyler], [typeof i9.PortalModule, typeof i10.A11yModule, typeof i11.CdkScrollableModule, typeof i12.KbqCommonModule, typeof i13.KbqIconModule, typeof i14.KbqToolTipModule], [typeof i12.KbqCommonModule, typeof i1.KbqTabGroup, typeof i2.KbqTabLabel, typeof i3.KbqTab, typeof i5.KbqTabNav, typeof i5.KbqTabLink, typeof i8.KbqTabContent, typeof i1.KbqAlignTabsCenterCssStyler, typeof i1.KbqAlignTabsEndCssStyler, typeof i1.KbqStretchTabsCssStyler, typeof i1.KbqVerticalTabsCssStyler]>;
386+
static ɵmod: i0.ɵɵNgModuleDeclaration<KbqTabsModule, [typeof i1.KbqTabGroup, typeof i2.KbqTabLabel, typeof i3.KbqTab, typeof i4.KbqTabLabelWrapper, typeof i5.KbqTabNav, typeof i5.KbqTabLink, typeof i6.KbqTabBody, typeof i6.KbqTabBodyPortal, typeof i7.KbqTabHeader, typeof i8.KbqTabContent, typeof i1.KbqAlignTabsCenterCssStyler, typeof i1.KbqAlignTabsEndCssStyler, typeof i1.KbqStretchTabsCssStyler, typeof i1.KbqUnderlinedTabsCssStyler, typeof i1.KbqVerticalTabsCssStyler], [typeof i9.PortalModule, typeof i10.A11yModule, typeof i11.CdkScrollableModule, typeof i12.KbqCommonModule, typeof i13.KbqIconModule, typeof i14.KbqToolTipModule], [typeof i12.KbqCommonModule, typeof i1.KbqTabGroup, typeof i2.KbqTabLabel, typeof i3.KbqTab, typeof i5.KbqTabNav, typeof i5.KbqTabLink, typeof i8.KbqTabContent, typeof i1.KbqAlignTabsCenterCssStyler, typeof i1.KbqAlignTabsEndCssStyler, typeof i1.KbqStretchTabsCssStyler, typeof i1.KbqUnderlinedTabsCssStyler, typeof i1.KbqVerticalTabsCssStyler]>;
387+
}
388+
389+
// @public (undocumented)
390+
export class KbqUnderlinedTabsCssStyler {
391+
// (undocumented)
392+
static ɵdir: i0.ɵɵDirectiveDeclaration<KbqUnderlinedTabsCssStyler, "kbq-tab-group[underlined], [kbq-tab-nav-bar][underlined]", never, {}, {}, never, never, false, never>;
393+
// (undocumented)
394+
static ɵfac: i0.ɵɵFactoryDeclaration<KbqUnderlinedTabsCssStyler, never>;
383395
}
384396

385397
// @public (undocumented)

0 commit comments

Comments
 (0)