From a2a4cff3d0d67868f384e1e9eec7cc738e260a27 Mon Sep 17 00:00:00 2001 From: Liam DeBeasi Date: Mon, 21 Jun 2021 16:52:50 -0400 Subject: [PATCH] fix(vue): IonTabs can now accept IonRouterOutlet (#23477) resolves #23321 --- packages/vue/src/components/IonTabs.ts | 61 ++++++++++++++++++--- packages/vue/test-app/src/router/index.ts | 2 +- packages/vue/test-app/src/views/TabsNew.vue | 54 ++++++++++++++++++ 3 files changed, 109 insertions(+), 8 deletions(-) create mode 100644 packages/vue/test-app/src/views/TabsNew.vue diff --git a/packages/vue/src/components/IonTabs.ts b/packages/vue/src/components/IonTabs.ts index e292d53035..9d9e711241 100644 --- a/packages/vue/src/components/IonTabs.ts +++ b/packages/vue/src/components/IonTabs.ts @@ -7,9 +7,22 @@ const DID_CHANGE = 'ionTabsDidChange'; export const IonTabs = defineComponent({ name: 'IonTabs', emits: [WILL_CHANGE, DID_CHANGE], + data() { + return { didWarn: false } + }, render() { - const { $slots: slots, $emit } = this; + const { $slots: slots, $emit, $data } = this; const slottedContent = slots.default && slots.default(); + let userProvidedRouterOutlet; + + if (slottedContent && slottedContent.length > 0) { + /** + * If developer passed in their own ion-router-outlet + * instance, then we should not init a default one + */ + userProvidedRouterOutlet = slottedContent.find((child: VNode) => child.type && (child.type as any).name === 'IonRouterOutlet'); + } + let childrenToRender = [ h('div', { class: 'tabs-inner', @@ -18,18 +31,52 @@ export const IonTabs = defineComponent({ 'flex': '1', 'contain': 'layout size style' } - }, [ - h(IonRouterOutlet, { tabs: true }) - ]) + }, (userProvidedRouterOutlet) ? userProvidedRouterOutlet : [h(IonRouterOutlet, { tabs: true })]) ]; + if (userProvidedRouterOutlet && !$data.didWarn) { + console.warn(`[@ionic/vue Deprecation] Starting in Ionic Vue v6.0, developers must add an 'ion-router-outlet' instance inside of 'ion-tabs'. + + Before: + + + + ... + + + + After: + + + + + ... + + + + Be sure to import 'IonRouterOutlet' from '@ionic/vue' and provide that import to your Vue component. See https://ionicframework.com/docs/vue/navigation#working-with-tabs for more information. + `); + + $data.didWarn = true; + } + /** * If ion-tab-bar has slot="top" it needs to be * rendered before `.tabs-inner` otherwise it will * not show above the tab content. */ if (slottedContent && slottedContent.length > 0) { - const slottedTabBar = slottedContent.find((child: VNode) => child.type && (child.type as any).name === 'IonTabBar'); + + /** + * Render all content except for router outlet + * since that needs to be inside of `.tabs-inner`. + */ + const filteredContent = slottedContent.filter((child: VNode) => ( + !child.type || + (child.type && (child.type as any).name !== 'IonRouterOutlet') + )); + + const slottedTabBar = filteredContent.find((child: VNode) => child.type && (child.type as any).name === 'IonTabBar'); const hasTopSlotTabBar = slottedTabBar && slottedTabBar.props?.slot === 'top'; if (slottedTabBar) { @@ -49,13 +96,13 @@ export const IonTabs = defineComponent({ if (hasTopSlotTabBar) { childrenToRender = [ - ...slottedContent, + ...filteredContent, ...childrenToRender ]; } else { childrenToRender = [ ...childrenToRender, - ...slottedContent + ...filteredContent ] } } diff --git a/packages/vue/test-app/src/router/index.ts b/packages/vue/test-app/src/router/index.ts index 5e408c4615..98818af6d4 100644 --- a/packages/vue/test-app/src/router/index.ts +++ b/packages/vue/test-app/src/router/index.ts @@ -118,7 +118,7 @@ const routes: Array = [ }, { path: '/tabs-new/', - component: () => import('@/views/Tabs.vue'), + component: () => import('@/views/TabsNew.vue'), children: [ { path: '', diff --git a/packages/vue/test-app/src/views/TabsNew.vue b/packages/vue/test-app/src/views/TabsNew.vue new file mode 100644 index 0000000000..9d4c4fff3a --- /dev/null +++ b/packages/vue/test-app/src/views/TabsNew.vue @@ -0,0 +1,54 @@ + + +