Skip to content

Commit 2500821

Browse files
feat(module:tabs): support for start and end extra content (#9097)
1 parent 1b1a013 commit 2500821

File tree

10 files changed

+124
-15
lines changed

10 files changed

+124
-15
lines changed

components/tabs/demo/extra.md

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,8 @@ title:
77

88
## zh-CN
99

10-
可以在页签右边添加附加操作
10+
可以在页签两边添加附加操作
1111

1212
## en-US
1313

14-
You can add extra actions to the right of Tabs.
15-
16-
17-
14+
You can add extra actions to the start or end or even both sides of Tabs.

components/tabs/demo/extra.ts

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
import { Component } from '@angular/core';
2+
import { FormsModule } from '@angular/forms';
23

34
import { NzButtonModule } from 'ng-zorro-antd/button';
5+
import { NzCheckboxModule } from 'ng-zorro-antd/checkbox';
46
import { NzTabsModule } from 'ng-zorro-antd/tabs';
57

68
@Component({
79
selector: 'nz-demo-tabs-extra',
8-
imports: [NzButtonModule, NzTabsModule],
10+
imports: [NzButtonModule, NzTabsModule, NzCheckboxModule, FormsModule],
911
template: `
1012
<nz-tabset [nzTabBarExtraContent]="extraTemplate">
1113
@for (tab of tabs; track tab) {
@@ -15,8 +17,34 @@ import { NzTabsModule } from 'ng-zorro-antd/tabs';
1517
<ng-template #extraTemplate>
1618
<button nz-button>Extra Action</button>
1719
</ng-template>
20+
21+
<br />
22+
<br />
23+
<p>You can also specify its direction or both side</p>
24+
<br />
25+
<nz-checkbox-group [nzOptions]="positionOptions" [(ngModel)]="positions" />
26+
<br />
27+
<br />
28+
29+
<nz-tabset>
30+
@if (positions.includes('start')) {
31+
<button *nzTabBarExtraContent="'start'" nz-button [style.margin-right.px]="16">Start Extra Action</button>
32+
}
33+
@if (positions.includes('end')) {
34+
<button *nzTabBarExtraContent="'end'" nz-button [style.margin-left.px]="16">End Extra Action</button>
35+
}
36+
37+
@for (tab of tabs; track tab) {
38+
<nz-tab [nzTitle]="'Tab ' + tab">Content of tab {{ tab }}</nz-tab>
39+
}
40+
</nz-tabset>
1841
`
1942
})
2043
export class NzDemoTabsExtraComponent {
2144
tabs = [1, 2, 3];
45+
positionOptions = [
46+
{ label: 'start', value: 'start' },
47+
{ label: 'end', value: 'end' }
48+
];
49+
positions = ['start', 'end'];
2250
}

components/tabs/doc/index.en-US.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,3 +107,11 @@ Show a link in tab's head. Used in router link mode.
107107
</nz-tab>
108108
</nz-tabset>
109109
```
110+
111+
### [nzTabBarExtraContent]
112+
113+
> Note: `*nzTabBarExtraContent` has a higher priority than `nz-tabset[nzTabBarExtraContent]`.
114+
115+
| Property | Description | Type | Default | Global Config |
116+
| ------------------------ | ------------------------- | ------------------ | ------- | ------------- |
117+
| `[nzTabBarExtraContent]` | Position of extra content | `'start' \| 'end'` | `'end'` |

components/tabs/doc/index.zh-CN.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,3 +110,11 @@ Ant Design 依次提供了三级选项卡,分别用于不同的场景。
110110
</nz-tab>
111111
</nz-tabset>
112112
```
113+
114+
### [nzTabBarExtraContent]
115+
116+
> 注意:`*nzTabBarExtraContent``nz-tabset[nzTabBarExtraContent]` 具有更高的优先级。
117+
118+
| 参数 | 说明 | 类型 | 默认值 |
119+
| ------------------------ | -------------- | ------------------ | ------- |
120+
| `[nzTabBarExtraContent]` | 附加内容的位置 | `'start' \| 'end'` | `'end'` |

components/tabs/public-api.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,18 @@
33
* found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE
44
*/
55

6+
export * from './interfaces';
67
export { NzTabAddButtonComponent as ɵNzTabAddButtonComponent } from './tab-add-button.component';
7-
export { NzTabsInkBarDirective as ɵNzTabsInkBarDirective } from './tabs-ink-bar.directive';
8+
export * from './tab-bar-extra-content.directive';
9+
export { NzTabBodyComponent as ɵNzTabBodyComponent } from './tab-body.component';
10+
export { NzTabCloseButtonComponent as ɵNzTabCloseButtonComponent } from './tab-close-button.component';
11+
export * from './tab-link.directive';
812
export { NzTabNavBarComponent as ɵNzTabNavBarComponent } from './tab-nav-bar.component';
913
export { NzTabNavItemDirective as ɵNzTabNavItemDirective } from './tab-nav-item.directive';
10-
export { NzTabBodyComponent as ɵNzTabBodyComponent } from './tab-body.component';
1114
export { NzTabNavOperationComponent as ɵNzTabNavOperationComponent } from './tab-nav-operation.component';
1215
export { NzTabScrollListDirective as ɵNzTabScrollListDirective } from './tab-scroll-list.directive';
13-
export { NzTabCloseButtonComponent as ɵNzTabCloseButtonComponent } from './tab-close-button.component';
1416
export * from './tab.component';
1517
export * from './tab.directive';
16-
export * from './tab-link.directive';
18+
export { NzTabsInkBarDirective as ɵNzTabsInkBarDirective } from './tabs-ink-bar.directive';
1719
export * from './tabs.module';
1820
export * from './tabset.component';
19-
export * from './interfaces';
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
/**
2+
* Use of this source code is governed by an MIT-style license that can be
3+
* found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE
4+
*/
5+
6+
import { Directive, inject, input, TemplateRef } from '@angular/core';
7+
8+
@Directive({
9+
selector: '[nzTabBarExtraContent]:not(nz-tabset)'
10+
})
11+
export class NzTabBarExtraContentDirective {
12+
readonly position = input<'start' | 'end'>('end', { alias: 'nzTabBarExtraContent' });
13+
readonly templateRef = inject(TemplateRef);
14+
}

components/tabs/tab-nav-bar.component.ts

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,9 @@ import {
2828
TemplateRef,
2929
ViewChild,
3030
ViewEncapsulation,
31-
booleanAttribute
31+
booleanAttribute,
32+
computed,
33+
input
3234
} from '@angular/core';
3335
import { Subject, animationFrameScheduler, asapScheduler, merge, of } from 'rxjs';
3436
import { auditTime, takeUntil } from 'rxjs/operators';
@@ -39,6 +41,7 @@ import { NzSafeAny } from 'ng-zorro-antd/core/types';
3941

4042
import { NzTabPositionMode, NzTabScrollEvent, NzTabScrollListOffsetEvent } from './interfaces';
4143
import { NzTabAddButtonComponent } from './tab-add-button.component';
44+
import { NzTabBarExtraContentDirective } from './tab-bar-extra-content.directive';
4245
import { NzTabNavItemDirective } from './tab-nav-item.directive';
4346
import { NzTabNavOperationComponent } from './tab-nav-operation.component';
4447
import { NzTabScrollListDirective } from './tab-scroll-list.directive';
@@ -54,6 +57,11 @@ const CSS_TRANSFORM_TIME = 150;
5457
changeDetection: ChangeDetectionStrategy.OnPush,
5558
encapsulation: ViewEncapsulation.None,
5659
template: `
60+
@if (startExtraContent()) {
61+
<div class="ant-tabs-extra-content">
62+
<ng-template [ngTemplateOutlet]="startExtraContent()!.templateRef"></ng-template>
63+
</div>
64+
}
5765
<div
5866
class="ant-tabs-nav-wrap"
5967
[class.ant-tabs-nav-wrap-ping-left]="pingLeft"
@@ -90,7 +98,11 @@ const CSS_TRANSFORM_TIME = 150;
9098
[addable]="addable"
9199
[items]="hiddenItems"
92100
></nz-tab-nav-operation>
93-
@if (extraTemplate) {
101+
@if (endExtraContent()) {
102+
<div class="ant-tabs-extra-content">
103+
<ng-template [ngTemplateOutlet]="endExtraContent()!.templateRef"></ng-template>
104+
</div>
105+
} @else if (extraTemplate) {
94106
<div class="ant-tabs-extra-content">
95107
<ng-template [ngTemplateOutlet]="extraTemplate"></ng-template>
96108
</div>
@@ -121,6 +133,11 @@ export class NzTabNavBarComponent implements AfterViewInit, AfterContentChecked,
121133
@Input() inkBarAnimated = true;
122134
@Input() extraTemplate?: TemplateRef<void>;
123135

136+
readonly extraContents = input.required<readonly NzTabBarExtraContentDirective[]>();
137+
138+
readonly startExtraContent = computed(() => this.extraContents().find(item => item.position() === 'start'));
139+
readonly endExtraContent = computed(() => this.extraContents().find(item => item.position() === 'end'));
140+
124141
@Input()
125142
get selectedIndex(): number {
126143
return this._selectedIndex;

components/tabs/tabs.module.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import { NgModule } from '@angular/core';
77

88
import { NzTabAddButtonComponent } from './tab-add-button.component';
9+
import { NzTabBarExtraContentDirective } from './tab-bar-extra-content.directive';
910
import { NzTabBodyComponent } from './tab-body.component';
1011
import { NzTabCloseButtonComponent } from './tab-close-button.component';
1112
import { NzTabLinkDirective, NzTabLinkTemplateDirective } from './tab-link.directive';
@@ -31,7 +32,8 @@ const DIRECTIVES = [
3132
NzTabDirective,
3233
NzTabBodyComponent,
3334
NzTabLinkDirective,
34-
NzTabLinkTemplateDirective
35+
NzTabLinkTemplateDirective,
36+
NzTabBarExtraContentDirective
3537
];
3638

3739
@NgModule({

components/tabs/tabset.component.spec.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -939,6 +939,18 @@ describe('NzTabSet', () => {
939939
flush();
940940
}));
941941
});
942+
943+
describe('extra content', () => {
944+
it('should be possible to render additional content on both sides', () => {
945+
const fixture = TestBed.createComponent(SimpleTabsWithExtraContentComponent);
946+
fixture.detectChanges();
947+
const tabsNav: HTMLDivElement = fixture.nativeElement.querySelector('.ant-tabs-nav');
948+
expect(tabsNav.firstElementChild?.classList).toContain('ant-tabs-extra-content');
949+
expect(tabsNav.firstElementChild?.textContent?.trim()).toEqual('Start Extra Action');
950+
expect(tabsNav.lastElementChild?.classList).toContain('ant-tabs-extra-content');
951+
expect(tabsNav.lastElementChild?.textContent?.trim()).toEqual('End Extra Action');
952+
});
953+
});
942954
});
943955

944956
@Component({
@@ -1259,3 +1271,20 @@ function getTranslate(transformValue: string): { x: number; y: number } {
12591271
y: match && match[2] ? Number.parseFloat(match[2]) : 0
12601272
};
12611273
}
1274+
1275+
@Component({
1276+
imports: [NzTabsModule],
1277+
template: `
1278+
<nz-tabset [(nzSelectedIndex)]="selectedIndex">
1279+
<button *nzTabBarExtraContent="'start'">Start Extra Action</button>
1280+
<button *nzTabBarExtraContent="'end'">End Extra Action</button>
1281+
1282+
<nz-tab nzTitle="Tab 0">Content of Tab Pane 0</nz-tab>
1283+
<nz-tab nzTitle="Tab 1">Content of Tab Pane 1</nz-tab>
1284+
<nz-tab nzTitle="Tab 2">Content of Tab Pane 2</nz-tab>
1285+
</nz-tabset>
1286+
`
1287+
})
1288+
class SimpleTabsWithExtraContentComponent {
1289+
selectedIndex = 0;
1290+
}

components/tabs/tabset.component.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import {
1616
ChangeDetectionStrategy,
1717
ChangeDetectorRef,
1818
Component,
19+
contentChildren,
1920
ContentChildren,
2021
EventEmitter,
2122
forwardRef,
@@ -49,6 +50,7 @@ import {
4950
NzTabScrollEvent,
5051
NzTabType
5152
} from './interfaces';
53+
import { NzTabBarExtraContentDirective } from './tab-bar-extra-content.directive';
5254
import { NzTabBodyComponent } from './tab-body.component';
5355
import { NzTabCloseButtonComponent } from './tab-close-button.component';
5456
import { NzTabLinkDirective } from './tab-link.directive';
@@ -83,6 +85,7 @@ let nextId = 0;
8385
[hideBar]="nzHideAll"
8486
[position]="position"
8587
[extraTemplate]="nzTabBarExtraContent"
88+
[extraContents]="extraContents()"
8689
(tabScroll)="nzTabListScroll.emit($event)"
8790
(selectFocusedIndex)="setSelectedIndex($event)"
8891
(addClicked)="onAdd()"
@@ -252,13 +255,15 @@ export class NzTabSetComponent implements OnInit, AfterContentChecked, OnDestroy
252255
// We filter out only the tabs that belong to this tab set in `tabs`.
253256
@ContentChildren(NzTabComponent, { descendants: true })
254257
allTabs: QueryList<NzTabComponent> = new QueryList<NzTabComponent>();
258+
255259
@ContentChildren(NzTabLinkDirective, { descendants: true })
256260
tabLinks: QueryList<NzTabLinkDirective> = new QueryList<NzTabLinkDirective>();
257261
@ViewChild(NzTabNavBarComponent, { static: false }) tabNavBarRef!: NzTabNavBarComponent;
258-
259262
// All the direct tabs for this tab set
260263
tabs: QueryList<NzTabComponent> = new QueryList<NzTabComponent>();
261264

265+
readonly extraContents = contentChildren(NzTabBarExtraContentDirective);
266+
262267
dir: Direction = 'ltr';
263268
private readonly tabSetId!: number;
264269
private destroy$ = new Subject<void>();

0 commit comments

Comments
 (0)