Skip to content

docs(modal, popover): add keepContentsMounted example #2432

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jul 5, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 20 additions & 1 deletion docs/api/modal.md
Original file line number Diff line number Diff line change
Expand Up @@ -236,10 +236,29 @@ Sheet modals allow users to interact with content behind the modal when the `bac

When the backdrop is disabled, users will be able to interact with elements outside the sheet modal using a pointer or keyboard. Assistive technologies may not focus outside the sheet modal by default due to the usage of `aria-modal`. We recommend avoiding features such as autofocus here as it can cause assistive technologies to jump between two interactive contexts without warning the user.

## Performance

### Mounting Inner Contents

The content of an inline `ion-modal` is unmounted when closed. If this content is expensive to render, developers can use the `keepContentsMounted` property to mount the content as soon as the modal is mounted. This can help optimize the responsiveness of your application as the inner contents will have already been mounted when the modal opens.

import Mount from '@site/static/usage/modal/performance/mount/index.md';

<Mount />

Developers should keep the following in mind when using `keepContentsMounted`:

- This feature should be used as a last resort in order to deal with existing performance problems. Try to identify and resolve performance bottlenecks before using this feature. Additionally, do not use this to anticipate performance problems.

- This feature is only needed when using a JavaScript Framework. Developers not using a framework can pass the contents to be rendered into the modal, and the contents will be rendered automatically.

- This feature only works with inline modals. Modals created with the `modalController` are not created ahead of time, so the inner contents are not created either.

- Any JavaScript Framework lifecycle hooks on the inner component will run as soon as the modal is mounted, not when the modal is presented.

<Props />
<Events />
<Methods />
<Parts />
<CustomProps />
<Slots />
<Slots />
21 changes: 20 additions & 1 deletion docs/api/popover.md
Original file line number Diff line number Diff line change
Expand Up @@ -233,10 +233,29 @@ type PositionAlign = 'start' | 'center' | 'end';
| `ArrowLeft` | When used in a child popover, closes the popover and returns focus to the parent popover. |
| `Space`, `Enter`, and `ArrowRight` | When focusing a trigger element, opens the associated popover. |

## Performance

### Mounting Inner Contents

The content of an inline `ion-popover` is unmounted when closed. If this content is expensive to render, developers can use the `keepContentsMounted` property to mount the content as soon as the popover is mounted. This can help optimize the responsiveness of your application as the inner contents will have already been mounted when the popover opens.

import Mount from '@site/static/usage/popover/performance/mount/index.md';

<Mount />

Developers should keep the following in mind when using `keepContentsMounted`:

- This feature should be used as a last resort in order to deal with existing performance problems. Try to identify and resolve performance bottlenecks before using this feature. Additionally, do not use this to anticipate performance problems.

- This feature is only needed when using a JavaScript Framework. Developers not using a framework can pass the contents to be rendered into the popover, and the contents will be rendered automatically.

- This feature only works with inline popovers. Popovers created with the `popoverController` are not created ahead of time, so the inner contents are not created either.

- Any JavaScript Framework lifecycle hooks on the inner component will run as soon as the popover is mounted, not when the popover is presented.

<Props />
<Events />
<Methods />
<Parts />
<CustomProps />
<Slots />
<Slots />
208 changes: 34 additions & 174 deletions src/components/page/reference/ReleaseNotes/release-notes.json

Large diffs are not rendered by default.

25 changes: 25 additions & 0 deletions static/usage/modal/performance/mount/angular.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
```html
<ion-header>
<ion-toolbar>
<ion-title>Example</ion-title>
</ion-toolbar>
</ion-header>
<ion-content class="ion-padding">
<ion-button id="open-modal" expand="block">Open Modal</ion-button>
<ion-modal [keepContentsMounted]="true" trigger="open-modal" #modal>
<ng-template>
<ion-header>
<ion-toolbar>
<ion-buttons slot="start">
<ion-button (click)="modal.dismiss()">Cancel</ion-button>
</ion-buttons>
<ion-title>Modal</ion-title>
</ion-toolbar>
</ion-header>
<ion-content class="ion-padding">
This content was mounted as soon as the modal was created.
</ion-content>
</ng-template>
</ion-modal>
</ion-content>
```
52 changes: 52 additions & 0 deletions static/usage/modal/performance/mount/demo.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Modal | Controller</title>
<link rel="stylesheet" href="../../../common.css" />
<script src="../../../common.js"></script>
<script type="module" src="https://cdn.jsdelivr.net/npm/@ionic/core@6/dist/ionic/ionic.esm.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@ionic/core@6/css/ionic.bundle.css" />
<script type="module">
import { modalController } from 'https://cdn.jsdelivr.net/npm/@ionic/core@6/dist/ionic/index.esm.js';
window.modalController = modalController;
</script>
</head>

<body>
<ion-app>
<ion-header>
<ion-toolbar>
<ion-title>Example</ion-title>
</ion-toolbar>
</ion-header>
<ion-content class="ion-padding">
<ion-button id="open-button" expand="block">Open Modal</ion-button>
<ion-modal trigger="open-button">
<ion-header>
<ion-toolbar>
<ion-buttons slot="start">
<ion-button onclick="close()">Close</ion-button>
</ion-buttons>
<ion-title>Modal</ion-title>
</ion-toolbar>
</ion-header>
<ion-content class="ion-padding">
This content was mounted as soon as the modal was created.
</ion-content>
</ion-modal>
</ion-content>
</ion-app>

<script>
const modal = document.querySelector('ion-modal');

const close = () => {
modal.dismiss();
}
</script>
</body>

</html>
17 changes: 17 additions & 0 deletions static/usage/modal/performance/mount/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import Playground from '@site/src/components/global/Playground';

import javascript from './javascript.md';
import react from './react.md';
import vue from './vue.md';
import angular from './angular.md';

<Playground
code={{
javascript,
react,
vue,
angular
}}
src="usage/modal/performance/mount/demo.html"
devicePreview
/>
31 changes: 31 additions & 0 deletions static/usage/modal/performance/mount/javascript.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
```html
<ion-header>
<ion-toolbar>
<ion-title>Controller Modal</ion-title>
</ion-toolbar>
</ion-header>
<ion-content class="ion-padding">
<ion-button id="open-modal" expand="block">Open Modal</ion-button>
<ion-modal trigger="open-button">
<ion-header>
<ion-toolbar>
<ion-buttons slot="start">
<ion-button onclick="close()">Close</ion-button>
</ion-buttons>
<ion-title>Modal</ion-title>
</ion-toolbar>
</ion-header>
<ion-content class="ion-padding">
This content was mounted as soon as the modal was created.
</ion-content>
</ion-modal>
</ion-content>

<script>
var modal = document.querySelector('ion-modal');

function close() {
modal.dimiss();
}
</script>
```
47 changes: 47 additions & 0 deletions static/usage/modal/performance/mount/react.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
```tsx
import React, { useRef } from 'react';
import {
IonButtons,
IonButton,
IonModal,
IonHeader,
IonContent,
IonToolbar,
IonTitle,
IonPage,
} from '@ionic/react';

function Example() {
const modal = useRef<HTMLIonModalElement>(null);

return (
<IonPage>
<IonHeader>
<IonToolbar>
<IonTitle>Example</IonTitle>
</IonToolbar>
</IonHeader>
<IonContent className="ion-padding">
<IonButton id="open-modal" expand="block">
Open Modal
</IonButton>
<IonModal ref={modal} keepContentsMounted={true} trigger="open-modal">
<IonHeader>
<IonToolbar>
<IonButtons slot="start">
<IonButton onClick={() => modal.current?.dismiss()}>Close</IonButton>
</IonButtons>
<IonTitle>Modal</IonTitle>
</IonToolbar>
</IonHeader>
<IonContent className="ion-padding">
This content was mounted as soon as the modal was created.
</IonContent>
</IonModal>
</IonContent>
</IonPage>
);
}

export default Example;
```
55 changes: 55 additions & 0 deletions static/usage/modal/performance/mount/vue.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
```html
<template>
<ion-header>
<ion-toolbar>
<ion-title>Example</ion-title>
</ion-toolbar>
</ion-header>
<ion-content class="ion-padding">
<ion-button id="open-modal" expand="block">Open Modal</ion-button>
<ion-modal ref="modal" :keep-contents-mounted="true" trigger="open-modal">
<ion-header>
<ion-toolbar>
<ion-buttons slot="start">
<ion-button @click="close()">Cancel</ion-button>
</ion-buttons>
<ion-title>Modal</ion-title>
</ion-toolbar>
</ion-header>
<ion-content class="ion-padding">
This content was mounted as soon as the modal was created.
</ion-content>
</ion-modal>
</ion-content>
</template>

<script lang="ts">
import {
IonButtons,
IonButton,
IonModal,
IonHeader,
IonContent,
IonToolbar,
IonTitle,
} from '@ionic/vue';
import { defineComponent, ref } from 'vue';

export default defineComponent({
components: {
IonButtons,
IonButton,
IonModal,
IonHeader,
IonContent,
IonToolbar,
IonTitle,
},
methods: {
close() {
this.$refs.modal.$el.dismiss();
},
},
});
</script>
```
8 changes: 8 additions & 0 deletions static/usage/popover/performance/mount/angular.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
```html
<ion-button id="open-popover">Open Popover</ion-button>
<ion-popover [keepContentsMounted]="true" trigger="open-popover">
<ng-template>
<ion-content class="ion-padding">This content was mounted as soon as the popover was created.</ion-content>
</ng-template>
</ion-popover>
```
27 changes: 27 additions & 0 deletions static/usage/popover/performance/mount/demo.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Popover</title>
<link rel="stylesheet" href="../../../common.css" />
<script src="../../../common.js"></script>
<script type="module" src="https://cdn.jsdelivr.net/npm/@ionic/core@6/dist/ionic/ionic.esm.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@ionic/core@6/css/ionic.bundle.css" />
</head>

<body>
<ion-app>
<ion-content>
<div class="container">
<ion-button id="open-popover">Open Popover</ion-button>
<ion-popover keep-contents-mounted="true" trigger="open-popover">
<ion-content class="ion-padding">This content was mounted as soon as the popover was created.</ion-content>
</ion-popover>
</div>
</ion-content>
</ion-app>
</body>

</html>
17 changes: 17 additions & 0 deletions static/usage/popover/performance/mount/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import Playground from '@site/src/components/global/Playground';

import javascript from './javascript.md';
import react from './react.md';
import vue from './vue.md';
import angular from './angular.md';

<Playground
code={{
javascript,
react,
vue,
angular
}}
size="medium"
src="usage/popover/performance/mount/demo.html"
/>
6 changes: 6 additions & 0 deletions static/usage/popover/performance/mount/javascript.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
```html
<ion-button id="open-popover">Open Popover</ion-button>
<ion-popover keep-contents-mounted="true" trigger="open-popover">
<ion-content class="ion-padding">This content was mounted as soon as the popover was created.</ion-content>
</ion-popover>
```
15 changes: 15 additions & 0 deletions static/usage/popover/performance/mount/react.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
```tsx
import React from 'react';
import { IonButton, IonContent, IonPopover } from '@ionic/react';
function Example() {
return (
<IonContent>
<IonButton id="open-popover">Open Popover</IonButton>
<IonPopover keepContentsMounted={true} trigger="open-popover">
<IonContent class="ion-padding">This content was mounted as soon as the popover was created.</IonContent>
</IonPopover>
</IonContent>
);
}
export default Example;
```
19 changes: 19 additions & 0 deletions static/usage/popover/performance/mount/vue.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
```html
<template>
<ion-content>
<ion-button id="open-popover">Open Popover</ion-button>
<ion-popover :keep-contents-mounted="true" trigger="open-popover">
<ion-content class="ion-padding">This content was mounted as soon as the popover was created.</ion-content>
</ion-popover>
</ion-content>
</template>

<script lang="ts">
import { IonButton, IonContent, IonPopover } from '@ionic/vue';
import { defineComponent } from 'vue';

export default defineComponent({
components: { IonButton, IonContent, IonPopover },
});
</script>
```