mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-08-17 18:54:11 +08:00
chore(): sync vue tabs changes with master
This commit is contained in:
18
packages/vue/package-lock.json
generated
18
packages/vue/package-lock.json
generated
@ -1,15 +1,15 @@
|
||||
{
|
||||
"name": "@ionic/vue",
|
||||
"version": "5.5.5",
|
||||
"version": "5.6.0",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@ionic/vue",
|
||||
"version": "5.5.5",
|
||||
"version": "5.6.0",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@ionic/core": "5.5.4",
|
||||
"@ionic/core": "5.5.5",
|
||||
"ionicons": "^5.1.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
@ -53,9 +53,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@ionic/core": {
|
||||
"version": "5.5.4",
|
||||
"resolved": "https://registry.npmjs.org/@ionic/core/-/core-5.5.4.tgz",
|
||||
"integrity": "sha512-IjbGN8vh3XuJ2ulo3BMlMflcWlUhvEGEexr29JKFvb+O4bWKP5sC2fkqSrswrIstOmv7axm7CeIi2MNRkwYwVA==",
|
||||
"version": "5.5.5",
|
||||
"resolved": "https://registry.npmjs.org/@ionic/core/-/core-5.5.5.tgz",
|
||||
"integrity": "sha512-i5KTiRuGa9PTUULy8rTQDDDw3FbxzYiOoSdKGNC0v7UOpMv9ReRixMWd5J0cfY17VRrua2aqNsqZXYwuInVnRQ==",
|
||||
"dependencies": {
|
||||
"ionicons": "^5.1.2",
|
||||
"tslib": "^1.10.0"
|
||||
@ -599,9 +599,9 @@
|
||||
}
|
||||
},
|
||||
"@ionic/core": {
|
||||
"version": "5.5.4",
|
||||
"resolved": "https://registry.npmjs.org/@ionic/core/-/core-5.5.4.tgz",
|
||||
"integrity": "sha512-IjbGN8vh3XuJ2ulo3BMlMflcWlUhvEGEexr29JKFvb+O4bWKP5sC2fkqSrswrIstOmv7axm7CeIi2MNRkwYwVA==",
|
||||
"version": "5.5.5",
|
||||
"resolved": "https://registry.npmjs.org/@ionic/core/-/core-5.5.5.tgz",
|
||||
"integrity": "sha512-i5KTiRuGa9PTUULy8rTQDDDw3FbxzYiOoSdKGNC0v7UOpMv9ReRixMWd5J0cfY17VRrua2aqNsqZXYwuInVnRQ==",
|
||||
"requires": {
|
||||
"ionicons": "^5.1.2",
|
||||
"tslib": "^1.10.0"
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ionic/vue",
|
||||
"version": "5.5.5",
|
||||
"version": "5.6.0",
|
||||
"description": "Vue specific wrapper for @ionic/core",
|
||||
"scripts": {
|
||||
"lint": "echo add linter",
|
||||
@ -57,7 +57,7 @@
|
||||
"vue-router": "^4.0.0-rc.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"@ionic/core": "5.5.5",
|
||||
"@ionic/core": "5.6.0",
|
||||
"ionicons": "^5.1.2"
|
||||
},
|
||||
"vetur": {
|
||||
|
@ -110,13 +110,23 @@ export const IonTabBar = defineComponent({
|
||||
const activeChild = childNodes.find((child: VNode) => isTabButton(child) && child.props?.tab === activeTab);
|
||||
const tabBar = this.$refs.ionTabBar;
|
||||
const tabDidChange = activeTab !== prevActiveTab;
|
||||
if (activeChild && tabBar) {
|
||||
tabDidChange && this.$props._tabsWillChange(activeTab);
|
||||
if (tabBar) {
|
||||
if (activeChild) {
|
||||
tabDidChange && this.$props._tabsWillChange(activeTab);
|
||||
|
||||
ionRouter.handleSetCurrentTab(activeTab);
|
||||
tabBar.selectedTab = tabState.activeTab = activeTab;
|
||||
ionRouter.handleSetCurrentTab(activeTab);
|
||||
tabBar.selectedTab = tabState.activeTab = activeTab;
|
||||
|
||||
tabDidChange && this.$props._tabsDidChange(activeTab);
|
||||
tabDidChange && this.$props._tabsDidChange(activeTab);
|
||||
/**
|
||||
* When going to a tab that does
|
||||
* not have an associated ion-tab-button
|
||||
* we need to remove the selected state from
|
||||
* the old tab.
|
||||
*/
|
||||
} else {
|
||||
tabBar.selectedTab = tabState.activeTab = '';
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1,5 +1,12 @@
|
||||
import { BackButtonEvent } from '@ionic/core';
|
||||
import { inject, ref, Ref } from 'vue';
|
||||
import {
|
||||
inject,
|
||||
ref,
|
||||
Ref,
|
||||
ComponentInternalInstance,
|
||||
getCurrentInstance
|
||||
} from 'vue';
|
||||
import { LifecycleHooks } from './utils';
|
||||
|
||||
type Handler = (processNextHandler: () => void) => Promise<any> | void | null;
|
||||
|
||||
@ -62,3 +69,38 @@ export const useKeyboard = (): IonKeyboardRef => {
|
||||
unregister
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an returns a function that
|
||||
* can be used to provide a lifecycle hook.
|
||||
*/
|
||||
const injectHook = (lifecycleType: LifecycleHooks, hook: Function, component: ComponentInternalInstance | null): Function | undefined => {
|
||||
if (component) {
|
||||
|
||||
// Add to public instance so it is accessible to IonRouterOutlet
|
||||
const target = component as any;
|
||||
const hooks = target.proxy[lifecycleType] || (target.proxy[lifecycleType] = []);
|
||||
const wrappedHook = (...args: unknown[]) => {
|
||||
if (target.isUnmounted) {
|
||||
return;
|
||||
}
|
||||
|
||||
return args ? hook(...args) : hook();
|
||||
};
|
||||
|
||||
hooks.push(wrappedHook);
|
||||
|
||||
return wrappedHook;
|
||||
} else {
|
||||
console.warn('[@ionic/vue]: Ionic Lifecycle Hooks can only be used during execution of setup().');
|
||||
}
|
||||
}
|
||||
|
||||
const createHook = <T extends Function = () => any>(lifecycle: LifecycleHooks) => {
|
||||
return (hook: T, target: ComponentInternalInstance | null = getCurrentInstance()) => injectHook(lifecycle, hook, target);
|
||||
}
|
||||
|
||||
export const onIonViewWillEnter = createHook(LifecycleHooks.WillEnter);
|
||||
export const onIonViewDidEnter = createHook(LifecycleHooks.DidEnter);
|
||||
export const onIonViewWillLeave = createHook(LifecycleHooks.WillLeave);
|
||||
export const onIonViewDidLeave = createHook(LifecycleHooks.DidLeave);
|
||||
|
@ -16,7 +16,17 @@ export { IonApp } from './components/IonApp';
|
||||
|
||||
export * from './components/Overlays';
|
||||
|
||||
export { IonKeyboardRef, IonRouter, useBackButton, useIonRouter, useKeyboard } from './hooks';
|
||||
export {
|
||||
IonKeyboardRef,
|
||||
IonRouter,
|
||||
useBackButton,
|
||||
useIonRouter,
|
||||
useKeyboard,
|
||||
onIonViewWillEnter,
|
||||
onIonViewDidEnter,
|
||||
onIonViewWillLeave,
|
||||
onIonViewDidLeave
|
||||
} from './hooks';
|
||||
|
||||
export {
|
||||
modalController,
|
||||
|
@ -3,6 +3,19 @@ import { Config as CoreConfig, LIFECYCLE_DID_ENTER, LIFECYCLE_DID_LEAVE, LIFECYC
|
||||
|
||||
type LIFECYCLE_EVENTS = typeof LIFECYCLE_WILL_ENTER | typeof LIFECYCLE_DID_ENTER | typeof LIFECYCLE_WILL_LEAVE | typeof LIFECYCLE_DID_LEAVE;
|
||||
|
||||
export enum LifecycleHooks {
|
||||
WillEnter = 'onIonViewWillEnter',
|
||||
DidEnter = 'onIonViewDidEnter',
|
||||
WillLeave = 'onIonViewWillLeave',
|
||||
DidLeave = 'onIonViewDidLeave'
|
||||
}
|
||||
const hookNames = {
|
||||
[LIFECYCLE_WILL_ENTER]: LifecycleHooks.WillEnter,
|
||||
[LIFECYCLE_DID_ENTER]: LifecycleHooks.DidEnter,
|
||||
[LIFECYCLE_WILL_LEAVE]: LifecycleHooks.WillLeave,
|
||||
[LIFECYCLE_DID_LEAVE]: LifecycleHooks.DidLeave
|
||||
}
|
||||
|
||||
const ids: { [k: string]: number } = { main: 0 };
|
||||
|
||||
export const generateId = (type = 'main') => {
|
||||
@ -21,6 +34,18 @@ export const fireLifecycle = (vueComponent: any, vueInstance: Ref<ComponentPubli
|
||||
if (instance?.[lifecycle]) {
|
||||
instance[lifecycle]();
|
||||
}
|
||||
|
||||
/**
|
||||
* Fire any Composition API
|
||||
* Ionic Lifecycle hooks
|
||||
*/
|
||||
if (instance) {
|
||||
const hook = hookNames[lifecycle];
|
||||
const hooks = instance[hook];
|
||||
if (hooks) {
|
||||
hooks.forEach((hook: Function) => hook());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const getConfig = (): CoreConfig | null => {
|
||||
|
@ -105,6 +105,10 @@ const routes: Array<RouteRecordRaw> = [
|
||||
next({ path: '/tabs/tab1' });
|
||||
},
|
||||
component: () => import('@/views/Tab3.vue')
|
||||
},
|
||||
{
|
||||
path: 'tab4',
|
||||
component: () => import('@/views/Tab4.vue')
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -22,6 +22,11 @@
|
||||
ionViewWillLeave: <div id="willLeave">{{ willLeave }}</div><br />
|
||||
ionViewDidLeave: <div id="didLeave">{{ didLeave }}</div><br />
|
||||
|
||||
onIonViewWillEnter: <div id="onWillEnter">{{ onWillEnter }}</div><br />
|
||||
onIonViewDidEnter: <div id="onDidEnter">{{ onDidEnter }}</div><br />
|
||||
onIonViewWillLeave: <div id="onWillLeave">{{ onWillLeave }}</div><br />
|
||||
onIonViewDidLeave: <div id="onDidLeave">{{ onDidLeave }}</div><br />
|
||||
|
||||
<ion-button router-link="/navigation" id="lifecycle-navigation">Go to another page</ion-button>
|
||||
</div>
|
||||
</ion-content>
|
||||
@ -37,9 +42,13 @@ import {
|
||||
IonHeader,
|
||||
IonPage,
|
||||
IonTitle,
|
||||
IonToolbar
|
||||
IonToolbar,
|
||||
onIonViewWillEnter,
|
||||
onIonViewDidEnter,
|
||||
onIonViewWillLeave,
|
||||
onIonViewDidLeave
|
||||
} from '@ionic/vue';
|
||||
import { defineComponent } from 'vue';
|
||||
import { defineComponent, ref } from 'vue';
|
||||
export default defineComponent({
|
||||
components: {
|
||||
IonButton,
|
||||
@ -65,6 +74,24 @@ export default defineComponent({
|
||||
this.didLeave++;
|
||||
}
|
||||
},
|
||||
setup() {
|
||||
const onWillEnter = ref(0);
|
||||
const onDidEnter = ref(0);
|
||||
const onWillLeave = ref(0);
|
||||
const onDidLeave = ref(0);
|
||||
|
||||
onIonViewWillEnter(() => onWillEnter.value += 1);
|
||||
onIonViewDidEnter(() => onDidEnter.value += 1);
|
||||
onIonViewWillLeave(() => onWillLeave.value += 1);
|
||||
onIonViewDidLeave(() => onDidLeave.value += 1);
|
||||
|
||||
return {
|
||||
onWillEnter,
|
||||
onDidEnter,
|
||||
onWillLeave,
|
||||
onDidLeave
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
willEnter: 0,
|
||||
|
27
packages/vue/test-app/src/views/Tab4.vue
Normal file
27
packages/vue/test-app/src/views/Tab4.vue
Normal file
@ -0,0 +1,27 @@
|
||||
<template>
|
||||
<ion-page data-pageid="tab4">
|
||||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-title>Tab 4</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
<ion-content :fullscreen="true">
|
||||
<ion-header collapse="condense">
|
||||
<ion-toolbar>
|
||||
<ion-title size="large">Tab 4</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
|
||||
<ExploreContainer name="Tab 4 page" />
|
||||
</ion-content>
|
||||
</ion-page>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { IonPage, IonHeader, IonToolbar, IonTitle, IonContent } from '@ionic/vue';
|
||||
import ExploreContainer from '@/components/ExploreContainer.vue';
|
||||
|
||||
export default {
|
||||
components: { ExploreContainer, IonHeader, IonToolbar, IonTitle, IonContent, IonPage }
|
||||
}
|
||||
</script>
|
@ -7,7 +7,11 @@ describe('Lifecycle', () => {
|
||||
ionViewWillEnter: 1,
|
||||
ionViewDidEnter: 1,
|
||||
ionViewWillLeave: 0,
|
||||
ionViewDidLeave: 0
|
||||
ionViewDidLeave: 0,
|
||||
onIonViewWillEnter: 1,
|
||||
onIonViewDidEnter: 1,
|
||||
onIonViewWillLeave: 0,
|
||||
onIonViewDidLeave: 0
|
||||
});
|
||||
|
||||
cy.get('#lifecycle-navigation').click();
|
||||
@ -16,7 +20,11 @@ describe('Lifecycle', () => {
|
||||
ionViewWillEnter: 1,
|
||||
ionViewDidEnter: 1,
|
||||
ionViewWillLeave: 1,
|
||||
ionViewDidLeave: 1
|
||||
ionViewDidLeave: 1,
|
||||
onIonViewWillEnter: 1,
|
||||
onIonViewDidEnter: 1,
|
||||
onIonViewWillLeave: 1,
|
||||
onIonViewDidLeave: 1
|
||||
});
|
||||
|
||||
cy.ionBackClick('navigation');
|
||||
@ -25,7 +33,11 @@ describe('Lifecycle', () => {
|
||||
ionViewWillEnter: 2,
|
||||
ionViewDidEnter: 2,
|
||||
ionViewWillLeave: 1,
|
||||
ionViewDidLeave: 1
|
||||
ionViewDidLeave: 1,
|
||||
onIonViewWillEnter: 2,
|
||||
onIonViewDidEnter: 2,
|
||||
onIonViewWillLeave: 1,
|
||||
onIonViewDidLeave: 1
|
||||
});
|
||||
});
|
||||
|
||||
@ -36,7 +48,11 @@ describe('Lifecycle', () => {
|
||||
ionViewWillEnter: 1,
|
||||
ionViewDidEnter: 1,
|
||||
ionViewWillLeave: 0,
|
||||
ionViewDidLeave: 0
|
||||
ionViewDidLeave: 0,
|
||||
onIonViewWillEnter: 1,
|
||||
onIonViewDidEnter: 1,
|
||||
onIonViewWillLeave: 0,
|
||||
onIonViewDidLeave: 0
|
||||
});
|
||||
});
|
||||
})
|
||||
@ -46,4 +62,9 @@ const testLifecycle = (selector, expected = {}) => {
|
||||
cy.get(`[data-pageid=${selector}] #didEnter`).should('have.text', expected.ionViewDidEnter);
|
||||
cy.get(`[data-pageid=${selector}] #willLeave`).should('have.text', expected.ionViewWillLeave);
|
||||
cy.get(`[data-pageid=${selector}] #didLeave`).should('have.text', expected.ionViewDidLeave);
|
||||
|
||||
cy.get(`[data-pageid=${selector}] #onWillEnter`).should('have.text', expected.onIonViewWillEnter);
|
||||
cy.get(`[data-pageid=${selector}] #onDidEnter`).should('have.text', expected.onIonViewDidEnter);
|
||||
cy.get(`[data-pageid=${selector}] #onWillLeave`).should('have.text', expected.onIonViewWillLeave);
|
||||
cy.get(`[data-pageid=${selector}] #onDidLeave`).should('have.text', expected.onIonViewDidLeave);
|
||||
}
|
||||
|
@ -190,6 +190,19 @@ describe('Tabs', () => {
|
||||
cy.ionPageVisible('tab2');
|
||||
cy.ionPageVisible('tabs');
|
||||
});
|
||||
|
||||
// Verifies fix for https://github.com/ionic-team/ionic-framework/issues/22597
|
||||
it('should deselect old tab button when going to a tab that does not have a tab button', () => {
|
||||
cy.visit('http://localhost:8080/tabs/tab1');
|
||||
|
||||
cy.get('ion-tab-button#tab-button-tab1').should('have.class', 'tab-selected');
|
||||
|
||||
cy.routerPush('/tabs/tab4');
|
||||
cy.ionPageHidden('tab1');
|
||||
cy.ionPageVisible('tab4');
|
||||
|
||||
cy.get('ion-tab-button#tab-button-tab1').should('not.have.class', 'tab-selected');
|
||||
});
|
||||
})
|
||||
|
||||
describe('Tabs - Swipe to Go Back', () => {
|
||||
|
@ -10,7 +10,7 @@
|
||||
"module": "es2015",
|
||||
"moduleResolution": "node",
|
||||
"noImplicitAny": true,
|
||||
"noImplicitReturns": true,
|
||||
"noImplicitReturns": false,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"outDir": "dist-transpiled",
|
||||
|
Reference in New Issue
Block a user