mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-08-18 11:17:19 +08:00
chore(): sync with master
chore(): sync with master
This commit is contained in:
@ -7,9 +7,22 @@ const DID_CHANGE = 'ionTabsDidChange';
|
|||||||
export const IonTabs = /*@__PURE__*/ defineComponent({
|
export const IonTabs = /*@__PURE__*/ defineComponent({
|
||||||
name: 'IonTabs',
|
name: 'IonTabs',
|
||||||
emits: [WILL_CHANGE, DID_CHANGE],
|
emits: [WILL_CHANGE, DID_CHANGE],
|
||||||
|
data() {
|
||||||
|
return { didWarn: false }
|
||||||
|
},
|
||||||
render() {
|
render() {
|
||||||
const { $slots: slots, $emit } = this;
|
const { $slots: slots, $emit, $data } = this;
|
||||||
const slottedContent = slots.default && slots.default();
|
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 = [
|
let childrenToRender = [
|
||||||
h('div', {
|
h('div', {
|
||||||
class: 'tabs-inner',
|
class: 'tabs-inner',
|
||||||
@ -18,18 +31,52 @@ export const IonTabs = /*@__PURE__*/ defineComponent({
|
|||||||
'flex': '1',
|
'flex': '1',
|
||||||
'contain': 'layout size style'
|
'contain': 'layout size style'
|
||||||
}
|
}
|
||||||
}, [
|
}, (userProvidedRouterOutlet) ? userProvidedRouterOutlet : [h(IonRouterOutlet)])
|
||||||
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:
|
||||||
|
|
||||||
|
<ion-tabs>
|
||||||
|
<ion-tab-bar slot="bottom">
|
||||||
|
...
|
||||||
|
</ion-tab-bar>
|
||||||
|
</ion-tabs>
|
||||||
|
|
||||||
|
After:
|
||||||
|
|
||||||
|
<ion-tabs>
|
||||||
|
<ion-router-outlet></ion-router-outlet>
|
||||||
|
<ion-tab-bar slot="bottom">
|
||||||
|
...
|
||||||
|
</ion-tab-bar>
|
||||||
|
</ion-tabs>
|
||||||
|
|
||||||
|
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
|
* If ion-tab-bar has slot="top" it needs to be
|
||||||
* rendered before `.tabs-inner` otherwise it will
|
* rendered before `.tabs-inner` otherwise it will
|
||||||
* not show above the tab content.
|
* not show above the tab content.
|
||||||
*/
|
*/
|
||||||
if (slottedContent && slottedContent.length > 0) {
|
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';
|
const hasTopSlotTabBar = slottedTabBar && slottedTabBar.props?.slot === 'top';
|
||||||
|
|
||||||
if (slottedTabBar) {
|
if (slottedTabBar) {
|
||||||
@ -49,13 +96,13 @@ export const IonTabs = /*@__PURE__*/ defineComponent({
|
|||||||
|
|
||||||
if (hasTopSlotTabBar) {
|
if (hasTopSlotTabBar) {
|
||||||
childrenToRender = [
|
childrenToRender = [
|
||||||
...slottedContent,
|
...filteredContent,
|
||||||
...childrenToRender
|
...childrenToRender
|
||||||
];
|
];
|
||||||
} else {
|
} else {
|
||||||
childrenToRender = [
|
childrenToRender = [
|
||||||
...childrenToRender,
|
...childrenToRender,
|
||||||
...slottedContent
|
...filteredContent
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,3 +26,4 @@ export const IonLoading = /*@__PURE__*/ defineOverlayContainer<JSX.IonLoading>('
|
|||||||
export const IonPicker = /*@__PURE__*/ defineOverlayContainer<JSX.IonPicker>('ion-picker', IonPickerCmp, ['animated', 'backdropDismiss', 'buttons', 'columns', 'cssClass', 'duration', 'enterAnimation', 'keyboardClose', 'leaveAnimation', 'mode', 'showBackdrop'], pickerController);
|
export const IonPicker = /*@__PURE__*/ defineOverlayContainer<JSX.IonPicker>('ion-picker', IonPickerCmp, ['animated', 'backdropDismiss', 'buttons', 'columns', 'cssClass', 'duration', 'enterAnimation', 'keyboardClose', 'leaveAnimation', 'mode', 'showBackdrop'], pickerController);
|
||||||
|
|
||||||
export const IonToast = /*@__PURE__*/ defineOverlayContainer<JSX.IonToast>('ion-toast', IonToastCmp, ['animated', 'buttons', 'color', 'cssClass', 'duration', 'enterAnimation', 'header', 'keyboardClose', 'leaveAnimation', 'message', 'mode', 'position', 'translucent'], toastController);
|
export const IonToast = /*@__PURE__*/ defineOverlayContainer<JSX.IonToast>('ion-toast', IonToastCmp, ['animated', 'buttons', 'color', 'cssClass', 'duration', 'enterAnimation', 'header', 'keyboardClose', 'leaveAnimation', 'message', 'mode', 'position', 'translucent'], toastController);
|
||||||
|
|
||||||
|
54
packages/vue/test-app/src/views/TabsNew.vue
Normal file
54
packages/vue/test-app/src/views/TabsNew.vue
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
<template>
|
||||||
|
<ion-page data-pageid="tabs">
|
||||||
|
<ion-content>
|
||||||
|
<ion-tabs id="tabs">
|
||||||
|
<ion-router-outlet></ion-router-outlet>
|
||||||
|
<ion-tab-bar slot="bottom">
|
||||||
|
<ion-tab-button
|
||||||
|
v-for="tab in tabs"
|
||||||
|
:tab="'tab' + tab.id"
|
||||||
|
:href="'/tabs-new/tab' + tab.id"
|
||||||
|
:key="tab.id"
|
||||||
|
>
|
||||||
|
<ion-icon :icon="tab.icon" />
|
||||||
|
<ion-label>Tab {{ tab.id }}</ion-label>
|
||||||
|
</ion-tab-button>
|
||||||
|
|
||||||
|
<ion-button id="add-tab" @click="addTab()">Add Tab</ion-button>
|
||||||
|
</ion-tab-bar>
|
||||||
|
</ion-tabs>
|
||||||
|
</ion-content>
|
||||||
|
</ion-page>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { IonButton, IonTabBar, IonTabButton, IonTabs, IonContent, IonLabel, IonIcon, IonPage, IonRouterOutlet } from '@ionic/vue';
|
||||||
|
import { ellipse, square, triangle, shield } from 'ionicons/icons';
|
||||||
|
import { useRouter } from 'vue-router';
|
||||||
|
import { ref, defineComponent } from 'vue';
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: 'Tabs',
|
||||||
|
components: { IonButton, IonContent, IonLabel, IonTabs, IonTabBar, IonTabButton, IonIcon, IonPage, IonRouterOutlet },
|
||||||
|
setup() {
|
||||||
|
const tabs = ref([
|
||||||
|
{ id: 1, icon: triangle },
|
||||||
|
{ id: 2, icon: ellipse },
|
||||||
|
{ id: 3, icon: square }
|
||||||
|
])
|
||||||
|
const router = useRouter();
|
||||||
|
const addTab = () => {
|
||||||
|
router.addRoute({ path: '/tabs/tab4', component: () => import('@/views/Tab4.vue') });
|
||||||
|
tabs.value = [
|
||||||
|
...tabs.value,
|
||||||
|
{
|
||||||
|
id: 4,
|
||||||
|
icon: shield
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
return { tabs, addTab }
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
Reference in New Issue
Block a user