fix(modal): account for safe area on devices with a notch (#20072)

* fix card modal on notch phones

* only apply safe area for card modal

* fix styles, fix gesture race condition

* a few more tweaks
This commit is contained in:
Liam DeBeasi
2019-12-16 11:34:54 -05:00
committed by Liam DeBeasi
parent 9d1fe2e14f
commit 1cabb53650
6 changed files with 28 additions and 14 deletions

View File

@ -27,14 +27,15 @@ export const iosEnterAnimation = (
.addAnimation([backdropAnimation, wrapperAnimation]); .addAnimation([backdropAnimation, wrapperAnimation]);
if (presentingEl) { if (presentingEl) {
const modalTransform = (presentingEl.tagName === 'ION-MODAL' && (presentingEl as HTMLIonModalElement).presentingElement !== undefined) ? 40 : 0; const modalTransform = (presentingEl.tagName === 'ION-MODAL' && (presentingEl as HTMLIonModalElement).presentingElement !== undefined) ? '-10px' : 'calc(var(--ion-safe-area-top) + 10px)';
const bodyEl = document.body; const bodyEl = document.body;
const toPresentingScale = SwipeToCloseDefaults.MIN_PRESENTING_SCALE; const toPresentingScale = SwipeToCloseDefaults.MIN_PRESENTING_SCALE;
const finalTransform = `translateY(${-modalTransform}px) scale(${toPresentingScale})`; const finalTransform = `translateY(${modalTransform}) scale(${toPresentingScale})`;
const presentingAnimation = createAnimation() const presentingAnimation = createAnimation()
.beforeStyles({ .beforeStyles({
'transform': 'translateY(0)' 'transform': 'translateY(0)',
'transform-origin': 'top center'
}) })
.afterStyles({ .afterStyles({
'transform': finalTransform 'transform': finalTransform

View File

@ -18,7 +18,7 @@ export const iosLeaveAnimation = (
const wrapperAnimation = createAnimation() const wrapperAnimation = createAnimation()
.addElement(baseEl.querySelector('.modal-wrapper')!) .addElement(baseEl.querySelector('.modal-wrapper')!)
.beforeStyles({ 'opacity': 1 }) .beforeStyles({ 'opacity': 1 })
.fromTo('transform', `translateY(0%)`, 'translateY(100%)'); .fromTo('transform', 'translateY(0%)', 'translateY(100%)');
const baseAnimation = createAnimation() const baseAnimation = createAnimation()
.addElement(baseEl) .addElement(baseEl)
@ -27,7 +27,7 @@ export const iosLeaveAnimation = (
.addAnimation([backdropAnimation, wrapperAnimation]); .addAnimation([backdropAnimation, wrapperAnimation]);
if (presentingEl) { if (presentingEl) {
const modalTransform = (presentingEl.tagName === 'ION-MODAL' && (presentingEl as HTMLIonModalElement).presentingElement !== undefined) ? 40 : 0; const modalTransform = (presentingEl.tagName === 'ION-MODAL' && (presentingEl as HTMLIonModalElement).presentingElement !== undefined) ? '-10px' : 'calc(var(--ion-safe-area-top) + 10px)';
const bodyEl = document.body; const bodyEl = document.body;
const currentPresentingScale = SwipeToCloseDefaults.MIN_PRESENTING_SCALE; const currentPresentingScale = SwipeToCloseDefaults.MIN_PRESENTING_SCALE;
const presentingAnimation = createAnimation() const presentingAnimation = createAnimation()
@ -44,7 +44,7 @@ export const iosLeaveAnimation = (
} }
}) })
.keyframes([ .keyframes([
{ offset: 0, transform: `translateY(${-modalTransform}px) scale(${currentPresentingScale})`, 'border-radius': '10px 10px 0 0' }, { offset: 0, transform: `translateY(${modalTransform}) scale(${currentPresentingScale})`, 'border-radius': '10px 10px 0 0' },
{ offset: 1, transform: 'translateY(0px) scale(1)', 'border-radius': '0px' } { offset: 1, transform: 'translateY(0px) scale(1)', 'border-radius': '0px' }
]); ]);

View File

@ -5,11 +5,7 @@ import { clamp } from '../../../utils/helpers';
// Defaults for the card swipe animation // Defaults for the card swipe animation
export const SwipeToCloseDefaults = { export const SwipeToCloseDefaults = {
MIN_BACKDROP_OPACITY: 0.4, MIN_PRESENTING_SCALE: 0.93,
MIN_PRESENTING_SCALE: 0.95,
MIN_Y_CARD: 44,
MIN_Y_FULLSCREEN: 0,
MIN_PRESENTING_Y: 0
}; };
export const createSwipeToCloseGesture = ( export const createSwipeToCloseGesture = (
@ -70,16 +66,20 @@ export const createSwipeToCloseGesture = (
const duration = (shouldComplete) ? computeDuration(step * height, velocity) : computeDuration((1 - step) * height, velocity); const duration = (shouldComplete) ? computeDuration(step * height, velocity) : computeDuration((1 - step) * height, velocity);
isOpen = shouldComplete; isOpen = shouldComplete;
gesture.enable(false);
animation animation
.onFinish(() => { .onFinish(() => {
if (shouldComplete) { if (shouldComplete) {
onDismiss(); onDismiss();
} else {
gesture.enable(true);
} }
}) })
.progressEnd((shouldComplete) ? 1 : 0, newStepValue, duration); .progressEnd((shouldComplete) ? 1 : 0, newStepValue, duration);
}; };
return createGesture({ const gesture = createGesture({
el, el,
gestureName: 'modalSwipeToClose', gestureName: 'modalSwipeToClose',
gesturePriority: 40, gesturePriority: 40,
@ -90,8 +90,9 @@ export const createSwipeToCloseGesture = (
onMove, onMove,
onEnd onEnd
}); });
return gesture;
}; };
const computeDuration = (remaining: number, velocity: number) => { const computeDuration = (remaining: number, velocity: number) => {
return clamp(100, remaining / Math.abs(velocity * 1.1), 400); return clamp(400, remaining / Math.abs(velocity * 1.1), 500);
}; };

View File

@ -20,10 +20,12 @@
} }
:host(.modal-card) { :host(.modal-card) {
--backdrop-opacity: 0.15;
align-items: flex-end; align-items: flex-end;
} }
:host(.modal-card) .modal-wrapper { :host(.modal-card) .modal-wrapper {
@include border-radius($modal-ios-border-radius, $modal-ios-border-radius, 0, 0); @include border-radius($modal-ios-border-radius, $modal-ios-border-radius, 0, 0);
height: calc(100% - 40px); height: calc(100% - var(--ion-safe-area-top) - 20px);
} }

View File

@ -4,6 +4,8 @@
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<title>Modal - Spec</title> <title>Modal - Spec</title>
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"> <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
<link href="../../../../../css/ionic.bundle.css" rel="stylesheet"> <link href="../../../../../css/ionic.bundle.css" rel="stylesheet">
<link href="../../../../../scripts/testing/styles.css" rel="stylesheet"> <link href="../../../../../scripts/testing/styles.css" rel="stylesheet">

View File

@ -30,6 +30,14 @@ body.backdrop-no-scroll {
// --overflow: hidden; // --overflow: hidden;
// } // }
// Modal - Card Style
// --------------------------------------------------
// The card style does not reach all the way to
// the top of the screen, so there does not need
// to be any safe area padding added
ion-modal.modal-card .ion-page > ion-header > ion-toolbar:first-child {
padding-top: 0px;
}
// Ionic Colors // Ionic Colors
// -------------------------------------------------- // --------------------------------------------------