mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-08-15 09:34:19 +08:00

<!-- Please refer to our contributing documentation for any questions on submitting a pull request, or let us know here if you need any help: https://ionicframework.com/docs/building/contributing --> <!-- Some docs updates need to be made in the `ionic-docs` repo, in a separate PR. See https://github.com/ionic-team/ionic-framework/blob/main/.github/CONTRIBUTING.md#modifying-documentation for details. --> <!-- Please do not submit updates to dependencies unless it fixes an issue. --> <!-- Please try to limit your pull request to one type (bugfix, feature, etc). Submit multiple pull requests if needed. --> ## What is the current behavior? <!-- Please describe the current behavior that you are modifying. --> <!-- Issues are required for both bug fixes and features. --> Issue URL: resolves #25199 Vue components show up as "Anonymous Component" in Vue Dev Tools. This is caused by our use of `displayName` instead of `name`. This required a fix in the Vue Output Target package. See https://github.com/ionic-team/stencil-ds-output-targets/pull/257 for more info. ## What is the new behavior? <!-- Please describe the behavior or changes that are being added by this PR. --> - Updates the Vue Output Target dependency - Functional components created manually in Ionic Vue now set `name` instead of `displayName`. Note: Non-functional components were never impacted by this bug. ## Does this introduce a breaking change? - [ ] Yes - [x] No <!-- If this introduces a breaking change, please describe the impact and migration path for existing applications below. --> ## Other information <!-- Any other information that is important to this PR such as screenshots of how the component looks before and after the change. --> Dev build: `7.0.2-dev.11681308435.141a05de`
192 lines
5.2 KiB
TypeScript
192 lines
5.2 KiB
TypeScript
import { defineComponent, h, ref, VNode, onMounted } from 'vue';
|
|
|
|
// TODO(FW-2969): types
|
|
|
|
export interface OverlayProps {
|
|
isOpen?: boolean;
|
|
}
|
|
|
|
const EMPTY_PROP = Symbol();
|
|
const DEFAULT_EMPTY_PROP = { default: EMPTY_PROP };
|
|
|
|
export const defineOverlayContainer = <Props extends object>(name: string, defineCustomElement: () => void, componentProps: string[] = [], controller?: any) => {
|
|
|
|
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' },
|
|
];
|
|
|
|
if (defineCustomElement !== undefined) {
|
|
defineCustomElement();
|
|
}
|
|
|
|
const overlay = ref();
|
|
const onVnodeMounted = async () => {
|
|
const isOpen = props.isOpen;
|
|
isOpen && (await present(props))
|
|
}
|
|
|
|
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 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;
|
|
}
|
|
|
|
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 () => {
|
|
return h(
|
|
'div',
|
|
{
|
|
style: { display: 'none' },
|
|
onVnodeMounted,
|
|
onVnodeUpdated,
|
|
onVnodeBeforeUnmount,
|
|
isOpen: props.isOpen === true
|
|
}
|
|
);
|
|
}
|
|
});
|
|
};
|
|
const createInlineComponent = () => {
|
|
return defineComponent((props, { slots }) => {
|
|
if (defineCustomElement !== undefined) {
|
|
defineCustomElement();
|
|
}
|
|
const isOpen = ref(false);
|
|
const elementRef = ref();
|
|
|
|
onMounted(() => {
|
|
elementRef.value.addEventListener('ion-mount', () => isOpen.value = true);
|
|
elementRef.value.addEventListener('will-present', () => isOpen.value = true);
|
|
elementRef.value.addEventListener('did-dismiss', () => isOpen.value = false);
|
|
});
|
|
|
|
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 as any)[key];
|
|
if (props.hasOwnProperty(key) && value !== EMPTY_PROP) {
|
|
restOfProps[key] = value;
|
|
}
|
|
}
|
|
|
|
return h(
|
|
name,
|
|
{ ...restOfProps, ref: elementRef },
|
|
(isOpen.value || restOfProps.keepContentsMounted) ? slots : undefined
|
|
)
|
|
}
|
|
});
|
|
}
|
|
|
|
const Container = (controller !== undefined) ? createControllerComponent() : createInlineComponent();
|
|
|
|
Container.name = name;
|
|
|
|
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;
|
|
}
|