diff --git a/packages/vue/src/components/IonTabs.ts b/packages/vue/src/components/IonTabs.ts index d4f9dac129..d519cc02ff 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 = /*@__PURE__*/ 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 = /*@__PURE__*/ defineComponent({ 'flex': '1', 'contain': 'layout size style' } - }, [ - h(IonRouterOutlet) - ]) + }, (userProvidedRouterOutlet) ? userProvidedRouterOutlet : [h(IonRouterOutlet)]) ]; + 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 = /*@__PURE__*/ defineComponent({ if (hasTopSlotTabBar) { childrenToRender = [ - ...slottedContent, + ...filteredContent, ...childrenToRender ]; } else { childrenToRender = [ ...childrenToRender, - ...slottedContent + ...filteredContent ] } } diff --git a/packages/vue/src/components/Overlays.ts b/packages/vue/src/components/Overlays.ts index a63d903066..1279d8839f 100644 --- a/packages/vue/src/components/Overlays.ts +++ b/packages/vue/src/components/Overlays.ts @@ -18,11 +18,12 @@ import { IonToast as IonToastCmp } from '@ionic/core/components/ion-toast.js' import { defineOverlayContainer } from '../vue-component-lib/overlays'; export const IonActionSheet = /*@__PURE__*/ defineOverlayContainer('ion-action-sheet', IonActionSheetCmp, ['animated', 'backdropDismiss', 'buttons', 'cssClass', 'enterAnimation', 'header', 'keyboardClose', 'leaveAnimation', 'mode', 'subHeader', 'translucent'], actionSheetController); - + export const IonAlert = /*@__PURE__*/ defineOverlayContainer('ion-alert', IonAlertCmp, ['animated', 'backdropDismiss', 'buttons', 'cssClass', 'enterAnimation', 'header', 'inputs', 'keyboardClose', 'leaveAnimation', 'message', 'mode', 'subHeader', 'translucent'], alertController); - + export const IonLoading = /*@__PURE__*/ defineOverlayContainer('ion-loading', IonLoadingCmp, ['animated', 'backdropDismiss', 'cssClass', 'duration', 'enterAnimation', 'keyboardClose', 'leaveAnimation', 'message', 'mode', 'showBackdrop', 'spinner', 'translucent'], loadingController); - + export const IonPicker = /*@__PURE__*/ defineOverlayContainer('ion-picker', IonPickerCmp, ['animated', 'backdropDismiss', 'buttons', 'columns', 'cssClass', 'duration', 'enterAnimation', 'keyboardClose', 'leaveAnimation', 'mode', 'showBackdrop'], pickerController); - + export const IonToast = /*@__PURE__*/ defineOverlayContainer('ion-toast', IonToastCmp, ['animated', 'buttons', 'color', 'cssClass', 'duration', 'enterAnimation', 'header', 'keyboardClose', 'leaveAnimation', 'message', 'mode', 'position', 'translucent'], toastController); + 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 @@ + + +