MOBILE-3833 tabs: Remove MAX_HEIGHT_TO_HIDE_TABS and scroll hide
parent
0336ab0808
commit
f56cfa3ab6
|
@ -26,7 +26,7 @@ import {
|
||||||
SimpleChange,
|
SimpleChange,
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
import { IonSlides } from '@ionic/angular';
|
import { IonSlides } from '@ionic/angular';
|
||||||
import { BackButtonEvent, ScrollDetail } from '@ionic/core';
|
import { BackButtonEvent } from '@ionic/core';
|
||||||
import { Subscription } from 'rxjs';
|
import { Subscription } from 'rxjs';
|
||||||
|
|
||||||
import { Platform, Translate } from '@singletons';
|
import { Platform, Translate } from '@singletons';
|
||||||
|
@ -45,9 +45,6 @@ export class CoreTabsBaseComponent<T extends CoreTabBase> implements OnInit, Aft
|
||||||
|
|
||||||
// Minimum tab's width.
|
// Minimum tab's width.
|
||||||
protected static readonly MIN_TAB_WIDTH = 107;
|
protected static readonly MIN_TAB_WIDTH = 107;
|
||||||
// @todo [4.0]
|
|
||||||
// Max height that allows tab hiding. WARNING: Hide tabs on scroll disabled. If confirmed, remove the associated code.
|
|
||||||
protected static readonly MAX_HEIGHT_TO_HIDE_TABS = 0;
|
|
||||||
|
|
||||||
@Input() selectedIndex = 0; // Index of the tab to select.
|
@Input() selectedIndex = 0; // Index of the tab to select.
|
||||||
@Input() hideUntil = false; // Determine when should the contents be shown.
|
@Input() hideUntil = false; // Determine when should the contents be shown.
|
||||||
|
@ -73,10 +70,6 @@ export class CoreTabsBaseComponent<T extends CoreTabBase> implements OnInit, Aft
|
||||||
protected initialized = false;
|
protected initialized = false;
|
||||||
protected afterViewInitTriggered = false;
|
protected afterViewInitTriggered = false;
|
||||||
|
|
||||||
protected tabBarHeight = 0;
|
|
||||||
protected tabsElement?: HTMLElement; // The tabs parent element. It's the element that will be "scrolled" to hide tabs.
|
|
||||||
protected tabBarElement?: HTMLIonTabBarElement; // The top tab bar element.
|
|
||||||
protected tabsShown = true;
|
|
||||||
protected resizeListener?: CoreEventObserver;
|
protected resizeListener?: CoreEventObserver;
|
||||||
protected isDestroyed = false;
|
protected isDestroyed = false;
|
||||||
protected isCurrentView = true;
|
protected isCurrentView = true;
|
||||||
|
@ -91,9 +84,6 @@ export class CoreTabsBaseComponent<T extends CoreTabBase> implements OnInit, Aft
|
||||||
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 slidesSwiper: any; // eslint-disable-line @typescript-eslint/no-explicit-any
|
||||||
protected slidesSwiperLoaded = false;
|
protected slidesSwiperLoaded = false;
|
||||||
protected scrollElements: Record<string | number, HTMLElement> = {}; // Scroll elements for each loaded tab.
|
|
||||||
protected lastScroll = 0;
|
|
||||||
protected previousLastScroll = 0;
|
|
||||||
|
|
||||||
tabAction: CoreTabsRoleTab<T>;
|
tabAction: CoreTabsRoleTab<T>;
|
||||||
|
|
||||||
|
@ -128,7 +118,6 @@ export class CoreTabsBaseComponent<T extends CoreTabBase> implements OnInit, Aft
|
||||||
}
|
}
|
||||||
|
|
||||||
this.afterViewInitTriggered = true;
|
this.afterViewInitTriggered = true;
|
||||||
this.tabBarElement = this.element.nativeElement.querySelector('ion-tab-bar');
|
|
||||||
|
|
||||||
if (!this.initialized && this.hideUntil) {
|
if (!this.initialized && this.hideUntil) {
|
||||||
// Tabs should be shown, initialize them.
|
// Tabs should be shown, initialize them.
|
||||||
|
@ -140,49 +129,6 @@ export class CoreTabsBaseComponent<T extends CoreTabBase> implements OnInit, Aft
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Calculate the tab bar height.
|
|
||||||
*/
|
|
||||||
protected calculateTabBarHeight(): void {
|
|
||||||
if (!this.tabBarElement) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.tabBarHeight = this.tabBarElement.offsetHeight;
|
|
||||||
|
|
||||||
this.applyScroll(this.tabsShown, this.lastScroll);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Apply scroll to hiding tabs.
|
|
||||||
*
|
|
||||||
* @param showTabs Show or completely hide tabs.
|
|
||||||
* @param scroll Scroll position.
|
|
||||||
*/
|
|
||||||
protected applyScroll(showTabs: boolean, scroll?: number): void {
|
|
||||||
if (!this.tabBarElement || !this.tabBarHeight) {
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (showTabs) {
|
|
||||||
// Smooth translation.
|
|
||||||
this.tabBarElement.classList.remove('tabs-hidden');
|
|
||||||
if (scroll === 0) {
|
|
||||||
this.tabBarElement.style.height = '';
|
|
||||||
this.previousLastScroll = this.lastScroll;
|
|
||||||
this.lastScroll = 0;
|
|
||||||
} else if (scroll !== undefined) {
|
|
||||||
this.tabBarElement.style.height = (this.tabBarHeight - scroll) + 'px';
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this.tabBarElement.classList.add('tabs-hidden');
|
|
||||||
this.tabBarElement.style.height = '';
|
|
||||||
}
|
|
||||||
|
|
||||||
this.tabsShown = showTabs;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritdoc
|
* @inheritdoc
|
||||||
*/
|
*/
|
||||||
|
@ -260,15 +206,6 @@ export class CoreTabsBaseComponent<T extends CoreTabBase> implements OnInit, Aft
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (window.innerHeight >= CoreTabsBaseComponent.MAX_HEIGHT_TO_HIDE_TABS) {
|
|
||||||
// Ensure tabbar is shown.
|
|
||||||
this.applyScroll(true, 0);
|
|
||||||
this.calculateTabBarHeight();
|
|
||||||
} else if (!this.tabsShown) {
|
|
||||||
// Don't recalculate.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await this.calculateMaxSlides();
|
await this.calculateMaxSlides();
|
||||||
|
|
||||||
await this.updateSlides();
|
await this.updateSlides();
|
||||||
|
@ -314,9 +251,6 @@ export class CoreTabsBaseComponent<T extends CoreTabBase> implements OnInit, Aft
|
||||||
this.firstSelectedTab = selectedTab.id!;
|
this.firstSelectedTab = selectedTab.id!;
|
||||||
this.selectTab(this.firstSelectedTab);
|
this.selectTab(this.firstSelectedTab);
|
||||||
|
|
||||||
// Setup tab scrolling.
|
|
||||||
this.calculateTabBarHeight();
|
|
||||||
|
|
||||||
this.initialized = true;
|
this.initialized = true;
|
||||||
|
|
||||||
// Check which arrows should be shown.
|
// Check which arrows should be shown.
|
||||||
|
@ -374,8 +308,6 @@ export class CoreTabsBaseComponent<T extends CoreTabBase> implements OnInit, Aft
|
||||||
|
|
||||||
this.slideChanged();
|
this.slideChanged();
|
||||||
|
|
||||||
this.calculateTabBarHeight();
|
|
||||||
|
|
||||||
// @todo: This call to update() can trigger JS errors in the console if tabs are re-loaded and there's only 1 tab.
|
// @todo: This call to update() can trigger JS errors in the console if tabs are re-loaded and there's only 1 tab.
|
||||||
// For some reason, swiper.slides is undefined inside the Slides class, and the swiper is marked as destroyed.
|
// 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.
|
// Changing *ngIf="hideUntil" to [hidden] doesn't solve the issue, and it causes another error to be raised.
|
||||||
|
@ -484,61 +416,6 @@ export class CoreTabsBaseComponent<T extends CoreTabBase> implements OnInit, Aft
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Show or hide the tabs. This is used when the user is scrolling inside a tab.
|
|
||||||
*
|
|
||||||
* @param scrollTop Scroll top.
|
|
||||||
* @param scrollElement Content scroll element to check measures.
|
|
||||||
*/
|
|
||||||
showHideTabs(scrollTop: number, scrollElement: HTMLElement): void {
|
|
||||||
if (!this.tabBarElement || !this.tabsElement || !scrollElement) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Always show on very tall screens.
|
|
||||||
if (window.innerHeight >= CoreTabsBaseComponent.MAX_HEIGHT_TO_HIDE_TABS) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.tabBarHeight && this.tabBarElement.offsetHeight != this.tabBarHeight) {
|
|
||||||
// Wrong tab height, recalculate it.
|
|
||||||
this.calculateTabBarHeight();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.tabBarHeight) {
|
|
||||||
// We don't have the tab bar height, this means the tab bar isn't shown.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (scrollTop <= 0) {
|
|
||||||
// Ensure tabbar is shown.
|
|
||||||
this.applyScroll(true, 0);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (scrollTop == this.lastScroll || scrollTop == this.previousLastScroll) {
|
|
||||||
// Ensure scroll has been modified to avoid flicks.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.tabsShown && scrollTop > this.tabBarHeight) {
|
|
||||||
// Hide tabs.
|
|
||||||
this.applyScroll(false);
|
|
||||||
} else if (!this.tabsShown && scrollTop <= this.tabBarHeight) {
|
|
||||||
this.applyScroll(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.tabsShown && scrollElement.scrollHeight > scrollElement.clientHeight + (this.tabBarHeight - scrollTop)) {
|
|
||||||
// Smooth translation.
|
|
||||||
this.applyScroll(true, scrollTop);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use lastScroll after moving the tabs to avoid flickering.
|
|
||||||
this.previousLastScroll = this.lastScroll;
|
|
||||||
this.lastScroll = scrollTop;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Select a tab by ID.
|
* Select a tab by ID.
|
||||||
*
|
*
|
||||||
|
@ -626,39 +503,6 @@ export class CoreTabsBaseComponent<T extends CoreTabBase> implements OnInit, Aft
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Listen scroll events in an element's inner ion-content (if any).
|
|
||||||
*
|
|
||||||
* @param element Element to search ion-content in.
|
|
||||||
* @param id ID of the tab/page.
|
|
||||||
* @return Promise resolved when done.
|
|
||||||
*/
|
|
||||||
async listenContentScroll(element: HTMLElement, id: number | string): Promise<void> {
|
|
||||||
if (this.scrollElements[id]) {
|
|
||||||
return; // Already set.
|
|
||||||
}
|
|
||||||
|
|
||||||
let content = element.querySelector('ion-content');
|
|
||||||
if (!content) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Search the inner ion-content if there's more than one.
|
|
||||||
let childContent = content.querySelector('ion-content') || null;
|
|
||||||
while (childContent != null) {
|
|
||||||
content = childContent;
|
|
||||||
childContent = content.querySelector('ion-content') || null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const scroll = await content.getScrollElement();
|
|
||||||
|
|
||||||
content.scrollEvents = true;
|
|
||||||
this.scrollElements[id] = scroll;
|
|
||||||
content.addEventListener('ionScroll', (e: CustomEvent<ScrollDetail>): void => {
|
|
||||||
this.showHideTabs(e.detail.scrollTop, scroll);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adapt tabs to a window resize.
|
* Adapt tabs to a window resize.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -97,7 +97,6 @@ export class CoreTabsOutletComponent extends CoreTabsBaseComponent<CoreTabsOutle
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.tabsElement = this.element.nativeElement.querySelector('ion-tabs');
|
|
||||||
this.stackEventsSubscription = this.ionTabs.outlet.stackEvents.subscribe(async (stackEvent: StackEvent) => {
|
this.stackEventsSubscription = this.ionTabs.outlet.stackEvents.subscribe(async (stackEvent: StackEvent) => {
|
||||||
if (!this.isCurrentView) {
|
if (!this.isCurrentView) {
|
||||||
return;
|
return;
|
||||||
|
@ -118,14 +117,6 @@ export class CoreTabsOutletComponent extends CoreTabsBaseComponent<CoreTabsOutle
|
||||||
}
|
}
|
||||||
|
|
||||||
this.showHideNavBarButtons(stackEvent.enteringView.element.tagName);
|
this.showHideNavBarButtons(stackEvent.enteringView.element.tagName);
|
||||||
|
|
||||||
await this.listenContentScroll(stackEvent.enteringView.element, stackEvent.enteringView.id);
|
|
||||||
|
|
||||||
const scrollElement = this.scrollElements[stackEvent.enteringView.id];
|
|
||||||
if (scrollElement) {
|
|
||||||
// Show or hide tabs based on the new page scroll.
|
|
||||||
this.showHideTabs(scrollElement.scrollTop, scrollElement);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
this.outletActivatedSubscription = this.ionTabs.outlet.activateEvents.subscribe(() => {
|
this.outletActivatedSubscription = this.ionTabs.outlet.activateEvents.subscribe(() => {
|
||||||
this.lastActiveComponent = this.ionTabs.outlet.component;
|
this.lastActiveComponent = this.ionTabs.outlet.component;
|
||||||
|
|
|
@ -120,9 +120,6 @@ export class CoreTabComponent implements OnInit, OnDestroy, CoreTabBase {
|
||||||
this.loaded = true;
|
this.loaded = true;
|
||||||
this.ionSelect.emit(this);
|
this.ionSelect.emit(this);
|
||||||
this.showHideNavBarButtons(true);
|
this.showHideNavBarButtons(true);
|
||||||
|
|
||||||
// Setup tab scrolling.
|
|
||||||
this.tabs.listenContentScroll(this.element, this.id!);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -67,7 +67,6 @@ export class CoreTabsComponent extends CoreTabsBaseComponent<CoreTabComponent> i
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.tabsElement = this.element.nativeElement;
|
|
||||||
this.originalTabsContainer = this.originalTabsRef?.nativeElement;
|
this.originalTabsContainer = this.originalTabsRef?.nativeElement;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -92,14 +91,6 @@ export class CoreTabsComponent extends CoreTabsBaseComponent<CoreTabComponent> i
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.calculateSlides();
|
this.calculateSlides();
|
||||||
});
|
});
|
||||||
|
|
||||||
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.
|
|
||||||
// Use timeout to wait for the view to be rendered. 0 ms should be enough, use 50 to be sure.
|
|
||||||
setTimeout(() => {
|
|
||||||
this.calculateTabBarHeight();
|
|
||||||
}, 50);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue