Skip to content

Commit f028c88

Browse files
committed
docs(modal): add playgrounds for sheet and card model drag events
1 parent 4080b0d commit f028c88

8 files changed

Lines changed: 668 additions & 0 deletions

File tree

docs/api/modal.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import Methods from '@ionic-internal/component-api/v8/modal/methods.md';
77
import Parts from '@ionic-internal/component-api/v8/modal/parts.md';
88
import CustomProps from '@ionic-internal/component-api/v8/modal/custom-props.mdx';
99
import Slots from '@ionic-internal/component-api/v8/modal/slots.md';
10+
import SheetDragEvents from '@site/static/usage/v8/modal/sheet/drag-events/index.md';
1011

1112
<head>
1213
<title>ion-modal: Ionic Mobile App Custom Modal API Component</title>
@@ -165,6 +166,12 @@ import SheetScrollingContentExample from '@site/static/usage/v8/modal/sheet/expa
165166

166167
<SheetScrollingContentExample />
167168

169+
### Drag Events
170+
171+
When using a sheet modal, you may want to perform certain actions based on the dragging of the sheet. Ionic emits several events related to dragging that can be used for this purpose, such as `ionDragStart`, `ionDragMove`, and `ionDragEnd`.
172+
173+
<SheetDragEvents />
174+
168175
## Styling
169176

170177
Modals are presented at the root of your application so they overlay your entire app. This behavior applies to both inline modals and modals presented from a controller. As a result, custom modal styles can not be scoped to a particular component as they will not apply to the modal. Instead, styles must be applied globally. For most developers, placing the custom styles in `global.css` is sufficient.
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
```html
2+
<ion-header>
3+
<ion-toolbar>
4+
<ion-title>App</ion-title>
5+
</ion-toolbar>
6+
</ion-header>
7+
<ion-content class="ion-padding">
8+
<ion-button id="open-modal" expand="block">Open Sheet Modal</ion-button>
9+
10+
<ion-modal
11+
#modal
12+
trigger="open-modal"
13+
[initialBreakpoint]="0.25"
14+
[breakpoints]="[0, 0.25, 0.5, 0.75]"
15+
(ionDragStart)="onDragStart()"
16+
(ionDragMove)="onDragMove($event)"
17+
(ionDragEnd)="onDragEnd($event)"
18+
>
19+
<ng-template>
20+
<ion-content class="ion-padding">
21+
<div class="ion-margin-top">
22+
<ion-label>Drag the handle to adjust the modal's opacity based on a custom max opacity.</ion-label>
23+
</div>
24+
</ion-content>
25+
</ng-template>
26+
</ion-modal>
27+
</ion-content>
28+
```
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
```ts
2+
import { Component, ElementRef, ViewChild } from '@angular/core';
3+
import { IonButton, IonContent, IonHeader, IonLabel, IonModal, IonTitle, IonToolbar } from '@ionic/angular/standalone';
4+
5+
@Component({
6+
selector: 'app-example',
7+
templateUrl: 'example.component.html',
8+
standalone: true,
9+
imports: [IonButton, IonContent, IonHeader, IonLabel, IonModal, IonTitle, IonToolbar],
10+
})
11+
export class ExampleComponent {
12+
@ViewChild('modal', { read: ElementRef })
13+
modal!: ElementRef<HTMLIonModalElement>;
14+
15+
private baseOpacity!: number;
16+
private readonly MAX_OPACITY = 0.8;
17+
18+
onDragStart() {
19+
const modalEl = this.modal.nativeElement;
20+
const style = getComputedStyle(modalEl);
21+
22+
// Fetch the current variable value
23+
this.baseOpacity = parseFloat(style.getPropertyValue('--backdrop-opacity'));
24+
25+
// Ensure transitions are off during the active drag
26+
modalEl.style.transition = 'none';
27+
}
28+
29+
onDragMove(event: any) {
30+
// `progress` is a value from 1 (top) to 0 (bottom)
31+
const { progress } = event.detail;
32+
const modalEl = this.modal.nativeElement;
33+
const initialBreakpoint = modalEl.initialBreakpoint!;
34+
let dynamicOpacity: number;
35+
36+
/**
37+
* Dragging Down: Progress is between 0 and the initial breakpoint
38+
*/
39+
if (progress <= initialBreakpoint) {
40+
/**
41+
* Calculate how far down the user has dragged from the initial
42+
* breakpoint as a ratio
43+
*/
44+
const downwardProgressRatio = progress / initialBreakpoint;
45+
/**
46+
* Multiplying this by `baseOpacity` ensures that as progress hits 0,
47+
* the opacity also hits 0 without a sudden jump
48+
*/
49+
dynamicOpacity = downwardProgressRatio * this.baseOpacity;
50+
} else {
51+
/**
52+
* Dragging Up: Progress is between the initial breakpoint and 1.0
53+
*/
54+
55+
/**
56+
* Calculate how far up the user has dragged from the initial
57+
* breakpoint as a ratio
58+
*/
59+
const distanceFromStart = progress - initialBreakpoint;
60+
/**
61+
* Calculate the total available space to drag up from the initial
62+
* breakpoint to the top (1.0)
63+
*/
64+
const range = 1 - initialBreakpoint;
65+
/**
66+
* Normalizes that distance into a 0.0 to 1.0 value relative to
67+
* the available upward space
68+
*/
69+
const currentGain = distanceFromStart / range;
70+
71+
// Scale from `baseOpacity` up to `MAX_OPACITY`
72+
dynamicOpacity = this.baseOpacity + currentGain * (this.MAX_OPACITY - this.baseOpacity);
73+
}
74+
75+
modalEl.style.setProperty('--backdrop-opacity', dynamicOpacity.toString());
76+
}
77+
78+
onDragEnd(event: any) {
79+
// `currentBreakpoint` tells us which snap point the modal will animate to after the drag ends
80+
const { currentBreakpoint } = event.detail;
81+
const modalEl = this.modal.nativeElement;
82+
83+
/**
84+
* If the modal is snapping to the closed state (0), reset the
85+
* styles.
86+
*/
87+
if (currentBreakpoint === 0) {
88+
modalEl.style.removeProperty('--backdrop-opacity');
89+
return;
90+
}
91+
92+
// Determine the target opacity for the snap-back animation
93+
let targetOpacity = this.baseOpacity;
94+
/**
95+
* If snapping to the top (1), set opacity to MAX_OPACITY for a nice
96+
* visual effect.
97+
*/
98+
if (currentBreakpoint === 1) {
99+
targetOpacity = this.MAX_OPACITY;
100+
}
101+
102+
// Re-enable transition for a smooth snap-back
103+
modalEl.style.transition = '--backdrop-opacity 0.3s ease';
104+
modalEl.style.setProperty('--backdrop-opacity', targetOpacity.toString());
105+
}
106+
}
107+
```
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
6+
<title>Modal</title>
7+
<link rel="stylesheet" href="../../../common.css" />
8+
<script src="../../../common.js"></script>
9+
<script type="module" src="https://cdn.jsdelivr.net/npm/@ionic/core@8/dist/ionic/ionic.esm.js"></script>
10+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@ionic/core@8/css/ionic.bundle.css" />
11+
</head>
12+
13+
<body>
14+
<ion-app>
15+
<ion-header>
16+
<ion-toolbar>
17+
<ion-title>App</ion-title>
18+
</ion-toolbar>
19+
</ion-header>
20+
<ion-content class="ion-padding">
21+
<ion-button id="open-modal" expand="block">Open Sheet Modal</ion-button>
22+
23+
<ion-modal trigger="open-modal" initial-breakpoint="0.25">
24+
<ion-content class="ion-padding">
25+
<div class="ion-margin-top">
26+
<ion-label>Drag the handle to adjust the modal's opacity based on a custom max opacity.</ion-label>
27+
</div>
28+
</ion-content>
29+
</ion-modal>
30+
</ion-content>
31+
</ion-app>
32+
33+
<script>
34+
const modal = document.querySelector('ion-modal');
35+
modal.breakpoints = [0, 0.25, 0.5, 0.75, 1];
36+
37+
let baseOpacity;
38+
const MAX_OPACITY = 0.8;
39+
40+
modal.addEventListener('ionDragStart', () => {
41+
const style = getComputedStyle(modal);
42+
43+
// Fetch the current variable value
44+
baseOpacity = parseFloat(style.getPropertyValue('--backdrop-opacity'));
45+
46+
// Ensure transitions are off during the active drag
47+
modal.style.transition = 'none';
48+
});
49+
50+
modal.addEventListener('ionDragMove', (event) => {
51+
// `progress` is a value from 1 (top) to 0 (bottom)
52+
const { progress } = event.detail;
53+
const initialBreakpoint = modal.initialBreakpoint;
54+
let dynamicOpacity;
55+
56+
/**
57+
* Dragging Down: Progress is between 0 and the initial breakpoint
58+
*/
59+
if (progress <= initialBreakpoint) {
60+
/**
61+
* Calculate how far down the user has dragged from the initial
62+
* breakpoint as a ratio
63+
*/
64+
const downwardProgressRatio = progress / initialBreakpoint;
65+
/**
66+
* Multiplying this by `baseOpacity` ensures that as progress hits 0,
67+
* the opacity also hits 0 without a sudden jump
68+
*/
69+
dynamicOpacity = downwardProgressRatio * baseOpacity;
70+
} else {
71+
/**
72+
* Dragging Up: Progress is between the initial breakpoint and 1.0
73+
*/
74+
75+
/**
76+
* Calculate how far up the user has dragged from the initial
77+
* breakpoint as a ratio
78+
*/
79+
const distanceFromStart = progress - initialBreakpoint;
80+
/**
81+
* Calculate the total available space to drag up from the initial
82+
* breakpoint to the top (1.0)
83+
*/
84+
const range = 1 - initialBreakpoint;
85+
/**
86+
* Normalizes that distance into a 0.0 to 1.0 value relative to
87+
* the available upward space
88+
*/
89+
const currentGain = distanceFromStart / range;
90+
91+
// Scale from `baseOpacity` up to `MAX_OPACITY`
92+
dynamicOpacity = baseOpacity + currentGain * (MAX_OPACITY - baseOpacity);
93+
}
94+
95+
modal.style.setProperty('--backdrop-opacity', dynamicOpacity.toString());
96+
});
97+
98+
modal.addEventListener('ionDragEnd', (event) => {
99+
// `currentBreakpoint` tells us which snap point the modal will animate to after the drag ends
100+
const { currentBreakpoint } = event.detail;
101+
102+
/**
103+
* If the modal is snapping to the closed state (0), reset the
104+
* styles.
105+
*/
106+
if (currentBreakpoint === 0) {
107+
modal.style.removeProperty('--backdrop-opacity');
108+
return;
109+
}
110+
111+
// Determine the target opacity for the snap-back animation
112+
let targetOpacity = baseOpacity;
113+
/**
114+
* If snapping to the top (1), set opacity to MAX_OPACITY for a nice
115+
* visual effect.
116+
*/
117+
if (currentBreakpoint === 1) {
118+
targetOpacity = MAX_OPACITY;
119+
}
120+
121+
// Re-enable transition for a smooth snap-back
122+
modal.style.transition = '--backdrop-opacity 0.3s ease';
123+
modal.style.setProperty('--backdrop-opacity', targetOpacity.toString());
124+
});
125+
</script>
126+
</body>
127+
</html>
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import Playground from '@site/src/components/global/Playground';
2+
3+
import javascript from './javascript.md';
4+
5+
import react from './react.md';
6+
7+
import vue from './vue.md';
8+
9+
import angular_example_component_html from './angular/example_component_html.md';
10+
import angular_example_component_ts from './angular/example_component_ts.md';
11+
12+
<Playground
13+
version="8"
14+
code={{
15+
javascript,
16+
react,
17+
vue,
18+
angular: {
19+
files: {
20+
'src/app/example.component.html': angular_example_component_html,
21+
'src/app/example.component.ts': angular_example_component_ts,
22+
},
23+
},
24+
}}
25+
src="usage/v8/modal/sheet/drag-events/demo.html"
26+
devicePreview
27+
includeIonContent={false}
28+
/>

0 commit comments

Comments
 (0)