mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-08-16 10:01:59 +08:00
refactor(tabs): ion-tabbar can be used in standalone mode
This commit is contained in:
@ -824,7 +824,7 @@ export class Tab {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export declare interface Tabs extends StencilComponents<'IonTabs'> {}
|
export declare interface Tabs extends StencilComponents<'IonTabs'> {}
|
||||||
@Component({ selector: 'ion-tabs', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, template: '<ng-content></ng-content>', inputs: ['mode', 'color', 'name', 'tabbarHidden', 'tabbarHighlight', 'tabbarLayout', 'tabbarPlacement', 'translucent', 'useRouter'] })
|
@Component({ selector: 'ion-tabs', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, template: '<ng-content></ng-content>', inputs: ['name', 'tabbarHidden', 'useRouter'] })
|
||||||
export class Tabs {
|
export class Tabs {
|
||||||
ionChange: EventEmitter<CustomEvent>;
|
ionChange: EventEmitter<CustomEvent>;
|
||||||
ionNavWillLoad: EventEmitter<CustomEvent>;
|
ionNavWillLoad: EventEmitter<CustomEvent>;
|
||||||
@ -834,7 +834,7 @@ export class Tabs {
|
|||||||
constructor(r: ElementRef) {
|
constructor(r: ElementRef) {
|
||||||
const el = r.nativeElement;
|
const el = r.nativeElement;
|
||||||
proxyMethods(this, el, ['select', 'setRouteId', 'getRouteId', 'getTab', 'getSelected']);
|
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']);
|
proxyOutputs(this, el, ['ionChange', 'ionNavWillLoad', 'ionNavWillChange', 'ionNavDidChange']);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
48
core/src/components.d.ts
vendored
48
core/src/components.d.ts
vendored
@ -4596,10 +4596,6 @@ export namespace Components {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface IonTabs {
|
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<RouteID | undefined>;
|
'getRouteId': () => Promise<RouteID | undefined>;
|
||||||
/**
|
/**
|
||||||
* Get the currently selected tab
|
* Get the currently selected tab
|
||||||
@ -4610,10 +4606,6 @@ export namespace Components {
|
|||||||
*/
|
*/
|
||||||
'getTab': (tabOrIndex: string | number | HTMLIonTabElement) => Promise<HTMLIonTabElement | undefined>;
|
'getTab': (tabOrIndex: string | number | HTMLIonTabElement) => Promise<HTMLIonTabElement | undefined>;
|
||||||
/**
|
/**
|
||||||
* The mode determines which platform styles to use. Possible values are: `"ios"` or `"md"`.
|
|
||||||
*/
|
|
||||||
'mode': Mode;
|
|
||||||
/**
|
|
||||||
* A unique name for the tabs.
|
* A unique name for the tabs.
|
||||||
*/
|
*/
|
||||||
'name'?: string;
|
'name'?: string;
|
||||||
@ -4627,35 +4619,11 @@ export namespace Components {
|
|||||||
*/
|
*/
|
||||||
'tabbarHidden': boolean;
|
'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.
|
* If true, the tabs will use the router and `selectedTab` will not do anything.
|
||||||
*/
|
*/
|
||||||
'useRouter': boolean;
|
'useRouter': boolean;
|
||||||
}
|
}
|
||||||
interface IonTabsAttributes extends StencilHTMLAttributes {
|
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.
|
* A unique name for the tabs.
|
||||||
*/
|
*/
|
||||||
@ -4681,22 +4649,6 @@ export namespace Components {
|
|||||||
*/
|
*/
|
||||||
'tabbarHidden'?: boolean;
|
'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.
|
* If true, the tabs will use the router and `selectedTab` will not do anything.
|
||||||
*/
|
*/
|
||||||
'useRouter'?: boolean;
|
'useRouter'?: boolean;
|
||||||
|
@ -7,7 +7,6 @@
|
|||||||
);
|
);
|
||||||
|
|
||||||
display: flex;
|
display: flex;
|
||||||
position: relative;
|
|
||||||
|
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
@ -21,6 +20,7 @@
|
|||||||
user-select: none;
|
user-select: none;
|
||||||
|
|
||||||
z-index: $z-index-toolbar;
|
z-index: $z-index-toolbar;
|
||||||
|
box-sizing: content-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
:host(.ion-color) {
|
:host(.ion-color) {
|
||||||
|
@ -35,10 +35,14 @@ export class Tabbar implements ComponentInterface {
|
|||||||
*/
|
*/
|
||||||
@Prop() placement: TabbarPlacement = 'bottom';
|
@Prop() placement: TabbarPlacement = 'bottom';
|
||||||
|
|
||||||
/** The selected tab component */
|
/**
|
||||||
|
* The selected tab component
|
||||||
|
*/
|
||||||
@Prop() selectedTab?: HTMLIonTabElement;
|
@Prop() selectedTab?: HTMLIonTabElement;
|
||||||
|
|
||||||
/** The tabs to render */
|
/**
|
||||||
|
* The tabs to render
|
||||||
|
*/
|
||||||
@Prop() tabs: HTMLIonTabElement[] = [];
|
@Prop() tabs: HTMLIonTabElement[] = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -51,7 +55,9 @@ export class Tabbar implements ComponentInterface {
|
|||||||
*/
|
*/
|
||||||
@Prop() translucent = false;
|
@Prop() translucent = false;
|
||||||
|
|
||||||
/** Emitted when the tab bar is clicked */
|
/**
|
||||||
|
* Emitted when the tab bar is clicked
|
||||||
|
*/
|
||||||
@Event() ionTabbarClick!: EventEmitter<HTMLIonTabElement>;
|
@Event() ionTabbarClick!: EventEmitter<HTMLIonTabElement>;
|
||||||
|
|
||||||
@Listen('body:keyboardWillHide')
|
@Listen('body:keyboardWillHide')
|
||||||
@ -70,10 +76,6 @@ export class Tabbar implements ComponentInterface {
|
|||||||
this.updateHighlight();
|
this.updateHighlight();
|
||||||
}
|
}
|
||||||
|
|
||||||
private getSelectedButton(): HTMLElement | null {
|
|
||||||
return this.el.shadowRoot!.querySelector('.tab-btn-selected');
|
|
||||||
}
|
|
||||||
|
|
||||||
@Watch('selectedTab')
|
@Watch('selectedTab')
|
||||||
@Listen('window:resize')
|
@Listen('window:resize')
|
||||||
private updateHighlight() {
|
private updateHighlight() {
|
||||||
@ -81,7 +83,7 @@ export class Tabbar implements ComponentInterface {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.queue.read(() => {
|
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;
|
const highlight = this.el.shadowRoot!.querySelector('.tabbar-highlight') as HTMLElement;
|
||||||
if (btn && highlight) {
|
if (btn && highlight) {
|
||||||
highlight.style.transform = `translate3d(${btn.offsetLeft}px,0,0) scaleX(${btn.offsetWidth})`;
|
highlight.style.transform = `translate3d(${btn.offsetLeft}px,0,0) scaleX(${btn.offsetWidth})`;
|
||||||
@ -94,6 +96,7 @@ export class Tabbar implements ComponentInterface {
|
|||||||
return {
|
return {
|
||||||
role: 'tablist',
|
role: 'tablist',
|
||||||
'aria-hidden': keyboardVisible ? 'true' : null,
|
'aria-hidden': keyboardVisible ? 'true' : null,
|
||||||
|
'slot': 'tabbar',
|
||||||
class: {
|
class: {
|
||||||
...createColorClasses(color),
|
...createColorClasses(color),
|
||||||
'tabbar-translucent': translucent,
|
'tabbar-translucent': translucent,
|
||||||
|
@ -15,21 +15,15 @@
|
|||||||
z-index: $z-index-page-container;
|
z-index: $z-index-page-container;
|
||||||
}
|
}
|
||||||
|
|
||||||
ion-tabbar {
|
.tabs-inner {
|
||||||
@include position(null, 0, 0, 0);
|
position: relative;
|
||||||
|
|
||||||
|
flex: 1;
|
||||||
|
|
||||||
|
contain: layout size style;
|
||||||
|
}
|
||||||
|
|
||||||
|
:host(.tabbar-hidden) ion-tabbar,
|
||||||
|
:host(.tabbar-hidden)::slotted(ion-tabbar) {
|
||||||
display: none;
|
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;
|
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,11 @@
|
|||||||
import { Build, Component, Element, Event, EventEmitter, Listen, Method, Prop, State } from '@stencil/core';
|
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 { Config, NavOutlet, RouteID, RouteWrite } from '../../interface';
|
||||||
import { createThemedClasses } from '../../utils/theme';
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
tag: 'ion-tabs',
|
tag: 'ion-tabs',
|
||||||
styleUrl: 'tabs.scss',
|
styleUrl: 'tabs.scss',
|
||||||
scoped: true
|
shadow: true
|
||||||
})
|
})
|
||||||
export class Tabs implements NavOutlet {
|
export class Tabs implements NavOutlet {
|
||||||
|
|
||||||
@ -14,6 +13,7 @@ export class Tabs implements NavOutlet {
|
|||||||
private transitioning = false;
|
private transitioning = false;
|
||||||
private tabsId = (++tabIds);
|
private tabsId = (++tabIds);
|
||||||
private leavingTab?: HTMLIonTabElement;
|
private leavingTab?: HTMLIonTabElement;
|
||||||
|
private userTabbarEl?: HTMLIonTabbarElement;
|
||||||
|
|
||||||
@Element() el!: HTMLStencilElement;
|
@Element() el!: HTMLStencilElement;
|
||||||
|
|
||||||
@ -23,19 +23,6 @@ export class Tabs implements NavOutlet {
|
|||||||
@Prop({ context: 'config' }) config!: Config;
|
@Prop({ context: 'config' }) config!: Config;
|
||||||
@Prop({ context: 'document' }) doc!: Document;
|
@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.
|
* A unique name for the tabs.
|
||||||
*/
|
*/
|
||||||
@ -46,29 +33,6 @@ export class Tabs implements NavOutlet {
|
|||||||
*/
|
*/
|
||||||
@Prop() tabbarHidden = false;
|
@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.
|
* 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<void>;
|
@Event() ionNavDidChange!: EventEmitter<void>;
|
||||||
|
|
||||||
componentWillLoad() {
|
async componentWillLoad() {
|
||||||
if (!this.useRouter) {
|
if (!this.useRouter) {
|
||||||
this.useRouter = !!this.doc.querySelector('ion-router') && !this.el.closest('[no-router]');
|
this.useRouter = !!this.doc.querySelector('ion-router') && !this.el.closest('[no-router]');
|
||||||
}
|
}
|
||||||
|
this.userTabbarEl = this.el.querySelector('ion-tabbar') || undefined;
|
||||||
|
|
||||||
this.loadConfig('tabbarPlacement', 'bottom');
|
await this.initTabs();
|
||||||
this.loadConfig('tabbarLayout', 'icon-top');
|
|
||||||
this.loadConfig('tabbarHighlight', false);
|
|
||||||
|
|
||||||
this.initTabs();
|
|
||||||
|
|
||||||
this.ionNavWillLoad.emit();
|
this.ionNavWillLoad.emit();
|
||||||
|
this.componentWillUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidLoad() {
|
componentDidLoad() {
|
||||||
@ -117,6 +79,14 @@ export class Tabs implements NavOutlet {
|
|||||||
this.selectedTab = this.leavingTab = undefined;
|
this.selectedTab = this.leavingTab = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
componentWillUpdate() {
|
||||||
|
const tabbarEl = this.userTabbarEl;
|
||||||
|
if (tabbarEl) {
|
||||||
|
tabbarEl.tabs = this.tabs.slice();
|
||||||
|
tabbarEl.selectedTab = this.selectedTab;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Listen('ionTabMutated')
|
@Listen('ionTabMutated')
|
||||||
protected onTabMutated() {
|
protected onTabMutated() {
|
||||||
this.el.forceUpdate();
|
this.el.forceUpdate();
|
||||||
@ -202,6 +172,7 @@ export class Tabs implements NavOutlet {
|
|||||||
tab.btnId = 'tab-' + id;
|
tab.btnId = 'tab-' + id;
|
||||||
tab.id = 'tabpanel-' + id;
|
tab.id = 'tabpanel-' + id;
|
||||||
});
|
});
|
||||||
|
return Promise.all(tabs.map(tab => tab.componentOnReady()));
|
||||||
}
|
}
|
||||||
|
|
||||||
private async initSelect(): Promise<void> {
|
private async initSelect(): Promise<void> {
|
||||||
@ -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<void> {
|
private setActive(selectedTab: HTMLIonTabElement): Promise<void> {
|
||||||
if (this.transitioning) {
|
if (this.transitioning) {
|
||||||
return Promise.reject('transitioning already happening');
|
return Promise.reject('transitioning already happening');
|
||||||
@ -300,26 +264,24 @@ export class Tabs implements NavOutlet {
|
|||||||
hostData() {
|
hostData() {
|
||||||
return {
|
return {
|
||||||
class: {
|
class: {
|
||||||
...createThemedClasses(this.mode, 'tabs'),
|
'tabbar-hidden': this.tabbarHidden
|
||||||
'tabbar-visible': !this.tabbarHidden
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return [
|
||||||
<ion-tabbar
|
<div class="tabs-inner">
|
||||||
mode={this.mode}
|
<slot></slot>
|
||||||
tabs={this.tabs.slice()}
|
</div>,
|
||||||
color={this.color}
|
<slot name="tabbar">
|
||||||
selectedTab={this.selectedTab}
|
<ion-tabbar
|
||||||
highlight={this.tabbarHighlight}
|
tabs={this.tabs.slice()}
|
||||||
placement={this.tabbarPlacement}
|
selectedTab={this.selectedTab}
|
||||||
layout={this.tabbarLayout}
|
>
|
||||||
translucent={this.translucent}
|
</ion-tabbar>
|
||||||
>
|
</slot>
|
||||||
</ion-tabbar>
|
];
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,6 +63,8 @@
|
|||||||
|
|
||||||
<ion-tab disabled label="Messages" icon="chatboxes" component="page-one">
|
<ion-tab disabled label="Messages" icon="chatboxes" component="page-one">
|
||||||
</ion-tab>
|
</ion-tab>
|
||||||
|
|
||||||
|
<ion-tabbar></ion-tabbar>
|
||||||
</ion-tabs>
|
</ion-tabs>
|
||||||
</ion-app>
|
</ion-app>
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user