mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2026-03-13 10:22:08 +08:00
fix(vue): prevent race conditions when opening overlays (#22883)
resolves #22880
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { defineComponent, h, ref } from 'vue';
|
||||
import { defineComponent, h, ref, VNode } from 'vue';
|
||||
|
||||
export interface OverlayProps {
|
||||
isOpen?: boolean;
|
||||
@@ -21,28 +21,61 @@ export const defineOverlayContainer = <Props extends object>(name: string, compo
|
||||
isOpen && (await present(props))
|
||||
}
|
||||
|
||||
const onVnodeUpdated = async () => {
|
||||
const isOpen = props.isOpen;
|
||||
const onVnodeUpdated = async (node: VNode, prevNode: VNode) => {
|
||||
const isOpen = node.props!.isOpen;
|
||||
const prevIsOpen = prevNode.props!.isOpen;
|
||||
|
||||
/**
|
||||
* Do not do anything if this prop
|
||||
* did not change.
|
||||
*/
|
||||
if (isOpen === prevIsOpen) return;
|
||||
|
||||
if (isOpen) {
|
||||
await overlay.value?.present() || present(props);
|
||||
await present(props);
|
||||
} else {
|
||||
await overlay.value?.dismiss();
|
||||
overlay.value = undefined;
|
||||
await dismiss();
|
||||
}
|
||||
}
|
||||
|
||||
const onVnodeBeforeUnmount = async () => {
|
||||
await overlay.value?.dismiss();
|
||||
await dismiss();
|
||||
}
|
||||
|
||||
const dismiss = async () => {
|
||||
if (!overlay.value) return;
|
||||
|
||||
await overlay.value;
|
||||
|
||||
overlay.value = overlay.value.dismiss();
|
||||
|
||||
await overlay.value;
|
||||
|
||||
overlay.value = undefined;
|
||||
}
|
||||
|
||||
const present = async (props: Readonly<Props>) => {
|
||||
/**
|
||||
* Do not open another instance
|
||||
* if one is already opened.
|
||||
*/
|
||||
if (overlay.value) {
|
||||
await overlay.value;
|
||||
}
|
||||
|
||||
if (overlay.value?.present) {
|
||||
await overlay.value.present();
|
||||
return;
|
||||
}
|
||||
|
||||
const component = slots.default && slots.default()[0];
|
||||
overlay.value = await controller.create({
|
||||
overlay.value = controller.create({
|
||||
...props,
|
||||
component
|
||||
});
|
||||
|
||||
overlay.value = await overlay.value;
|
||||
|
||||
eventListeners.forEach(eventListener => {
|
||||
overlay.value.addEventListener(eventListener.componentEv, () => {
|
||||
emit(eventListener.frameworkEv);
|
||||
@@ -59,7 +92,8 @@ export const defineOverlayContainer = <Props extends object>(name: string, compo
|
||||
style: { display: 'none' },
|
||||
onVnodeMounted,
|
||||
onVnodeUpdated,
|
||||
onVnodeBeforeUnmount
|
||||
onVnodeBeforeUnmount,
|
||||
isOpen: props.isOpen
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@@ -62,7 +62,9 @@
|
||||
|
||||
<br />
|
||||
|
||||
<ion-button expand="block" @click="present($event)" id="present-overlay">Present Overlay</ion-button>
|
||||
<ion-button @click="present($event)" id="present-overlay">Present Overlay</ion-button>
|
||||
|
||||
<ion-button @click="changeLoadingProps()" id="change-loading-props">Quickly Change Loading Props</ion-button>
|
||||
|
||||
<ion-action-sheet
|
||||
:is-open="isActionSheetOpen"
|
||||
@@ -314,7 +316,18 @@ export default defineComponent({
|
||||
}
|
||||
}
|
||||
|
||||
const changeLoadingProps = () => {
|
||||
setLoadingRef(true);
|
||||
setTimeout(() => {
|
||||
setLoadingRef(false);
|
||||
setTimeout(() => {
|
||||
setLoadingRef(true);
|
||||
}, 10);
|
||||
}, 10);
|
||||
}
|
||||
|
||||
return {
|
||||
changeLoadingProps,
|
||||
overlayProps,
|
||||
present,
|
||||
componentType,
|
||||
|
||||
@@ -59,7 +59,7 @@ describe('Overlays', () => {
|
||||
cy.get('ion-toast').should('not.exist');
|
||||
});
|
||||
|
||||
it('it should pass props to modal via controller', () => {
|
||||
it('should pass props to modal via controller', () => {
|
||||
cy.get('ion-radio#ion-modal').click();
|
||||
cy.get('ion-radio#controller').click();
|
||||
|
||||
@@ -69,7 +69,7 @@ describe('Overlays', () => {
|
||||
cy.get('ion-modal ion-content').should('have.text', 'Custom Title');
|
||||
});
|
||||
|
||||
it('it should pass props to modal via component', () => {
|
||||
it('should pass props to modal via component', () => {
|
||||
cy.get('ion-radio#ion-modal').click();
|
||||
cy.get('ion-radio#component').click();
|
||||
|
||||
@@ -79,7 +79,7 @@ describe('Overlays', () => {
|
||||
cy.get('ion-modal ion-content').should('have.text', 'Custom Title');
|
||||
});
|
||||
|
||||
it('it should pass props to popover via controller', () => {
|
||||
it('should pass props to popover via controller', () => {
|
||||
cy.get('ion-radio#ion-popover').click();
|
||||
cy.get('ion-radio#controller').click();
|
||||
|
||||
@@ -89,7 +89,7 @@ describe('Overlays', () => {
|
||||
cy.get('ion-popover ion-content').should('have.text', 'Custom Title');
|
||||
});
|
||||
|
||||
it('it should pass props to popover via component', () => {
|
||||
it('should pass props to popover via component', () => {
|
||||
cy.get('ion-radio#ion-popover').click();
|
||||
cy.get('ion-radio#component').click();
|
||||
|
||||
@@ -98,4 +98,10 @@ describe('Overlays', () => {
|
||||
|
||||
cy.get('ion-popover ion-content').should('have.text', 'Custom Title');
|
||||
});
|
||||
|
||||
it('should only open one instance at a time when props change quickly on component', () => {
|
||||
cy.get('#change-loading-props').click();
|
||||
|
||||
cy.get('ion-loading').should('have.length', 1);
|
||||
});
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user