mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-08-15 17:42:15 +08:00
fix(vue): modal and popover components now correctly pass properties (#23761)
resolves #23698
This commit is contained in:
@ -1,118 +1,185 @@
|
||||
import { defineComponent, h, ref, VNode } from 'vue';
|
||||
import { defineComponent, h, ref, VNode, onMounted } from 'vue';
|
||||
import { defineCustomElement } from '../utils';
|
||||
|
||||
export interface OverlayProps {
|
||||
isOpen?: boolean;
|
||||
}
|
||||
|
||||
export const defineOverlayContainer = <Props extends object>(name: string, customElement: any, componentProps: string[] = [], controller: any) => {
|
||||
const eventListeners = [
|
||||
{ componentEv: `${name}-will-present`, frameworkEv: 'willPresent' },
|
||||
{ componentEv: `${name}-did-present`, frameworkEv: 'didPresent' },
|
||||
{ componentEv: `${name}-will-dismiss`, frameworkEv: 'willDismiss' },
|
||||
{ componentEv: `${name}-did-dismiss`, frameworkEv: 'didDismiss' },
|
||||
];
|
||||
const EMPTY_PROP = Symbol();
|
||||
const DEFAULT_EMPTY_PROP = { default: EMPTY_PROP };
|
||||
|
||||
const Container = defineComponent<Props & OverlayProps>((props, { slots, emit }) => {
|
||||
defineCustomElement(name, customElement);
|
||||
export const defineOverlayContainer = <Props extends object>(name: string, customElement: any, componentProps: string[] = [], controller?: any) => {
|
||||
|
||||
const overlay = ref();
|
||||
const onVnodeMounted = async () => {
|
||||
const isOpen = props.isOpen;
|
||||
isOpen && (await present(props))
|
||||
}
|
||||
const createControllerComponent = () => {
|
||||
return defineComponent<Props & OverlayProps>((props, { slots, emit }) => {
|
||||
const eventListeners = [
|
||||
{ componentEv: `${name}-will-present`, frameworkEv: 'willPresent' },
|
||||
{ componentEv: `${name}-did-present`, frameworkEv: 'didPresent' },
|
||||
{ componentEv: `${name}-will-dismiss`, frameworkEv: 'willDismiss' },
|
||||
{ componentEv: `${name}-did-dismiss`, frameworkEv: 'didDismiss' },
|
||||
];
|
||||
|
||||
const onVnodeUpdated = async (node: VNode, prevNode: VNode) => {
|
||||
const isOpen = node.props!.isOpen;
|
||||
const prevIsOpen = prevNode.props!.isOpen;
|
||||
defineCustomElement(name, customElement);
|
||||
|
||||
/**
|
||||
* Do not do anything if this prop
|
||||
* did not change.
|
||||
*/
|
||||
if (isOpen === prevIsOpen) return;
|
||||
const overlay = ref();
|
||||
const onVnodeMounted = async () => {
|
||||
const isOpen = props.isOpen;
|
||||
isOpen && (await present(props))
|
||||
}
|
||||
|
||||
if (isOpen) {
|
||||
await present(props);
|
||||
} else {
|
||||
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 present(props);
|
||||
} else {
|
||||
await dismiss();
|
||||
}
|
||||
}
|
||||
|
||||
const onVnodeBeforeUnmount = async () => {
|
||||
await dismiss();
|
||||
}
|
||||
}
|
||||
|
||||
const onVnodeBeforeUnmount = async () => {
|
||||
await dismiss();
|
||||
}
|
||||
const dismiss = async () => {
|
||||
if (!overlay.value) return;
|
||||
|
||||
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;
|
||||
|
||||
overlay.value = overlay.value.dismiss();
|
||||
|
||||
await overlay.value;
|
||||
|
||||
overlay.value = undefined;
|
||||
}
|
||||
|
||||
if (overlay.value?.present) {
|
||||
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;
|
||||
}
|
||||
|
||||
let restOfProps: any = {};
|
||||
|
||||
/**
|
||||
* We can use Object.entries here
|
||||
* to avoid the hasOwnProperty check,
|
||||
* but that would require 2 iterations
|
||||
* where as this only requires 1.
|
||||
*/
|
||||
for (const key in props) {
|
||||
const value = props[key] as any;
|
||||
if (props.hasOwnProperty(key) && value !== EMPTY_PROP) {
|
||||
restOfProps[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* These are getting passed as props.
|
||||
* Potentially a Vue bug with Web Components?
|
||||
*/
|
||||
delete restOfProps.onWillPresent;
|
||||
delete restOfProps.onDidPresent;
|
||||
delete restOfProps.onWillDismiss;
|
||||
delete restOfProps.onDidDismiss;
|
||||
|
||||
const component = slots.default && slots.default()[0];
|
||||
overlay.value = controller.create({
|
||||
...restOfProps,
|
||||
component
|
||||
});
|
||||
|
||||
overlay.value = await overlay.value;
|
||||
|
||||
eventListeners.forEach(eventListener => {
|
||||
overlay.value.addEventListener(eventListener.componentEv, () => {
|
||||
emit(eventListener.frameworkEv);
|
||||
});
|
||||
})
|
||||
|
||||
await overlay.value.present();
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* These are getting passed as props.
|
||||
* Potentially a Vue bug with Web Components?
|
||||
*/
|
||||
const restOfProps = { ...(props as any) };
|
||||
delete restOfProps.onWillPresent;
|
||||
delete restOfProps.onDidPresent;
|
||||
delete restOfProps.onWillDismiss;
|
||||
delete restOfProps.onDidDismiss;
|
||||
return () => {
|
||||
return h(
|
||||
'div',
|
||||
{
|
||||
style: { display: 'none' },
|
||||
onVnodeMounted,
|
||||
onVnodeUpdated,
|
||||
onVnodeBeforeUnmount,
|
||||
isOpen: props.isOpen === true
|
||||
}
|
||||
);
|
||||
}
|
||||
});
|
||||
};
|
||||
const createInlineComponent = () => {
|
||||
return defineComponent((props, { slots }) => {
|
||||
defineCustomElement(name, customElement);
|
||||
const isOpen = ref(false);
|
||||
const elementRef = ref();
|
||||
|
||||
const component = slots.default && slots.default()[0];
|
||||
overlay.value = controller.create({
|
||||
...restOfProps,
|
||||
component
|
||||
onMounted(() => {
|
||||
elementRef.value.addEventListener('will-present', () => isOpen.value = true);
|
||||
elementRef.value.addEventListener('did-dismiss', () => isOpen.value = false);
|
||||
});
|
||||
|
||||
overlay.value = await overlay.value;
|
||||
return () => {
|
||||
let restOfProps: any = {};
|
||||
|
||||
eventListeners.forEach(eventListener => {
|
||||
overlay.value.addEventListener(eventListener.componentEv, () => {
|
||||
emit(eventListener.frameworkEv);
|
||||
});
|
||||
})
|
||||
|
||||
await overlay.value.present();
|
||||
}
|
||||
|
||||
return () => {
|
||||
return h(
|
||||
'div',
|
||||
{
|
||||
style: { display: 'none' },
|
||||
onVnodeMounted,
|
||||
onVnodeUpdated,
|
||||
onVnodeBeforeUnmount,
|
||||
isOpen: props.isOpen
|
||||
/**
|
||||
* We can use Object.entries here
|
||||
* to avoid the hasOwnProperty check,
|
||||
* but that would require 2 iterations
|
||||
* where as this only requires 1.
|
||||
*/
|
||||
for (const key in props) {
|
||||
const value = (props as any)[key];
|
||||
if (props.hasOwnProperty(key) && value !== EMPTY_PROP) {
|
||||
restOfProps[key] = value;
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
return h(
|
||||
name,
|
||||
{ ...restOfProps, ref: elementRef },
|
||||
(isOpen.value) ? slots : undefined
|
||||
)
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const Container = (controller !== undefined) ? createControllerComponent() : createInlineComponent();
|
||||
|
||||
Container.displayName = name;
|
||||
Container.props = [...componentProps, 'isOpen'];
|
||||
Container.emits = ['willPresent', 'didPresent', 'willDismiss', 'didDismiss'];
|
||||
|
||||
Container.props = {
|
||||
'isOpen': DEFAULT_EMPTY_PROP
|
||||
};
|
||||
|
||||
componentProps.forEach(componentProp => {
|
||||
Container.props[componentProp] = DEFAULT_EMPTY_PROP;
|
||||
});
|
||||
|
||||
if (controller !== undefined) {
|
||||
Container.emits = ['willPresent', 'didPresent', 'willDismiss', 'didDismiss'];
|
||||
}
|
||||
|
||||
return Container;
|
||||
}
|
||||
|
Reference in New Issue
Block a user