fix(modal, popover): overlays now automatically determine if they are inline (#23434)

This commit is contained in:
Liam DeBeasi
2021-06-08 14:25:06 -04:00
committed by GitHub
parent c716617945
commit 8dbe8ba7bc
4 changed files with 79 additions and 32 deletions

View File

@ -1395,7 +1395,6 @@ export namespace Components {
* Animation to use when the modal is presented. * Animation to use when the modal is presented.
*/ */
"enterAnimation"?: AnimationBuilder; "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. * 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; "event": any;
"getParentPopover": () => Promise<HTMLIonPopoverElement | null>; "getParentPopover": () => Promise<HTMLIonPopoverElement | null>;
"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. * 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. * Animation to use when the modal is presented.
*/ */
"enterAnimation"?: AnimationBuilder; "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. * 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. * The event to pass to the popover animation.
*/ */
"event"?: any; "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. * 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.
*/ */

View File

@ -39,6 +39,9 @@ export class Modal implements ComponentInterface, OverlayInterface {
private currentTransition?: Promise<any>; private currentTransition?: Promise<any>;
private destroyTriggerInteraction?: () => void; private destroyTriggerInteraction?: () => void;
private inline = false;
private workingDelegate?: FrameworkDelegate;
// Reference to the user's provided modal content // Reference to the user's provided modal content
private usersElement?: HTMLElement; private usersElement?: HTMLElement;
@ -51,9 +54,6 @@ export class Modal implements ComponentInterface, OverlayInterface {
@Element() el!: HTMLIonModalElement; @Element() el!: HTMLIonModalElement;
/** @internal */
@Prop() inline = true;
/** @internal */ /** @internal */
@Prop() overlayIndex!: number; @Prop() overlayIndex!: number;
@ -249,6 +249,39 @@ export class Modal implements ComponentInterface, OverlayInterface {
this.destroyTriggerInteraction = configureTriggerInteraction(triggerEl, el); 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. * Present the modal overlay after it has been created.
*/ */
@ -275,14 +308,8 @@ export class Modal implements ComponentInterface, OverlayInterface {
modal: this.el modal: this.el
}; };
/** const { inline, delegate } = this.getDelegate(true);
* If using modal inline this.usersElement = await attachComponent(delegate, this.el, this.component, ['ion-page'], data, 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);
await deepReady(this.usersElement); await deepReady(this.usersElement);
@ -362,7 +389,7 @@ export class Modal implements ComponentInterface, OverlayInterface {
const dismissed = await this.currentTransition; const dismissed = await this.currentTransition;
if (dismissed) { if (dismissed) {
const delegate = (this.inline) ? this.delegate || this.coreDelegate : this.delegate; const { delegate } = this.getDelegate();
await detachComponent(delegate, this.usersElement); await detachComponent(delegate, this.usersElement);
if (this.animation) { if (this.animation) {
this.animation.destroy(); this.animation.destroy();

View File

@ -45,6 +45,9 @@ export class Popover implements ComponentInterface, OverlayInterface {
private destroyKeyboardInteraction?: () => void; private destroyKeyboardInteraction?: () => void;
private destroyDismissInteraction?: () => void; private destroyDismissInteraction?: () => void;
private inline = false;
private workingDelegate?: FrameworkDelegate;
private triggerEv?: Event; private triggerEv?: Event;
private focusDescendantOnPresent = false; private focusDescendantOnPresent = false;
@ -54,9 +57,6 @@ export class Popover implements ComponentInterface, OverlayInterface {
@Element() el!: HTMLIonPopoverElement; @Element() el!: HTMLIonPopoverElement;
/** @internal */
@Prop() inline = true;
/** @internal */ /** @internal */
@Prop() delegate?: FrameworkDelegate; @Prop() delegate?: FrameworkDelegate;
@ -314,6 +314,39 @@ export class Popover implements ComponentInterface, OverlayInterface {
this.focusDescendantOnPresent = false; 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. * Present the popover overlay after it has been created.
*/ */
@ -340,14 +373,8 @@ export class Popover implements ComponentInterface, OverlayInterface {
popover: this.el popover: this.el
}; };
/** const { inline, delegate } = this.getDelegate(true);
* If using popover inline this.usersElement = await attachComponent(delegate, this.el, this.component, ['popover-viewport'], data, 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);
await deepReady(this.usersElement); await deepReady(this.usersElement);
this.configureKeyboardInteraction(); this.configureKeyboardInteraction();
@ -421,7 +448,7 @@ export class Popover implements ComponentInterface, OverlayInterface {
* we potentially need to use the coreDelegate * we potentially need to use the coreDelegate
* so that this works in vanilla JS apps * 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); await detachComponent(delegate, this.usersElement);
} }

View File

@ -53,11 +53,8 @@ export const createOverlay = <T extends HTMLIonOverlayElement>(tagName: string,
/** /**
* Convert the passed in overlay options into props * Convert the passed in overlay options into props
* that get passed down into the new overlay. * 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 // append the overlay element to the document body
getAppRoot(document).appendChild(element); getAppRoot(document).appendChild(element);