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.
*/
"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<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.
*/
@ -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.
*/

View File

@ -39,6 +39,9 @@ export class Modal implements ComponentInterface, OverlayInterface {
private currentTransition?: Promise<any>;
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();

View File

@ -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);
}

View File

@ -53,11 +53,8 @@ export const createOverlay = <T extends HTMLIonOverlayElement>(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);