diff --git a/core/src/components.d.ts b/core/src/components.d.ts index fe1eded51c..951214ae83 100644 --- a/core/src/components.d.ts +++ b/core/src/components.d.ts @@ -1395,7 +1395,6 @@ export namespace Components { * Animation to use when the modal is presented. */ "enterAnimation"?: AnimationBuilder; - "inline": boolean; /** * If `true`, the modal will open. If `false`, the modal will close. Use this if you need finer grained control over presentation, otherwise just use the modalController or the `trigger` property. Note: `isOpen` will not automatically be set back to `false` when the modal dismisses. You will need to do that in your code. */ @@ -1707,7 +1706,6 @@ export namespace Components { */ "event": any; "getParentPopover": () => Promise; - "inline": boolean; /** * If `true`, the popover will open. If `false`, the popover will close. Use this if you need finer grained control over presentation, otherwise just use the popoverController or the `trigger` property. Note: `isOpen` will not automatically be set back to `false` when the popover dismisses. You will need to do that in your code. */ @@ -4854,7 +4852,6 @@ declare namespace LocalJSX { * Animation to use when the modal is presented. */ "enterAnimation"?: AnimationBuilder; - "inline"?: boolean; /** * If `true`, the modal will open. If `false`, the modal will close. Use this if you need finer grained control over presentation, otherwise just use the modalController or the `trigger` property. Note: `isOpen` will not automatically be set back to `false` when the modal dismisses. You will need to do that in your code. */ @@ -5099,7 +5096,6 @@ declare namespace LocalJSX { * The event to pass to the popover animation. */ "event"?: any; - "inline"?: boolean; /** * If `true`, the popover will open. If `false`, the popover will close. Use this if you need finer grained control over presentation, otherwise just use the popoverController or the `trigger` property. Note: `isOpen` will not automatically be set back to `false` when the popover dismisses. You will need to do that in your code. */ diff --git a/core/src/components/modal/modal.tsx b/core/src/components/modal/modal.tsx index dcdfef9e39..e22db547a7 100644 --- a/core/src/components/modal/modal.tsx +++ b/core/src/components/modal/modal.tsx @@ -39,6 +39,9 @@ export class Modal implements ComponentInterface, OverlayInterface { private currentTransition?: Promise; private destroyTriggerInteraction?: () => void; + private inline = false; + private workingDelegate?: FrameworkDelegate; + // Reference to the user's provided modal content private usersElement?: HTMLElement; @@ -51,9 +54,6 @@ export class Modal implements ComponentInterface, OverlayInterface { @Element() el!: HTMLIonModalElement; - /** @internal */ - @Prop() inline = true; - /** @internal */ @Prop() overlayIndex!: number; @@ -249,6 +249,39 @@ export class Modal implements ComponentInterface, OverlayInterface { this.destroyTriggerInteraction = configureTriggerInteraction(triggerEl, el); } + /** + * Determines whether or not an overlay + * is being used inline or via a controller/JS + * and returns the correct delegate. + * By default, subsequent calls to getDelegate + * will use a cached version of the delegate. + * This is useful for calling dismiss after + * present so that the correct delegate is given. + */ + private getDelegate(force = false) { + if (this.workingDelegate && !force) { + return { + delegate: this.workingDelegate, + inline: this.inline + } + } + + /** + * If using overlay inline + * we potentially need to use the coreDelegate + * so that this works in vanilla JS apps. + * If a user has already placed the overlay + * as a direct descendant of ion-app or + * the body, then we can assume that + * the overlay is already in the correct place. + */ + const parentEl = this.el.parentNode as HTMLElement | null; + const inline = this.inline = parentEl !== null && parentEl.tagName !== 'ION-APP' && parentEl.tagName !== 'BODY'; + const delegate = this.workingDelegate = (inline) ? this.delegate || this.coreDelegate : this.delegate + + return { inline, delegate } + } + /** * Present the modal overlay after it has been created. */ @@ -275,14 +308,8 @@ export class Modal implements ComponentInterface, OverlayInterface { modal: this.el }; - /** - * If using modal inline - * we potentially need to use the coreDelegate - * so that this works in vanilla JS apps - */ - const delegate = (this.inline) ? this.delegate || this.coreDelegate : this.delegate; - - this.usersElement = await attachComponent(delegate, this.el, this.component, ['ion-page'], data, this.inline); + const { inline, delegate } = this.getDelegate(true); + this.usersElement = await attachComponent(delegate, this.el, this.component, ['ion-page'], data, inline); await deepReady(this.usersElement); @@ -362,7 +389,7 @@ export class Modal implements ComponentInterface, OverlayInterface { const dismissed = await this.currentTransition; if (dismissed) { - const delegate = (this.inline) ? this.delegate || this.coreDelegate : this.delegate; + const { delegate } = this.getDelegate(); await detachComponent(delegate, this.usersElement); if (this.animation) { this.animation.destroy(); diff --git a/core/src/components/popover/popover.tsx b/core/src/components/popover/popover.tsx index cf383cab31..97499695cc 100644 --- a/core/src/components/popover/popover.tsx +++ b/core/src/components/popover/popover.tsx @@ -45,6 +45,9 @@ export class Popover implements ComponentInterface, OverlayInterface { private destroyKeyboardInteraction?: () => void; private destroyDismissInteraction?: () => void; + private inline = false; + private workingDelegate?: FrameworkDelegate; + private triggerEv?: Event; private focusDescendantOnPresent = false; @@ -54,9 +57,6 @@ export class Popover implements ComponentInterface, OverlayInterface { @Element() el!: HTMLIonPopoverElement; - /** @internal */ - @Prop() inline = true; - /** @internal */ @Prop() delegate?: FrameworkDelegate; @@ -314,6 +314,39 @@ export class Popover implements ComponentInterface, OverlayInterface { this.focusDescendantOnPresent = false; } + /** + * Determines whether or not an overlay + * is being used inline or via a controller/JS + * and returns the correct delegate. + * By default, subsequent calls to getDelegate + * will use a cached version of the delegate. + * This is useful for calling dismiss after + * present so that the correct delegate is given. + */ + private getDelegate(force = false) { + if (this.workingDelegate && !force) { + return { + delegate: this.workingDelegate, + inline: this.inline + } + } + + /** + * If using overlay inline + * we potentially need to use the coreDelegate + * so that this works in vanilla JS apps. + * If a user has already placed the overlay + * as a direct descendant of ion-app or + * the body, then we can assume that + * the overlay is already in the correct place. + */ + const parentEl = this.el.parentNode as HTMLElement | null; + const inline = this.inline = parentEl !== null && parentEl.tagName !== 'ION-APP' && parentEl.tagName !== 'BODY'; + const delegate = this.workingDelegate = (inline) ? this.delegate || this.coreDelegate : this.delegate + + return { inline, delegate } + } + /** * Present the popover overlay after it has been created. */ @@ -340,14 +373,8 @@ export class Popover implements ComponentInterface, OverlayInterface { popover: this.el }; - /** - * If using popover inline - * we potentially need to use the coreDelegate - * so that this works in vanilla JS apps - */ - const delegate = (this.inline) ? this.delegate || this.coreDelegate : this.delegate; - - this.usersElement = await attachComponent(delegate, this.el, this.component, ['popover-viewport'], data, this.inline); + const { inline, delegate } = this.getDelegate(true); + this.usersElement = await attachComponent(delegate, this.el, this.component, ['popover-viewport'], data, inline); await deepReady(this.usersElement); this.configureKeyboardInteraction(); @@ -421,7 +448,7 @@ export class Popover implements ComponentInterface, OverlayInterface { * we potentially need to use the coreDelegate * so that this works in vanilla JS apps */ - const delegate = (this.inline) ? this.delegate || this.coreDelegate : this.delegate; + const { delegate } = this.getDelegate(); await detachComponent(delegate, this.usersElement); } diff --git a/core/src/utils/overlays.ts b/core/src/utils/overlays.ts index 65b5681996..7683bdca67 100644 --- a/core/src/utils/overlays.ts +++ b/core/src/utils/overlays.ts @@ -53,11 +53,8 @@ export const createOverlay = (tagName: string, /** * Convert the passed in overlay options into props * that get passed down into the new overlay. - * Inline is needed for ion-popover as it can - * be presented via a controller or written - * inline in a template. */ - Object.assign(element, { ...opts, inline: false }); + Object.assign(element, { ...opts }); // append the overlay element to the document body getAppRoot(document).appendChild(element);