mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-08-16 01:52:19 +08:00
feat(modal): scroll at any breakpoint
This commit is contained in:
@ -1085,6 +1085,7 @@ ion-modal,prop,keyboardClose,boolean,true,false,false
|
||||
ion-modal,prop,leaveAnimation,((baseEl: any, opts?: any) => Animation) | undefined,undefined,false,false
|
||||
ion-modal,prop,mode,"ios" | "md",undefined,false,false
|
||||
ion-modal,prop,presentingElement,HTMLElement | undefined,undefined,false,false
|
||||
ion-modal,prop,scrollAtEdge,boolean,true,false,false
|
||||
ion-modal,prop,showBackdrop,boolean,true,false,false
|
||||
ion-modal,prop,trigger,string | undefined,undefined,false,false
|
||||
ion-modal,method,dismiss,dismiss(data?: any, role?: string) => Promise<boolean>
|
||||
|
8
core/src/components.d.ts
vendored
8
core/src/components.d.ts
vendored
@ -1793,6 +1793,10 @@ export namespace Components {
|
||||
* The element that presented the modal. This is used for card presentation effects and for stacking multiple modals on top of each other. Only applies in iOS mode.
|
||||
*/
|
||||
"presentingElement"?: HTMLElement;
|
||||
/**
|
||||
* Determines whether or not the sheet modal will only scroll when fully expanded. If the value is `true`, the modal will only scroll when fully expanded. If the value is `false`, the modal will scroll at any breakpoint.
|
||||
*/
|
||||
"scrollAtEdge": boolean;
|
||||
/**
|
||||
* Move a sheet style modal to a specific breakpoint. The breakpoint value must be a value defined in your `breakpoints` array.
|
||||
*/
|
||||
@ -6618,6 +6622,10 @@ declare namespace LocalJSX {
|
||||
* The element that presented the modal. This is used for card presentation effects and for stacking multiple modals on top of each other. Only applies in iOS mode.
|
||||
*/
|
||||
"presentingElement"?: HTMLElement;
|
||||
/**
|
||||
* Determines whether or not the sheet modal will only scroll when fully expanded. If the value is `true`, the modal will only scroll when fully expanded. If the value is `false`, the modal will scroll at any breakpoint.
|
||||
*/
|
||||
"scrollAtEdge"?: boolean;
|
||||
/**
|
||||
* If `true`, a backdrop will be displayed behind the modal. This property controls whether or not the backdrop darkens the screen when the modal is presented. It does not control whether or not the backdrop is active or present in the DOM.
|
||||
*/
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { createAnimation } from '@utils/animation/animation';
|
||||
import { isIonContent, findClosestIonContent } from '@utils/content';
|
||||
import { createGesture } from '@utils/gesture';
|
||||
import { clamp, raf, getElementRoot } from '@utils/helpers';
|
||||
@ -51,7 +52,8 @@ export const createSheetGesture = (
|
||||
breakpoints: number[] = [],
|
||||
getCurrentBreakpoint: () => number,
|
||||
onDismiss: () => void,
|
||||
onBreakpointChange: (breakpoint: number) => void
|
||||
onBreakpointChange: (breakpoint: number) => void,
|
||||
scrollAtEdge: boolean
|
||||
) => {
|
||||
// Defaults for the sheet swipe animation
|
||||
const defaultBackdrop = [
|
||||
@ -71,6 +73,10 @@ export const createSheetGesture = (
|
||||
{ offset: 1, transform: 'translateY(100%)' },
|
||||
],
|
||||
BACKDROP_KEYFRAMES: backdropBreakpoint !== 0 ? customBackdrop : defaultBackdrop,
|
||||
CONTENT_KEYFRAMES: [
|
||||
{ offset: 0, maxHeight: '100%' },
|
||||
{ offset: 1, maxHeight: '0%' },
|
||||
],
|
||||
};
|
||||
|
||||
const contentEl = baseEl.querySelector('ion-content');
|
||||
@ -81,9 +87,20 @@ export const createSheetGesture = (
|
||||
const canDismissMaxStep = 0.95;
|
||||
const wrapperAnimation = animation.childAnimations.find((ani) => ani.id === 'wrapperAnimation');
|
||||
const backdropAnimation = animation.childAnimations.find((ani) => ani.id === 'backdropAnimation');
|
||||
let contentAnimation: Animation | undefined;
|
||||
const maxBreakpoint = breakpoints[breakpoints.length - 1];
|
||||
const minBreakpoint = breakpoints[0];
|
||||
|
||||
if (!scrollAtEdge) {
|
||||
contentAnimation = animation
|
||||
.addAnimation(
|
||||
createAnimation('contentAnimation')
|
||||
.addElement(contentEl!.parentElement!)
|
||||
.keyframes(SheetDefaults.CONTENT_KEYFRAMES)
|
||||
)
|
||||
.childAnimations.find((ani) => ani.id === 'contentAnimation');
|
||||
}
|
||||
|
||||
const enableBackdrop = () => {
|
||||
baseEl.style.setProperty('pointer-events', 'auto');
|
||||
backdropEl.style.setProperty('pointer-events', 'auto');
|
||||
@ -138,7 +155,7 @@ export const createSheetGesture = (
|
||||
}
|
||||
}
|
||||
|
||||
if (contentEl && currentBreakpoint !== maxBreakpoint) {
|
||||
if (contentEl && currentBreakpoint !== maxBreakpoint && scrollAtEdge) {
|
||||
contentEl.scrollY = false;
|
||||
}
|
||||
|
||||
@ -323,6 +340,20 @@ export const createSheetGesture = (
|
||||
},
|
||||
]);
|
||||
|
||||
if (contentAnimation) {
|
||||
/**
|
||||
* The modal content should scroll at any breakpoint.
|
||||
* In order to do this, the content needs to be completely
|
||||
* viewable so scrolling can access everything. Othewise,
|
||||
* the default behavior would show the content off the screen
|
||||
* and only allow scrolling when the sheet is fully expanded.
|
||||
*/
|
||||
contentAnimation.keyframes([
|
||||
{ offset: 0, maxHeight: `${(1 - breakpointOffset) * 100}%` },
|
||||
{ offset: 1, maxHeight: `${snapToBreakpoint * 100}%` },
|
||||
]);
|
||||
}
|
||||
|
||||
animation.progressStep(0);
|
||||
}
|
||||
|
||||
@ -339,13 +370,14 @@ export const createSheetGesture = (
|
||||
}
|
||||
|
||||
/**
|
||||
* If the sheet is going to be fully expanded then we should enable
|
||||
* scrolling immediately. The sheet modal animation takes ~500ms to finish
|
||||
* If the sheet is going to be fully expanded or if the sheet has toggled
|
||||
* to scroll at any breakpoint then we should enable scrolling immediately.
|
||||
* The sheet modal animation takes ~500ms to finish
|
||||
* so if we wait until then there is a visible delay for when scrolling is
|
||||
* re-enabled. Native iOS allows for scrolling on the sheet modal as soon
|
||||
* as the gesture is released, so we align with that.
|
||||
*/
|
||||
if (contentEl && snapToBreakpoint === breakpoints[breakpoints.length - 1]) {
|
||||
if (contentEl && (snapToBreakpoint === breakpoints[breakpoints.length - 1] || !scrollAtEdge)) {
|
||||
contentEl.scrollY = true;
|
||||
}
|
||||
|
||||
@ -365,6 +397,7 @@ export const createSheetGesture = (
|
||||
raf(() => {
|
||||
wrapperAnimation.keyframes([...SheetDefaults.WRAPPER_KEYFRAMES]);
|
||||
backdropAnimation.keyframes([...SheetDefaults.BACKDROP_KEYFRAMES]);
|
||||
contentAnimation?.keyframes([...SheetDefaults.CONTENT_KEYFRAMES]);
|
||||
animation.progressStart(true, 1 - snapToBreakpoint);
|
||||
currentBreakpoint = snapToBreakpoint;
|
||||
onBreakpointChange(currentBreakpoint);
|
||||
|
@ -289,6 +289,17 @@ export class Modal implements ComponentInterface, OverlayInterface {
|
||||
*/
|
||||
@Prop() canDismiss: boolean | ((data?: any, role?: string) => Promise<boolean>) = true;
|
||||
|
||||
/**
|
||||
* Determines whether or not the sheet modal will only
|
||||
* scroll when fully expanded.
|
||||
*
|
||||
* If the value is `true`, the modal will only scroll
|
||||
* when fully expanded.
|
||||
* If the value is `false`, the modal will scroll at
|
||||
* any breakpoint.
|
||||
*/
|
||||
@Prop() scrollAtEdge = true;
|
||||
|
||||
/**
|
||||
* Emitted after the modal has presented.
|
||||
*/
|
||||
@ -687,7 +698,8 @@ export class Modal implements ComponentInterface, OverlayInterface {
|
||||
this.currentBreakpoint = breakpoint;
|
||||
this.ionBreakpointDidChange.emit({ breakpoint });
|
||||
}
|
||||
}
|
||||
},
|
||||
this.scrollAtEdge
|
||||
);
|
||||
|
||||
this.gesture = gesture;
|
||||
|
@ -184,11 +184,17 @@
|
||||
${items}
|
||||
</ion-list>
|
||||
</ion-content>
|
||||
<ion-footer>
|
||||
<ion-toolbar>
|
||||
<ion-title>Footer</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-footer>
|
||||
`;
|
||||
|
||||
let extraOptions = {
|
||||
initialBreakpoint: 0.25,
|
||||
breakpoints: [0, 0.25, 0.5, 0.75, 1],
|
||||
prefersScrollingWhenScrolledToEdge: false,
|
||||
};
|
||||
|
||||
if (options) {
|
||||
|
Reference in New Issue
Block a user