MOBILE-3833 tabs: Fix tab size calculations
parent
f56cfa3ab6
commit
b2246a01c5
|
@ -22,7 +22,6 @@ import {
|
||||||
OnDestroy,
|
OnDestroy,
|
||||||
AfterViewInit,
|
AfterViewInit,
|
||||||
ViewChild,
|
ViewChild,
|
||||||
ElementRef,
|
|
||||||
SimpleChange,
|
SimpleChange,
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
import { IonSlides } from '@ionic/angular';
|
import { IonSlides } from '@ionic/angular';
|
||||||
|
@ -34,6 +33,8 @@ import { CoreSettingsHelper } from '@features/settings/services/settings-helper'
|
||||||
import { CoreAriaRoleTab, CoreAriaRoleTabFindable } from './aria-role-tab';
|
import { CoreAriaRoleTab, CoreAriaRoleTabFindable } from './aria-role-tab';
|
||||||
import { CoreEventObserver } from '@singletons/events';
|
import { CoreEventObserver } from '@singletons/events';
|
||||||
import { CoreDom } from '@singletons/dom';
|
import { CoreDom } from '@singletons/dom';
|
||||||
|
import { CoreUtils } from '@services/utils/utils';
|
||||||
|
import { CoreError } from './errors/error';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class to abstract some common code for tabs.
|
* Class to abstract some common code for tabs.
|
||||||
|
@ -54,6 +55,7 @@ export class CoreTabsBaseComponent<T extends CoreTabBase> implements OnInit, Aft
|
||||||
|
|
||||||
tabs: T[] = []; // List of tabs.
|
tabs: T[] = []; // List of tabs.
|
||||||
|
|
||||||
|
hideTabs = false;
|
||||||
selected?: string; // Selected tab id.
|
selected?: string; // Selected tab id.
|
||||||
showPrevButton = false;
|
showPrevButton = false;
|
||||||
showNextButton = false;
|
showNextButton = false;
|
||||||
|
@ -65,10 +67,11 @@ export class CoreTabsBaseComponent<T extends CoreTabBase> implements OnInit, Aft
|
||||||
initialSlide: 0,
|
initialSlide: 0,
|
||||||
slidesPerView: 3,
|
slidesPerView: 3,
|
||||||
centerInsufficientSlides: true,
|
centerInsufficientSlides: true,
|
||||||
|
threshold: 10,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
protected slidesElement?: HTMLIonSlidesElement;
|
||||||
protected initialized = false;
|
protected initialized = false;
|
||||||
protected afterViewInitTriggered = false;
|
|
||||||
|
|
||||||
protected resizeListener?: CoreEventObserver;
|
protected resizeListener?: CoreEventObserver;
|
||||||
protected isDestroyed = false;
|
protected isDestroyed = false;
|
||||||
|
@ -79,54 +82,41 @@ export class CoreTabsBaseComponent<T extends CoreTabBase> implements OnInit, Aft
|
||||||
|
|
||||||
protected firstSelectedTab?: string; // ID of the first selected tab to control history.
|
protected firstSelectedTab?: string; // ID of the first selected tab to control history.
|
||||||
protected backButtonFunction: (event: BackButtonEvent) => void;
|
protected backButtonFunction: (event: BackButtonEvent) => void;
|
||||||
protected languageChangedSubscription?: Subscription;
|
|
||||||
// Swiper 6 documentation: https://swiper6.vercel.app/
|
// Swiper 6 documentation: https://swiper6.vercel.app/
|
||||||
protected isInTransition = false; // Wether Slides is in transition.
|
protected isInTransition = false; // Wether Slides is in transition.
|
||||||
protected slidesSwiper: any; // eslint-disable-line @typescript-eslint/no-explicit-any
|
protected subscriptions: Subscription[] = [];
|
||||||
protected slidesSwiperLoaded = false;
|
|
||||||
|
|
||||||
tabAction: CoreTabsRoleTab<T>;
|
tabAction: CoreTabsRoleTab<T>;
|
||||||
|
|
||||||
constructor(
|
constructor() {
|
||||||
protected element: ElementRef,
|
|
||||||
) {
|
|
||||||
this.backButtonFunction = this.backButtonClicked.bind(this);
|
this.backButtonFunction = this.backButtonClicked.bind(this);
|
||||||
|
|
||||||
this.tabAction = new CoreTabsRoleTab(this);
|
this.tabAction = new CoreTabsRoleTab(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component being initialized.
|
* @inheritdoc
|
||||||
*/
|
*/
|
||||||
ngOnInit(): void {
|
async ngOnInit(): Promise<void> {
|
||||||
this.direction = Platform.isRTL ? 'rtl' : 'ltr';
|
this.direction = Platform.isRTL ? 'rtl' : 'ltr';
|
||||||
|
|
||||||
// Change the side when the language changes.
|
// Change the side when the language changes.
|
||||||
this.languageChangedSubscription = Translate.onLangChange.subscribe(() => {
|
this.subscriptions.push(Translate.onLangChange.subscribe(() => {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.direction = Platform.isRTL ? 'rtl' : 'ltr';
|
this.direction = Platform.isRTL ? 'rtl' : 'ltr';
|
||||||
});
|
});
|
||||||
});
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* View has been initialized.
|
* @inheritdoc
|
||||||
*/
|
*/
|
||||||
async ngAfterViewInit(): Promise<void> {
|
ngAfterViewInit(): void {
|
||||||
if (this.isDestroyed) {
|
if (this.isDestroyed) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.afterViewInitTriggered = true;
|
this.init();
|
||||||
|
|
||||||
if (!this.initialized && this.hideUntil) {
|
|
||||||
// Tabs should be shown, initialize them.
|
|
||||||
await this.initializeTabs();
|
|
||||||
}
|
|
||||||
|
|
||||||
this.resizeListener = CoreDom.onWindowResize(() => {
|
|
||||||
this.windowResized();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -134,14 +124,7 @@ export class CoreTabsBaseComponent<T extends CoreTabBase> implements OnInit, Aft
|
||||||
*/
|
*/
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
ngOnChanges(changes: Record<string, SimpleChange>): void {
|
ngOnChanges(changes: Record<string, SimpleChange>): void {
|
||||||
// Wait for ngAfterViewInit so it works in the case that each tab has its own component.
|
this.init();
|
||||||
if (!this.initialized && this.hideUntil && this.afterViewInitTriggered) {
|
|
||||||
// Tabs should be shown, initialize them.
|
|
||||||
// Use a setTimeout so child components update their inputs before initializing the tabs.
|
|
||||||
setTimeout(() => {
|
|
||||||
this.initializeTabs();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -206,6 +189,16 @@ export class CoreTabsBaseComponent<T extends CoreTabBase> implements OnInit, Aft
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.numTabsShown = this.tabs.reduce((prev: number, current) => current.enabled ? prev + 1 : prev, 0);
|
||||||
|
|
||||||
|
if (this.numTabsShown <= 1) {
|
||||||
|
this.hideTabs = true;
|
||||||
|
|
||||||
|
// Only one, nothing to do here.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.hideTabs = false;
|
||||||
|
|
||||||
await this.calculateMaxSlides();
|
await this.calculateMaxSlides();
|
||||||
|
|
||||||
await this.updateSlides();
|
await this.updateSlides();
|
||||||
|
@ -233,28 +226,81 @@ export class CoreTabsBaseComponent<T extends CoreTabBase> implements OnInit, Aft
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize the tabs, determining the first tab to be shown.
|
* Init the component.
|
||||||
*/
|
*/
|
||||||
protected async initializeTabs(): Promise<void> {
|
protected async init(): Promise<void> {
|
||||||
// Initialize slider.
|
if (!this.hideUntil) {
|
||||||
this.slidesSwiper = await this.slides?.getSwiper();
|
// Hidden, do nothing.
|
||||||
this.slidesSwiper.once('progress', () => {
|
|
||||||
this.slidesSwiperLoaded = true;
|
|
||||||
this.calculateSlides();
|
|
||||||
});
|
|
||||||
|
|
||||||
const selectedTab = this.calculateInitialTab();
|
|
||||||
if (!selectedTab) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.firstSelectedTab = selectedTab.id!;
|
try {
|
||||||
this.selectTab(this.firstSelectedTab);
|
await this.initializeSlider();
|
||||||
|
await this.initializeTabs();
|
||||||
|
} catch {
|
||||||
|
// Something went wrong, ignore.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize the slider elements.
|
||||||
|
*/
|
||||||
|
protected async initializeSlider(): Promise<void> {
|
||||||
|
if (this.initialized) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.slidesElement) {
|
||||||
|
// Already initializated, await for ready.
|
||||||
|
await this.slidesElement.componentOnReady();
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.slides) {
|
||||||
|
await CoreUtils.nextTick();
|
||||||
|
}
|
||||||
|
const slidesSwiper = await this.slides?.getSwiper();
|
||||||
|
if (!slidesSwiper || !this.slides) {
|
||||||
|
throw new CoreError('Swiper not found, will try on next change.');
|
||||||
|
}
|
||||||
|
|
||||||
|
this.slidesElement = <HTMLIonSlidesElement>slidesSwiper.el;
|
||||||
|
await this.slidesElement.componentOnReady();
|
||||||
|
|
||||||
this.initialized = true;
|
this.initialized = true;
|
||||||
|
|
||||||
|
// Subscribe to changes.
|
||||||
|
this.subscriptions.push(this.slides.ionSlideDidChange.subscribe(() => {
|
||||||
|
this.slideChanged();
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize the tabs, determining the first tab to be shown.
|
||||||
|
*/
|
||||||
|
protected async initializeTabs(): Promise<void> {
|
||||||
|
if (!this.initialized || !this.slidesElement) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const selectedTab = this.calculateInitialTab();
|
||||||
|
if (!selectedTab) {
|
||||||
|
// No enabled tabs, return.
|
||||||
|
throw new CoreError('No enabled tabs.');
|
||||||
|
}
|
||||||
|
|
||||||
|
this.firstSelectedTab = selectedTab.id;
|
||||||
|
if (this.firstSelectedTab !== undefined) {
|
||||||
|
this.selectTab(this.firstSelectedTab);
|
||||||
|
}
|
||||||
|
|
||||||
// Check which arrows should be shown.
|
// Check which arrows should be shown.
|
||||||
this.calculateSlides();
|
this.calculateSlides();
|
||||||
|
|
||||||
|
this.resizeListener = CoreDom.onWindowResize(() => {
|
||||||
|
this.calculateSlides();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -277,7 +323,7 @@ export class CoreTabsBaseComponent<T extends CoreTabBase> implements OnInit, Aft
|
||||||
* Method executed when the slides are changed.
|
* Method executed when the slides are changed.
|
||||||
*/
|
*/
|
||||||
async slideChanged(): Promise<void> {
|
async slideChanged(): Promise<void> {
|
||||||
if (!this.slidesSwiperLoaded) {
|
if (!this.slidesElement) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -302,17 +348,15 @@ export class CoreTabsBaseComponent<T extends CoreTabBase> implements OnInit, Aft
|
||||||
* Updates the number of slides to show.
|
* Updates the number of slides to show.
|
||||||
*/
|
*/
|
||||||
protected async updateSlides(): Promise<void> {
|
protected async updateSlides(): Promise<void> {
|
||||||
this.numTabsShown = this.tabs.reduce((prev: number, current) => current.enabled ? prev + 1 : prev, 0);
|
if (!this.slides) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.slidesOpts = { ...this.slidesOpts, slidesPerView: Math.min(this.maxSlides, this.numTabsShown) };
|
this.slidesOpts = { ...this.slidesOpts, slidesPerView: Math.min(this.maxSlides, this.numTabsShown) };
|
||||||
|
|
||||||
this.slideChanged();
|
await this.slideChanged();
|
||||||
|
|
||||||
// @todo: This call to update() can trigger JS errors in the console if tabs are re-loaded and there's only 1 tab.
|
await this.slides.update();
|
||||||
// For some reason, swiper.slides is undefined inside the Slides class, and the swiper is marked as destroyed.
|
|
||||||
// Changing *ngIf="hideUntil" to [hidden] doesn't solve the issue, and it causes another error to be raised.
|
|
||||||
// This can be tested in lesson as a student, play a lesson and go back to the entry page.
|
|
||||||
await this.slides!.update();
|
|
||||||
|
|
||||||
if (!this.hasSliddenToInitial && this.selectedIndex && this.selectedIndex >= this.slidesOpts.slidesPerView) {
|
if (!this.hasSliddenToInitial && this.selectedIndex && this.selectedIndex >= this.slidesOpts.slidesPerView) {
|
||||||
this.hasSliddenToInitial = true;
|
this.hasSliddenToInitial = true;
|
||||||
|
@ -320,7 +364,7 @@ export class CoreTabsBaseComponent<T extends CoreTabBase> implements OnInit, Aft
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (this.shouldSlideToInitial) {
|
if (this.shouldSlideToInitial) {
|
||||||
this.slides!.slideTo(this.selectedIndex, 0);
|
this.slides?.slideTo(this.selectedIndex, 0);
|
||||||
this.shouldSlideToInitial = false;
|
this.shouldSlideToInitial = false;
|
||||||
}
|
}
|
||||||
}, 400);
|
}, 400);
|
||||||
|
@ -339,17 +383,23 @@ export class CoreTabsBaseComponent<T extends CoreTabBase> implements OnInit, Aft
|
||||||
* Calculate the number of slides that can fit on the screen.
|
* Calculate the number of slides that can fit on the screen.
|
||||||
*/
|
*/
|
||||||
protected async calculateMaxSlides(): Promise<void> {
|
protected async calculateMaxSlides(): Promise<void> {
|
||||||
if (!this.slidesSwiperLoaded) {
|
if (!this.slidesElement || !this.slides) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.maxSlides = 3;
|
this.maxSlides = 3;
|
||||||
let width = this.slidesSwiper.width;
|
await CoreUtils.nextTick();
|
||||||
if (!width) {
|
|
||||||
this.slidesSwiper.updateSize();
|
|
||||||
width = this.slidesSwiper.width;
|
|
||||||
|
|
||||||
|
let width: number = this.slidesElement.getBoundingClientRect().width;
|
||||||
if (!width) {
|
if (!width) {
|
||||||
|
const slidesSwiper = await this.slides.getSwiper();
|
||||||
|
|
||||||
|
await slidesSwiper.updateSize();
|
||||||
|
await CoreUtils.nextTick();
|
||||||
|
|
||||||
|
width = slidesSwiper.width;
|
||||||
|
if (!width) {
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -456,12 +506,12 @@ export class CoreTabsBaseComponent<T extends CoreTabBase> implements OnInit, Aft
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.selected) {
|
if (this.selected && this.slides) {
|
||||||
// Check if we need to slide to the tab because it's not visible.
|
// Check if we need to slide to the tab because it's not visible.
|
||||||
const firstVisibleTab = await this.slides!.getActiveIndex();
|
const firstVisibleTab = await this.slides.getActiveIndex();
|
||||||
const lastVisibleTab = firstVisibleTab + this.slidesOpts.slidesPerView - 1;
|
const lastVisibleTab = firstVisibleTab + this.slidesOpts.slidesPerView - 1;
|
||||||
if (index < firstVisibleTab || index > lastVisibleTab) {
|
if (index < firstVisibleTab || index > lastVisibleTab) {
|
||||||
await this.slides!.slideTo(index, 0, true);
|
await this.slides.slideTo(index, 0, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -470,9 +520,9 @@ export class CoreTabsBaseComponent<T extends CoreTabBase> implements OnInit, Aft
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ok = await this.loadTab(tabToSelect);
|
const suceeded = await this.loadTab(tabToSelect);
|
||||||
|
|
||||||
if (ok !== false) {
|
if (suceeded !== false) {
|
||||||
this.tabSelected(tabToSelect, index);
|
this.tabSelected(tabToSelect, index);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -503,15 +553,6 @@ export class CoreTabsBaseComponent<T extends CoreTabBase> implements OnInit, Aft
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Adapt tabs to a window resize.
|
|
||||||
*/
|
|
||||||
protected windowResized(): void {
|
|
||||||
setTimeout(() => {
|
|
||||||
this.calculateSlides();
|
|
||||||
}, 200);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component destroyed.
|
* Component destroyed.
|
||||||
*/
|
*/
|
||||||
|
@ -519,7 +560,7 @@ export class CoreTabsBaseComponent<T extends CoreTabBase> implements OnInit, Aft
|
||||||
this.isDestroyed = true;
|
this.isDestroyed = true;
|
||||||
|
|
||||||
this.resizeListener?.off();
|
this.resizeListener?.off();
|
||||||
this.languageChangedSubscription?.unsubscribe();
|
this.subscriptions.forEach((subscription) => subscription.unsubscribe());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -541,8 +582,8 @@ class CoreTabsRoleTab<T extends CoreTabBase> extends CoreAriaRoleTab<CoreTabsBas
|
||||||
*/
|
*/
|
||||||
getSelectableTabs(): CoreAriaRoleTabFindable[] {
|
getSelectableTabs(): CoreAriaRoleTabFindable[] {
|
||||||
return this.componentInstance.tabs.filter((tab) => tab.enabled).map((tab) => ({
|
return this.componentInstance.tabs.filter((tab) => tab.enabled).map((tab) => ({
|
||||||
id: tab.id!,
|
id: tab.id || '',
|
||||||
findIndex: tab.id!,
|
findIndex: tab.id || '',
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,10 +6,9 @@
|
||||||
<ion-icon *ngIf="showPrevButton" name="fas-chevron-left" [attr.aria-label]="'core.previous' | translate"></ion-icon>
|
<ion-icon *ngIf="showPrevButton" name="fas-chevron-left" [attr.aria-label]="'core.previous' | translate"></ion-icon>
|
||||||
</ion-col>
|
</ion-col>
|
||||||
<ion-col class="ion-no-padding" size="10">
|
<ion-col class="ion-no-padding" size="10">
|
||||||
<ion-slides (ionSlideDidChange)="slideChanged()" [options]="slidesOpts" [dir]="direction" role="tablist"
|
<ion-slides [options]="slidesOpts" [dir]="direction" role="tablist" [attr.aria-label]="description">
|
||||||
[attr.aria-label]="description">
|
|
||||||
<ng-container *ngFor="let tab of tabs">
|
<ng-container *ngFor="let tab of tabs">
|
||||||
<ion-slide role="presentation" [id]="tab.id! + '-tab'" class="tab-slide" tabindex="-1"
|
<ion-slide role="presentation" class="tab-slide" [id]=" tab.id! + '-tab'" tabindex="-1"
|
||||||
[class.selected]="selected == tab.id">
|
[class.selected]="selected == tab.id">
|
||||||
<ion-tab-button (ionTabButtonClick)="selectTab(tab.id, $event)" (keydown)="tabAction.keyDown($event)"
|
<ion-tab-button (ionTabButtonClick)="selectTab(tab.id, $event)" (keydown)="tabAction.keyDown($event)"
|
||||||
(keyup)="tabAction.keyUp(tab.id, $event)" [tab]="tab.page" [layout]="layout" class="{{tab.class}}"
|
(keyup)="tabAction.keyUp(tab.id, $event)" [tab]="tab.page" [layout]="layout" class="{{tab.class}}"
|
||||||
|
|
|
@ -70,7 +70,7 @@ export class CoreTabsOutletComponent extends CoreTabsBaseComponent<CoreTabsOutle
|
||||||
protected existsInNavigationStack = false;
|
protected existsInNavigationStack = false;
|
||||||
|
|
||||||
constructor(element: ElementRef) {
|
constructor(element: ElementRef) {
|
||||||
super(element);
|
super();
|
||||||
|
|
||||||
CoreComponentsRegistry.register(element.nativeElement, this);
|
CoreComponentsRegistry.register(element.nativeElement, this);
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,10 +5,9 @@
|
||||||
<ion-icon *ngIf="showPrevButton" name="fas-chevron-left" [attr.aria-label]="'core.previous' | translate"></ion-icon>
|
<ion-icon *ngIf="showPrevButton" name="fas-chevron-left" [attr.aria-label]="'core.previous' | translate"></ion-icon>
|
||||||
</ion-col>
|
</ion-col>
|
||||||
<ion-col class="ion-no-padding" size="10">
|
<ion-col class="ion-no-padding" size="10">
|
||||||
<ion-slides (ionSlideDidChange)="slideChanged()" [options]="slidesOpts" [dir]="direction" role="tablist"
|
<ion-slides [options]="slidesOpts" [dir]="direction" role="tablist" [attr.aria-label]="description">
|
||||||
[attr.aria-label]="description">
|
|
||||||
<ng-container *ngFor="let tab of tabs">
|
<ng-container *ngFor="let tab of tabs">
|
||||||
<ion-slide *ngIf="tab.enabled" role="presentation" [hidden]="!hideUntil" class="tab-slide" [id]="tab.id! + '-tab'"
|
<ion-slide *ngIf="tab.enabled" role="presentation" class="tab-slide" [id]="tab.id! + '-tab'"
|
||||||
[class.selected]="selected == tab.id">
|
[class.selected]="selected == tab.id">
|
||||||
<ion-tab-button (click)="selectTab(tab.id, $event)" (keydown)="tabAction.keyDown($event)"
|
<ion-tab-button (click)="selectTab(tab.id, $event)" (keydown)="tabAction.keyDown($event)"
|
||||||
(keyup)="tabAction.keyUp(tab.id, $event)" class="{{tab.class}}" [layout]="layout" role="tab"
|
(keyup)="tabAction.keyUp(tab.id, $event)" class="{{tab.class}}" [layout]="layout" role="tab"
|
||||||
|
|
|
@ -65,7 +65,7 @@ export class CoreTabComponent implements OnInit, OnDestroy, CoreTabBase {
|
||||||
return this.isEnabled;
|
return this.isEnabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Input() id?: string; // An ID to identify the tab.
|
@Input() id = ''; // An ID to identify the tab.
|
||||||
@Output() ionSelect: EventEmitter<CoreTabComponent> = new EventEmitter<CoreTabComponent>();
|
@Output() ionSelect: EventEmitter<CoreTabComponent> = new EventEmitter<CoreTabComponent>();
|
||||||
|
|
||||||
@ContentChild(TemplateRef) template?: TemplateRef<unknown>; // Template defined by the content.
|
@ContentChild(TemplateRef) template?: TemplateRef<unknown>; // Template defined by the content.
|
||||||
|
@ -82,7 +82,7 @@ export class CoreTabComponent implements OnInit, OnDestroy, CoreTabBase {
|
||||||
element: ElementRef,
|
element: ElementRef,
|
||||||
) {
|
) {
|
||||||
this.element = element.nativeElement;
|
this.element = element.nativeElement;
|
||||||
|
this.id = this.id || 'core-tab-' + CoreUtils.getUniqueId('CoreTabComponent');
|
||||||
this.element.setAttribute('role', 'tabpanel');
|
this.element.setAttribute('role', 'tabpanel');
|
||||||
this.element.setAttribute('tabindex', '0');
|
this.element.setAttribute('tabindex', '0');
|
||||||
this.element.setAttribute('aria-hidden', 'true');
|
this.element.setAttribute('aria-hidden', 'true');
|
||||||
|
@ -92,7 +92,6 @@ export class CoreTabComponent implements OnInit, OnDestroy, CoreTabBase {
|
||||||
* Component being initialized.
|
* Component being initialized.
|
||||||
*/
|
*/
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.id = this.id || 'core-tab-' + CoreUtils.getUniqueId('CoreTabComponent');
|
|
||||||
this.element.setAttribute('aria-labelledby', this.id + '-tab');
|
this.element.setAttribute('aria-labelledby', this.id + '-tab');
|
||||||
this.element.setAttribute('id', this.id);
|
this.element.setAttribute('id', this.id);
|
||||||
|
|
||||||
|
|
|
@ -39,7 +39,7 @@
|
||||||
ion-tab-button {
|
ion-tab-button {
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
ion-label {
|
ion-label {
|
||||||
font-size: 16px;
|
font-size: 14px;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
@ -109,8 +109,3 @@
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
:host-context(.ios) {
|
|
||||||
--height: 53px;
|
|
||||||
}
|
|
||||||
|
|
|
@ -51,12 +51,6 @@ export class CoreTabsComponent extends CoreTabsBaseComponent<CoreTabComponent> i
|
||||||
|
|
||||||
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.
|
||||||
|
|
||||||
constructor(
|
|
||||||
element: ElementRef,
|
|
||||||
) {
|
|
||||||
super(element);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* View has been initialized.
|
* View has been initialized.
|
||||||
*/
|
*/
|
||||||
|
@ -84,7 +78,7 @@ export class CoreTabsComponent extends CoreTabsBaseComponent<CoreTabComponent> i
|
||||||
*/
|
*/
|
||||||
addTab(tab: CoreTabComponent): void {
|
addTab(tab: CoreTabComponent): void {
|
||||||
// Check if tab is already in the list.
|
// Check if tab is already in the list.
|
||||||
if (this.getTabIndex(tab.id!) == -1) {
|
if (this.getTabIndex(tab.id) === -1) {
|
||||||
this.tabs.push(tab);
|
this.tabs.push(tab);
|
||||||
this.sortTabs();
|
this.sortTabs();
|
||||||
|
|
||||||
|
@ -100,7 +94,7 @@ export class CoreTabsComponent extends CoreTabsBaseComponent<CoreTabComponent> i
|
||||||
* @param tab The tab to remove.
|
* @param tab The tab to remove.
|
||||||
*/
|
*/
|
||||||
removeTab(tab: CoreTabComponent): void {
|
removeTab(tab: CoreTabComponent): void {
|
||||||
const index = this.getTabIndex(tab.id!);
|
const index = this.getTabIndex(tab.id);
|
||||||
this.tabs.splice(index, 1);
|
this.tabs.splice(index, 1);
|
||||||
|
|
||||||
this.calculateSlides();
|
this.calculateSlides();
|
||||||
|
|
|
@ -131,6 +131,7 @@ export class CoreCourseIndexPage implements OnInit, OnDestroy {
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
CoreDomUtils.showErrorModal(error);
|
CoreDomUtils.showErrorModal(error);
|
||||||
CoreNavigator.back();
|
CoreNavigator.back();
|
||||||
|
this.loaded = true;
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -224,9 +225,9 @@ export class CoreCourseIndexPage implements OnInit, OnDestroy {
|
||||||
// Select the tab if needed.
|
// Select the tab if needed.
|
||||||
this.firstTabName = undefined;
|
this.firstTabName = undefined;
|
||||||
if (tabToLoad) {
|
if (tabToLoad) {
|
||||||
setTimeout(() => {
|
await CoreUtils.nextTick();
|
||||||
this.tabsComponent?.selectByIndex(tabToLoad!);
|
|
||||||
});
|
this.tabsComponent?.selectByIndex(tabToLoad);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue