MOBILE-2430 tabs: Add tab sliding effect
parent
eb36ee048c
commit
19269f36f9
|
@ -25,7 +25,7 @@
|
||||||
<!-- Content. -->
|
<!-- Content. -->
|
||||||
<core-loading [hideUntil]="loaded" class="core-loading-center">
|
<core-loading [hideUntil]="loaded" class="core-loading-center">
|
||||||
|
|
||||||
<core-tabs [selectedIndex]="selectedTab">
|
<core-tabs [hideUntil]="loaded" [selectedIndex]="selectedTab">
|
||||||
<!-- Page contents. -->
|
<!-- Page contents. -->
|
||||||
<core-tab [title]="'addon.mod_wiki.viewpage' | translate" icon="document">
|
<core-tab [title]="'addon.mod_wiki.viewpage' | translate" icon="document">
|
||||||
<ng-template>
|
<ng-template>
|
||||||
|
|
|
@ -1,12 +1,26 @@
|
||||||
<core-loading [hideUntil]="hideUntil" class="core-loading-center">
|
<core-loading [hideUntil]="hideUntil" class="core-loading-center">
|
||||||
<div class="core-tabs-bar" #topTabs [hidden]="!tabs || tabs.length < 2">
|
<div class="core-tabs-bar" #topTabs [hidden]="!tabs || tabs.length < 2">
|
||||||
|
<ion-row>
|
||||||
|
<ion-col class="col-with-arrow" (click)="slidePrev()" no-padding col-1>
|
||||||
|
<ion-icon *ngIf="showPrevButton" name="arrow-back"></ion-icon>
|
||||||
|
</ion-col>
|
||||||
|
<ion-col no-padding col-10>
|
||||||
|
<ion-slides (ionSlideDidChange)="slideChanged()" [slidesPerView]="slidesShown">
|
||||||
<ng-container *ngFor="let tab of tabs; let idx = index">
|
<ng-container *ngFor="let tab of tabs; let idx = index">
|
||||||
<a *ngIf="tab.show" [attr.aria-selected]="selected == idx" (click)="selectTab(idx)">
|
<ion-slide *ngIf="tab.show">
|
||||||
|
<a [attr.aria-selected]="selected == idx" (click)="selectTab(idx)" class="tab-slide">
|
||||||
<core-icon *ngIf="tab.icon" [name]="tab.icon"></core-icon>
|
<core-icon *ngIf="tab.icon" [name]="tab.icon"></core-icon>
|
||||||
<span *ngIf="tab.title">{{ tab.title }}</span>
|
<span *ngIf="tab.title">{{ tab.title }}</span>
|
||||||
<ion-badge *ngIf="tab.badge" [color]="tab.badgeStyle" class="tab-badge">{{tab.badge}}</ion-badge>
|
<ion-badge *ngIf="tab.badge" [color]="tab.badgeStyle" class="tab-badge">{{tab.badge}}</ion-badge>
|
||||||
</a>
|
</a>
|
||||||
|
</ion-slide>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
</ion-slides>
|
||||||
|
</ion-col>
|
||||||
|
<ion-col class="col-with-arrow" (click)="slideNext()" no-padding col-1>
|
||||||
|
<ion-icon *ngIf="showNextButton" name="arrow-forward"></ion-icon>
|
||||||
|
</ion-col>
|
||||||
|
</ion-row>
|
||||||
</div>
|
</div>
|
||||||
<div class="core-tabs-content-container" #originalTabs>
|
<div class="core-tabs-content-container" #originalTabs>
|
||||||
<ng-content></ng-content>
|
<ng-content></ng-content>
|
||||||
|
|
|
@ -6,7 +6,11 @@
|
||||||
width: 100%;
|
width: 100%;
|
||||||
background: $core-top-tabs-background;
|
background: $core-top-tabs-background;
|
||||||
|
|
||||||
> a {
|
.row {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
a.tab-slide {
|
||||||
@extend .tab-button;
|
@extend .tab-button;
|
||||||
|
|
||||||
background: $core-top-tabs-background;
|
background: $core-top-tabs-background;
|
||||||
|
@ -20,9 +24,25 @@
|
||||||
border-bottom: 2px solid $core-top-tabs-color-active !important;
|
border-bottom: 2px solid $core-top-tabs-color-active !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ion-col {
|
||||||
|
text-align: center;
|
||||||
|
font-size: 1.6rem;
|
||||||
|
line-height: 1.6rem;
|
||||||
|
|
||||||
|
&.col-with-arrow {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
ion-icon {
|
||||||
|
color: #ccc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.md .core-tabs-bar > a {
|
.md .core-tabs-bar a.tab-slide {
|
||||||
// @extend .tabs-md .tab-button;
|
// @extend .tabs-md .tab-button;
|
||||||
min-height: $tabs-md-tab-min-height;
|
min-height: $tabs-md-tab-min-height;
|
||||||
|
|
||||||
|
@ -30,7 +50,7 @@
|
||||||
color: $tabs-md-tab-text-color;
|
color: $tabs-md-tab-text-color;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ios .core-tabs-bar > a {
|
.ios .core-tabs-bar a.tab-slide {
|
||||||
// @extend .tabs-ios .tab-button;
|
// @extend .tabs-ios .tab-button;
|
||||||
max-width: $tabs-ios-tab-max-width;
|
max-width: $tabs-ios-tab-max-width;
|
||||||
min-height: $tabs-ios-tab-min-height;
|
min-height: $tabs-ios-tab-min-height;
|
||||||
|
@ -40,7 +60,7 @@
|
||||||
color: $tabs-ios-tab-text-color;
|
color: $tabs-ios-tab-text-color;
|
||||||
}
|
}
|
||||||
|
|
||||||
.wp .core-tabs-bar > a {
|
.wp .core-tabs-bar a.tab-slide {
|
||||||
//@extend .tabs-wp .tab-button;
|
//@extend .tabs-wp .tab-button;
|
||||||
@include border-radius(0);
|
@include border-radius(0);
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,7 @@ import {
|
||||||
SimpleChange
|
SimpleChange
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
import { CoreTabComponent } from './tab';
|
import { CoreTabComponent } from './tab';
|
||||||
import { Content } from 'ionic-angular';
|
import { Content, Slides } from 'ionic-angular';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This component displays some tabs that usually share data between them.
|
* This component displays some tabs that usually share data between them.
|
||||||
|
@ -48,9 +48,16 @@ export class CoreTabsComponent implements OnInit, AfterViewInit, OnChanges {
|
||||||
@Output() ionChange: EventEmitter<CoreTabComponent> = new EventEmitter<CoreTabComponent>(); // Emitted when the tab changes.
|
@Output() ionChange: EventEmitter<CoreTabComponent> = new EventEmitter<CoreTabComponent>(); // Emitted when the tab changes.
|
||||||
@ViewChild('originalTabs') originalTabsRef: ElementRef;
|
@ViewChild('originalTabs') originalTabsRef: ElementRef;
|
||||||
@ViewChild('topTabs') topTabs: ElementRef;
|
@ViewChild('topTabs') topTabs: ElementRef;
|
||||||
|
@ViewChild(Slides) slides: Slides;
|
||||||
|
|
||||||
tabs: CoreTabComponent[] = []; // List of tabs.
|
tabs: CoreTabComponent[] = []; // List of tabs.
|
||||||
selected: number; // Selected tab number.
|
selected: number; // Selected tab number.
|
||||||
|
showPrevButton: boolean;
|
||||||
|
showNextButton: boolean;
|
||||||
|
maxSlides = 3;
|
||||||
|
slidesShown = this.maxSlides;
|
||||||
|
numTabsShown = 0;
|
||||||
|
|
||||||
protected originalTabsContainer: HTMLElement; // The container of the original tabs. It will include each tab's content.
|
protected originalTabsContainer: HTMLElement; // The container of the original tabs. It will include each tab's content.
|
||||||
protected initialized = false;
|
protected initialized = false;
|
||||||
protected afterViewInitTriggered = false;
|
protected afterViewInitTriggered = false;
|
||||||
|
@ -77,10 +84,16 @@ export class CoreTabsComponent implements OnInit, AfterViewInit, OnChanges {
|
||||||
*/
|
*/
|
||||||
ngAfterViewInit(): void {
|
ngAfterViewInit(): void {
|
||||||
this.afterViewInitTriggered = true;
|
this.afterViewInitTriggered = true;
|
||||||
|
|
||||||
if (!this.initialized && this.hideUntil) {
|
if (!this.initialized && this.hideUntil) {
|
||||||
// Tabs should be shown, initialize them.
|
// Tabs should be shown, initialize them.
|
||||||
this.initializeTabs();
|
this.initializeTabs();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
window.addEventListener('resize', () => {
|
||||||
|
this.calculateMaxSlides();
|
||||||
|
this.updateSlides();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -107,6 +120,7 @@ export class CoreTabsComponent implements OnInit, AfterViewInit, OnChanges {
|
||||||
if (this.getIndex(tab) == -1) {
|
if (this.getIndex(tab) == -1) {
|
||||||
this.tabs.push(tab);
|
this.tabs.push(tab);
|
||||||
this.sortTabs();
|
this.sortTabs();
|
||||||
|
this.updateSlides();
|
||||||
|
|
||||||
if (this.initialized && this.tabs.length > 1 && this.tabBarHeight == 0) {
|
if (this.initialized && this.tabs.length > 1 && this.tabBarHeight == 0) {
|
||||||
// Calculate the tabBarHeight again now that there is more than 1 tab and the bar will be seen.
|
// Calculate the tabBarHeight again now that there is more than 1 tab and the bar will be seen.
|
||||||
|
@ -190,9 +204,72 @@ export class CoreTabsComponent implements OnInit, AfterViewInit, OnChanges {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check which arrows should be shown
|
||||||
|
this.calculateMaxSlides();
|
||||||
|
this.updateSlides();
|
||||||
|
|
||||||
this.initialized = true;
|
this.initialized = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method executed when the slides are changed.
|
||||||
|
*/
|
||||||
|
slideChanged(): void {
|
||||||
|
const currentIndex = this.slides.getActiveIndex();
|
||||||
|
if (this.slidesShown >= this.numTabsShown) {
|
||||||
|
this.showPrevButton = false;
|
||||||
|
this.showNextButton = false;
|
||||||
|
} else if (typeof currentIndex !== 'undefined') {
|
||||||
|
this.showPrevButton = currentIndex > 0;
|
||||||
|
this.showNextButton = currentIndex < this.numTabsShown - this.slidesShown;
|
||||||
|
} else {
|
||||||
|
this.showPrevButton = false;
|
||||||
|
this.showNextButton = this.numTabsShown > this.slidesShown;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update slides.
|
||||||
|
*/
|
||||||
|
protected updateSlides(): void {
|
||||||
|
this.numTabsShown = this.tabs.reduce((prev: number, current: any) => {
|
||||||
|
return current.show ? prev + 1 : prev;
|
||||||
|
}, 0);
|
||||||
|
|
||||||
|
this.slidesShown = Math.min(this.maxSlides, this.numTabsShown);
|
||||||
|
this.slides.update();
|
||||||
|
this.slides.resize();
|
||||||
|
|
||||||
|
this.slideChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected calculateMaxSlides(): void {
|
||||||
|
if (this.slides && this.slides.renderedWidth) {
|
||||||
|
this.maxSlides = Math.floor(this.slides.renderedWidth / 120);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.maxSlides = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method that shows the next slide.
|
||||||
|
*/
|
||||||
|
slideNext(): void {
|
||||||
|
if (this.showNextButton) {
|
||||||
|
this.slides.slideNext();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method that shows the previous slide.
|
||||||
|
*/
|
||||||
|
slidePrev(): void {
|
||||||
|
if (this.showPrevButton) {
|
||||||
|
this.slides.slidePrev();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Show or hide the tabs. This is used when the user is scrolling inside a tab.
|
* Show or hide the tabs. This is used when the user is scrolling inside a tab.
|
||||||
*
|
*
|
||||||
|
@ -221,6 +298,8 @@ export class CoreTabsComponent implements OnInit, AfterViewInit, OnChanges {
|
||||||
removeTab(tab: CoreTabComponent): void {
|
removeTab(tab: CoreTabComponent): void {
|
||||||
const index = this.getIndex(tab);
|
const index = this.getIndex(tab);
|
||||||
this.tabs.splice(index, 1);
|
this.tabs.splice(index, 1);
|
||||||
|
|
||||||
|
this.updateSlides();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -252,6 +331,10 @@ export class CoreTabsComponent implements OnInit, AfterViewInit, OnChanges {
|
||||||
currentTab.unselectTab();
|
currentTab.unselectTab();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.selected) {
|
||||||
|
this.slides.slideTo(index);
|
||||||
|
}
|
||||||
|
|
||||||
this.selected = index;
|
this.selected = index;
|
||||||
newTab.selectTab();
|
newTab.selectTab();
|
||||||
this.ionChange.emit(newTab);
|
this.ionChange.emit(newTab);
|
||||||
|
|
Loading…
Reference in New Issue