diff --git a/components/tab-bar/demo/basic.ts b/components/tab-bar/demo/basic.ts index 006cf6e4..5e37ea40 100644 --- a/components/tab-bar/demo/basic.ts +++ b/components/tab-bar/demo/basic.ts @@ -10,25 +10,20 @@ import { Component } from '@angular/core'; [activeTab]="selectedIndex" [hidden]="hidden" [tabBarPosition]="topFlag ? 'top' : 'bottom'" + (onPress)="tabBarTabOnPress($event)" > - - - - -
-
-
- -
-
-
-
+ + +
+
+
+ +
+
Clicked Life tab, show Life information
@@ -36,24 +31,20 @@ import { Component } from '@angular/core';
-
- - - - -
-
-
- -
-
-
-
+ + + +
+
+
+ +
+
Clicked Koubei tab, show Koubei information
@@ -61,23 +52,19 @@ import { Component } from '@angular/core';
-
- - - - -
-
-
- -
-
-
-
+ + + +
+
+
+ +
+
Clicked Friend tab, show Friend information
@@ -85,22 +72,18 @@ import { Component } from '@angular/core';
-
- - - - -
-
-
- -
-
-
-
+ + + +
+
+
+ +
+
Clicked My tab, show My information
@@ -108,9 +91,8 @@ import { Component } from '@angular/core';
-
+ - @@ -124,7 +106,7 @@ import { Component } from '@angular/core'; (click)="changePosition($event)"> Click to change tab-bar position top/bottom - Click to switch fullscreen @@ -153,6 +135,7 @@ export class DemoTabBarBasicComponent { } else { this.selectedIndex++; } + console.log('selectedIndex: ', this.selectedIndex); } showFullScreen(event) { @@ -173,7 +156,8 @@ export class DemoTabBarBasicComponent { this.topFlag = !this.topFlag; } - onPress(event) { - console.log('event: ', event); + tabBarTabOnPress(pressParam: any) { + console.log('onPress Params: ', pressParam); + this.selectedIndex = pressParam.index; } } diff --git a/components/tab-bar/doc/index.en-US.md b/components/tab-bar/doc/index.en-US.md index 9a6b6839..3fb04b14 100644 --- a/components/tab-bar/doc/index.en-US.md +++ b/components/tab-bar/doc/index.en-US.md @@ -23,14 +23,16 @@ Properties | Descrition | Type | Default | unselectedTintColor | unselected's font color | String | '#888' | | hidden | whether it is hidden | Boolean | false | | tabBarPosition | tabbar position | 'top'\|'bottom' | 'bottom' | +| prerenderingSiblingsNumber| pre-render nearby sibling, -1: render all the siblings, 0: render current page | number | 1 | +| onPress | on press the bar | (index: number, title: string, key: string) => void | false | -### TabBar.Item +### TabBarItem Properties | Descrition | Type | Default -----------|------------|------|-------- | badge | badge number | Number \ String | 无 | | dot | show red dot on right-top(invalid when set badge number) | Boolean | false | -| onPress | on press the bar | (title: string, key: string) => void | false | + | icon | the default icon | ref demo | | | selectedIcon | the icon of selected | ref demo | | | title | title | String | | diff --git a/components/tab-bar/doc/index.zh-CN.md b/components/tab-bar/doc/index.zh-CN.md index 9c88a8fe..58590a17 100644 --- a/components/tab-bar/doc/index.zh-CN.md +++ b/components/tab-bar/doc/index.zh-CN.md @@ -24,14 +24,15 @@ subtitle: 标签栏 | unselectedTintColor | 未选中的字体颜色 | String | '#888' | | hidden | 是否隐藏 | Boolean | false | | tabBarPosition | tabbar 位置 | 'top'\|'bottom' | 'bottom' | +| prerenderingSiblingsNumber| 预加载相邻的tab内容, -1: 加载所有的tab内容, 0: 仅加载当前tab内容 | number | 1 | +| onPress | bar 点击触发 | (index: number, title: string, key: string) => void | false | -### TabBar.Item +### TabBarItem 属性 | 说明 | 类型 | 默认值 ----|-----|------|------ | badge | 徽标数 | Number \ String | 无 | | dot | 是否在右上角显示小红点(在设置badge的情况下失效) | Boolean | false | -| onPress | bar 点击触发 | (title: string, key: string) => void | false | | icon | 默认展示图片 | 见 demo | | | selectedIcon | 选中后的展示图片 | 见 demo | | | title | 标题文字 | String | | diff --git a/components/tab-bar/public-api.ts b/components/tab-bar/public-api.ts index 5fcaee84..8d3c7d6e 100644 --- a/components/tab-bar/public-api.ts +++ b/components/tab-bar/public-api.ts @@ -1,3 +1,3 @@ export * from './tab-bar.module'; export * from './tab-bar.component'; -export * from './tab-bar-tab.component'; \ No newline at end of file +export * from './tab-bar-item.component'; \ No newline at end of file diff --git a/components/tab-bar/tab-bar-item.component.html b/components/tab-bar/tab-bar-item.component.html new file mode 100644 index 00000000..c52e77fb --- /dev/null +++ b/components/tab-bar/tab-bar-item.component.html @@ -0,0 +1,32 @@ + + + + + +
+ + + + + + + + + + + + +
+

+ {{title}} +

+
+ + + + + + + {{title}} + \ No newline at end of file diff --git a/components/tab-bar/tab-bar-item.component.ts b/components/tab-bar/tab-bar-item.component.ts new file mode 100644 index 00000000..9ec6dcea --- /dev/null +++ b/components/tab-bar/tab-bar-item.component.ts @@ -0,0 +1,42 @@ +import { + Component, + Input, + ViewChild, + TemplateRef +} from '@angular/core'; +import { TabPane } from '../tabs/tab-pane.component'; + +@Component({ + selector: 'TabBarItem, nzm-tab-bar-item', + templateUrl: './tab-bar-item.component.html' +}) +export class TabBarItem extends TabPane { + prefixCls: string = 'am-tab-bar-tab'; + selected: boolean = false; + tintColor: string = '#108ee9'; + unselectedTintColor: string = '#888'; + + @ViewChild('tabBarTab') + tabBarTab: TemplateRef; + + @Input() + key: string = ''; + @Input() + title: string = ''; + @Input() + dot: boolean = false; + @Input() + badge: number | string = null; + @Input() + icon: string | TemplateRef = null; + @Input() + selectedIcon: string | TemplateRef = null; + + constructor() { + super(); + } + + isTemplateRef(value) { + return value instanceof TemplateRef; + } +} diff --git a/components/tab-bar/tab-bar-tab.component.html b/components/tab-bar/tab-bar-tab.component.html deleted file mode 100644 index 3fc0cfe0..00000000 --- a/components/tab-bar/tab-bar-tab.component.html +++ /dev/null @@ -1,24 +0,0 @@ -
- - - - - - - - - - - - -
-

- {{title}} -

- - - - - - {{title}} - \ No newline at end of file diff --git a/components/tab-bar/tab-bar-tab.component.ts b/components/tab-bar/tab-bar-tab.component.ts deleted file mode 100644 index 03c8b44e..00000000 --- a/components/tab-bar/tab-bar-tab.component.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { - Component, - AfterContentInit, - HostBinding, - EventEmitter, - Input, - Output, - TemplateRef, - Optional -} from '@angular/core'; -import { TabPane } from '../tabs/tab-pane.component'; - -export interface OnPressEvent { - title: string; - key: string; -} - -@Component({ - selector: 'TabBarTab, nzm-tab-bar-tab', - templateUrl: './tab-bar-tab.component.html' -}) -export class TabBarTab implements AfterContentInit { - prefixCls: string = 'am-tab-bar-tab'; - selected: boolean = false; - tintColor: string = '#108ee9'; - unselectedTintColor: string = '#888'; - - @Input() - key: string = ''; - @Input() - title: string = ''; - @Input() - dot: boolean = false; - @Input() - badge: number | string = null; - @Input() - icon: string | TemplateRef = null; - @Input() - selectedIcon: string | TemplateRef = null; - @Output() - onPress: EventEmitter = new EventEmitter(); - - @HostBinding('class.am-tab-bar-tab') - tabBarTab: boolean = true; - - constructor(@Optional() public tabPane: TabPane) {} - - isTemplateRef(value) { - return value instanceof TemplateRef; - } - - ngAfterContentInit() { - if (this.tabPane.active) { - this.selected = true; - } else { - this.selected = false; - } - this.tintColor = this.tabPane.tintColor; - this.unselectedTintColor = this.tabPane.unselectedTintColor; - } -} diff --git a/components/tab-bar/tab-bar.component.html b/components/tab-bar/tab-bar.component.html index 1da3aa02..6bae9813 100644 --- a/components/tab-bar/tab-bar.component.html +++ b/components/tab-bar/tab-bar.component.html @@ -5,18 +5,17 @@ [tabDirection]="'horizontal'" [tabBarPosition]='tabBarPosition' [renderTabBar]="TabBarBar" - [tabPanesComponent]="tabPanes" -> - - + [prerenderingSiblingsNumber]="prerenderingSiblingsNumber" + [tabPanesContent]="tabBarItems" +>
-
- +
+
diff --git a/components/tab-bar/tab-bar.component.spec.ts b/components/tab-bar/tab-bar.component.spec.ts index c6a2ee2e..d153da80 100644 --- a/components/tab-bar/tab-bar.component.spec.ts +++ b/components/tab-bar/tab-bar.component.spec.ts @@ -33,164 +33,222 @@ describe('tabbar', () => { component.hidden = true; fixture.detectChanges(); expect(tabBarEle.nativeElement.querySelector('.am-tab-bar-bar').classList).toContain( - 'am-tab-bar-bar-hidden-' + component.tabBarPosition + 'am-tab-bar-bar-hidden-' + (component.topFlag ? 'top' : 'bottom') ); }); it('tabBarPosition work', () => { expect(tabBarEle.nativeElement.querySelector('.am-tabs').classList).toContain('am-tabs-bottom'); - component.tabBarPosition = 'top'; + component.topFlag = true; fixture.detectChanges(); expect(tabBarEle.nativeElement.querySelector('.am-tabs').classList).toContain('am-tabs-top'); }); it('badge work', () => { - expect(tabBarEle.nativeElement.querySelectorAll('tabbartab')[0].querySelector('badge')).toBeTruthy(); + expect(tabBarEle.nativeElement.querySelectorAll('.am-tab-bar-tab')[0].querySelector('badge')).toBeTruthy(); }); it('dot work', () => { - expect(tabBarEle.nativeElement.querySelectorAll('tabbartab')[2].querySelector('.am-badge-dot')).toBeTruthy(); + expect(tabBarEle.nativeElement.querySelectorAll('.am-tab-bar-tab')[2].querySelector('.am-badge-dot')).toBeTruthy(); }); it('title work', () => { expect( - tabBarEle.nativeElement.querySelectorAll('tabbartab')[2].querySelector('.am-tab-bar-tab-title').innerText - ).toContain('title3'); + tabBarEle.nativeElement.querySelectorAll('.am-tab-bar-tab')[2].querySelector('.am-tab-bar-tab-title').innerText + ).toContain('Friend'); }); it('barTintColor work', () => { - expect(tabBarEle.nativeElement.querySelector('.am-tab-bar-bar').style.backgroundColor).toContain('rgb(0, 0, 0)'); + expect(tabBarEle.nativeElement.querySelector('.am-tab-bar-bar').style.backgroundColor).toContain('white'); }); it('onPress work', fakeAsync(() => { - component.onPress = jasmine.createSpy('onPress is callback'); - const tab = tabBarEle.nativeElement.querySelectorAll('tabbartab')[component.activeTab - 1]; + component.selectedIndex = 1; + fixture.detectChanges(); + component.press = jasmine.createSpy('onPress is callback'); + const tab = tabBarEle.nativeElement.querySelectorAll('.am-tab-bar-tab')[component.selectedIndex - 1]; tick(100); fixture.detectChanges(); tab.click(); fixture.detectChanges(); - expect(component.onPress).toHaveBeenCalledTimes(1); - expect( - tabBarEle.nativeElement.querySelectorAll('tabbartab')[1].querySelector('.am-tab-bar-tab-title').style.color - ).toContain('rgb(136, 136, 136)'); - expect( - tabBarEle.nativeElement.querySelectorAll('tabbartab')[0].querySelector('.am-tab-bar-tab-title').style.color - ).toContain('rgb(17, 17, 17)'); + expect(component.press).toHaveBeenCalledTimes(1); })); it('unselectedTintColor work', () => { expect( tabBarEle.nativeElement - .querySelectorAll('tabbartab') - [component.activeTab + 1].querySelector('.am-tab-bar-tab-title').style.color + .querySelectorAll('.am-tab-bar-tab') + [component.selectedIndex + 1].querySelector('.am-tab-bar-tab-title').style.color ).toContain('rgb(136, 136, 136)'); }); it('tintColor work', () => { expect( - tabBarEle.nativeElement.querySelectorAll('tabbartab')[component.activeTab].querySelector('.am-tab-bar-tab-title') + tabBarEle.nativeElement.querySelectorAll('.am-tab-bar-tab')[component.selectedIndex].querySelector('.am-tab-bar-tab-title') .style.color - ).toContain('rgb(17, 17, 17)'); + ).toContain('rgb(16, 142, 233)'); }); }); @Component({ selector: 'test-tabbar', template: ` - - - - - -
-
- -
-
-
+ [ngStyle]="tabbarStyle" + [activeTab]="selectedIndex" + [hidden]="hidden" + [tabBarPosition]="topFlag ? 'top' : 'bottom'" + (onPress)="press($event)" + > + + +
+
-
content1
-
- - - - -
-
- -
-
-
+ +
+
-
content2
-
- +
Clicked Life tab, show Life information
+ + + +
+ + - - - -
-
- -
-
-
+ +
+
-
content3
- - - - - -
-
- -
-
-
+ +
+
+
+
+
Clicked Koubei tab, show Koubei information
+ + + +
+
+ + +
+
+
+ +
+
-
content4
- +
+
Clicked Friend tab, show Friend information
+ + + +
+
+ + +
+
+
+ +
+
+
+
+
Clicked My tab, show My information
+ + + +
+
+ + + Click to next tab-bar + + + Click to show/hide tab-bar + + + Click to change tab-bar position top/bottom + + + Click to switch fullscreen + + ` }) export class TestTabBarComponent { hidden: boolean = false; - tabBarPosition: string = 'bottom'; - activeTab: number = 1; - unselectedTintColor: string = '#888'; - tintColor: string = '#111'; + fullScreen: boolean = false; + topFlag: boolean = false; + tintColor: string = 'rgb(16, 142, 233)'; + unselectedTintColor: string = 'rgb(136, 136, 136)'; + tabbarStyle: object = { height: '400px' }; + selectedIndex: number = 1; + + showTabBar(event) { + event.preventDefault(); + this.hidden = !this.hidden; + } - constructor() {} + showNextTabBar(event) { + event.preventDefault(); + const PANE_COUNT = 4; + if (this.selectedIndex == PANE_COUNT - 1) { + this.selectedIndex = 0; + } else { + this.selectedIndex++; + } + console.log('selectedIndex: ', this.selectedIndex); + } - onPress() { - console.log('onPress'); + showFullScreen(event) { + event.preventDefault(); + this.fullScreen = !this.fullScreen; + this.tabbarStyle = this.fullScreen + ? { + position: 'fixed', + height: '100%', + width: '100%', + top: 0 + } + : { height: '400px' }; } + + changePosition(event) { + event.preventDefault(); + this.topFlag = !this.topFlag; + } + + press(pressParam: any) { + console.log('onPress Params: ', pressParam); + this.selectedIndex = pressParam.index; + } + } diff --git a/components/tab-bar/tab-bar.component.ts b/components/tab-bar/tab-bar.component.ts index f98f71b9..0fc62be9 100644 --- a/components/tab-bar/tab-bar.component.ts +++ b/components/tab-bar/tab-bar.component.ts @@ -1,8 +1,12 @@ -import { Component, AfterContentInit, Input, ContentChildren, QueryList, HostBinding } from '@angular/core'; -import { TabPane } from '../tabs/tab-pane.component'; -import { TabBarTab } from './tab-bar-tab.component'; +import { Component, AfterContentInit, Input, Output, ContentChildren, QueryList, HostBinding, EventEmitter } from '@angular/core'; +import { TabBarItem } from './tab-bar-item.component'; export type TabBarTabPositionType = 'top' | 'bottom'; +export interface OnPressEvent { + index: number; + title: string; + key: string; +} @Component({ selector: 'TabBar, nzm-tab-bar', @@ -14,21 +18,21 @@ export class TabBar implements AfterContentInit { private _tintColor: string = '#108ee9'; private _unselectedTintColor: string = '#888'; - @ContentChildren(TabPane, { descendants: true }) - tabPanes: QueryList; - @ContentChildren(TabBarTab, { descendants: true }) - tabBarTabs: QueryList; + @ContentChildren(TabBarItem, { descendants: true }) + tabBarItems: QueryList; @Input() hidden: boolean = false; @Input() + prerenderingSiblingsNumber: number = 1; + @Input() get activeTab(): number { return this._activeTab; } set activeTab(tab: number) { this._activeTab = tab; - if (this.tabPanes && this.tabPanes.length > 0) { - this.selectTabPane(tab); + if (this.tabBarItems && this.tabBarItems.length > 0) { + this.selectTabBarItem(tab); } } @Input() @@ -41,9 +45,9 @@ export class TabBar implements AfterContentInit { } set tintColor(color: string) { this._tintColor = color; - if (this.tabBarTabs && this.tabBarTabs.length > 0) { - this.tabBarTabs.forEach((tabBarTab: TabBarTab) => { - tabBarTab.tintColor = this._tintColor; + if (this.tabBarItems && this.tabBarItems.length > 0) { + this.tabBarItems.forEach((tabBarItem: TabBarItem) => { + tabBarItem.tintColor = this._tintColor; }); } } @@ -53,50 +57,35 @@ export class TabBar implements AfterContentInit { } set unselectedTintColor(color: string) { this._unselectedTintColor = color; - if (this.tabBarTabs && this.tabBarTabs.length > 0) { - this.tabBarTabs.forEach((tabBarTab: TabBarTab) => { - tabBarTab.unselectedTintColor = this._unselectedTintColor; + if (this.tabBarItems && this.tabBarItems.length > 0) { + this.tabBarItems.forEach((tabBarItem: TabBarItem) => { + tabBarItem.unselectedTintColor = this._unselectedTintColor; }); } } + @Output() + onPress: EventEmitter = new EventEmitter(); @HostBinding('class.am-tab-bar') tabBar: boolean = true; - constructor() {} + constructor() { } - selectTabPane(index: number) { - this.tabPanes.forEach((tabPane: TabPane, indexKey: number) => { - if (index < indexKey) { - tabPane.position = 'right-without-animation'; - } else if (index > indexKey) { - tabPane.position = 'left-without-animation'; - } else { - tabPane.position = 'center-without-animation'; - } - if (index !== indexKey) { - tabPane.active = false; - } else { - tabPane.active = true; - } - }); - if (this.tabBarTabs && this.tabBarTabs.length > 0) { - this.tabBarTabs.forEach((tabBarTab: TabBarTab) => { - tabBarTab.selected = false; - }); - this.tabBarTabs.toArray()[index].selected = true; - this.tabBarTabs.toArray()[index].onPress.emit({ - title: this.tabBarTabs.toArray()[index].title, - key: this.tabBarTabs.toArray()[index].key + selectTabBarItem(index: number) { + if (this.tabBarItems && this.tabBarItems.length > 0) { + this.tabBarItems.forEach((tabBarItem: TabBarItem) => { + tabBarItem.selected = false; }); + this.tabBarItems.toArray()[index].selected = true; } } + tabBarTabOnPress(pressParam: OnPressEvent) { + this.onPress.emit(pressParam); + } + ngAfterContentInit() { - this.selectTabPane(this.activeTab); - this.tabPanes.forEach((tabPane: TabPane) => { - tabPane.tintColor = this.tintColor; - tabPane.unselectedTintColor = this.unselectedTintColor; - }); + this.selectTabBarItem(this.activeTab); } + } diff --git a/components/tab-bar/tab-bar.module.ts b/components/tab-bar/tab-bar.module.ts index 4031e04c..b52d75b0 100644 --- a/components/tab-bar/tab-bar.module.ts +++ b/components/tab-bar/tab-bar.module.ts @@ -3,14 +3,14 @@ import { CommonModule } from '@angular/common'; import { TabsModule } from '../tabs/tabs.module'; import { TabBar } from './tab-bar.component'; import { BadgeModule } from '../badge/badge.module'; -import { TabBarTab } from './tab-bar-tab.component'; +import { TabBarItem } from './tab-bar-item.component'; export { TabPane } from '../tabs/tabs.module'; @NgModule({ imports: [CommonModule, TabsModule, BadgeModule], - exports: [TabBar, TabBarTab], - declarations: [TabBar, TabBarTab], + exports: [TabBar, TabBarItem], + declarations: [TabBar, TabBarItem], providers: [] }) -export class TabBarModule {} +export class TabBarModule { } diff --git a/components/tabs/default-tab-bar.component.ts b/components/tabs/default-tab-bar.component.ts index c009caa8..17787d0d 100644 --- a/components/tabs/default-tab-bar.component.ts +++ b/components/tabs/default-tab-bar.component.ts @@ -6,8 +6,9 @@ import { ViewChild, ElementRef, HostBinding, - AfterViewInit, - ContentChildren + AfterContentInit, + ContentChildren, + ChangeDetectorRef } from '@angular/core'; import { TabBarPositionType } from './PropsType'; @@ -16,7 +17,7 @@ import { TabBarPositionType } from './PropsType'; selector: 'DefaultTabBar, nzm-default-tab-bar', templateUrl: './default-tab-bar.component.html' }) -export class DefaultTabBarComponent implements AfterViewInit { +export class DefaultTabBar implements AfterContentInit { prefixCls: string = 'am-tabs-default-bar'; inkBarStyle: object = {}; tabsBarStyle: object = {}; @@ -45,6 +46,8 @@ export class DefaultTabBarComponent implements AfterViewInit { @Input() tabBarBackgroundColor: string = '#FFF'; @Input() + tabTitleSize: number = 0; + @Input() tabBarPosition: TabBarPositionType = 'top'; @Input() get activeTab(): number { @@ -63,58 +66,87 @@ export class DefaultTabBarComponent implements AfterViewInit { @HostBinding('class.am-tabs-tab-bar-wrap') tabBarWrap = true; - constructor(private _renderer: Renderer2) { } + constructor(private _renderer: Renderer2, private _ref: ChangeDetectorRef) { } onTouchStart(event) { - if ('top' === this.tabBarPosition || 'bottom' === this.tabBarPosition) { - this._startPosition = event && event.changedTouches && event.changedTouches[0] && event.changedTouches[0].clientX; - } else { - this._startPosition = event && event.changedTouches && event.changedTouches[0] && event.changedTouches[0].clientY; + if ((this.tabTitleSize > 0 && + (this.tabTitleSize * this.tabTitles.length > + (('top' === this.tabBarPosition || 'bottom' === this.tabBarPosition) ? + this.tabsBarSwipe.nativeElement.offsetWidth : + this.tabsBarSwipe.nativeElement.offsetHeight + ) + ) + ) || (this.tabTitleSize <= 0 && this.page < this.tabTitles.length) + ) { + if ('top' === this.tabBarPosition || 'bottom' === this.tabBarPosition) { + this._startPosition = event && event.changedTouches && event.changedTouches[0] && event.changedTouches[0].clientX; + } else { + this._startPosition = event && event.changedTouches && event.changedTouches[0] && event.changedTouches[0].clientY; + } } } onTouchMove(event) { event.preventDefault(); event.stopPropagation(); - if ('top' === this.tabBarPosition || 'bottom' === this.tabBarPosition) { - this.setTabBarNavSwipingPosition( - event.changedTouches[0].clientX - this._startPosition, - this.tabTitles.first.nativeElement.offsetWidth, - this.tabsBarSwipe.nativeElement.offsetWidth - ); - this.tabsBarStyle = { - transition: '0ms', - transform: 'translate3d(' + this.tabBarNavSwipingPosition + 'px, 0px, 0px)', - webkitTransform: 'translate3d(' + this.tabBarNavSwipingPosition + 'px, 0px, 0px)' - }; - } else { - this.setTabBarNavSwipingPosition( - event.changedTouches[0].clientY - this._startPosition, - this.tabTitles.first.nativeElement.offsetHeight, - this.tabsBarSwipe.nativeElement.offsetHeight - ); - this.tabsBarStyle = { - transition: '0ms', - transform: 'translate3d(0, ' + this.tabBarNavSwipingPosition + 'px, 0px)', - webkitTransform: 'translate3d(0, ' + this.tabBarNavSwipingPosition + 'px, 0px)' - }; + if ((this.tabTitleSize > 0 && + (this.tabTitleSize * this.tabTitles.length > + (('top' === this.tabBarPosition || 'bottom' === this.tabBarPosition) ? + this.tabsBarSwipe.nativeElement.offsetWidth : + this.tabsBarSwipe.nativeElement.offsetHeight + ) + ) + ) || (this.tabTitleSize <= 0 && this.page < this.tabTitles.length) + ) { + if ('top' === this.tabBarPosition || 'bottom' === this.tabBarPosition) { + this.setTabBarNavSwipingPosition( + event.changedTouches[0].clientX - this._startPosition, + this.tabTitles.first.nativeElement.offsetWidth, + this.tabsBarSwipe.nativeElement.offsetWidth + ); + this.tabsBarStyle = { + transition: '0ms', + transform: 'translate3d(' + this.tabBarNavSwipingPosition + 'px, 0px, 0px)', + webkitTransform: 'translate3d(' + this.tabBarNavSwipingPosition + 'px, 0px, 0px)' + }; + } else { + this.setTabBarNavSwipingPosition( + event.changedTouches[0].clientY - this._startPosition, + this.tabTitles.first.nativeElement.offsetHeight, + this.tabsBarSwipe.nativeElement.offsetHeight + ); + this.tabsBarStyle = { + transition: '0ms', + transform: 'translate3d(0, ' + this.tabBarNavSwipingPosition + 'px, 0px)', + webkitTransform: 'translate3d(0, ' + this.tabBarNavSwipingPosition + 'px, 0px)' + }; + } } + } onTouchEnd() { - this.tabBarNavSwipedPosition = this.tabBarNavSwipingPosition; + if ((this.tabTitleSize > 0 && + (this.tabTitleSize * this.tabTitles.length > + (('top' === this.tabBarPosition || 'bottom' === this.tabBarPosition) ? + this.tabsBarSwipe.nativeElement.offsetWidth : + this.tabsBarSwipe.nativeElement.offsetHeight + ) + ) + ) || (this.tabTitleSize <= 0 && this.page < this.tabTitles.length) + ) { + this.tabBarNavSwipedPosition = this.tabBarNavSwipingPosition; + } } onContentChange() { this.setTabsStyle(); - this.setInkBarStatus(this.activeTab); + this.setInkBarStatus(this.selectedKey); } - ngAfterViewInit() { + ngAfterContentInit() { this.setTabsStyle(); - if (this.selectedKey > 0) { - this.setTabBarStyleCenter(); - } + this.setTabBarStyleCenter(); this.setInkBarStatus(this.selectedKey); } @@ -125,7 +157,7 @@ export class DefaultTabBarComponent implements AfterViewInit { this._renderer.setStyle( tabTitle.nativeElement, 'width', - this.getTabSize(this.page, this.tabTitles.length) + '%' + this.tabTitleSize > 0 ? (this.tabTitleSize + 'px') : (this.getTabSize(this.page, this.tabTitles.length) + '%') ); }); } else { @@ -133,7 +165,7 @@ export class DefaultTabBarComponent implements AfterViewInit { this._renderer.setStyle( tabTitle.nativeElement, 'height', - this.getTabSize(this.page, this.tabTitles.length) + '%' + this.tabTitleSize > 0 ? (this.tabTitleSize + 'px') : this.getTabSize(this.page, this.tabTitles.length) + '%' ); }); } @@ -142,13 +174,19 @@ export class DefaultTabBarComponent implements AfterViewInit { private setTabBarStyleCenter() { if ('top' === this.tabBarPosition || 'bottom' === this.tabBarPosition) { - this.setTabBarNavSwipedPosition(this.tabTitles.first.nativeElement.offsetWidth, this.tabsBarSwipe.nativeElement.offsetWidth); + this.setTabBarNavSwipedPosition(this.tabTitleSize > 0 ? this.tabTitleSize : + (this.tabsBarSwipe.nativeElement.offsetWidth / Math.min(this.tabTitles.length, this.page)), + this.tabsBarSwipe.nativeElement.offsetWidth + ); this.tabsBarStyle = { transform: 'translate3d(' + this.tabBarNavSwipedPosition + 'px, 0px, 0px)', webkitTransform: 'translate3d(' + this.tabBarNavSwipedPosition + ', 0px, 0px)' }; } else { - this.setTabBarNavSwipedPosition(this.tabTitles.first.nativeElement.offsetHeight, this.tabsBarSwipe.nativeElement.offsetHeight); + this.setTabBarNavSwipedPosition(this.tabTitleSize > 0 ? this.tabTitleSize : + (this.tabsBarSwipe.nativeElement.offsetHeight / Math.min(this.tabTitles.length, this.page)), + this.tabsBarSwipe.nativeElement.offsetHeight + ); this.tabsBarStyle = { transform: 'translate3d(0, ' + this.tabBarNavSwipedPosition + 'px, 0px)', webkitTransform: 'translate3d(0, ' + this.tabBarNavSwipedPosition + 'px, 0px)' @@ -163,7 +201,9 @@ export class DefaultTabBarComponent implements AfterViewInit { this.inkBarLength = this.tabTitles.toArray()[key].nativeElement.style.width; this.inkBarStyle = { width: this.inkBarLength, - left: (this.selectedKey * 100) / this.page + '%' + left: this.tabTitleSize > 0 ? + (this.selectedKey * this.tabTitleSize + 'px') : + ((this.selectedKey * 100) / Math.min(this.tabTitles.length, this.page) + '%') }; Object.assign(this.inkBarStyle, this.tabBarUnderlineStyle); } else { @@ -171,19 +211,21 @@ export class DefaultTabBarComponent implements AfterViewInit { this.inkBarLength = this.tabTitles.toArray()[key].nativeElement.style.height; this.inkBarStyle = { height: this.inkBarLength, - top: (this.selectedKey * 100) / this.page + '%' + top: this.tabTitleSize > 0 ? + (this.selectedKey * this.tabTitleSize + 'px') : + ((this.selectedKey * 100) / Math.min(this.tabTitles.length, this.page) + '%') }; Object.assign(this.inkBarStyle, this.tabBarUnderlineStyle); } + this._ref.detectChanges(); } } private setTabBarNavSwipingPosition(swipingDistance: number, swipingItemLength: number, viewportLength: number) { if (this.tabBarNavSwipedPosition + swipingDistance > 0) { this.tabBarNavSwipingPosition = 0; - } else if ( - this.tabBarNavSwipedPosition + swipingDistance < - viewportLength - swipingItemLength * this.tabTitles.length + } else if ((this.tabBarNavSwipedPosition + swipingDistance) < + (viewportLength - swipingItemLength * this.tabTitles.length) ) { this.tabBarNavSwipingPosition = viewportLength - swipingItemLength * this.tabTitles.length; this.showNext = false; @@ -199,22 +241,27 @@ export class DefaultTabBarComponent implements AfterViewInit { } private setTabBarNavSwipedPosition(swipingItemLength: number, viewportLength: number) { - if ( - this.selectedKey >= Math.floor(this.page / 2) && - this.selectedKey <= this.tabTitles.length - 1 - Math.floor(this.page / 2) - ) { - this.tabBarNavSwipedPosition = viewportLength / 2 - swipingItemLength * (this.selectedKey + 1 / 2); - } else if (this.selectedKey < Math.floor(this.page / 2)) { - this.tabBarNavSwipedPosition = 0; - } else { - this.tabBarNavSwipedPosition = viewportLength - swipingItemLength * this.tabTitles.length; + if (this.selectedKey * swipingItemLength + this.tabBarNavSwipedPosition <= 0) { + if (0 === this.selectedKey) { + this.tabBarNavSwipedPosition = 0; + } else { + this.tabBarNavSwipedPosition = (1 - this.selectedKey) * swipingItemLength; + } + } else if ((this.selectedKey + 1) * swipingItemLength >= + (viewportLength - this.tabBarNavSwipedPosition) + ) { + if (this.tabTitles.length - 1 === this.selectedKey) { + this.tabBarNavSwipedPosition = viewportLength - (this.selectedKey + 1) * swipingItemLength; + } else { + this.tabBarNavSwipedPosition = viewportLength - (this.selectedKey + 2) * swipingItemLength; + } } if (this.tabBarNavSwipedPosition < 0) { this.showPrev = true; } else { this.showPrev = false; } - if (this.tabBarNavSwipedPosition + swipingItemLength * this.tabTitles.length - viewportLength > 0) { + if ((this.tabBarNavSwipedPosition + swipingItemLength * this.tabTitles.length - viewportLength) > 0) { this.showNext = true; } else { this.showNext = false; diff --git a/components/tabs/demo/basic.md b/components/tabs/demo/basic.md index 3d2b80c3..c8bdef2c 100755 --- a/components/tabs/demo/basic.md +++ b/components/tabs/demo/basic.md @@ -2,7 +2,7 @@ order: 0 title: zh-CN: 基本用法 - en-US: Basic usage + en-US: Basic Usage --- ## zh-CN diff --git a/components/tabs/demo/basic.ts b/components/tabs/demo/basic.ts index 85a2828d..174c5591 100644 --- a/components/tabs/demo/basic.ts +++ b/components/tabs/demo/basic.ts @@ -11,8 +11,7 @@ import { Component } from '@angular/core'; (onChange)="onChange($event)" (onTabClick)="onTabClick($event)" > - @@ -23,8 +22,7 @@ import { Component } from '@angular/core'; Content of first tab - @@ -35,9 +33,7 @@ import { Component } from '@angular/core'; Content of second tab - +
Third Tab
@@ -55,24 +51,18 @@ import { Component } from '@angular/core'; (onChange)="onChange($event)" (onTabClick)="onTabClick($event)" > - -
+ +
Content of first tab
- -
+ +
Content of second tab
- -
+ +
Content of third tab
@@ -88,7 +78,7 @@ import { Component } from '@angular/core'; }) export class DemoTabsBasicComponent { flag = true; - index = 0; + index = 1; onChange(item) { console.log('onChange', item); diff --git a/components/tabs/demo/dynamic.md b/components/tabs/demo/dynamic.md new file mode 100644 index 00000000..6580b7b8 --- /dev/null +++ b/components/tabs/demo/dynamic.md @@ -0,0 +1,13 @@ +--- +order: 4 +title: + zh-CN: 动态创建 + en-US: 'Dynamic TabPane' +--- +## zh-CN + +TabPane被动态创建 + +## en-US + +TabPanes are created dynamicly \ No newline at end of file diff --git a/components/tabs/demo/dynamic.ts b/components/tabs/demo/dynamic.ts new file mode 100644 index 00000000..6a1fe860 --- /dev/null +++ b/components/tabs/demo/dynamic.ts @@ -0,0 +1,69 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'demo-tabs-dynamic', + template: ` + + +
+
{{tabListItem.content}}
+
Add Pane +
+
+
+
+ `, + styles: [ + ` + /deep/ .am-badge { + text-align: right; + } + ` + ] +}) +export class DemoTabsDynamicComponent { + flag = true; + activeTabIndex = 0; + + tabList: any[] = [{ + title: '1st Tab', + content: '1st Tab Content' + }, { + title: '2nd Tab', + content: '2nd Tab Content' + }, { + title: '3rd Tab', + content: '3rd Tab Content' + }]; + + onChange(item) { + console.log('onChange', item); + } + + onTabClick(item) { + console.log('onTabClick', item); + } + + selectCard(e) { + console.log(' ', JSON.stringify(e)); + } + + changeIndex() { + this.activeTabIndex = 0; + } + + onClick() { + this.tabList.push({ + title: '' + (this.tabList.length + 1) + 'th Tab', + content: '' + (this.tabList.length + 1) + 'th Tab Content' + }); + } +} diff --git a/components/tabs/demo/fixedheight.ts b/components/tabs/demo/fixedheight.ts index cb5b8d63..c6fc1cca 100644 --- a/components/tabs/demo/fixedheight.ts +++ b/components/tabs/demo/fixedheight.ts @@ -9,24 +9,18 @@ import { Component } from '@angular/core'; (onChange)="onChange($event)" (onTabClick)="onTabClick($event)" > - -
+ +
Content of first tab
- -
+ +
Content of second tab
- -
+ +
Content of third tab
diff --git a/components/tabs/demo/fixedtabtitlesize.md b/components/tabs/demo/fixedtabtitlesize.md new file mode 100755 index 00000000..10946347 --- /dev/null +++ b/components/tabs/demo/fixedtabtitlesize.md @@ -0,0 +1,14 @@ +--- +order: 6 +title: + zh-CN: TabTitle固定尺寸 + en-US: fixed TabTitle Size +--- + +## zh-CN + +为TabTitle设置尺寸(单位:px)。 + +## en-US + +Set TabTitle Size(unit: px). \ No newline at end of file diff --git a/components/tabs/demo/fixedtabtitlesize.ts b/components/tabs/demo/fixedtabtitlesize.ts new file mode 100755 index 00000000..9406142c --- /dev/null +++ b/components/tabs/demo/fixedtabtitlesize.ts @@ -0,0 +1,92 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'demo-tabs-fixedtabtitlesize', + template: ` + + + + +
First Tab
+
+
+
+ Content of first tab +
+
+ + + +
Second Tab
+
+
+
+ Content of second tab +
+
+
+ + + +
+ Content of first tab +
+
+ +
+ Content of second tab +
+
+ +
+ Content of third tab +
+
+
+ `, + styles: [ + ` + /deep/ .am-badge { + text-align: right; + } + ` + ] +}) +export class DemoTabsFixedtabtitlesizeComponent { + flag = true; + index = 1; + + onChange(item) { + console.log('onChange', item); + } + + onTabClick(item) { + console.log('onTabClick', item); + } + + selectCard(e) { + console.log(' ', JSON.stringify(e)); + } + + changeIndex() { + this.index = 0; + } +} diff --git a/components/tabs/demo/multitabs.ts b/components/tabs/demo/multitabs.ts index b31ff60e..92f32f1c 100644 --- a/components/tabs/demo/multitabs.ts +++ b/components/tabs/demo/multitabs.ts @@ -3,55 +3,58 @@ import { Component } from '@angular/core'; @Component({ selector: 'demo-tabs-multitabs', template: ` - - -
Content of 1 tab
-
- - Content of 2 tab - - - Content of 3 tab - - - Content of 4 tab - - - Content of 5 tab - - - Content of 6 tab - - - Content of 7 tab - - - Content of 8 tab - - - Content of 9 tab - -
+ +
+ Content of 1st tab +
+
+ +
+ Content of 2nd tab +
+
+ +
+ Content of 3rd tab +
+
+ +
+ Content of 4th tab +
+
+ +
+ Content of 5th tab +
+
+ +
+ Content of 6th tab +
+
+ +
+ Content of 7th tab +
+
+ +
+ Content of 8th tab +
+
+ +
+ Content of 9th tab +
+
+ `, styles: [ ` diff --git a/components/tabs/demo/noanim.md b/components/tabs/demo/noanim.md index bce5a024..eae21efc 100644 --- a/components/tabs/demo/noanim.md +++ b/components/tabs/demo/noanim.md @@ -2,7 +2,7 @@ order: 2 title: zh-CN: 无动画 - en-US: 'No animation' + en-US: 'No Animation' --- ## zh-CN diff --git a/components/tabs/demo/noanim.ts b/components/tabs/demo/noanim.ts index 6843d947..93fb0560 100644 --- a/components/tabs/demo/noanim.ts +++ b/components/tabs/demo/noanim.ts @@ -10,24 +10,18 @@ import { Component } from '@angular/core'; (onChange)="onChange($event)" (onTabClick)="onTabClick($event)" > - -
+ +
Content of first tab
- -
+ +
Content of second tab
- -
+ +
Content of third tab
diff --git a/components/tabs/demo/vertical.ts b/components/tabs/demo/vertical.ts index 54eec81b..739f25dc 100644 --- a/components/tabs/demo/vertical.ts +++ b/components/tabs/demo/vertical.ts @@ -3,36 +3,30 @@ import { Component } from '@angular/core'; @Component({ selector: 'demo-tabs-vertical', template: ` - - -
- Content of first tab -
-
- -
- Content of second tab -
-
- -
- Content of third tab -
-
-
+ + +
+ Content of first tab +
+
+ +
+ Content of second tab +
+
+ +
+ Content of third tab +
+
+
`, styles: [ ` diff --git a/components/tabs/doc/index.en-US.md b/components/tabs/doc/index.en-US.md index 6016267b..df7257d6 100644 --- a/components/tabs/doc/index.en-US.md +++ b/components/tabs/doc/index.en-US.md @@ -28,6 +28,7 @@ animated | Whether to change tabs with animation | boolean | true | false onChange | Callback when tab is switched | (index: number) => void | | false onTabClick | on tab click | (index: number) => void | | false distanceToChangeTab | distance to change tab, width ratio | number | 0.3 | false +| prerenderingSiblingsNumber| pre-render nearby sibling, -1: render all the siblings, 0: render current page | number | 1 | tabDirection | tab paging direction | 'horizontal' \| 'vertical' | horizontal | false tabBarUnderlineStyle | style of the default tab bar's underline | object | | false tabBarBackgroundColor | color of the default tab bar's background | string | | false diff --git a/components/tabs/doc/index.zh-CN.md b/components/tabs/doc/index.zh-CN.md index 86dd6ce3..d3c6a370 100644 --- a/components/tabs/doc/index.zh-CN.md +++ b/components/tabs/doc/index.zh-CN.md @@ -28,6 +28,7 @@ animated | 是否开启切换动画 | boolean | true | false onChange | tab变化时触发 | (index: number) => void | | false onTabClick | tab 被点击的回调 | (index: number) => void | | false distanceToChangeTab | 滑动切换阈值(宽度比例) | number | 0.3 | false +| prerenderingSiblingsNumber| 预加载相邻的tab内容, -1: 加载所有的tab内容, 0: 仅加载当前tab内容 | number | 1 | tabDirection | Tab方向 | 'horizontal' \| 'vertical' | horizontal | false tabBarUnderlineStyle | tabBar下划线样式 | object | | false tabBarBackgroundColor | tabBar背景色 | string | | false diff --git a/components/tabs/public-api.ts b/components/tabs/public-api.ts index 3df8f93a..38fb60c7 100644 --- a/components/tabs/public-api.ts +++ b/components/tabs/public-api.ts @@ -1,4 +1,5 @@ export * from './tabs.module'; export * from './tabs.component'; export * from './tab-pane.component'; +export * from './tab-pane-body.component'; export * from './default-tab-bar.component'; diff --git a/components/tabs/style/addon.less b/components/tabs/style/addon.less index ec958dad..31014fe7 100644 --- a/components/tabs/style/addon.less +++ b/components/tabs/style/addon.less @@ -6,22 +6,53 @@ .@{tabs-prefix-cls} { &-pane-wrap { - position: absolute; - top: 0; - left: 0; - } - &-pane-wrap:first-child { - position: relative; + touch-action: pan-x pan-y; } } -.@{tabs-default-bar-prefix-cls}-top, -.@{tabs-default-bar-prefix-cls}-bottom { - .@{tabs-default-bar-prefix-cls}-underline { - left: 0; +.@{tabs-default-bar-prefix-cls} { + &-top, + &-bottom { + &-underline { + left: 0; + } + } + + &-top { + border-bottom: 1px solid rgba(217, 217, 217, 0.5); + .@{tabs-default-bar-prefix-cls} { + &-tab::after { + height: 0 !important; + } + } + } + &-bottom { + border-top: 1px solid rgba(217, 217, 217, 0.5); + .@{tabs-default-bar-prefix-cls} { + &-tab::before { + height: 0 !important; + } + } + } + &-left { + border-right: 1px solid rgba(217, 217, 217, 0.5); + .@{tabs-default-bar-prefix-cls} { + &-tab::after { + width: 0 !important; + } + } + } + &-right { + border-left: 1px solid rgba(217, 217, 217, 0.5); + .@{tabs-default-bar-prefix-cls} { + &-tab::before { + width: 0 !important; + } + } } } + .@{tabs-default-bar-prefix-cls}-left, .@{tabs-default-bar-prefix-cls}-right { .@{tabs-default-bar-prefix-cls}-underline { diff --git a/components/tabs/style/index.less b/components/tabs/style/index.less index 2babed46..6306a6f5 100644 --- a/components/tabs/style/index.less +++ b/components/tabs/style/index.less @@ -39,6 +39,7 @@ flex: 1; width: 100%; height: 100%; + min-height: 0; &-animated { transition: transform @effect-duration @easing-in-out, left @effect-duration @easing-in-out, top @effect-duration @easing-in-out; diff --git a/components/tabs/tab-pane-body.component.html b/components/tabs/tab-pane-body.component.html new file mode 100644 index 00000000..c9cf5dee --- /dev/null +++ b/components/tabs/tab-pane-body.component.html @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/components/tabs/tab-pane-body.component.ts b/components/tabs/tab-pane-body.component.ts new file mode 100644 index 00000000..b47b8597 --- /dev/null +++ b/components/tabs/tab-pane-body.component.ts @@ -0,0 +1,29 @@ +import { Component, OnInit, Input, HostBinding, TemplateRef, ViewEncapsulation } from '@angular/core'; + +@Component({ + selector: '[tab-pane-body]', + templateUrl: './tab-pane-body.component.html', + encapsulation: ViewEncapsulation.None +}) +export class TabPaneBody implements OnInit { + + @Input() active: boolean = false; + @Input() hidden: boolean = false; + @Input() content: TemplateRef; + + @HostBinding('class.am-tabs-pane-wrap') + paneWrap: boolean = true; + @HostBinding('class.am-tabs-pane-wrap-active') + get wrapActive(): boolean { + return this.active; + } + @HostBinding('class.am-tabs-pane-wrap-inactive') + get wrapInactive(): boolean { + return !this.active; + } + + constructor() { } + + ngOnInit() { } + +} diff --git a/components/tabs/tab-pane.component.html b/components/tabs/tab-pane.component.html index f16322a0..b06f3d0f 100644 --- a/components/tabs/tab-pane.component.html +++ b/components/tabs/tab-pane.component.html @@ -1 +1,3 @@ - \ No newline at end of file + + + \ No newline at end of file diff --git a/components/tabs/tab-pane.component.ts b/components/tabs/tab-pane.component.ts index d5ca50b6..2ea64bdf 100644 --- a/components/tabs/tab-pane.component.ts +++ b/components/tabs/tab-pane.component.ts @@ -1,120 +1,17 @@ -import { Component, Input, HostBinding, TemplateRef } from '@angular/core'; -import { trigger, state, style } from '@angular/animations'; - -export type TabPanelPositionState = - | 'center-with-animation' - | 'center-without-animation' - | 'top-with-animation' - | 'top-with-animation-with-higher-zindex' - | 'top-without-animation' - | 'left-with-animation' - | 'left-with-animation-with-higher-zindex' - | 'left-without-animation' - | 'bottom-with-animation' - | 'bottom-with-animation-with-higher-zindex' - | 'bottom-without-animation' - | 'right-with-animation' - | 'right-with-animation-with-higher-zindex' - | 'right-without-animation'; +import { Component, Input, ViewChild, TemplateRef } from '@angular/core'; @Component({ selector: 'TabPane, nzm-tab-pane', - templateUrl: './tab-pane.component.html', - styles: [`:host {touch-action: auto}`], - host: { - '[@translateTabPane]': 'position', - '(@translateTabPane.start)': 'onTranslateTabStarted($event)', - '(@translateTabPane.done)': 'onTranslateTabComplete($event)' - }, - animations: [ - trigger('translateTabPane', [ - state( - 'left-with-animation', - style({ - transform: 'translate3d(-100%, 0, 0)', - transition: '.3s cubic-bezier(0.35, 0, 0.25, 1)' - }) - ), - state( - 'left-with-animation-with-higher-zindex', - style({ - 'z-index': 100, - transform: 'translate3d(-100%, 0, 0)', - transition: '.3s cubic-bezier(0.35, 0, 0.25, 1)' - }) - ), - state('left-without-animation', style({transform: 'translate3d(-100%, 0, 0)' })), - state( - 'right-with-animation', - style({ - transform: 'translate3d(100%, 0, 0)', - transition: '.3s cubic-bezier(0.35, 0, 0.25, 1)' - }) - ), - state( - 'right-with-animation-with-higher-zindex', - style({ - 'z-index': 100, - transform: 'translate3d(100%, 0, 0)', - transition: '.3s cubic-bezier(0.35, 0, 0.25, 1)' - }) - ), - state('right-without-animation', style({transform: 'translate3d(100%, 0, 0)' })), - state( - 'top-with-animation', - style({ - transform: 'translate3d(0, -100%, 0)', - transition: '.3s cubic-bezier(0.35, 0, 0.25, 1)' - }) - ), - state( - 'top-with-animation-with-higher-zindex', - style({ - 'z-index': 100, - transform: 'translate3d(0, -100%, 0)', - transition: '.3s cubic-bezier(0.35, 0, 0.25, 1)' - }) - ), - state('top-without-animation', style({ transform: 'translate3d(0, -100%, 0)' })), - state( - 'bottom-with-animation', - style({ - transform: 'translate3d(0, 100%, 0)', - transition: '.3s cubic-bezier(0.35, 0, 0.25, 1)' - }) - ), - state( - 'bottom-with-animation-with-higher-zindex', - style({ - 'z-index': 100, - transform: 'translate3d(0, 100%, 0)', - transition: '.3s cubic-bezier(0.35, 0, 0.25, 1)' - }) - ), - state('bottom-without-animation', style({ transform: 'translate3d(0, 100%, 0)' })), - state( - 'center-with-animation', - style({ - 'z-index': 100, - transform: 'translate3d(0, 0, 0)', - transition: '.3s cubic-bezier(0.35, 0, 0.25, 1)' - }) - ), - state('center-without-animation', style({ transform: 'translate3d(0, 0, 0)' })) - ]) - ] + templateUrl: './tab-pane.component.html' }) export class TabPane { - prefixCls: string = 'am-tabs-pane'; - tintColor: string = '#108ee9'; - unselectedTintColor: string = '#888'; - active: boolean = true; - isTitleString: boolean = true; - showTabPanelContent: boolean = false; - position: TabPanelPositionState; + + public isTitleString: boolean = true; private _title: string | TemplateRef; + @ViewChild('content') content: TemplateRef; + @Input() get title(): string | TemplateRef { return this._title; @@ -124,54 +21,6 @@ export class TabPane { this._title = value; } - @HostBinding('class.am-tabs-pane-wrap') - paneWrap: boolean = true; - @HostBinding('class.am-tabs-pane-wrap-active') - get wrapActive(): boolean { - return this.active; - } - @HostBinding('class.am-tabs-pane-wrap-inactive') - get wrapInactive(): boolean { - return !this.active; - } - constructor() {} - onTranslateTabStarted(e: any) { - if ( - (e.toState == 'center-with-animation' || - e.toState == 'center-without-animation' || - e.toState == 'left-with-animation-with-higher-zindex' || - e.toState == 'right-with-animation-with-higher-zindex' || - e.toState == 'top-with-animation-with-higher-zindex' || - e.toState == 'bottom-with-animation-with-higher-zindex') && - (this.position == 'center-with-animation' || - this.position == 'center-without-animation' || - this.position == 'left-with-animation-with-higher-zindex' || - this.position == 'right-with-animation-with-higher-zindex' || - this.position == 'top-with-animation-with-higher-zindex' || - this.position == 'bottom-with-animation-with-higher-zindex') - ) { - this.showTabPanelContent = true; - } - } - - onTranslateTabComplete(e: any) { - if ( - e.toState !== 'center-with-animation' && - e.toState !== 'center-without-animation' && - e.toState !== 'left-with-animation-with-higher-zindex' && - e.toState !== 'right-with-animation-with-higher-zindex' && - e.toState !== 'top-with-animation-with-higher-zindex' && - e.toState !== 'bottom-with-animation-with-higher-zindex' && - this.position !== 'center-with-animation' && - this.position !== 'center-without-animation' && - this.position !== 'left-with-animation-with-higher-zindex' && - this.position !== 'right-with-animation-with-higher-zindex' && - this.position !== 'top-with-animation-with-higher-zindex' && - this.position !== 'bottom-with-animation-with-higher-zindex' - ) { - this.showTabPanelContent = false; - } - } } diff --git a/components/tabs/tabs.component.html b/components/tabs/tabs.component.html index 8a9cb873..a20e8666 100644 --- a/components/tabs/tabs.component.html +++ b/components/tabs/tabs.component.html @@ -10,7 +10,12 @@ (touchmove)="onTouchMove($event)" (touchend)="onTouchEnd($event)" > - +
diff --git a/components/tabs/tabs.component.spec.ts b/components/tabs/tabs.component.spec.ts index d5bfd649..266a4ba0 100644 --- a/components/tabs/tabs.component.spec.ts +++ b/components/tabs/tabs.component.spec.ts @@ -251,102 +251,34 @@ describe('tab', () => { }); it('selectTabPane function work', () => { - component.tabDirection = 'horizontal'; - fixture.detectChanges(); - component.tabs.selectTabPane(2); - fixture.detectChanges(); - component.tabPanes.map((item, i) => { - if (i > 3) { - expect(item.position).toEqual('right-with-animation'); - } else if (i === 3) { - expect(item.position).toEqual('right-with-animation-with-higher-zindex'); - } else if (i === 1) { - expect(item.position).toEqual('left-with-animation-with-higher-zindex'); - } else if (i < 1) { - expect(item.position).toEqual('left-with-animation'); - } else { - expect(item.position).toEqual('center-with-animation'); - } - }); - component.animated = false; component.tabDirection = 'horizontal'; fixture.detectChanges(); component.tabs.selectTabPane(2); fixture.detectChanges(); - component.tabPanes.map((item, i) => { - if (i > 2) { - expect(item.position).toEqual('right-without-animation'); - } else if (i < 2) { - expect(item.position).toEqual('left-without-animation'); - } else { - expect(item.position).toEqual('center-without-animation'); - } - }); + expect(tabsEle.querySelector('.am-tabs-content-wrap').style.transform).toContain('translate3d(-200%, 0px, 0px)'); + expect(tabsEle.querySelector('.am-tabs-content-wrap').classList).not.toContain('am-tabs-content-wrap-animated'); component.animated = true; - component.tabDirection = 'vertical'; - fixture.detectChanges(); - component.tabs.selectTabPane(2); fixture.detectChanges(); - component.tabPanes.map((item, i) => { - if (i > 3) { - expect(item.position).toEqual('bottom-with-animation'); - } else if (i === 3) { - expect(item.position).toEqual('bottom-with-animation-with-higher-zindex'); - } else if (i === 1) { - expect(item.position).toEqual('top-with-animation-with-higher-zindex'); - } else if (i < 1) { - expect(item.position).toEqual('top-with-animation'); - } else { - expect(item.position).toEqual('center-with-animation'); - } - }); + expect(tabsEle.querySelector('.am-tabs-content-wrap').classList).toContain('am-tabs-content-wrap-animated'); + component.animated = false; component.tabDirection = 'vertical'; fixture.detectChanges(); component.tabs.selectTabPane(2); fixture.detectChanges(); - component.tabPanes.map((item, i) => { - if (i > 2) { - expect(item.position).toEqual('bottom-without-animation'); - } else if (i < 2) { - expect(item.position).toEqual('top-without-animation'); - } else { - expect(item.position).toEqual('center-without-animation'); - } - component.animated = false; - }); + expect(tabsEle.querySelector('.am-tabs-content-wrap').style.transform).toContain('translate3d(0px, -200%, 0px)'); + expect(tabsEle.querySelector('.am-tabs-content-wrap').classList).not.toContain('am-tabs-content-wrap-animated'); - component.tabDirection = 'test'; - fixture.detectChanges(); - component.tabs.selectTabPane(2); - fixture.detectChanges(); - component.tabPanes.map((item, i) => { - if (i > 2) { - expect(item.position).toEqual('bottom-without-animation'); - } else if (i < 2) { - expect(item.position).toEqual('top-without-animation'); - } else { - expect(item.position).toEqual('center-without-animation'); - } - component.animated = true; - }); - - component.tabDirection = 'test'; + component.animated = true; + component.tabDirection = 'vertical'; fixture.detectChanges(); component.tabs.selectTabPane(2); fixture.detectChanges(); - component.tabPanes.map((item, i) => { - if (i > 2) { - expect(item.position).toEqual('bottom-without-animation'); - } else if (i < 2) { - expect(item.position).toEqual('top-without-animation'); - } else { - expect(item.position).toEqual('center-with-animation'); - } - }); + expect(tabsEle.querySelector('.am-tabs-content-wrap').style.transform).toContain('translate3d(0px, -200%, 0px)'); + expect(tabsEle.querySelector('.am-tabs-content-wrap').classList).toContain('am-tabs-content-wrap-animated'); }); it('onTabClick work', () => { @@ -479,12 +411,10 @@ describe('tab', () => { [tabBarUnderlineStyle]="{'border': '1px red solid'}" [tabBarTextStyle]="{'font-size': '33px'}" [tabBarActiveTextColor]="'red'"> - +
Content of 1 tab
- +
Tab 2
@@ -492,19 +422,19 @@ describe('tab', () => { Content of first tab
- + Content of 3 tab - + Content of 4 tab - + Content of 5 tab - + Content of 6 tab - + Content of 7 tab diff --git a/components/tabs/tabs.component.ts b/components/tabs/tabs.component.ts index 03fe9d5c..0651eb69 100644 --- a/components/tabs/tabs.component.ts +++ b/components/tabs/tabs.component.ts @@ -25,7 +25,7 @@ export class Tabs implements DoCheck, AfterContentInit { prefixCls: string = 'am-tabs'; selectedKey: number = 0; keyToSelect: number = 0; - paneMoveStyle: string = ''; + paneMoveStyle: string = 'translate3d(0, 0, 0)'; private _startTime: number = 0; private _startPosition: number = 0; @@ -58,6 +58,8 @@ export class Tabs implements DoCheck, AfterContentInit { @Input() distanceToChangeTab: number = 0.3; @Input() + tabTitleSize: number = 0; + @Input() tabBarActiveTextColor: string = ''; @Input() tabBarInactiveTextColor: string = ''; @@ -66,10 +68,12 @@ export class Tabs implements DoCheck, AfterContentInit { @Input() tabBarBackgroundColor: string = '#FFF'; @Input() + prerenderingSiblingsNumber: number = 1; + @Input() tabBarTextStyle: object = {}; /** should be removed when https://github.com/angular/angular/issues/20810 resolved **/ @Input() - tabPanesComponent: QueryList = null; + tabPanesContent: QueryList = null; @Input() get activeTab(): number { return this.selectedKey; @@ -161,7 +165,7 @@ export class Tabs implements DoCheck, AfterContentInit { } getCurrentTabPanes(): QueryList { - return this.tabPanesComponent || this.tabPanes; + return this.tabPanesContent || this.tabPanes; } onTouchStart(event) { @@ -193,7 +197,7 @@ export class Tabs implements DoCheck, AfterContentInit { this.swipeable && this.animated ) { - this.paneMoveStyle = 'translate3d(' + distance + 'px, 0, 0 )'; + this.paneMoveStyle = 'translate3d(calc(-' + this.selectedKey * 100 + '% + ' + distance + 'px), 0, 0 )'; } } else if ('vertical' === this._tabDirection) { const distance = event.changedTouches[0].clientY - this._startPosition; @@ -208,7 +212,7 @@ export class Tabs implements DoCheck, AfterContentInit { this.swipeable && this.animated ) { - this.paneMoveStyle = 'translate3d(0, ' + distance + 'px, 0 )'; + this.paneMoveStyle = 'translate3d(0, calc(-' + this.selectedKey * 100 + '% + ' + distance + 'px, 0 )'; } } } @@ -231,7 +235,7 @@ export class Tabs implements DoCheck, AfterContentInit { this.keyToSelect--; } } - this.paneMoveStyle = 'translate3d(0, 0, 0 )'; + this.paneMoveStyle = 'translate3d(-' + this.selectedKey * 100 + '%, 0, 0 )'; } else if ('vertical' === this._tabDirection) { const distance = event.changedTouches[0].clientY - this._startPosition; const distanceToChangeTabPx = this.tabContent.nativeElement.offsetHeight * this.distanceToChangeTab; @@ -247,7 +251,7 @@ export class Tabs implements DoCheck, AfterContentInit { this.keyToSelect--; } } - this.paneMoveStyle = 'translate3d(0, 0, 0 )'; + this.paneMoveStyle = 'translate3d(0, -' + this.selectedKey * 100 + '%, 0 )'; } } } @@ -266,65 +270,14 @@ export class Tabs implements DoCheck, AfterContentInit { } private selectTabPane(index: number) { - const keyToSelect = Math.min(this.getCurrentTabPanes().length - 1, Math.max(index || 0, 0)); - this.getCurrentTabPanes().forEach((tabPane: TabPane, indexKey: number) => { - if (keyToSelect < indexKey) { - if (this.animated) { - if ('horizontal' === this._tabDirection) { - if (keyToSelect === indexKey - 1) { - tabPane.position = 'right-with-animation-with-higher-zindex'; - } else { - tabPane.position = 'right-with-animation'; - } - } else if ('vertical' === this._tabDirection) { - if (keyToSelect === indexKey - 1) { - tabPane.position = 'bottom-with-animation-with-higher-zindex'; - } else { - tabPane.position = 'bottom-with-animation'; - } - } - } else { - if ('horizontal' === this._tabDirection) { - tabPane.position = 'right-without-animation'; - } else if ('vertical' === this._tabDirection) { - tabPane.position = 'bottom-without-animation'; - } - } - } else if (keyToSelect > indexKey) { - if (this.animated) { - if ('horizontal' === this._tabDirection) { - if (keyToSelect === indexKey + 1) { - tabPane.position = 'left-with-animation-with-higher-zindex'; - } else { - tabPane.position = 'left-with-animation'; - } - } else if ('vertical' === this._tabDirection) { - if (keyToSelect === indexKey + 1) { - tabPane.position = 'top-with-animation-with-higher-zindex'; - } else { - tabPane.position = 'top-with-animation'; - } - } - } else { - if ('horizontal' === this._tabDirection) { - tabPane.position = 'left-without-animation'; - } else if ('vertical' === this._tabDirection) { - tabPane.position = 'top-without-animation'; - } - } - } else { - if (this.animated) { - tabPane.position = 'center-with-animation'; - } else { - tabPane.position = 'center-without-animation'; - } - } - if (keyToSelect !== indexKey) { - tabPane.active = false; - } else { - tabPane.active = true; + if (this.getCurrentTabPanes() && this.getCurrentTabPanes().length > 0) { + const actualKeyToSelect = Math.min(this.getCurrentTabPanes().length - 1, Math.max(index || 0, 0)); + if ('horizontal' === this._tabDirection) { + this.paneMoveStyle = 'translate3d(-' + actualKeyToSelect * 100 + '%, 0, 0 )'; + } else if ('vertical' === this._tabDirection) { + this.paneMoveStyle = 'translate3d(0, -' + actualKeyToSelect * 100 + '%, 0 )'; } - }); + } } private getVelocity(deltaDistance, deltaTime) { diff --git a/components/tabs/tabs.module.ts b/components/tabs/tabs.module.ts index d1b015d9..ddfa19c3 100644 --- a/components/tabs/tabs.module.ts +++ b/components/tabs/tabs.module.ts @@ -3,14 +3,15 @@ import { CommonModule } from '@angular/common'; import { ObserversModule } from '@angular/cdk/observers'; import { Tabs } from './tabs.component'; import { TabPane } from './tab-pane.component'; -import { DefaultTabBarComponent } from './default-tab-bar.component'; +import { TabPaneBody } from './tab-pane-body.component'; +import { DefaultTabBar } from './default-tab-bar.component'; export { TabPane } from './tab-pane.component'; @NgModule({ imports: [CommonModule, ObserversModule], - declarations: [TabPane, Tabs, DefaultTabBarComponent], - exports: [TabPane, Tabs, DefaultTabBarComponent], + declarations: [TabPane, Tabs, TabPaneBody, DefaultTabBar], + exports: [TabPane, Tabs, TabPaneBody, DefaultTabBar], providers: [] }) -export class TabsModule {} +export class TabsModule { }