@ionic/core
Version:
Base components for Ionic
244 lines (237 loc) • 9.29 kB
JavaScript
/*!
* (C) Ionic http://ionicframework.com - MIT License
*/
'use strict';
var index = require('./index-BiQyIvoc.js');
var frameworkDelegate = require('./framework-delegate-JbKIVMzR.js');
require('./helpers-Cqr69qxA.js');
require('./dir-zwDPRE5K.js');
const tabCss = () => `:host(.tab-hidden){display:none !important}`;
const Tab = class {
constructor(hostRef) {
index.registerInstance(this, hostRef);
/** @internal */
this.active = false;
}
async componentWillLoad() {
if (this.active) {
await this.setActive();
}
}
/** Set the active component for the tab */
async setActive() {
await this.prepareLazyLoaded();
this.active = true;
}
changeActive(isActive) {
if (isActive) {
this.prepareLazyLoaded().catch((e) => {
index.printIonError('[ion-tab] - Exception in prepareLazyLoaded:', e);
});
}
}
prepareLazyLoaded() {
if (this.component == null) {
return Promise.resolve(undefined);
}
/**
* The attach is cached so that concurrent activations share a single
* attempt and later activations reuse the result. The cache is cleared
* only when the attach fails, so a tab whose component failed to load
* can be retried the next time it is activated.
*/
if (this.loadPromise === undefined) {
this.loadPromise = frameworkDelegate.attachComponent(this.delegate, this.el, this.component, ['ion-page']).catch((e) => {
this.loadPromise = undefined;
throw e;
});
}
return this.loadPromise;
}
render() {
const { tab, active, component } = this;
return (index.h(index.Host, { key: '8ecbd7a85d3370ed8ed40561cd8134e71584d6aa', role: "tabpanel", "aria-hidden": !active ? 'true' : null, "aria-labelledby": `tab-button-${tab}`, class: {
'ion-page': component === undefined,
'tab-hidden': !active,
} }, index.h("slot", { key: '8d6326d75812888e33fd09665b7e588e1e270df1' })));
}
get el() { return index.getElement(this); }
static get watchers() { return {
"active": [{
"changeActive": 0
}]
}; }
};
Tab.style = tabCss();
const tabsCss = () => `:host{left:0;right:0;top:0;bottom:0;display:-ms-flexbox;display:flex;position:absolute;-ms-flex-direction:column;flex-direction:column;width:100%;height:100%;contain:layout size style;z-index:0}.tabs-inner{position:relative;-ms-flex:1;flex:1;contain:layout size style}`;
const Tabs = class {
constructor(hostRef) {
index.registerInstance(this, hostRef);
this.ionNavWillLoad = index.createEvent(this, "ionNavWillLoad", 7);
this.ionTabsWillChange = index.createEvent(this, "ionTabsWillChange", 3);
this.ionTabsDidChange = index.createEvent(this, "ionTabsDidChange", 3);
this.transitioning = false;
/** @internal */
this.useRouter = false;
this.onTabClicked = (ev) => {
const { href, tab } = ev.detail;
if (this.useRouter && href !== undefined) {
const router = document.querySelector('ion-router');
if (router) {
router.push(href);
}
}
else {
this.select(tab);
}
};
}
async componentWillLoad() {
if (!this.useRouter) {
/**
* JavaScript and StencilJS use `ion-router`, while
* the other frameworks use `ion-router-outlet`.
*
* If either component is present then tabs will not use
* a basic tab-based navigation. It will use the history
* stack or URL updates associated with the router.
*/
this.useRouter =
(!!this.el.querySelector('ion-router-outlet') || !!document.querySelector('ion-router')) &&
!this.el.closest('[no-router]');
}
if (!this.useRouter) {
const tabs = this.tabs;
if (tabs.length > 0) {
await this.select(tabs[0]);
}
}
this.ionNavWillLoad.emit();
}
componentDidLoad() {
this.updateTabBar();
}
componentDidUpdate() {
this.updateTabBar();
}
updateTabBar() {
const tabBar = this.el.querySelector('ion-tab-bar');
if (!tabBar) {
return;
}
const tab = this.selectedTab ? this.selectedTab.tab : undefined;
// If tabs has no selected tab but tab-bar already has a selected-tab set,
// don't overwrite it. This handles cases where tab-bar is used without ion-tab elements.
if (tab === undefined) {
return;
}
if (tabBar.selectedTab === tab) {
return;
}
tabBar.selectedTab = tab;
}
/**
* Select a tab by the value of its `tab` property or an element reference. This method is only available for vanilla JavaScript projects. The Angular, React, and Vue implementations of tabs are coupled to each framework's router.
*
* @param tab The tab instance to select. If passed a string, it should be the value of the tab's `tab` property.
*/
async select(tab) {
const selectedTab = getTab(this.tabs, tab);
if (!this.shouldSwitch(selectedTab)) {
return false;
}
await this.setActive(selectedTab);
await this.notifyRouter();
this.tabSwitch();
return true;
}
/**
* Get a specific tab by the value of its `tab` property or an element reference. This method is only available for vanilla JavaScript projects. The Angular, React, and Vue implementations of tabs are coupled to each framework's router.
*
* @param tab The tab instance to select. If passed a string, it should be the value of the tab's `tab` property.
*/
async getTab(tab) {
return getTab(this.tabs, tab);
}
/**
* Get the currently selected tab. This method is only available for vanilla JavaScript projects. The Angular, React, and Vue implementations of tabs are coupled to each framework's router.
*/
getSelected() {
return Promise.resolve(this.selectedTab ? this.selectedTab.tab : undefined);
}
/** @internal */
async setRouteId(id) {
const selectedTab = getTab(this.tabs, id);
if (!this.shouldSwitch(selectedTab)) {
return { changed: false, element: this.selectedTab };
}
await this.setActive(selectedTab);
return {
changed: true,
element: this.selectedTab,
markVisible: () => this.tabSwitch(),
};
}
/** @internal */
async getRouteId() {
const tabId = this.selectedTab?.tab;
return tabId !== undefined ? { id: tabId, element: this.selectedTab } : undefined;
}
setActive(selectedTab) {
if (this.transitioning) {
return Promise.reject('transitioning already happening');
}
this.transitioning = true;
this.leavingTab = this.selectedTab;
this.selectedTab = selectedTab;
this.ionTabsWillChange.emit({ tab: selectedTab.tab });
selectedTab.active = true;
this.updateTabBar();
return Promise.resolve();
}
tabSwitch() {
const selectedTab = this.selectedTab;
const leavingTab = this.leavingTab;
this.leavingTab = undefined;
this.transitioning = false;
if (!selectedTab) {
return;
}
if (leavingTab !== selectedTab) {
if (leavingTab) {
leavingTab.active = false;
}
this.ionTabsDidChange.emit({ tab: selectedTab.tab });
}
}
notifyRouter() {
if (this.useRouter) {
const router = document.querySelector('ion-router');
if (router) {
return router.navChanged('forward');
}
}
return Promise.resolve(false);
}
shouldSwitch(selectedTab) {
const leavingTab = this.selectedTab;
return selectedTab !== undefined && selectedTab !== leavingTab && !this.transitioning;
}
get tabs() {
return Array.from(this.el.querySelectorAll('ion-tab'));
}
render() {
return (index.h(index.Host, { key: '7b8c6455e0673e343d0c759ed85e0d9970de3bb8', onIonTabButtonClick: this.onTabClicked }, index.h("slot", { key: '7d0d840ae03312ca22bcb694ffca738d6648218e', name: "top" }), index.h("div", { key: 'f336d903800af34af0d36adfc94223b940111e6a', class: "tabs-inner" }, index.h("slot", { key: '14656154959a24ae176eff20f6463043180d2eae' })), index.h("slot", { key: '2be4d114f058d24b0e70fb12cfb0eccd3d6e6a74', name: "bottom" })));
}
get el() { return index.getElement(this); }
};
const getTab = (tabs, tab) => {
const tabEl = typeof tab === 'string' ? tabs.find((t) => t.tab === tab) : tab;
if (!tabEl) {
index.printIonError(`[ion-tabs] - Tab with id: "${tabEl}" does not exist`);
}
return tabEl;
};
Tabs.style = tabsCss();
exports.ion_tab = Tab;
exports.ion_tabs = Tabs;