feat(modal): add static backdrop behaviour when using ionic theme (#29932)

Issue number: internal

---------

<!-- Please do not submit updates to dependencies unless it fixes an
issue. -->

<!-- Please try to limit your pull request to one type (bugfix, feature,
etc). Submit multiple pull requests if needed. -->

## What is the current behavior?
The modal's backdrop opacity changes dynamically according to the
current breakpoint, independently of what theme is being used.

## What is the new behavior?
- The modal's backdrop opacity is now static once it has finished
entering the screen and while dragging the modal's handle.

## Does this introduce a breaking change?

- [ ] Yes
- [x] No

<!--
  If this introduces a breaking change:
1. Describe the impact and migration path for existing applications
below.
  2. Update the BREAKING.md file with the breaking change.
3. Add "BREAKING CHANGE: [...]" to the commit description when merging.
See
https://github.com/ionic-team/ionic-framework/blob/main/docs/CONTRIBUTING.md#footer
for more information.
-->


## Other information

<!-- Any other information that is important to this PR such as
screenshots of how the component looks before and after the change. -->
This commit is contained in:
Pedro Lourenço
2024-10-18 19:36:27 +01:00
committed by GitHub
parent d8bdf398fc
commit 0b18b6e3d2
30 changed files with 30 additions and 15 deletions

View File

@ -4,7 +4,7 @@ import type { ModalAnimationOptions } from '../modal-interface';
import { getBackdropValueForSheet } from '../utils';
export const createSheetEnterAnimation = (opts: ModalAnimationOptions) => {
const { currentBreakpoint, backdropBreakpoint } = opts;
const { currentBreakpoint, backdropBreakpoint, staticBackdropOpacity } = opts;
/**
* If the backdropBreakpoint is undefined, then the backdrop
@ -12,7 +12,9 @@ export const createSheetEnterAnimation = (opts: ModalAnimationOptions) => {
* current breakpoint, then the backdrop should be fading in.
*/
const shouldShowBackdrop = backdropBreakpoint === undefined || backdropBreakpoint < currentBreakpoint!;
const initialBackdrop = shouldShowBackdrop ? `calc(var(--backdrop-opacity) * ${currentBreakpoint!})` : '0';
const initialBackdrop = shouldShowBackdrop
? `calc(var(--backdrop-opacity) * ${staticBackdropOpacity ? 1 : currentBreakpoint!})`
: '0';
const backdropAnimation = createAnimation('backdropAnimation').fromTo('opacity', 0, initialBackdrop);

View File

@ -51,18 +51,19 @@ export const createSheetGesture = (
breakpoints: number[] = [],
getCurrentBreakpoint: () => number,
onDismiss: () => void,
onBreakpointChange: (breakpoint: number) => void
onBreakpointChange: (breakpoint: number) => void,
staticBackdropOpacity: boolean
) => {
// Defaults for the sheet swipe animation
const defaultBackdrop = [
{ offset: 0, opacity: 'var(--backdrop-opacity)' },
{ offset: 1, opacity: 0.01 },
{ offset: 1, opacity: staticBackdropOpacity ? 'var(--backdrop-opacity)' : 0.01 },
];
const customBackdrop = [
{ offset: 0, opacity: 'var(--backdrop-opacity)' },
{ offset: 1 - backdropBreakpoint, opacity: 0 },
{ offset: 1, opacity: 0 },
{ offset: 1 - backdropBreakpoint, opacity: staticBackdropOpacity ? 'var(--backdrop-opacity)' : 0 },
{ offset: 1, opacity: staticBackdropOpacity ? 'var(--backdrop-opacity)' : 0 },
];
const SheetDefaults = {
@ -312,14 +313,15 @@ export const createSheetGesture = (
backdropAnimation.keyframes([
{
offset: 0,
opacity: `calc(var(--backdrop-opacity) * ${getBackdropValueForSheet(
1 - breakpointOffset,
backdropBreakpoint
)})`,
opacity: staticBackdropOpacity
? 'var(--backdrop-opacity)'
: `calc(var(--backdrop-opacity) * ${getBackdropValueForSheet(1 - breakpointOffset, backdropBreakpoint)})`,
},
{
offset: 1,
opacity: `calc(var(--backdrop-opacity) * ${getBackdropValueForSheet(snapToBreakpoint, backdropBreakpoint)})`,
opacity: staticBackdropOpacity
? 'var(--backdrop-opacity)'
: `calc(var(--backdrop-opacity) * ${getBackdropValueForSheet(snapToBreakpoint, backdropBreakpoint)})`,
},
]);

View File

@ -27,6 +27,7 @@ export interface ModalAnimationOptions {
presentingEl?: HTMLElement;
currentBreakpoint?: number;
backdropBreakpoint?: number;
staticBackdropOpacity?: boolean;
}
export interface ModalBreakpointChangeEventDetail {

View File

@ -7,7 +7,7 @@
:host {
--background: #{globals.$ionic-color-base-white};
--box-shadow: #{globals.$ionic-elevation-300};
--backdrop-opacity: 1;
--backdrop-opacity: 0.7;
color: globals.$ionic-color-neutral-1200;
}

View File

@ -573,6 +573,7 @@ export class Modal implements ComponentInterface, OverlayInterface {
presentingEl: presentingElement,
currentBreakpoint: this.initialBreakpoint,
backdropBreakpoint: this.backdropBreakpoint,
staticBackdropOpacity: getIonTheme(this) === 'ionic',
});
/* tslint:disable-next-line */
@ -679,6 +680,7 @@ export class Modal implements ComponentInterface, OverlayInterface {
presentingEl: this.presentingElement,
currentBreakpoint: initialBreakpoint,
backdropBreakpoint,
staticBackdropOpacity: getIonTheme(this) === 'ionic',
}));
ani.progressStart(true, 1);
@ -698,7 +700,8 @@ export class Modal implements ComponentInterface, OverlayInterface {
this.currentBreakpoint = breakpoint;
this.ionBreakpointDidChange.emit({ breakpoint });
}
}
},
getIonTheme(this) === 'ionic'
);
this.gesture = gesture;
@ -1044,10 +1047,17 @@ interface ModalOverlayOptions {
*/
currentBreakpoint?: number;
/**
* The point after which the backdrop will being
* The point after which the backdrop will begin
* to fade in when using a sheet modal.
*/
backdropBreakpoint: number;
/**
* Defines whether the backdrop should have a
* static opacity = var(--backdrop-opacity).
* This option is true only when the widget
* is using the ionic theme.
*/
staticBackdropOpacity?: boolean;
}
type ModalPresentOptions = ModalOverlayOptions;

View File

@ -60,7 +60,7 @@ configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, config }) =>
});
});
configs().forEach(({ title, screenshot, config }) => {
configs({ modes: ['ios', 'md', 'ionic-md'] }).forEach(({ title, screenshot, config }) => {
test.describe(title('modal: rendering'), () => {
const runVisualTests = async (page: E2EPage, screenshotModifier = '') => {
await page.goto('/src/components/modal/test/basic', config);

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 28 KiB