diff --git a/angular/src/directives/proxies.ts b/angular/src/directives/proxies.ts
index 12800b94b4..1a2b083a47 100644
--- a/angular/src/directives/proxies.ts
+++ b/angular/src/directives/proxies.ts
@@ -824,7 +824,7 @@ export class Tab {
}
export declare interface Tabs extends StencilComponents<'IonTabs'> {}
-@Component({ selector: 'ion-tabs', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, template: '', inputs: ['mode', 'color', 'name', 'tabbarHidden', 'tabbarHighlight', 'tabbarLayout', 'tabbarPlacement', 'translucent', 'useRouter'] })
+@Component({ selector: 'ion-tabs', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, template: '', inputs: ['name', 'tabbarHidden', 'useRouter'] })
export class Tabs {
ionChange: EventEmitter;
ionNavWillLoad: EventEmitter;
@@ -834,7 +834,7 @@ export class Tabs {
constructor(r: ElementRef) {
const el = r.nativeElement;
proxyMethods(this, el, ['select', 'setRouteId', 'getRouteId', 'getTab', 'getSelected']);
- proxyInputs(this, el, ['mode', 'color', 'name', 'tabbarHidden', 'tabbarHighlight', 'tabbarLayout', 'tabbarPlacement', 'translucent', 'useRouter']);
+ proxyInputs(this, el, ['name', 'tabbarHidden', 'useRouter']);
proxyOutputs(this, el, ['ionChange', 'ionNavWillLoad', 'ionNavWillChange', 'ionNavDidChange']);
}
}
diff --git a/core/src/components.d.ts b/core/src/components.d.ts
index b34b211b72..c768d7ea3b 100644
--- a/core/src/components.d.ts
+++ b/core/src/components.d.ts
@@ -4596,10 +4596,6 @@ export namespace Components {
}
interface IonTabs {
- /**
- * The color to use from your application's color palette. Default options are: `"primary"`, `"secondary"`, `"tertiary"`, `"success"`, `"warning"`, `"danger"`, `"light"`, `"medium"`, and `"dark"`. For more information on colors, see [theming](/docs/theming/basics).
- */
- 'color'?: Color;
'getRouteId': () => Promise;
/**
* Get the currently selected tab
@@ -4610,10 +4606,6 @@ export namespace Components {
*/
'getTab': (tabOrIndex: string | number | HTMLIonTabElement) => Promise;
/**
- * The mode determines which platform styles to use. Possible values are: `"ios"` or `"md"`.
- */
- 'mode': Mode;
- /**
* A unique name for the tabs.
*/
'name'?: string;
@@ -4627,35 +4619,11 @@ export namespace Components {
*/
'tabbarHidden': boolean;
/**
- * If true, show the tab highlight bar under the selected tab.
- */
- 'tabbarHighlight'?: boolean;
- /**
- * Set the layout of the text and icon in the tabbar. Available options: `"icon-top"`, `"icon-start"`, `"icon-end"`, `"icon-bottom"`, `"icon-hide"`, `"label-hide"`.
- */
- 'tabbarLayout'?: TabbarLayout;
- /**
- * Set the position of the tabbar, relative to the content. Available options: `"top"`, `"bottom"`.
- */
- 'tabbarPlacement'?: TabbarPlacement;
- /**
- * If true, the tabs will be translucent. Note: In order to scroll content behind the tabs, the `fullscreen` attribute needs to be set on the content. Defaults to `false`.
- */
- 'translucent': boolean;
- /**
* If true, the tabs will use the router and `selectedTab` will not do anything.
*/
'useRouter': boolean;
}
interface IonTabsAttributes extends StencilHTMLAttributes {
- /**
- * The color to use from your application's color palette. Default options are: `"primary"`, `"secondary"`, `"tertiary"`, `"success"`, `"warning"`, `"danger"`, `"light"`, `"medium"`, and `"dark"`. For more information on colors, see [theming](/docs/theming/basics).
- */
- 'color'?: Color;
- /**
- * The mode determines which platform styles to use. Possible values are: `"ios"` or `"md"`.
- */
- 'mode'?: Mode;
/**
* A unique name for the tabs.
*/
@@ -4681,22 +4649,6 @@ export namespace Components {
*/
'tabbarHidden'?: boolean;
/**
- * If true, show the tab highlight bar under the selected tab.
- */
- 'tabbarHighlight'?: boolean;
- /**
- * Set the layout of the text and icon in the tabbar. Available options: `"icon-top"`, `"icon-start"`, `"icon-end"`, `"icon-bottom"`, `"icon-hide"`, `"label-hide"`.
- */
- 'tabbarLayout'?: TabbarLayout;
- /**
- * Set the position of the tabbar, relative to the content. Available options: `"top"`, `"bottom"`.
- */
- 'tabbarPlacement'?: TabbarPlacement;
- /**
- * If true, the tabs will be translucent. Note: In order to scroll content behind the tabs, the `fullscreen` attribute needs to be set on the content. Defaults to `false`.
- */
- 'translucent'?: boolean;
- /**
* If true, the tabs will use the router and `selectedTab` will not do anything.
*/
'useRouter'?: boolean;
diff --git a/core/src/components/tabbar/tabbar.scss b/core/src/components/tabbar/tabbar.scss
index 929ebbaa23..ae6277f708 100644
--- a/core/src/components/tabbar/tabbar.scss
+++ b/core/src/components/tabbar/tabbar.scss
@@ -7,7 +7,6 @@
);
display: flex;
- position: relative;
align-items: center;
justify-content: center;
@@ -21,6 +20,7 @@
user-select: none;
z-index: $z-index-toolbar;
+ box-sizing: content-box;
}
:host(.ion-color) {
diff --git a/core/src/components/tabbar/tabbar.tsx b/core/src/components/tabbar/tabbar.tsx
index a2f8b1ace6..1d2c6e20ec 100644
--- a/core/src/components/tabbar/tabbar.tsx
+++ b/core/src/components/tabbar/tabbar.tsx
@@ -35,10 +35,14 @@ export class Tabbar implements ComponentInterface {
*/
@Prop() placement: TabbarPlacement = 'bottom';
- /** The selected tab component */
+ /**
+ * The selected tab component
+ */
@Prop() selectedTab?: HTMLIonTabElement;
- /** The tabs to render */
+ /**
+ * The tabs to render
+ */
@Prop() tabs: HTMLIonTabElement[] = [];
/**
@@ -51,7 +55,9 @@ export class Tabbar implements ComponentInterface {
*/
@Prop() translucent = false;
- /** Emitted when the tab bar is clicked */
+ /**
+ * Emitted when the tab bar is clicked
+ */
@Event() ionTabbarClick!: EventEmitter;
@Listen('body:keyboardWillHide')
@@ -70,10 +76,6 @@ export class Tabbar implements ComponentInterface {
this.updateHighlight();
}
- private getSelectedButton(): HTMLElement | null {
- return this.el.shadowRoot!.querySelector('.tab-btn-selected');
- }
-
@Watch('selectedTab')
@Listen('window:resize')
private updateHighlight() {
@@ -81,7 +83,7 @@ export class Tabbar implements ComponentInterface {
return;
}
this.queue.read(() => {
- const btn = this.getSelectedButton();
+ const btn = this.el.shadowRoot!.querySelector('.tab-btn-selected') as HTMLElement | null;
const highlight = this.el.shadowRoot!.querySelector('.tabbar-highlight') as HTMLElement;
if (btn && highlight) {
highlight.style.transform = `translate3d(${btn.offsetLeft}px,0,0) scaleX(${btn.offsetWidth})`;
@@ -94,6 +96,7 @@ export class Tabbar implements ComponentInterface {
return {
role: 'tablist',
'aria-hidden': keyboardVisible ? 'true' : null,
+ 'slot': 'tabbar',
class: {
...createColorClasses(color),
'tabbar-translucent': translucent,
diff --git a/core/src/components/tabs/tabs.scss b/core/src/components/tabs/tabs.scss
index 23fa4d7ddd..3f075deac2 100644
--- a/core/src/components/tabs/tabs.scss
+++ b/core/src/components/tabs/tabs.scss
@@ -15,21 +15,15 @@
z-index: $z-index-page-container;
}
-ion-tabbar {
- @include position(null, 0, 0, 0);
+.tabs-inner {
+ position: relative;
+ flex: 1;
+
+ contain: layout size style;
+}
+
+:host(.tabbar-hidden) ion-tabbar,
+:host(.tabbar-hidden)::slotted(ion-tabbar) {
display: none;
- position: absolute;
-}
-
-:host(.tabbar-visible.tabs-md)::slotted(ion-tab) {
- bottom: 56px; // tabbar height (it's fixed!)
-}
-
-:host(.tabbar-visible.tabs-ios)::slotted(ion-tab) {
- bottom: 50px; // tabbar height (it's fixed!)
-}
-
-:host(.tabbar-visible) ion-tabbar {
- display: flex;
}
diff --git a/core/src/components/tabs/tabs.tsx b/core/src/components/tabs/tabs.tsx
index ecb5d82c98..d789f92aa0 100644
--- a/core/src/components/tabs/tabs.tsx
+++ b/core/src/components/tabs/tabs.tsx
@@ -1,12 +1,11 @@
import { Build, Component, Element, Event, EventEmitter, Listen, Method, Prop, State } from '@stencil/core';
-import { Color, Config, IonicConfig, Mode, NavOutlet, RouteID, RouteWrite, TabbarLayout, TabbarPlacement } from '../../interface';
-import { createThemedClasses } from '../../utils/theme';
+import { Config, NavOutlet, RouteID, RouteWrite } from '../../interface';
@Component({
tag: 'ion-tabs',
styleUrl: 'tabs.scss',
- scoped: true
+ shadow: true
})
export class Tabs implements NavOutlet {
@@ -14,6 +13,7 @@ export class Tabs implements NavOutlet {
private transitioning = false;
private tabsId = (++tabIds);
private leavingTab?: HTMLIonTabElement;
+ private userTabbarEl?: HTMLIonTabbarElement;
@Element() el!: HTMLStencilElement;
@@ -23,19 +23,6 @@ export class Tabs implements NavOutlet {
@Prop({ context: 'config' }) config!: Config;
@Prop({ context: 'document' }) doc!: Document;
- /**
- * The mode determines which platform styles to use.
- * Possible values are: `"ios"` or `"md"`.
- */
- @Prop() mode!: Mode;
-
- /**
- * The color to use from your application's color palette.
- * Default options are: `"primary"`, `"secondary"`, `"tertiary"`, `"success"`, `"warning"`, `"danger"`, `"light"`, `"medium"`, and `"dark"`.
- * For more information on colors, see [theming](/docs/theming/basics).
- */
- @Prop() color?: Color;
-
/**
* A unique name for the tabs.
*/
@@ -46,29 +33,6 @@ export class Tabs implements NavOutlet {
*/
@Prop() tabbarHidden = false;
- /**
- * If true, show the tab highlight bar under the selected tab.
- */
- @Prop({ mutable: true }) tabbarHighlight?: boolean;
-
- /**
- * Set the layout of the text and icon in the tabbar. Available options: `"icon-top"`, `"icon-start"`, `"icon-end"`, `"icon-bottom"`, `"icon-hide"`, `"label-hide"`.
- */
- @Prop({ mutable: true }) tabbarLayout?: TabbarLayout;
-
- /**
- * Set the position of the tabbar, relative to the content. Available options: `"top"`, `"bottom"`.
- */
- @Prop({ mutable: true }) tabbarPlacement?: TabbarPlacement;
-
- /**
- * If true, the tabs will be translucent.
- * Note: In order to scroll content behind the tabs, the `fullscreen`
- * attribute needs to be set on the content.
- * Defaults to `false`.
- */
- @Prop() translucent = false;
-
/**
* If true, the tabs will use the router and `selectedTab` will not do anything.
*/
@@ -94,18 +58,16 @@ export class Tabs implements NavOutlet {
*/
@Event() ionNavDidChange!: EventEmitter;
- componentWillLoad() {
+ async componentWillLoad() {
if (!this.useRouter) {
this.useRouter = !!this.doc.querySelector('ion-router') && !this.el.closest('[no-router]');
}
+ this.userTabbarEl = this.el.querySelector('ion-tabbar') || undefined;
- this.loadConfig('tabbarPlacement', 'bottom');
- this.loadConfig('tabbarLayout', 'icon-top');
- this.loadConfig('tabbarHighlight', false);
-
- this.initTabs();
+ await this.initTabs();
this.ionNavWillLoad.emit();
+ this.componentWillUpdate();
}
componentDidLoad() {
@@ -117,6 +79,14 @@ export class Tabs implements NavOutlet {
this.selectedTab = this.leavingTab = undefined;
}
+ componentWillUpdate() {
+ const tabbarEl = this.userTabbarEl;
+ if (tabbarEl) {
+ tabbarEl.tabs = this.tabs.slice();
+ tabbarEl.selectedTab = this.selectedTab;
+ }
+ }
+
@Listen('ionTabMutated')
protected onTabMutated() {
this.el.forceUpdate();
@@ -202,6 +172,7 @@ export class Tabs implements NavOutlet {
tab.btnId = 'tab-' + id;
tab.id = 'tabpanel-' + id;
});
+ return Promise.all(tabs.map(tab => tab.componentOnReady()));
}
private async initSelect(): Promise {
@@ -236,13 +207,6 @@ export class Tabs implements NavOutlet {
}
}
- private loadConfig(attrKey: keyof IonicConfig, fallback: any) {
- const val = (this as any)[attrKey];
- if (typeof val === 'undefined') {
- (this as any)[attrKey] = this.config.get(attrKey, fallback);
- }
- }
-
private setActive(selectedTab: HTMLIonTabElement): Promise {
if (this.transitioning) {
return Promise.reject('transitioning already happening');
@@ -300,26 +264,24 @@ export class Tabs implements NavOutlet {
hostData() {
return {
class: {
- ...createThemedClasses(this.mode, 'tabs'),
- 'tabbar-visible': !this.tabbarHidden
+ 'tabbar-hidden': this.tabbarHidden
}
};
}
render() {
- return (
-
-
- );
+ return [
+
+
+
,
+
+
+
+
+ ];
}
}
diff --git a/core/src/components/tabs/test/basic/index.html b/core/src/components/tabs/test/basic/index.html
index 530cd6411a..0e6c88d21d 100644
--- a/core/src/components/tabs/test/basic/index.html
+++ b/core/src/components/tabs/test/basic/index.html
@@ -63,6 +63,8 @@
+
+