fix(vue): overlays function properly when used via controller or template (#22155)

resolves #22090
This commit is contained in:
Liam DeBeasi
2020-09-24 11:18:28 -04:00
committed by GitHub
parent b76bfa36c2
commit fe5fadf19c
14 changed files with 343 additions and 1065 deletions

View File

@ -0,0 +1,75 @@
import { defineComponent, h, ref } from 'vue';
export interface OverlayProps {
isOpen?: boolean;
}
export const defineOverlayContainer = <Props extends object>(name: string, componentProps: string[] = [], controller: any) => {
// TODO
const eventPrefix = name.toLowerCase().split('-').join('');
const eventListeners = [
{ componentEv: `${eventPrefix}willpresent`, frameworkEv: 'onWillPresent' },
{ componentEv: `${eventPrefix}didpresent`, frameworkEv: 'onDidPresent' },
{ componentEv: `${eventPrefix}willdismiss`, frameworkEv: 'onWillDismiss' },
{ componentEv: `${eventPrefix}diddismiss`, frameworkEv: 'onDidDismiss' },
];
const Container = defineComponent<Props & OverlayProps>((props, { slots, emit }) => {
const overlay = ref();
const content = ref();
const onVnodeMounted = async () => {
const isOpen = props.isOpen;
isOpen && (await present(props))
}
const onVnodeUpdated = async () => {
const isOpen = props.isOpen;
if (isOpen) {
await overlay.value?.present() || present(props);
} else {
await overlay.value?.dismiss();
overlay.value = undefined;
}
}
const onVnodeBeforeUnmount = async () => {
await overlay.value?.dismiss();
overlay.value = undefined;
}
const present = async (props: Readonly<Props>) => {
const component = (slots) ? h('div', { ref: content }, slots) : undefined;
overlay.value = await controller.create({
...props,
component
});
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
}
);
}
});
Container.displayName = name;
Container.props = [...componentProps, 'isOpen'];
Container.emits = eventListeners.map(ev => ev.frameworkEv);
return Container;
}

View File

@ -1,4 +1,4 @@
import { FunctionalComponent, VNode, defineComponent, h, inject, ref, Ref } from 'vue';
import { VNode, defineComponent, h, inject, ref, Ref } from 'vue';
export interface InputProps extends Object {
modelValue: string | boolean;
@ -40,7 +40,7 @@ const getElementClasses = (ref: Ref<HTMLElement | undefined>, componentClasses:
* options for the component such as router or v-model
* integrations.
*/
export const defineContainer = <Props extends object>(name: string, componentProps: string[] = [], componentOptions: ComponentOptions = {}) => {
export const defineContainer = <Props>(name: string, componentProps: string[] = [], componentOptions: ComponentOptions = {}) => {
const { modelProp, modelUpdateEvent, routerLinkComponent } = componentOptions;
/**
@ -48,7 +48,7 @@ export const defineContainer = <Props extends object>(name: string, componentPro
* Note: The `props` here are not all properties on a component.
* They refer to whatever properties are set on an instance of a component.
*/
const Container: FunctionalComponent<Props & InputProps> = defineComponent((props, { attrs, slots, emit }) => {
const Container = defineComponent<Props & InputProps>((props, { attrs, slots, emit }) => {
const containerRef = ref<HTMLElement>();
const classes = new Set(getComponentClasses(attrs.class));
const onVnodeBeforeMount = (vnode: VNode) => {