Skip to content

docs(popover): add presenting playgrounds #2346

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 10 commits into from
May 24, 2022
Merged
66 changes: 54 additions & 12 deletions docs/api/popover.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,12 @@ There are two ways to use `ion-popover`: inline or via the `popoverController`.

When using `ion-popover` with Angular, React, or Vue, the component you pass in will be destroyed when the popover is dismissed. As this functionality is provided by the JavaScript framework, using `ion-popover` without a JavaScript framework will not destroy the component you passed in. If this is a needed functionality, we recommend using the `popoverController` instead.

### When to use

Using a popover inline is useful when you do not want to explicitly wire up click events to open the popover. For example, you can use the `trigger` property to designate a button that should present the popover when clicked. You can also use the `trigger-action` property to customize whether the popover should be presented when the trigger is left clicked, right clicked, or hovered over.

If you need fine grained control over when the popover is presented and dismissed, we recommend you use the `popoverController`.

### Angular

Since the component you passed in needs to be created when the popover is presented and destroyed when the popover is dismissed, we are unable to project the content using `<ng-content>` internally. Instead, we use `<ng-container>` which expects an `<ng-template>` to be passed in. As a result, when passing in your component you will need to wrap it in an `<ng-template>`:
Expand All @@ -54,11 +60,27 @@ Since the component you passed in needs to be created when the popover is presen
</ion-popover>
```

### When to use
### Triggers

Using a popover inline is useful when you do not want to explicitly wire up click events to open the popover. For example, you can use the `trigger` property to designate a button that should present the popover when clicked. You can also use the `trigger-action` property to customize whether the popover should be presented when the trigger is left clicked, right clicked, or hovered over.
A trigger for an inline `ion-popover` is the element that will open a popover when interacted with. The interaction behavior can be customized by setting the `trigger-action` property. Note that `trigger-action="context-menu"` will prevent your system's default context menu from opening.

:::note
Triggers are not applicable when using the `popoverController` because the `ion-popover` is not created ahead of time.
:::

import InlineTrigger from '@site/static/usage/popover/presenting/inline-trigger/index.md';

<InlineTrigger />

### isOpen Property

Inline popovers can also be opened by setting the `isOpen` property to `true`. This method can be used if you need finer grained control over the popover than with a trigger.

Note that `isOpen` will *not* automatically be set back to `false` when the popover dismisses. It needs to be set back manually, using a `didDismiss` listener for example.

import IsOpenTrigger from '@site/static/usage/popover/presenting/inline-isopen/index.md';

If you need fine grained control over when the popover is presented and dismissed, we recommend you use the `popoverController`.
<IsOpenTrigger />

## Controller Popovers

Expand All @@ -68,25 +90,29 @@ If you need fine grained control over when the popover is presented and dismisse

We typically recommend that you write your popovers inline as it streamlines the amount of code in your application. You should only use the `popoverController` for complex use cases where writing a popover inline is impractical. When using a controller, your popover is not created ahead of time, so properties such as `trigger` and `trigger-action` are not applicable here. In addition, nested popovers are not compatible with the controller approach because the popover is automatically added to the root of your application when the `create` method is called.

### React

## Styling
Instead of a controller, React has a hook called `useIonPopover` which behaves in a similar fashion. Note that `useIonPopover` requires being a descendant of `<IonApp>`. If you need to use a popover outside of an `<IonApp>`, consider using an inline popover instead.

Popovers are presented at the root of your application so they overlay your entire app. This behavior applies to both inline popovers and popovers presented from a controller. As a result, custom popover styles can not be scoped to a particular component as they will not apply to the popover. Instead, styles must be applied globally. For most developers, placing the custom styles in `global.css` is sufficient.
### Usage

:::note
If you are building an Ionic Angular app, the styles need to be added to a global stylesheet file. Read [Style Placement](#style-placement) in the Angular section below for more information.
:::
import ControllerExample from '@site/static/usage/popover/presenting/controller/index.md';

<ControllerExample />


## Triggers
## Styling

A trigger for an `ion-popover` is the element that will open a popover when interacted with. The interaction behavior can be customized by setting the `trigger-action` property. Note that `trigger-action="context-menu"` will prevent your system's default context menu from opening.
Popovers are presented at the root of your application so they overlay your entire app. This behavior applies to both inline popovers and popovers presented from a controller. As a result, custom popover styles can not be scoped to a particular component as they will not apply to the popover. Instead, styles must be applied globally. For most developers, placing the custom styles in `global.css` is sufficient.

:::note
Triggers are not applicable when using the `popoverController` because the `ion-popover` is not created ahead of time.
If you are building an Ionic Angular app, the styles need to be added to a global stylesheet file.
:::

import Styling from '@site/static/usage/popover/customization/styling/index.md';

<Styling />


## Positioning

Expand All @@ -102,13 +128,25 @@ Regardless of what you choose for your reference point, you can position a popov

The `alignment` property allows you to line up an edge of your popover with a corresponding edge on your trigger element. The exact edge that is used depends on the value of the `side` property.

### Side and Alignment Demo

import Positioning from '@site/static/usage/popover/customization/positioning/index.md';

<Positioning />

### Offsets

If you need finer grained control over the positioning of your popover you can use the `--offset-x` and `--offset-y` CSS Variables. For example, `--offset-x: 10px` will move your popover content to the right by `10px`.

## Sizing

When making dropdown menus, you may want to have the width of the popover match the width of the trigger element. Doing this without knowing the trigger width ahead of time is tricky. You can set the `size` property to `'cover'` and Ionic Framework will ensure that the width of the popover matches the width of your trigger element. If you are using the `popoverController`, you must provide an event via the `event` option and Ionic Framework will use `event.target` as the reference element.
When making dropdown menus, you may want to have the width of the popover match the width of the trigger element. Doing this without knowing the trigger width ahead of time is tricky. You can set the `size` property to `'cover'` and Ionic Framework will ensure that the width of the popover matches the width of your trigger element.

If you are using the `popoverController`, you must provide an event via the `event` option and Ionic Framework will use `event.target` as the reference element. See the [controller demo](#controller-popovers) for an example of this pattern.

import Sizing from '@site/static/usage/popover/customization/sizing/index.md';

<Sizing />

## Nested Popovers

Expand All @@ -120,6 +158,10 @@ You can use the `dismissOnSelect` property to automatically close the popover wh
Nested popovers cannot be created when using the `popoverController` because the popover is automatically added to the root of your application when the `create` method is called.
:::

import NestedPopover from '@site/static/usage/popover/nested/index.md';

<NestedPopover />


## Interfaces

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
```css
ion-popover {
--width: 80px;
}

.container {
align-items: center;
display: flex;
flex-direction: column;
padding: 80px;
}
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
```html
<div class="container">
<ion-button id="top-center">Side=Top, Alignment=Center</ion-button>
<ion-popover trigger="top-center" side="top" alignment="center">
<ng-template>
<ion-content class="ion-padding">Hello World!</ion-content>
</ng-template>
</ion-popover>

<ion-button id="bottom-start">Side=Bottom, Alignment=Start</ion-button>
<ion-popover trigger="bottom-start" side="bottom" alignment="start">
<ng-template>
<ion-content class="ion-padding">Hello World!</ion-content>
</ng-template>
</ion-popover>

<ion-button id="left-start">Side=Left, Alignment=Start</ion-button>
<ion-popover trigger="left-start" side="left" alignment="start">
<ng-template>
<ion-content class="ion-padding">Hello World!</ion-content>
</ng-template>
</ion-popover>

<ion-button id="right-end">Side=Right, Alignment=End</ion-button>
<ion-popover trigger="right-end" side="right" alignment="end">
<ng-template>
<ion-content class="ion-padding">Hello World!</ion-content>
</ng-template>
</ion-popover>
</div>
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
```ts
import { Component } from '@angular/core';

@Component({
selector: 'app-root',
templateUrl: 'app.component.html',
styleUrls: ['app.component.css']
})
export class AppComponent { }

```
52 changes: 52 additions & 0 deletions static/usage/popover/customization/positioning/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>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" />

<style>
.container {
flex-direction: column;
}

ion-popover {
--width: 80px;
}
</style>
</head>

<body>
<ion-app>
<ion-content>
<div class="container">
<ion-button id="top-center">Side=Top, Alignment=Center</ion-button>
<ion-popover trigger="top-center" side="top" alignment="center">
<ion-content class="ion-padding">Hello World!</ion-content>
</ion-popover>

<ion-button id="bottom-start">Side=Bottom, Alignment=Start</ion-button>
<ion-popover trigger="bottom-start" side="bottom" alignment="start">
<ion-content class="ion-padding">Hello World!</ion-content>
</ion-popover>

<ion-button id="left-start">Side=Left, Alignment=Start</ion-button>
<ion-popover trigger="left-start" side="left" alignment="start">
<ion-content class="ion-padding">Hello World!</ion-content>
</ion-popover>

<ion-button id="right-end">Side=Right, Alignment=End</ion-button>
<ion-popover trigger="right-end" side="right" alignment="end">
<ion-content class="ion-padding">Hello World!</ion-content>
</ion-popover>
</div>
</ion-content>
</ion-app>
</body>

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

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

import reactTS from './react/react_ts.md';
import reactCSS from './react/react_css.md';

import angularHTML from './angular/angular_html.md';
import angularCSS from './angular/angular_css.md';
import angularTS from './angular/angular_ts.md';

<Playground
size="400px"
code={{
javascript,
react: {
files: {
'src/main.tsx': reactTS,
'src/main.css': reactCSS
}
},
vue,
angular: {
files: {
'src/app/app.component.html': angularHTML,
'src/app/app.component.css': angularCSS,
'src/app/app.component.ts': angularTS
}
}
}}
src="usage/popover/customization/positioning/demo.html"
/>
36 changes: 36 additions & 0 deletions static/usage/popover/customization/positioning/javascript.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
```html
<div class="container">
<ion-button id="top-center">Side=Top, Alignment=Center</ion-button>
<ion-popover trigger="top-center" side="top" alignment="center">
<ion-content class="ion-padding">Hello World!</ion-content>
</ion-popover>

<ion-button id="bottom-start">Side=Bottom, Alignment=Start</ion-button>
<ion-popover trigger="bottom-start" side="bottom" alignment="start">
<ion-content class="ion-padding">Hello World!</ion-content>
</ion-popover>

<ion-button id="left-start">Side=Left, Alignment=Start</ion-button>
<ion-popover trigger="left-start" side="left" alignment="start">
<ion-content class="ion-padding">Hello World!</ion-content>
</ion-popover>

<ion-button id="right-end">Side=Right, Alignment=End</ion-button>
<ion-popover trigger="right-end" side="right" alignment="end">
<ion-content class="ion-padding">Hello World!</ion-content>
</ion-popover>
</div>

<style>
ion-popover {
--width: 80px;
}

.container {
align-items: center;
display: flex;
flex-direction: column;
padding: 80px;
}
</style>
```
12 changes: 12 additions & 0 deletions static/usage/popover/customization/positioning/react/react_css.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
```css
ion-popover {
--width: 80px;
}

.container {
align-items: center;
display: flex;
flex-direction: column;
padding: 80px;
}
```
33 changes: 33 additions & 0 deletions static/usage/popover/customization/positioning/react/react_ts.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
```tsx
import React from 'react';
import { IonButton, IonContent, IonPopover } from '@ionic/react';

import './main.css';

function Example() {
return (
<div className="container">
<IonButton id="top-center">Side=Top, Alignment=Center</IonButton>
<IonPopover trigger="top-center" side="top" alignment="center">
<IonContent class="ion-padding">Hello World!</IonContent>
</IonPopover>

<IonButton id="bottom-start">Side=Bottom, Alignment=Start</IonButton>
<IonPopover trigger="bottom-start" side="bottom" alignment="start">
<IonContent class="ion-padding">Hello World!</IonContent>
</IonPopover>

<IonButton id="left-start">Side=Left, Alignment=Start</IonButton>
<IonPopover trigger="left-start" side="left" alignment="start">
<IonContent class="ion-padding">Hello World!</IonContent>
</IonPopover>

<IonButton id="right-end">Side=Right, Alignment=End</IonButton>
<IonPopover trigger="right-end" side="right" alignment="end">
<IonContent class="ion-padding">Hello World!</IonContent>
</IonPopover>
</div>
);
}
export default Example;
```
Loading