From e3bbfd0b05962591af317613d83599e97d384211 Mon Sep 17 00:00:00 2001 From: "Manu Mtz.-Almeida" Date: Tue, 9 Oct 2018 14:27:02 -0500 Subject: [PATCH] refactor(tabs): ion-tabbar can be used in standalone mode --- angular/src/directives/proxies.ts | 4 +- core/src/components.d.ts | 48 ---------- core/src/components/tabbar/tabbar.scss | 2 +- core/src/components/tabbar/tabbar.tsx | 19 ++-- core/src/components/tabs/tabs.scss | 24 ++--- core/src/components/tabs/tabs.tsx | 96 ++++++------------- .../src/components/tabs/test/basic/index.html | 2 + 7 files changed, 54 insertions(+), 141 deletions(-) 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 @@ + +