diff --git a/core/src/components/action-sheet/action-sheet.tsx b/core/src/components/action-sheet/action-sheet.tsx index 5d79ab90f51..5ad98716e87 100644 --- a/core/src/components/action-sheet/action-sheet.tsx +++ b/core/src/components/action-sheet/action-sheet.tsx @@ -23,6 +23,7 @@ import type { AnimationBuilder, CssClassMap, FrameworkDelegate, OverlayInterface import type { OverlayEventDetail } from '../../utils/overlays-interface'; import type { ActionSheetButton } from './action-sheet-interface'; +import { ionicEnterAnimation } from './animations/ionic.enter'; import { iosEnterAnimation } from './animations/ios.enter'; import { iosLeaveAnimation } from './animations/ios.leave'; import { mdEnterAnimation } from './animations/md.enter'; @@ -228,7 +229,7 @@ export class ActionSheet implements ComponentInterface, OverlayInterface { await this.delegateController.attachViewToDom(); - await present(this, 'actionSheetEnter', iosEnterAnimation, mdEnterAnimation); + await present(this, 'actionSheetEnter', iosEnterAnimation, mdEnterAnimation, ionicEnterAnimation); unlock(); } diff --git a/core/src/components/action-sheet/animations/ionic.enter.ts b/core/src/components/action-sheet/animations/ionic.enter.ts new file mode 100644 index 00000000000..e93dcd35419 --- /dev/null +++ b/core/src/components/action-sheet/animations/ionic.enter.ts @@ -0,0 +1,30 @@ +import { createAnimation } from '@utils/animation/animation'; + +import type { Animation } from '../../../interface'; + +/** + * MD Action Sheet Enter Animation + */ +export const ionicEnterAnimation = (baseEl: HTMLElement): Animation => { + const baseAnimation = createAnimation(); + const backdropAnimation = createAnimation(); + const wrapperAnimation = createAnimation(); + + backdropAnimation + .addElement(baseEl.querySelector('ion-backdrop')!) + .fromTo('opacity', 0.01, 'var(--backdrop-opacity)') + .beforeStyles({ + 'pointer-events': 'none', + }) + .afterClearStyles(['pointer-events']); + + wrapperAnimation + .addElement(baseEl.querySelector('.action-sheet-wrapper')!) + .fromTo('transform', 'translateY(100%)', 'translateY(0%)'); + + return baseAnimation + .addElement(baseEl) + .easing('cubic-bezier(.36,.66,.04,1)') + .duration(400) + .addAnimation([backdropAnimation, wrapperAnimation]); +}; diff --git a/core/src/components/action-sheet/animations/ionic.leave.ts b/core/src/components/action-sheet/animations/ionic.leave.ts new file mode 100644 index 00000000000..0ffeb88fcfd --- /dev/null +++ b/core/src/components/action-sheet/animations/ionic.leave.ts @@ -0,0 +1,24 @@ +import { createAnimation } from '@utils/animation/animation'; + +import type { Animation } from '../../../interface'; + +/** + * MD Action Sheet Leave Animation + */ +export const ionicLeaveAnimation = (baseEl: HTMLElement): Animation => { + const baseAnimation = createAnimation(); + const backdropAnimation = createAnimation(); + const wrapperAnimation = createAnimation(); + + backdropAnimation.addElement(baseEl.querySelector('ion-backdrop')!).fromTo('opacity', 'var(--backdrop-opacity)', 0); + + wrapperAnimation + .addElement(baseEl.querySelector('.action-sheet-wrapper')!) + .fromTo('transform', 'translateY(0%)', 'translateY(100%)'); + + return baseAnimation + .addElement(baseEl) + .easing('cubic-bezier(.36,.66,.04,1)') + .duration(450) + .addAnimation([backdropAnimation, wrapperAnimation]); +}; diff --git a/core/src/components/alert/alert.tsx b/core/src/components/alert/alert.tsx index e4e98b67a42..73481866206 100644 --- a/core/src/components/alert/alert.tsx +++ b/core/src/components/alert/alert.tsx @@ -28,6 +28,7 @@ import type { OverlayEventDetail } from '../../utils/overlays-interface'; import type { IonicSafeString } from '../../utils/sanitization'; import type { AlertButton, AlertInput } from './alert-interface'; +import { ionicEnterAnimation } from './animations/ionic.enter'; import { iosEnterAnimation } from './animations/ios.enter'; import { iosLeaveAnimation } from './animations/ios.leave'; import { mdEnterAnimation } from './animations/md.enter'; @@ -415,7 +416,7 @@ export class Alert implements ComponentInterface, OverlayInterface { await this.delegateController.attachViewToDom(); - await present(this, 'alertEnter', iosEnterAnimation, mdEnterAnimation).then(() => { + await present(this, 'alertEnter', iosEnterAnimation, mdEnterAnimation, ionicEnterAnimation).then(() => { /** * Check if alert has only one button and no inputs. * If so, then focus on the button. Otherwise, focus the alert wrapper. diff --git a/core/src/components/alert/animations/ionic.enter.ts b/core/src/components/alert/animations/ionic.enter.ts new file mode 100644 index 00000000000..b2015becd77 --- /dev/null +++ b/core/src/components/alert/animations/ionic.enter.ts @@ -0,0 +1,31 @@ +import { createAnimation } from '@utils/animation/animation'; + +import type { Animation } from '../../../interface'; + +/** + * Ionic Alert Enter Animation + */ +export const ionicEnterAnimation = (baseEl: HTMLElement): Animation => { + const baseAnimation = createAnimation(); + const backdropAnimation = createAnimation(); + const wrapperAnimation = createAnimation(); + + backdropAnimation + .addElement(baseEl.querySelector('ion-backdrop')!) + .fromTo('opacity', 0.01, 'var(--backdrop-opacity)') + .beforeStyles({ + 'pointer-events': 'none', + }) + .afterClearStyles(['pointer-events']); + + wrapperAnimation.addElement(baseEl.querySelector('.alert-wrapper')!).keyframes([ + { offset: 0, opacity: '0.01', transform: 'scale(0.9)' }, + { offset: 1, opacity: '1', transform: 'scale(1)' }, + ]); + + return baseAnimation + .addElement(baseEl) + .easing('ease-in-out') + .duration(150) + .addAnimation([backdropAnimation, wrapperAnimation]); +}; diff --git a/core/src/components/alert/animations/ionic.leave.ts b/core/src/components/alert/animations/ionic.leave.ts new file mode 100644 index 00000000000..d16d079be7a --- /dev/null +++ b/core/src/components/alert/animations/ionic.leave.ts @@ -0,0 +1,22 @@ +import { createAnimation } from '@utils/animation/animation'; + +import type { Animation } from '../../../interface'; + +/** + * Md Alert Leave Animation + */ +export const ionicLeaveAnimation = (baseEl: HTMLElement): Animation => { + const baseAnimation = createAnimation(); + const backdropAnimation = createAnimation(); + const wrapperAnimation = createAnimation(); + + backdropAnimation.addElement(baseEl.querySelector('ion-backdrop')!).fromTo('opacity', 'var(--backdrop-opacity)', 0); + + wrapperAnimation.addElement(baseEl.querySelector('.alert-wrapper')!).fromTo('opacity', 0.99, 0); + + return baseAnimation + .addElement(baseEl) + .easing('ease-in-out') + .duration(150) + .addAnimation([backdropAnimation, wrapperAnimation]); +}; diff --git a/core/src/components/item-sliding/test/shapes/item-sliding.e2e.ts-snapshots/item-sliding-rectangular-ionic-md-ltr-light-Mobile-Safari-linux.png b/core/src/components/item-sliding/test/shapes/item-sliding.e2e.ts-snapshots/item-sliding-rectangular-ionic-md-ltr-light-Mobile-Safari-linux.png index 49eb363a380..8c37b2cbc07 100644 Binary files a/core/src/components/item-sliding/test/shapes/item-sliding.e2e.ts-snapshots/item-sliding-rectangular-ionic-md-ltr-light-Mobile-Safari-linux.png and b/core/src/components/item-sliding/test/shapes/item-sliding.e2e.ts-snapshots/item-sliding-rectangular-ionic-md-ltr-light-Mobile-Safari-linux.png differ diff --git a/core/src/components/item-sliding/test/shapes/item-sliding.e2e.ts-snapshots/item-sliding-round-ionic-md-ltr-light-Mobile-Safari-linux.png b/core/src/components/item-sliding/test/shapes/item-sliding.e2e.ts-snapshots/item-sliding-round-ionic-md-ltr-light-Mobile-Safari-linux.png index 91cc083d380..3bc0ee848bc 100644 Binary files a/core/src/components/item-sliding/test/shapes/item-sliding.e2e.ts-snapshots/item-sliding-round-ionic-md-ltr-light-Mobile-Safari-linux.png and b/core/src/components/item-sliding/test/shapes/item-sliding.e2e.ts-snapshots/item-sliding-round-ionic-md-ltr-light-Mobile-Safari-linux.png differ diff --git a/core/src/components/item-sliding/test/shapes/item-sliding.e2e.ts-snapshots/item-sliding-soft-ionic-md-ltr-light-Mobile-Safari-linux.png b/core/src/components/item-sliding/test/shapes/item-sliding.e2e.ts-snapshots/item-sliding-soft-ionic-md-ltr-light-Mobile-Safari-linux.png index f0ecfd908fd..d5c999183a4 100644 Binary files a/core/src/components/item-sliding/test/shapes/item-sliding.e2e.ts-snapshots/item-sliding-soft-ionic-md-ltr-light-Mobile-Safari-linux.png and b/core/src/components/item-sliding/test/shapes/item-sliding.e2e.ts-snapshots/item-sliding-soft-ionic-md-ltr-light-Mobile-Safari-linux.png differ diff --git a/core/src/components/loading/animations/ionic.enter.ts b/core/src/components/loading/animations/ionic.enter.ts new file mode 100644 index 00000000000..b93c1f73cd0 --- /dev/null +++ b/core/src/components/loading/animations/ionic.enter.ts @@ -0,0 +1,31 @@ +import { createAnimation } from '@utils/animation/animation'; + +import type { Animation } from '../../../interface'; + +/** + * Ionic Loading Enter Animation + */ +export const ionicEnterAnimation = (baseEl: HTMLElement): Animation => { + const baseAnimation = createAnimation(); + const backdropAnimation = createAnimation(); + const wrapperAnimation = createAnimation(); + + backdropAnimation + .addElement(baseEl.querySelector('ion-backdrop')!) + .fromTo('opacity', 0.01, 'var(--backdrop-opacity)') + .beforeStyles({ + 'pointer-events': 'none', + }) + .afterClearStyles(['pointer-events']); + + wrapperAnimation.addElement(baseEl.querySelector('.loading-wrapper')!).keyframes([ + { offset: 0, opacity: 0.01, transform: 'scale(1.1)' }, + { offset: 1, opacity: 1, transform: 'scale(1)' }, + ]); + + return baseAnimation + .addElement(baseEl) + .easing('ease-in-out') + .duration(200) + .addAnimation([backdropAnimation, wrapperAnimation]); +}; diff --git a/core/src/components/loading/animations/ionic.leave.ts b/core/src/components/loading/animations/ionic.leave.ts new file mode 100644 index 00000000000..3d8dcf0af64 --- /dev/null +++ b/core/src/components/loading/animations/ionic.leave.ts @@ -0,0 +1,25 @@ +import { createAnimation } from '@utils/animation/animation'; + +import type { Animation } from '../../../interface'; + +/** + * Md Loading Leave Animation + */ +export const ionicLeaveAnimation = (baseEl: HTMLElement): Animation => { + const baseAnimation = createAnimation(); + const backdropAnimation = createAnimation(); + const wrapperAnimation = createAnimation(); + + backdropAnimation.addElement(baseEl.querySelector('ion-backdrop')!).fromTo('opacity', 'var(--backdrop-opacity)', 0); + + wrapperAnimation.addElement(baseEl.querySelector('.loading-wrapper')!).keyframes([ + { offset: 0, opacity: 0.99, transform: 'scale(1)' }, + { offset: 1, opacity: 0, transform: 'scale(0.9)' }, + ]); + + return baseAnimation + .addElement(baseEl) + .easing('ease-in-out') + .duration(200) + .addAnimation([backdropAnimation, wrapperAnimation]); +}; diff --git a/core/src/components/loading/loading.tsx b/core/src/components/loading/loading.tsx index 77c6055bc0a..28af3ca6656 100644 --- a/core/src/components/loading/loading.tsx +++ b/core/src/components/loading/loading.tsx @@ -23,6 +23,7 @@ import type { OverlayEventDetail } from '../../utils/overlays-interface'; import type { IonicSafeString } from '../../utils/sanitization'; import type { SpinnerTypes } from '../spinner/spinner-configs'; +import { ionicEnterAnimation } from './animations/ionic.enter'; import { iosEnterAnimation } from './animations/ios.enter'; import { iosLeaveAnimation } from './animations/ios.leave'; import { mdEnterAnimation } from './animations/md.enter'; @@ -255,7 +256,7 @@ export class Loading implements ComponentInterface, OverlayInterface { await this.delegateController.attachViewToDom(); - await present(this, 'loadingEnter', iosEnterAnimation, mdEnterAnimation); + await present(this, 'loadingEnter', iosEnterAnimation, mdEnterAnimation, ionicEnterAnimation); if (this.duration > 0) { this.durationTimeout = setTimeout(() => this.dismiss(), this.duration + 10); diff --git a/core/src/components/modal/animations/ionic.enter.ts b/core/src/components/modal/animations/ionic.enter.ts new file mode 100644 index 00000000000..0579189d13a --- /dev/null +++ b/core/src/components/modal/animations/ionic.enter.ts @@ -0,0 +1,53 @@ +import { createAnimation } from '@utils/animation/animation'; +import { getElementRoot } from '@utils/helpers'; + +import type { Animation } from '../../../interface'; +import type { ModalAnimationOptions } from '../modal-interface'; + +import { createSheetEnterAnimation } from './sheet'; + +const createEnterAnimation = () => { + const backdropAnimation = createAnimation() + .fromTo('opacity', 0.01, 'var(--backdrop-opacity)') + .beforeStyles({ + 'pointer-events': 'none', + }) + .afterClearStyles(['pointer-events']); + + const wrapperAnimation = createAnimation().keyframes([ + { offset: 0, opacity: 0.01, transform: 'translateY(40px)' }, + { offset: 1, opacity: 1, transform: `translateY(0px)` }, + ]); + + return { backdropAnimation, wrapperAnimation, contentAnimation: undefined }; +}; + +/** + * Ionic Modal Enter Animation + */ +export const ionicEnterAnimation = (baseEl: HTMLElement, opts: ModalAnimationOptions): Animation => { + const { currentBreakpoint, expandToScroll } = opts; + const root = getElementRoot(baseEl); + const { wrapperAnimation, backdropAnimation, contentAnimation } = + currentBreakpoint !== undefined ? createSheetEnterAnimation(opts) : createEnterAnimation(); + + backdropAnimation.addElement(root.querySelector('ion-backdrop')!); + + wrapperAnimation.addElement(root.querySelector('.modal-wrapper')!); + + // The content animation is only added if scrolling is enabled for + // all the breakpoints. + !expandToScroll && contentAnimation?.addElement(baseEl.querySelector('.ion-page')!); + + backdropAnimation.duration(300).easing('ease-out'); + wrapperAnimation.duration(400).easing('cubic-bezier(0.32, 0.68, 0, 1)'); + contentAnimation?.duration(400).easing('cubic-bezier(0.32, 0.68, 0, 1)'); + + const baseAnimation = createAnimation().addElement(baseEl).addAnimation([backdropAnimation, wrapperAnimation]); + + if (contentAnimation) { + baseAnimation.addAnimation(contentAnimation); + } + + return baseAnimation; +}; diff --git a/core/src/components/modal/animations/ionic.leave.ts b/core/src/components/modal/animations/ionic.leave.ts new file mode 100644 index 00000000000..ce7bce765ac --- /dev/null +++ b/core/src/components/modal/animations/ionic.leave.ts @@ -0,0 +1,38 @@ +import { createAnimation } from '@utils/animation/animation'; +import { getElementRoot } from '@utils/helpers'; + +import type { Animation } from '../../../interface'; +import type { ModalAnimationOptions } from '../modal-interface'; + +import { createSheetLeaveAnimation } from './sheet'; + +const createLeaveAnimation = () => { + const backdropAnimation = createAnimation().fromTo('opacity', 'var(--backdrop-opacity)', 0); + + const wrapperAnimation = createAnimation().keyframes([ + { offset: 0, opacity: 0.99, transform: `translateY(0px)` }, + { offset: 1, opacity: 0, transform: 'translateY(40px)' }, + ]); + + return { backdropAnimation, wrapperAnimation }; +}; + +/** + * Md Modal Leave Animation + */ +export const ionicLeaveAnimation = (baseEl: HTMLElement, opts: ModalAnimationOptions): Animation => { + const { currentBreakpoint } = opts; + const root = getElementRoot(baseEl); + const { wrapperAnimation, backdropAnimation } = + currentBreakpoint !== undefined ? createSheetLeaveAnimation(opts) : createLeaveAnimation(); + + backdropAnimation.addElement(root.querySelector('ion-backdrop')!); + wrapperAnimation.addElement(root.querySelector('.modal-wrapper')!); + + backdropAnimation.duration(250).easing('ease-in'); + wrapperAnimation.duration(400).easing('cubic-bezier(0.4, 0, 1, 1)'); + + const baseAnimation = createAnimation().addElement(baseEl).addAnimation([backdropAnimation, wrapperAnimation]); + + return baseAnimation; +}; diff --git a/core/src/components/modal/gestures/sheet.ts b/core/src/components/modal/gestures/sheet.ts index 6617a183cfc..3d3980580e2 100644 --- a/core/src/components/modal/gestures/sheet.ts +++ b/core/src/components/modal/gestures/sheet.ts @@ -53,7 +53,7 @@ export const createSheetGesture = ( getCurrentBreakpoint: () => number, onDismiss: () => void, onBreakpointChange: (breakpoint: number) => void, - staticBackdropOpacity: boolean, + isIonicTheme: boolean, onDragStart: () => void, onDragMove: (detail: ModalDragEventDetail) => void, onDragEnd: (detail: ModalDragEventDetail) => void @@ -61,13 +61,13 @@ export const createSheetGesture = ( // Defaults for the sheet swipe animation const defaultBackdrop = [ { offset: 0, opacity: 'var(--backdrop-opacity)' }, - { offset: 1, opacity: staticBackdropOpacity ? 'var(--backdrop-opacity)' : 0.01 }, + { offset: 1, opacity: isIonicTheme ? 'var(--backdrop-opacity)' : 0.01 }, ]; const customBackdrop = [ { offset: 0, opacity: 'var(--backdrop-opacity)' }, - { offset: 1 - backdropBreakpoint, opacity: staticBackdropOpacity ? 'var(--backdrop-opacity)' : 0 }, - { offset: 1, opacity: staticBackdropOpacity ? 'var(--backdrop-opacity)' : 0 }, + { offset: 1 - backdropBreakpoint, opacity: isIonicTheme ? 'var(--backdrop-opacity)' : 0 }, + { offset: 1, opacity: isIonicTheme ? 'var(--backdrop-opacity)' : 0 }, ]; const SheetDefaults = { @@ -430,7 +430,9 @@ export const createSheetGesture = ( offset = clamp(0.0001, processedStep, maxStep); animation.progressStep(offset); - const snapBreakpoint = calculateSnapBreakpoint(detail.deltaY); + const snapBreakpoint = isIonicTheme + ? calculateIonicSnapBreakpoint(detail.deltaY, detail.velocityY, detail.currentY) + : calculateSnapBreakpoint(detail.deltaY); const eventDetail: ModalDragEventDetail = { currentY: detail.currentY, @@ -444,7 +446,9 @@ export const createSheetGesture = ( }; const onEnd = (detail: GestureDetail) => { - const snapBreakpoint = calculateSnapBreakpoint(detail.deltaY); + const snapBreakpoint = isIonicTheme + ? calculateIonicSnapBreakpoint(detail.deltaY, detail.velocityY, detail.currentY) + : calculateSnapBreakpoint(detail.deltaY); const eventDetail: ModalDragEventDetail = { currentY: detail.currentY, @@ -499,6 +503,15 @@ export const createSheetGesture = ( const shouldPreventDismiss = canDismiss && breakpoint === 0; const snapToBreakpoint = shouldPreventDismiss ? currentBreakpoint : breakpoint; + /** + * Detect snap-back behavior: when the snap target is the same as the current breakpoint, + * the user released before crossing the threshold to a new breakpoint. + * Apply different timing and easing for snap-back vs. snap-to-new. + */ + const isSnapBack = snapToBreakpoint === currentBreakpoint; + const duration = isIonicTheme ? (isSnapBack ? 300 : 400) : 500; + const easing = isSnapBack ? 'cubic-bezier(0.34, 1.4, 0.64, 1)' : 'cubic-bezier(0.32, 0.68, 0, 1)'; + const shouldRemainOpen = snapToBreakpoint !== 0; currentBreakpoint = 0; @@ -515,13 +528,13 @@ export const createSheetGesture = ( backdropAnimation.keyframes([ { offset: 0, - opacity: staticBackdropOpacity + opacity: isIonicTheme ? 'var(--backdrop-opacity)' : `calc(var(--backdrop-opacity) * ${getBackdropValueForSheet(1 - breakpointOffset, backdropBreakpoint)})`, }, { offset: 1, - opacity: staticBackdropOpacity + opacity: isIonicTheme ? 'var(--backdrop-opacity)' : `calc(var(--backdrop-opacity) * ${getBackdropValueForSheet(snapToBreakpoint, backdropBreakpoint)})`, }, @@ -544,6 +557,13 @@ export const createSheetGesture = ( animation.progressStep(0); } + /** + * Apply the appropriate easing curve for this snap behavior. + */ + if (isIonicTheme) { + animation.easing(easing); + } + /** * Gesture should remain disabled until the * snapping animation completes. @@ -641,7 +661,7 @@ export const createSheetGesture = ( }, { oneTimeCallback: true } ) - .progressEnd(1, 0, animated ? 500 : 0); + .progressEnd(1, 0, animated ? duration : 0); }); }; @@ -676,6 +696,57 @@ export const createSheetGesture = ( return snapBreakpoint; }; + /** + * Calculates the Ionic-specific snap breakpoint using velocity-based logic. + * This provides a more intuitive and responsive sheet behavior for the Ionic theme. + * + * Rules: + * 1. Fast downward flick (> 500 px/s) always dismisses, regardless of position + * 2. Fast upward flick (> 400 px/s) snaps to the next breakpoint above + * 3. If dragged 40% below current snap point without fast upward flick, dismisses + * 4. Otherwise, falls back to position-based snap (closest breakpoint) + * + * @param deltaY The change in Y position since gesture started + * @param velocityY The velocity in pixels per millisecond + * @param currentY The current Y position of the gesture + * @returns The snap breakpoint value + */ + const calculateIonicSnapBreakpoint = (deltaY: number, velocityY: number, currentY: number): number => { + // Convert velocity from px/ms to px/s for easier threshold comparison + const velocityYPerSecond = velocityY * 1000; + + // Calculate current progress (0 = fully closed, 1 = fully expanded) + const currentProgress = calculateProgress(currentY); + + // Rule 1: Fast downward flick always dismisses + if (velocityYPerSecond > 500) { + return minBreakpoint; + } + + // Rule 2: Fast upward flick moves to next breakpoint above + if (velocityYPerSecond < -400) { + // Find next breakpoint above current position + const nextBreakpoint = breakpoints.find((bp) => bp > currentProgress); + // If no breakpoint above, stay at max breakpoint + return nextBreakpoint ?? maxBreakpoint; + } + + // Rule 3: 40% dismissal rule (only if not flicking up and 0 breakpoint exists) + if (minBreakpoint === 0 && currentBreakpoint > 0) { + // Calculate how far we've moved below the current snap point + const distanceBelowSnap = currentBreakpoint - currentProgress; + const percentageBelowSnap = distanceBelowSnap / currentBreakpoint; + + // If dragged more than 40% below and not flicking up, dismiss + if (percentageBelowSnap > 0.4 && velocityYPerSecond <= 400) { + return 0; + } + } + + // Rule 4: Fallback to position-based snap (existing logic) + return calculateSnapBreakpoint(deltaY); + }; + /** * Calculates the progress of the swipe gesture. * diff --git a/core/src/components/modal/modal.ionic.scss b/core/src/components/modal/modal.ionic.scss index 30306fce969..ec46628a9bc 100644 --- a/core/src/components/modal/modal.ionic.scss +++ b/core/src/components/modal/modal.ionic.scss @@ -8,7 +8,7 @@ --background: #{globals.$ion-bg-surface-default}; --box-shadow: #{globals.$ion-elevation-3}; // Backdrop opacity is 1 because the backdrop's background color has an alpha value - --backdrop-opacity: 1; + --backdrop-opacity: 0.7; color: globals.$ion-text-default; } diff --git a/core/src/components/modal/modal.tsx b/core/src/components/modal/modal.tsx index 49acd40c560..482ef315ede 100644 --- a/core/src/components/modal/modal.tsx +++ b/core/src/components/modal/modal.tsx @@ -35,6 +35,7 @@ import type { import { KEYBOARD_DID_OPEN } from '../../utils/keyboard/keyboard'; import type { OverlayEventDetail } from '../../utils/overlays-interface'; +import { ionicEnterAnimation } from './animations/ionic.enter'; import { iosEnterAnimation } from './animations/ios.enter'; import { iosLeaveAnimation } from './animations/ios.leave'; import { portraitToLandscapeTransition, landscapeToPortraitTransition } from './animations/ios.transition'; @@ -677,7 +678,7 @@ export class Modal implements ComponentInterface, OverlayInterface { setCardStatusBarDark(); } - await present(this, 'modalEnter', iosEnterAnimation, mdEnterAnimation, { + await present(this, 'modalEnter', iosEnterAnimation, mdEnterAnimation, ionicEnterAnimation, { presentingEl: presentingElement, currentBreakpoint: this.initialBreakpoint, backdropBreakpoint: this.backdropBreakpoint, diff --git a/core/src/components/modal/test/basic/modal.e2e.ts-snapshots/modal-basic-present-ionic-md-ltr-light-Mobile-Chrome-linux.png b/core/src/components/modal/test/basic/modal.e2e.ts-snapshots/modal-basic-present-ionic-md-ltr-light-Mobile-Chrome-linux.png index c124abc6cd0..ab9bc7cc88a 100644 Binary files a/core/src/components/modal/test/basic/modal.e2e.ts-snapshots/modal-basic-present-ionic-md-ltr-light-Mobile-Chrome-linux.png and b/core/src/components/modal/test/basic/modal.e2e.ts-snapshots/modal-basic-present-ionic-md-ltr-light-Mobile-Chrome-linux.png differ diff --git a/core/src/components/modal/test/basic/modal.e2e.ts-snapshots/modal-basic-present-ionic-md-ltr-light-Mobile-Firefox-linux.png b/core/src/components/modal/test/basic/modal.e2e.ts-snapshots/modal-basic-present-ionic-md-ltr-light-Mobile-Firefox-linux.png index c424abd7209..863db27b243 100644 Binary files a/core/src/components/modal/test/basic/modal.e2e.ts-snapshots/modal-basic-present-ionic-md-ltr-light-Mobile-Firefox-linux.png and b/core/src/components/modal/test/basic/modal.e2e.ts-snapshots/modal-basic-present-ionic-md-ltr-light-Mobile-Firefox-linux.png differ diff --git a/core/src/components/modal/test/basic/modal.e2e.ts-snapshots/modal-basic-present-ionic-md-ltr-light-Mobile-Safari-linux.png b/core/src/components/modal/test/basic/modal.e2e.ts-snapshots/modal-basic-present-ionic-md-ltr-light-Mobile-Safari-linux.png index 48b443029fa..63413bc8890 100644 Binary files a/core/src/components/modal/test/basic/modal.e2e.ts-snapshots/modal-basic-present-ionic-md-ltr-light-Mobile-Safari-linux.png and b/core/src/components/modal/test/basic/modal.e2e.ts-snapshots/modal-basic-present-ionic-md-ltr-light-Mobile-Safari-linux.png differ diff --git a/core/src/components/modal/test/basic/modal.e2e.ts-snapshots/modal-basic-present-ionic-md-rtl-light-Mobile-Chrome-linux.png b/core/src/components/modal/test/basic/modal.e2e.ts-snapshots/modal-basic-present-ionic-md-rtl-light-Mobile-Chrome-linux.png index ce266be6e3e..6209284bdda 100644 Binary files a/core/src/components/modal/test/basic/modal.e2e.ts-snapshots/modal-basic-present-ionic-md-rtl-light-Mobile-Chrome-linux.png and b/core/src/components/modal/test/basic/modal.e2e.ts-snapshots/modal-basic-present-ionic-md-rtl-light-Mobile-Chrome-linux.png differ diff --git a/core/src/components/modal/test/basic/modal.e2e.ts-snapshots/modal-basic-present-ionic-md-rtl-light-Mobile-Firefox-linux.png b/core/src/components/modal/test/basic/modal.e2e.ts-snapshots/modal-basic-present-ionic-md-rtl-light-Mobile-Firefox-linux.png index b78c67efcd9..8b6cc73ad5a 100644 Binary files a/core/src/components/modal/test/basic/modal.e2e.ts-snapshots/modal-basic-present-ionic-md-rtl-light-Mobile-Firefox-linux.png and b/core/src/components/modal/test/basic/modal.e2e.ts-snapshots/modal-basic-present-ionic-md-rtl-light-Mobile-Firefox-linux.png differ diff --git a/core/src/components/modal/test/basic/modal.e2e.ts-snapshots/modal-basic-present-ionic-md-rtl-light-Mobile-Safari-linux.png b/core/src/components/modal/test/basic/modal.e2e.ts-snapshots/modal-basic-present-ionic-md-rtl-light-Mobile-Safari-linux.png index a408c4c240f..a135451d5b2 100644 Binary files a/core/src/components/modal/test/basic/modal.e2e.ts-snapshots/modal-basic-present-ionic-md-rtl-light-Mobile-Safari-linux.png and b/core/src/components/modal/test/basic/modal.e2e.ts-snapshots/modal-basic-present-ionic-md-rtl-light-Mobile-Safari-linux.png differ diff --git a/core/src/components/modal/test/basic/modal.e2e.ts-snapshots/modal-basic-present-tablet-ionic-md-ltr-light-Mobile-Chrome-linux.png b/core/src/components/modal/test/basic/modal.e2e.ts-snapshots/modal-basic-present-tablet-ionic-md-ltr-light-Mobile-Chrome-linux.png index 7f52d50ea5f..5b0099508e6 100644 Binary files a/core/src/components/modal/test/basic/modal.e2e.ts-snapshots/modal-basic-present-tablet-ionic-md-ltr-light-Mobile-Chrome-linux.png and b/core/src/components/modal/test/basic/modal.e2e.ts-snapshots/modal-basic-present-tablet-ionic-md-ltr-light-Mobile-Chrome-linux.png differ diff --git a/core/src/components/modal/test/basic/modal.e2e.ts-snapshots/modal-basic-present-tablet-ionic-md-ltr-light-Mobile-Firefox-linux.png b/core/src/components/modal/test/basic/modal.e2e.ts-snapshots/modal-basic-present-tablet-ionic-md-ltr-light-Mobile-Firefox-linux.png index d1013377846..d25a5ace276 100644 Binary files a/core/src/components/modal/test/basic/modal.e2e.ts-snapshots/modal-basic-present-tablet-ionic-md-ltr-light-Mobile-Firefox-linux.png and b/core/src/components/modal/test/basic/modal.e2e.ts-snapshots/modal-basic-present-tablet-ionic-md-ltr-light-Mobile-Firefox-linux.png differ diff --git a/core/src/components/modal/test/basic/modal.e2e.ts-snapshots/modal-basic-present-tablet-ionic-md-ltr-light-Mobile-Safari-linux.png b/core/src/components/modal/test/basic/modal.e2e.ts-snapshots/modal-basic-present-tablet-ionic-md-ltr-light-Mobile-Safari-linux.png index d3f7b22e276..a72a1b7d6b5 100644 Binary files a/core/src/components/modal/test/basic/modal.e2e.ts-snapshots/modal-basic-present-tablet-ionic-md-ltr-light-Mobile-Safari-linux.png and b/core/src/components/modal/test/basic/modal.e2e.ts-snapshots/modal-basic-present-tablet-ionic-md-ltr-light-Mobile-Safari-linux.png differ diff --git a/core/src/components/modal/test/basic/modal.e2e.ts-snapshots/modal-basic-present-tablet-ionic-md-rtl-light-Mobile-Chrome-linux.png b/core/src/components/modal/test/basic/modal.e2e.ts-snapshots/modal-basic-present-tablet-ionic-md-rtl-light-Mobile-Chrome-linux.png index 5e7c29d2fde..48cee8d6079 100644 Binary files a/core/src/components/modal/test/basic/modal.e2e.ts-snapshots/modal-basic-present-tablet-ionic-md-rtl-light-Mobile-Chrome-linux.png and b/core/src/components/modal/test/basic/modal.e2e.ts-snapshots/modal-basic-present-tablet-ionic-md-rtl-light-Mobile-Chrome-linux.png differ diff --git a/core/src/components/modal/test/basic/modal.e2e.ts-snapshots/modal-basic-present-tablet-ionic-md-rtl-light-Mobile-Firefox-linux.png b/core/src/components/modal/test/basic/modal.e2e.ts-snapshots/modal-basic-present-tablet-ionic-md-rtl-light-Mobile-Firefox-linux.png index f72f28998d0..6e88b7e4c85 100644 Binary files a/core/src/components/modal/test/basic/modal.e2e.ts-snapshots/modal-basic-present-tablet-ionic-md-rtl-light-Mobile-Firefox-linux.png and b/core/src/components/modal/test/basic/modal.e2e.ts-snapshots/modal-basic-present-tablet-ionic-md-rtl-light-Mobile-Firefox-linux.png differ diff --git a/core/src/components/modal/test/basic/modal.e2e.ts-snapshots/modal-basic-present-tablet-ionic-md-rtl-light-Mobile-Safari-linux.png b/core/src/components/modal/test/basic/modal.e2e.ts-snapshots/modal-basic-present-tablet-ionic-md-rtl-light-Mobile-Safari-linux.png index 2e00430ae35..cb3e519b4e9 100644 Binary files a/core/src/components/modal/test/basic/modal.e2e.ts-snapshots/modal-basic-present-tablet-ionic-md-rtl-light-Mobile-Safari-linux.png and b/core/src/components/modal/test/basic/modal.e2e.ts-snapshots/modal-basic-present-tablet-ionic-md-rtl-light-Mobile-Safari-linux.png differ diff --git a/core/src/components/modal/test/shape/modal.e2e.ts-snapshots/modal-shape-card-default-ionic-md-ltr-light-Mobile-Chrome-linux.png b/core/src/components/modal/test/shape/modal.e2e.ts-snapshots/modal-shape-card-default-ionic-md-ltr-light-Mobile-Chrome-linux.png index 9fb294a9fea..eb824a45504 100644 Binary files a/core/src/components/modal/test/shape/modal.e2e.ts-snapshots/modal-shape-card-default-ionic-md-ltr-light-Mobile-Chrome-linux.png and b/core/src/components/modal/test/shape/modal.e2e.ts-snapshots/modal-shape-card-default-ionic-md-ltr-light-Mobile-Chrome-linux.png differ diff --git a/core/src/components/modal/test/shape/modal.e2e.ts-snapshots/modal-shape-card-default-ionic-md-ltr-light-Mobile-Firefox-linux.png b/core/src/components/modal/test/shape/modal.e2e.ts-snapshots/modal-shape-card-default-ionic-md-ltr-light-Mobile-Firefox-linux.png index ba94d7bc50d..ba52e54004a 100644 Binary files a/core/src/components/modal/test/shape/modal.e2e.ts-snapshots/modal-shape-card-default-ionic-md-ltr-light-Mobile-Firefox-linux.png and b/core/src/components/modal/test/shape/modal.e2e.ts-snapshots/modal-shape-card-default-ionic-md-ltr-light-Mobile-Firefox-linux.png differ diff --git a/core/src/components/modal/test/shape/modal.e2e.ts-snapshots/modal-shape-card-default-ionic-md-ltr-light-Mobile-Safari-linux.png b/core/src/components/modal/test/shape/modal.e2e.ts-snapshots/modal-shape-card-default-ionic-md-ltr-light-Mobile-Safari-linux.png index bec761ea62b..a4112d5cc6a 100644 Binary files a/core/src/components/modal/test/shape/modal.e2e.ts-snapshots/modal-shape-card-default-ionic-md-ltr-light-Mobile-Safari-linux.png and b/core/src/components/modal/test/shape/modal.e2e.ts-snapshots/modal-shape-card-default-ionic-md-ltr-light-Mobile-Safari-linux.png differ diff --git a/core/src/components/modal/test/shape/modal.e2e.ts-snapshots/modal-shape-card-round-ionic-md-ltr-light-Mobile-Chrome-linux.png b/core/src/components/modal/test/shape/modal.e2e.ts-snapshots/modal-shape-card-round-ionic-md-ltr-light-Mobile-Chrome-linux.png index 9fb294a9fea..eb824a45504 100644 Binary files a/core/src/components/modal/test/shape/modal.e2e.ts-snapshots/modal-shape-card-round-ionic-md-ltr-light-Mobile-Chrome-linux.png and b/core/src/components/modal/test/shape/modal.e2e.ts-snapshots/modal-shape-card-round-ionic-md-ltr-light-Mobile-Chrome-linux.png differ diff --git a/core/src/components/modal/test/shape/modal.e2e.ts-snapshots/modal-shape-card-round-ionic-md-ltr-light-Mobile-Firefox-linux.png b/core/src/components/modal/test/shape/modal.e2e.ts-snapshots/modal-shape-card-round-ionic-md-ltr-light-Mobile-Firefox-linux.png index ba94d7bc50d..ba52e54004a 100644 Binary files a/core/src/components/modal/test/shape/modal.e2e.ts-snapshots/modal-shape-card-round-ionic-md-ltr-light-Mobile-Firefox-linux.png and b/core/src/components/modal/test/shape/modal.e2e.ts-snapshots/modal-shape-card-round-ionic-md-ltr-light-Mobile-Firefox-linux.png differ diff --git a/core/src/components/modal/test/shape/modal.e2e.ts-snapshots/modal-shape-card-round-ionic-md-ltr-light-Mobile-Safari-linux.png b/core/src/components/modal/test/shape/modal.e2e.ts-snapshots/modal-shape-card-round-ionic-md-ltr-light-Mobile-Safari-linux.png index bec761ea62b..a4112d5cc6a 100644 Binary files a/core/src/components/modal/test/shape/modal.e2e.ts-snapshots/modal-shape-card-round-ionic-md-ltr-light-Mobile-Safari-linux.png and b/core/src/components/modal/test/shape/modal.e2e.ts-snapshots/modal-shape-card-round-ionic-md-ltr-light-Mobile-Safari-linux.png differ diff --git a/core/src/components/modal/test/shape/modal.e2e.ts-snapshots/modal-shape-card-soft-ionic-md-ltr-light-Mobile-Chrome-linux.png b/core/src/components/modal/test/shape/modal.e2e.ts-snapshots/modal-shape-card-soft-ionic-md-ltr-light-Mobile-Chrome-linux.png index 53728ce5d03..cea8ad8cfcc 100644 Binary files a/core/src/components/modal/test/shape/modal.e2e.ts-snapshots/modal-shape-card-soft-ionic-md-ltr-light-Mobile-Chrome-linux.png and b/core/src/components/modal/test/shape/modal.e2e.ts-snapshots/modal-shape-card-soft-ionic-md-ltr-light-Mobile-Chrome-linux.png differ diff --git a/core/src/components/modal/test/shape/modal.e2e.ts-snapshots/modal-shape-card-soft-ionic-md-ltr-light-Mobile-Firefox-linux.png b/core/src/components/modal/test/shape/modal.e2e.ts-snapshots/modal-shape-card-soft-ionic-md-ltr-light-Mobile-Firefox-linux.png index c48e1d7cac3..38066005b93 100644 Binary files a/core/src/components/modal/test/shape/modal.e2e.ts-snapshots/modal-shape-card-soft-ionic-md-ltr-light-Mobile-Firefox-linux.png and b/core/src/components/modal/test/shape/modal.e2e.ts-snapshots/modal-shape-card-soft-ionic-md-ltr-light-Mobile-Firefox-linux.png differ diff --git a/core/src/components/modal/test/shape/modal.e2e.ts-snapshots/modal-shape-card-soft-ionic-md-ltr-light-Mobile-Safari-linux.png b/core/src/components/modal/test/shape/modal.e2e.ts-snapshots/modal-shape-card-soft-ionic-md-ltr-light-Mobile-Safari-linux.png index 91eb9c0f04d..0bde9487133 100644 Binary files a/core/src/components/modal/test/shape/modal.e2e.ts-snapshots/modal-shape-card-soft-ionic-md-ltr-light-Mobile-Safari-linux.png and b/core/src/components/modal/test/shape/modal.e2e.ts-snapshots/modal-shape-card-soft-ionic-md-ltr-light-Mobile-Safari-linux.png differ diff --git a/core/src/components/modal/test/shape/modal.e2e.ts-snapshots/modal-shape-sheet-default-ionic-md-ltr-light-Mobile-Chrome-linux.png b/core/src/components/modal/test/shape/modal.e2e.ts-snapshots/modal-shape-sheet-default-ionic-md-ltr-light-Mobile-Chrome-linux.png index bf2690c7084..f4e1c103491 100644 Binary files a/core/src/components/modal/test/shape/modal.e2e.ts-snapshots/modal-shape-sheet-default-ionic-md-ltr-light-Mobile-Chrome-linux.png and b/core/src/components/modal/test/shape/modal.e2e.ts-snapshots/modal-shape-sheet-default-ionic-md-ltr-light-Mobile-Chrome-linux.png differ diff --git a/core/src/components/modal/test/shape/modal.e2e.ts-snapshots/modal-shape-sheet-default-ionic-md-ltr-light-Mobile-Firefox-linux.png b/core/src/components/modal/test/shape/modal.e2e.ts-snapshots/modal-shape-sheet-default-ionic-md-ltr-light-Mobile-Firefox-linux.png index 7401ec4d5b0..eff7392a106 100644 Binary files a/core/src/components/modal/test/shape/modal.e2e.ts-snapshots/modal-shape-sheet-default-ionic-md-ltr-light-Mobile-Firefox-linux.png and b/core/src/components/modal/test/shape/modal.e2e.ts-snapshots/modal-shape-sheet-default-ionic-md-ltr-light-Mobile-Firefox-linux.png differ diff --git a/core/src/components/modal/test/shape/modal.e2e.ts-snapshots/modal-shape-sheet-default-ionic-md-ltr-light-Mobile-Safari-linux.png b/core/src/components/modal/test/shape/modal.e2e.ts-snapshots/modal-shape-sheet-default-ionic-md-ltr-light-Mobile-Safari-linux.png index e36ce3efebf..5cb832bb826 100644 Binary files a/core/src/components/modal/test/shape/modal.e2e.ts-snapshots/modal-shape-sheet-default-ionic-md-ltr-light-Mobile-Safari-linux.png and b/core/src/components/modal/test/shape/modal.e2e.ts-snapshots/modal-shape-sheet-default-ionic-md-ltr-light-Mobile-Safari-linux.png differ diff --git a/core/src/components/modal/test/shape/modal.e2e.ts-snapshots/modal-shape-sheet-rectangular-ionic-md-ltr-light-Mobile-Chrome-linux.png b/core/src/components/modal/test/shape/modal.e2e.ts-snapshots/modal-shape-sheet-rectangular-ionic-md-ltr-light-Mobile-Chrome-linux.png index 86c8346a900..a1dca48b8af 100644 Binary files a/core/src/components/modal/test/shape/modal.e2e.ts-snapshots/modal-shape-sheet-rectangular-ionic-md-ltr-light-Mobile-Chrome-linux.png and b/core/src/components/modal/test/shape/modal.e2e.ts-snapshots/modal-shape-sheet-rectangular-ionic-md-ltr-light-Mobile-Chrome-linux.png differ diff --git a/core/src/components/modal/test/shape/modal.e2e.ts-snapshots/modal-shape-sheet-rectangular-ionic-md-ltr-light-Mobile-Firefox-linux.png b/core/src/components/modal/test/shape/modal.e2e.ts-snapshots/modal-shape-sheet-rectangular-ionic-md-ltr-light-Mobile-Firefox-linux.png index 632b83ac458..678e9181c55 100644 Binary files a/core/src/components/modal/test/shape/modal.e2e.ts-snapshots/modal-shape-sheet-rectangular-ionic-md-ltr-light-Mobile-Firefox-linux.png and b/core/src/components/modal/test/shape/modal.e2e.ts-snapshots/modal-shape-sheet-rectangular-ionic-md-ltr-light-Mobile-Firefox-linux.png differ diff --git a/core/src/components/modal/test/shape/modal.e2e.ts-snapshots/modal-shape-sheet-rectangular-ionic-md-ltr-light-Mobile-Safari-linux.png b/core/src/components/modal/test/shape/modal.e2e.ts-snapshots/modal-shape-sheet-rectangular-ionic-md-ltr-light-Mobile-Safari-linux.png index 260ff57170c..1cc4800ebfc 100644 Binary files a/core/src/components/modal/test/shape/modal.e2e.ts-snapshots/modal-shape-sheet-rectangular-ionic-md-ltr-light-Mobile-Safari-linux.png and b/core/src/components/modal/test/shape/modal.e2e.ts-snapshots/modal-shape-sheet-rectangular-ionic-md-ltr-light-Mobile-Safari-linux.png differ diff --git a/core/src/components/modal/test/shape/modal.e2e.ts-snapshots/modal-shape-sheet-round-ionic-md-ltr-light-Mobile-Chrome-linux.png b/core/src/components/modal/test/shape/modal.e2e.ts-snapshots/modal-shape-sheet-round-ionic-md-ltr-light-Mobile-Chrome-linux.png index bf2690c7084..f4e1c103491 100644 Binary files a/core/src/components/modal/test/shape/modal.e2e.ts-snapshots/modal-shape-sheet-round-ionic-md-ltr-light-Mobile-Chrome-linux.png and b/core/src/components/modal/test/shape/modal.e2e.ts-snapshots/modal-shape-sheet-round-ionic-md-ltr-light-Mobile-Chrome-linux.png differ diff --git a/core/src/components/modal/test/shape/modal.e2e.ts-snapshots/modal-shape-sheet-round-ionic-md-ltr-light-Mobile-Firefox-linux.png b/core/src/components/modal/test/shape/modal.e2e.ts-snapshots/modal-shape-sheet-round-ionic-md-ltr-light-Mobile-Firefox-linux.png index 7401ec4d5b0..eff7392a106 100644 Binary files a/core/src/components/modal/test/shape/modal.e2e.ts-snapshots/modal-shape-sheet-round-ionic-md-ltr-light-Mobile-Firefox-linux.png and b/core/src/components/modal/test/shape/modal.e2e.ts-snapshots/modal-shape-sheet-round-ionic-md-ltr-light-Mobile-Firefox-linux.png differ diff --git a/core/src/components/modal/test/shape/modal.e2e.ts-snapshots/modal-shape-sheet-round-ionic-md-ltr-light-Mobile-Safari-linux.png b/core/src/components/modal/test/shape/modal.e2e.ts-snapshots/modal-shape-sheet-round-ionic-md-ltr-light-Mobile-Safari-linux.png index e36ce3efebf..5cb832bb826 100644 Binary files a/core/src/components/modal/test/shape/modal.e2e.ts-snapshots/modal-shape-sheet-round-ionic-md-ltr-light-Mobile-Safari-linux.png and b/core/src/components/modal/test/shape/modal.e2e.ts-snapshots/modal-shape-sheet-round-ionic-md-ltr-light-Mobile-Safari-linux.png differ diff --git a/core/src/components/modal/test/shape/modal.e2e.ts-snapshots/modal-shape-sheet-soft-ionic-md-ltr-light-Mobile-Chrome-linux.png b/core/src/components/modal/test/shape/modal.e2e.ts-snapshots/modal-shape-sheet-soft-ionic-md-ltr-light-Mobile-Chrome-linux.png index 1e67c596400..871a23732f4 100644 Binary files a/core/src/components/modal/test/shape/modal.e2e.ts-snapshots/modal-shape-sheet-soft-ionic-md-ltr-light-Mobile-Chrome-linux.png and b/core/src/components/modal/test/shape/modal.e2e.ts-snapshots/modal-shape-sheet-soft-ionic-md-ltr-light-Mobile-Chrome-linux.png differ diff --git a/core/src/components/modal/test/shape/modal.e2e.ts-snapshots/modal-shape-sheet-soft-ionic-md-ltr-light-Mobile-Firefox-linux.png b/core/src/components/modal/test/shape/modal.e2e.ts-snapshots/modal-shape-sheet-soft-ionic-md-ltr-light-Mobile-Firefox-linux.png index 7cc07c9bd90..d7bab6fd099 100644 Binary files a/core/src/components/modal/test/shape/modal.e2e.ts-snapshots/modal-shape-sheet-soft-ionic-md-ltr-light-Mobile-Firefox-linux.png and b/core/src/components/modal/test/shape/modal.e2e.ts-snapshots/modal-shape-sheet-soft-ionic-md-ltr-light-Mobile-Firefox-linux.png differ diff --git a/core/src/components/modal/test/shape/modal.e2e.ts-snapshots/modal-shape-sheet-soft-ionic-md-ltr-light-Mobile-Safari-linux.png b/core/src/components/modal/test/shape/modal.e2e.ts-snapshots/modal-shape-sheet-soft-ionic-md-ltr-light-Mobile-Safari-linux.png index d9d7455f7c2..9affcfd0a50 100644 Binary files a/core/src/components/modal/test/shape/modal.e2e.ts-snapshots/modal-shape-sheet-soft-ionic-md-ltr-light-Mobile-Safari-linux.png and b/core/src/components/modal/test/shape/modal.e2e.ts-snapshots/modal-shape-sheet-soft-ionic-md-ltr-light-Mobile-Safari-linux.png differ diff --git a/core/src/components/modal/test/sheet/modal.e2e.ts-snapshots/modal-half-sheet-present-ionic-md-ltr-light-Mobile-Chrome-linux.png b/core/src/components/modal/test/sheet/modal.e2e.ts-snapshots/modal-half-sheet-present-ionic-md-ltr-light-Mobile-Chrome-linux.png index 89287b5a5af..86c5d81c335 100644 Binary files a/core/src/components/modal/test/sheet/modal.e2e.ts-snapshots/modal-half-sheet-present-ionic-md-ltr-light-Mobile-Chrome-linux.png and b/core/src/components/modal/test/sheet/modal.e2e.ts-snapshots/modal-half-sheet-present-ionic-md-ltr-light-Mobile-Chrome-linux.png differ diff --git a/core/src/components/modal/test/sheet/modal.e2e.ts-snapshots/modal-half-sheet-present-ionic-md-ltr-light-Mobile-Firefox-linux.png b/core/src/components/modal/test/sheet/modal.e2e.ts-snapshots/modal-half-sheet-present-ionic-md-ltr-light-Mobile-Firefox-linux.png index 3e4c35483f1..1749e28567c 100644 Binary files a/core/src/components/modal/test/sheet/modal.e2e.ts-snapshots/modal-half-sheet-present-ionic-md-ltr-light-Mobile-Firefox-linux.png and b/core/src/components/modal/test/sheet/modal.e2e.ts-snapshots/modal-half-sheet-present-ionic-md-ltr-light-Mobile-Firefox-linux.png differ diff --git a/core/src/components/modal/test/sheet/modal.e2e.ts-snapshots/modal-half-sheet-present-ionic-md-ltr-light-Mobile-Safari-linux.png b/core/src/components/modal/test/sheet/modal.e2e.ts-snapshots/modal-half-sheet-present-ionic-md-ltr-light-Mobile-Safari-linux.png index 2abbd84d010..a1c525b7cb1 100644 Binary files a/core/src/components/modal/test/sheet/modal.e2e.ts-snapshots/modal-half-sheet-present-ionic-md-ltr-light-Mobile-Safari-linux.png and b/core/src/components/modal/test/sheet/modal.e2e.ts-snapshots/modal-half-sheet-present-ionic-md-ltr-light-Mobile-Safari-linux.png differ diff --git a/core/src/components/modal/test/sheet/modal.e2e.ts-snapshots/modal-sheet-present-ionic-ios-ltr-light-Mobile-Chrome-linux.png b/core/src/components/modal/test/sheet/modal.e2e.ts-snapshots/modal-sheet-present-ionic-ios-ltr-light-Mobile-Chrome-linux.png index 80f8cfc0ef2..11c675822c0 100644 Binary files a/core/src/components/modal/test/sheet/modal.e2e.ts-snapshots/modal-sheet-present-ionic-ios-ltr-light-Mobile-Chrome-linux.png and b/core/src/components/modal/test/sheet/modal.e2e.ts-snapshots/modal-sheet-present-ionic-ios-ltr-light-Mobile-Chrome-linux.png differ diff --git a/core/src/components/modal/test/sheet/modal.e2e.ts-snapshots/modal-sheet-present-ionic-ios-ltr-light-Mobile-Firefox-linux.png b/core/src/components/modal/test/sheet/modal.e2e.ts-snapshots/modal-sheet-present-ionic-ios-ltr-light-Mobile-Firefox-linux.png index 11730321e6c..82d9271e643 100644 Binary files a/core/src/components/modal/test/sheet/modal.e2e.ts-snapshots/modal-sheet-present-ionic-ios-ltr-light-Mobile-Firefox-linux.png and b/core/src/components/modal/test/sheet/modal.e2e.ts-snapshots/modal-sheet-present-ionic-ios-ltr-light-Mobile-Firefox-linux.png differ diff --git a/core/src/components/modal/test/sheet/modal.e2e.ts-snapshots/modal-sheet-present-ionic-ios-ltr-light-Mobile-Safari-linux.png b/core/src/components/modal/test/sheet/modal.e2e.ts-snapshots/modal-sheet-present-ionic-ios-ltr-light-Mobile-Safari-linux.png index 40499b6dc38..0f74ecbd69c 100644 Binary files a/core/src/components/modal/test/sheet/modal.e2e.ts-snapshots/modal-sheet-present-ionic-ios-ltr-light-Mobile-Safari-linux.png and b/core/src/components/modal/test/sheet/modal.e2e.ts-snapshots/modal-sheet-present-ionic-ios-ltr-light-Mobile-Safari-linux.png differ diff --git a/core/src/components/modal/test/sheet/modal.e2e.ts-snapshots/modal-sheet-present-ionic-md-ltr-light-Mobile-Chrome-linux.png b/core/src/components/modal/test/sheet/modal.e2e.ts-snapshots/modal-sheet-present-ionic-md-ltr-light-Mobile-Chrome-linux.png index b53ada4568a..1655a971fdd 100644 Binary files a/core/src/components/modal/test/sheet/modal.e2e.ts-snapshots/modal-sheet-present-ionic-md-ltr-light-Mobile-Chrome-linux.png and b/core/src/components/modal/test/sheet/modal.e2e.ts-snapshots/modal-sheet-present-ionic-md-ltr-light-Mobile-Chrome-linux.png differ diff --git a/core/src/components/modal/test/sheet/modal.e2e.ts-snapshots/modal-sheet-present-ionic-md-ltr-light-Mobile-Firefox-linux.png b/core/src/components/modal/test/sheet/modal.e2e.ts-snapshots/modal-sheet-present-ionic-md-ltr-light-Mobile-Firefox-linux.png index 8d7082b75b3..6ed19a863dc 100644 Binary files a/core/src/components/modal/test/sheet/modal.e2e.ts-snapshots/modal-sheet-present-ionic-md-ltr-light-Mobile-Firefox-linux.png and b/core/src/components/modal/test/sheet/modal.e2e.ts-snapshots/modal-sheet-present-ionic-md-ltr-light-Mobile-Firefox-linux.png differ diff --git a/core/src/components/modal/test/sheet/modal.e2e.ts-snapshots/modal-sheet-present-ionic-md-ltr-light-Mobile-Safari-linux.png b/core/src/components/modal/test/sheet/modal.e2e.ts-snapshots/modal-sheet-present-ionic-md-ltr-light-Mobile-Safari-linux.png index e67b44ad013..b1ad3e2b7b5 100644 Binary files a/core/src/components/modal/test/sheet/modal.e2e.ts-snapshots/modal-sheet-present-ionic-md-ltr-light-Mobile-Safari-linux.png and b/core/src/components/modal/test/sheet/modal.e2e.ts-snapshots/modal-sheet-present-ionic-md-ltr-light-Mobile-Safari-linux.png differ diff --git a/core/src/components/picker-legacy/animations/ionic.enter.ts b/core/src/components/picker-legacy/animations/ionic.enter.ts new file mode 100644 index 00000000000..eba64a5562c --- /dev/null +++ b/core/src/components/picker-legacy/animations/ionic.enter.ts @@ -0,0 +1,30 @@ +import { createAnimation } from '@utils/animation/animation'; + +import type { Animation } from '../../../interface'; + +/** + * Ionic Picker Enter Animation + */ +export const ionicEnterAnimation = (baseEl: HTMLElement): Animation => { + const baseAnimation = createAnimation(); + const backdropAnimation = createAnimation(); + const wrapperAnimation = createAnimation(); + + backdropAnimation + .addElement(baseEl.querySelector('ion-backdrop')!) + .fromTo('opacity', 0.01, 'var(--backdrop-opacity)') + .beforeStyles({ + 'pointer-events': 'none', + }) + .afterClearStyles(['pointer-events']); + + wrapperAnimation + .addElement(baseEl.querySelector('.picker-wrapper')!) + .fromTo('transform', 'translateY(100%)', 'translateY(0%)'); + + return baseAnimation + .addElement(baseEl) + .easing('cubic-bezier(.36,.66,.04,1)') + .duration(400) + .addAnimation([backdropAnimation, wrapperAnimation]); +}; diff --git a/core/src/components/picker-legacy/animations/ionic.leave.ts b/core/src/components/picker-legacy/animations/ionic.leave.ts new file mode 100644 index 00000000000..fab1c098a49 --- /dev/null +++ b/core/src/components/picker-legacy/animations/ionic.leave.ts @@ -0,0 +1,26 @@ +import { createAnimation } from '@utils/animation/animation'; + +import type { Animation } from '../../../interface'; + +/** + * iOS Picker Leave Animation + */ +export const ionicLeaveAnimation = (baseEl: HTMLElement): Animation => { + const baseAnimation = createAnimation(); + const backdropAnimation = createAnimation(); + const wrapperAnimation = createAnimation(); + + backdropAnimation + .addElement(baseEl.querySelector('ion-backdrop')!) + .fromTo('opacity', 'var(--backdrop-opacity)', 0.01); + + wrapperAnimation + .addElement(baseEl.querySelector('.picker-wrapper')!) + .fromTo('transform', 'translateY(0%)', 'translateY(100%)'); + + return baseAnimation + .addElement(baseEl) + .easing('cubic-bezier(.36,.66,.04,1)') + .duration(400) + .addAnimation([backdropAnimation, wrapperAnimation]); +}; diff --git a/core/src/components/picker-legacy/picker.tsx b/core/src/components/picker-legacy/picker.tsx index 1deeb8d141d..68eda18a3a0 100644 --- a/core/src/components/picker-legacy/picker.tsx +++ b/core/src/components/picker-legacy/picker.tsx @@ -21,6 +21,7 @@ import { getIonTheme } from '../../global/ionic-global'; import type { AnimationBuilder, CssClassMap, OverlayInterface, FrameworkDelegate } from '../../interface'; import type { OverlayEventDetail } from '../../utils/overlays-interface'; +import { ionicEnterAnimation } from './animations/ionic.enter'; import { iosEnterAnimation } from './animations/ios.enter'; import { iosLeaveAnimation } from './animations/ios.leave'; import type { PickerButton, PickerColumn } from './picker-interface'; @@ -240,7 +241,7 @@ export class Picker implements ComponentInterface, OverlayInterface { await this.delegateController.attachViewToDom(); - await present(this, 'pickerEnter', iosEnterAnimation, iosEnterAnimation, undefined); + await present(this, 'pickerEnter', iosEnterAnimation, iosEnterAnimation, ionicEnterAnimation, undefined); if (this.duration > 0) { this.durationTimeout = setTimeout(() => this.dismiss(), this.duration); diff --git a/core/src/components/popover/animations/ionic.enter.ts b/core/src/components/popover/animations/ionic.enter.ts new file mode 100644 index 00000000000..f0e3dba22a3 --- /dev/null +++ b/core/src/components/popover/animations/ionic.enter.ts @@ -0,0 +1,143 @@ +import { createAnimation } from '@utils/animation/animation'; +import { getElementRoot } from '@utils/helpers'; + +import type { Animation } from '../../../interface'; +import { calculateWindowAdjustment, getPopoverDimensions, getPopoverPosition, getSafeAreaInsets } from '../utils'; + +const POPOVER_MD_BODY_PADDING = 12; + +/** + * Ionic Popover Enter Animation + */ +// TODO(FW-2832): types +export const ionicEnterAnimation = (baseEl: HTMLElement, opts?: any): Animation => { + const { event: ev, size, trigger, reference, side, align } = opts; + const doc = baseEl.ownerDocument as any; + const isRTL = doc.dir === 'rtl'; + + const bodyWidth = doc.defaultView.innerWidth; + const bodyHeight = doc.defaultView.innerHeight; + + const root = getElementRoot(baseEl); + const contentEl = root.querySelector('.popover-content') as HTMLElement; + + const referenceSizeEl = trigger || ev?.detail?.ionShadowTarget || ev?.target; + const { contentWidth, contentHeight } = getPopoverDimensions(size, contentEl, referenceSizeEl); + + const defaultPosition = { + top: bodyHeight / 2 - contentHeight / 2, + left: bodyWidth / 2 - contentWidth / 2, + originX: isRTL ? 'right' : 'left', + originY: 'top', + }; + + const results = getPopoverPosition( + isRTL, + contentWidth, + contentHeight, + 0, + 0, + reference, + side, + align, + defaultPosition, + trigger, + ev + ); + + const padding = size === 'cover' ? 0 : POPOVER_MD_BODY_PADDING; + // MD mode now applies safe-area insets (previously passed 0, ignoring all safe areas). + // This is needed for Android edge-to-edge (API 36+) where system bars overlap content. + const safeArea = size === 'cover' ? { top: 0, bottom: 0, left: 0, right: 0 } : getSafeAreaInsets(doc as Document); + + const { + originX, + originY, + top, + left, + bottom, + checkSafeAreaLeft, + checkSafeAreaRight, + checkSafeAreaTop, + checkSafeAreaBottom, + addPopoverBottomClass, + } = calculateWindowAdjustment( + side, + results.top, + results.left, + padding, + bodyWidth, + bodyHeight, + contentWidth, + contentHeight, + safeArea, + results.originX, + results.originY, + results.referenceCoordinates + ); + + const safeAreaLeftCalc = ' + var(--ion-safe-area-left, 0px)'; + const safeAreaRightCalc = ' - var(--ion-safe-area-right, 0px)'; + + let leftValue = `${left}px`; + if (checkSafeAreaLeft) { + leftValue = `${left}px${safeAreaLeftCalc}`; + } + if (checkSafeAreaRight) { + leftValue = `${left}px${safeAreaRightCalc}`; + } + + let topValue = `${top}px`; + if (checkSafeAreaTop) { + topValue = `${top}px + var(--ion-safe-area-top, 0px)`; + } + + const baseAnimation = createAnimation(); + const backdropAnimation = createAnimation(); + const wrapperAnimation = createAnimation(); + const contentAnimation = createAnimation(); + const viewportAnimation = createAnimation(); + + backdropAnimation + .addElement(root.querySelector('ion-backdrop')!) + .fromTo('opacity', 0.01, 'var(--backdrop-opacity)') + .beforeStyles({ + 'pointer-events': 'none', + }) + .afterClearStyles(['pointer-events']); + + wrapperAnimation.addElement(root.querySelector('.popover-wrapper')!).duration(150).fromTo('opacity', 0.01, 1); + + contentAnimation + .addElement(contentEl) + .beforeStyles({ + top: `calc(${topValue} + var(--offset-y, 0px))`, + left: `calc(${leftValue} + var(--offset-x, 0px))`, + 'transform-origin': `${originY} ${originX}`, + }) + .beforeAddWrite(() => { + if (bottom !== undefined) { + let bottomValue = `${bottom}px`; + if (checkSafeAreaBottom) { + bottomValue = `${bottom}px + var(--ion-safe-area-bottom, 0px)`; + } + contentEl.style.setProperty('bottom', `calc(${bottomValue})`); + } + }) + .fromTo('transform', 'scale(0.8)', 'scale(1)'); + + viewportAnimation.addElement(root.querySelector('.popover-viewport')!).fromTo('opacity', 0.01, 1); + + return baseAnimation + .easing('cubic-bezier(0.36,0.66,0.04,1)') + .duration(300) + .beforeAddWrite(() => { + if (size === 'cover') { + baseEl.style.setProperty('--width', `${contentWidth}px`); + } + if (addPopoverBottomClass) { + baseEl.classList.add('popover-bottom'); + } + }) + .addAnimation([backdropAnimation, wrapperAnimation, contentAnimation, viewportAnimation]); +}; diff --git a/core/src/components/popover/animations/ionic.leave.ts b/core/src/components/popover/animations/ionic.leave.ts new file mode 100644 index 00000000000..38652bb26b6 --- /dev/null +++ b/core/src/components/popover/animations/ionic.leave.ts @@ -0,0 +1,33 @@ +import { createAnimation } from '@utils/animation/animation'; +import { getElementRoot } from '@utils/helpers'; + +import type { Animation } from '../../../interface'; + +/** + * Ionic Popover Leave Animation + */ +export const ionicLeaveAnimation = (baseEl: HTMLElement): Animation => { + const root = getElementRoot(baseEl); + const contentEl = root.querySelector('.popover-content') as HTMLElement; + const baseAnimation = createAnimation(); + const backdropAnimation = createAnimation(); + const wrapperAnimation = createAnimation(); + + backdropAnimation.addElement(root.querySelector('ion-backdrop')!).fromTo('opacity', 'var(--backdrop-opacity)', 0); + + wrapperAnimation.addElement(root.querySelector('.popover-wrapper')!).fromTo('opacity', 0.99, 0); + + return baseAnimation + .easing('ease') + .afterAddWrite(() => { + baseEl.style.removeProperty('--width'); + baseEl.classList.remove('popover-bottom'); + + contentEl.style.removeProperty('top'); + contentEl.style.removeProperty('left'); + contentEl.style.removeProperty('bottom'); + contentEl.style.removeProperty('transform-origin'); + }) + .duration(150) + .addAnimation([backdropAnimation, wrapperAnimation]); +}; diff --git a/core/src/components/popover/popover.tsx b/core/src/components/popover/popover.tsx index dfe725e31db..07effe1b171 100644 --- a/core/src/components/popover/popover.tsx +++ b/core/src/components/popover/popover.tsx @@ -22,6 +22,7 @@ import { getIonTheme } from '../../global/ionic-global'; import type { AnimationBuilder, ComponentProps, ComponentRef, FrameworkDelegate } from '../../interface'; import type { OverlayEventDetail } from '../../utils/overlays-interface'; +import { ionicEnterAnimation } from './animations/ionic.enter'; import { iosEnterAnimation } from './animations/ios.enter'; import { iosLeaveAnimation } from './animations/ios.leave'; import { mdEnterAnimation } from './animations/md.enter'; @@ -528,14 +529,21 @@ export class Popover implements ComponentInterface, PopoverInterface { await waitForMount(); } - await present(this, 'popoverEnter', iosEnterAnimation, mdEnterAnimation, { - event: event || this.event, - size: this.size, - trigger: this.triggerEl, - reference: this.reference, - side: this.side, - align: this.alignment, - }); + await present( + this, + 'popoverEnter', + iosEnterAnimation, + mdEnterAnimation, + ionicEnterAnimation, + { + event: event || this.event, + size: this.size, + trigger: this.triggerEl, + reference: this.reference, + side: this.side, + align: this.alignment, + } + ); /** * If popover is nested and was diff --git a/core/src/components/select-modal/test/basic/select-modal.e2e.ts-snapshots/select-modal-diff-ionic-md-ltr-light-Mobile-Chrome-linux.png b/core/src/components/select-modal/test/basic/select-modal.e2e.ts-snapshots/select-modal-diff-ionic-md-ltr-light-Mobile-Chrome-linux.png index f6eba3e6bd6..743aa408579 100644 Binary files a/core/src/components/select-modal/test/basic/select-modal.e2e.ts-snapshots/select-modal-diff-ionic-md-ltr-light-Mobile-Chrome-linux.png and b/core/src/components/select-modal/test/basic/select-modal.e2e.ts-snapshots/select-modal-diff-ionic-md-ltr-light-Mobile-Chrome-linux.png differ diff --git a/core/src/components/select-modal/test/basic/select-modal.e2e.ts-snapshots/select-modal-diff-ionic-md-ltr-light-Mobile-Firefox-linux.png b/core/src/components/select-modal/test/basic/select-modal.e2e.ts-snapshots/select-modal-diff-ionic-md-ltr-light-Mobile-Firefox-linux.png index 163cd74682f..b64c99575b3 100644 Binary files a/core/src/components/select-modal/test/basic/select-modal.e2e.ts-snapshots/select-modal-diff-ionic-md-ltr-light-Mobile-Firefox-linux.png and b/core/src/components/select-modal/test/basic/select-modal.e2e.ts-snapshots/select-modal-diff-ionic-md-ltr-light-Mobile-Firefox-linux.png differ diff --git a/core/src/components/select-modal/test/basic/select-modal.e2e.ts-snapshots/select-modal-diff-ionic-md-ltr-light-Mobile-Safari-linux.png b/core/src/components/select-modal/test/basic/select-modal.e2e.ts-snapshots/select-modal-diff-ionic-md-ltr-light-Mobile-Safari-linux.png index d04eb3cda2f..aa005cd2412 100644 Binary files a/core/src/components/select-modal/test/basic/select-modal.e2e.ts-snapshots/select-modal-diff-ionic-md-ltr-light-Mobile-Safari-linux.png and b/core/src/components/select-modal/test/basic/select-modal.e2e.ts-snapshots/select-modal-diff-ionic-md-ltr-light-Mobile-Safari-linux.png differ diff --git a/core/src/components/select-modal/test/basic/select-modal.e2e.ts-snapshots/select-modal-multiple-diff-ionic-md-ltr-light-Mobile-Chrome-linux.png b/core/src/components/select-modal/test/basic/select-modal.e2e.ts-snapshots/select-modal-multiple-diff-ionic-md-ltr-light-Mobile-Chrome-linux.png index 0564ed90308..c47f13cde4f 100644 Binary files a/core/src/components/select-modal/test/basic/select-modal.e2e.ts-snapshots/select-modal-multiple-diff-ionic-md-ltr-light-Mobile-Chrome-linux.png and b/core/src/components/select-modal/test/basic/select-modal.e2e.ts-snapshots/select-modal-multiple-diff-ionic-md-ltr-light-Mobile-Chrome-linux.png differ diff --git a/core/src/components/select-modal/test/basic/select-modal.e2e.ts-snapshots/select-modal-multiple-diff-ionic-md-ltr-light-Mobile-Firefox-linux.png b/core/src/components/select-modal/test/basic/select-modal.e2e.ts-snapshots/select-modal-multiple-diff-ionic-md-ltr-light-Mobile-Firefox-linux.png index 92d4d81a06e..9627de08483 100644 Binary files a/core/src/components/select-modal/test/basic/select-modal.e2e.ts-snapshots/select-modal-multiple-diff-ionic-md-ltr-light-Mobile-Firefox-linux.png and b/core/src/components/select-modal/test/basic/select-modal.e2e.ts-snapshots/select-modal-multiple-diff-ionic-md-ltr-light-Mobile-Firefox-linux.png differ diff --git a/core/src/components/select-modal/test/basic/select-modal.e2e.ts-snapshots/select-modal-multiple-diff-ionic-md-ltr-light-Mobile-Safari-linux.png b/core/src/components/select-modal/test/basic/select-modal.e2e.ts-snapshots/select-modal-multiple-diff-ionic-md-ltr-light-Mobile-Safari-linux.png index 207cfc3689f..1cccffe7315 100644 Binary files a/core/src/components/select-modal/test/basic/select-modal.e2e.ts-snapshots/select-modal-multiple-diff-ionic-md-ltr-light-Mobile-Safari-linux.png and b/core/src/components/select-modal/test/basic/select-modal.e2e.ts-snapshots/select-modal-multiple-diff-ionic-md-ltr-light-Mobile-Safari-linux.png differ diff --git a/core/src/components/tab-bar/test/basic/tab-bar.e2e.ts-snapshots/tab-bar-default-ionic-md-rtl-light-Mobile-Firefox-linux.png b/core/src/components/tab-bar/test/basic/tab-bar.e2e.ts-snapshots/tab-bar-default-ionic-md-rtl-light-Mobile-Firefox-linux.png index dd68e3fc46e..25508f2990f 100644 Binary files a/core/src/components/tab-bar/test/basic/tab-bar.e2e.ts-snapshots/tab-bar-default-ionic-md-rtl-light-Mobile-Firefox-linux.png and b/core/src/components/tab-bar/test/basic/tab-bar.e2e.ts-snapshots/tab-bar-default-ionic-md-rtl-light-Mobile-Firefox-linux.png differ diff --git a/core/src/components/toast/animations/ionic.enter.ts b/core/src/components/toast/animations/ionic.enter.ts new file mode 100644 index 00000000000..7d685dd252e --- /dev/null +++ b/core/src/components/toast/animations/ionic.enter.ts @@ -0,0 +1,38 @@ +import { createAnimation } from '@utils/animation/animation'; +import { getElementRoot } from '@utils/helpers'; + +import type { Animation } from '../../../interface'; +import type { ToastPresentOptions } from '../toast-interface'; + +import { getOffsetForMiddlePosition } from './utils'; + +/** + * Ionic Toast Enter Animation + */ +export const ionicEnterAnimation = (baseEl: HTMLElement, opts: ToastPresentOptions): Animation => { + const baseAnimation = createAnimation(); + const wrapperAnimation = createAnimation(); + const { position, top, bottom } = opts; + + const root = getElementRoot(baseEl); + const wrapperEl = root.querySelector('.toast-wrapper') as HTMLElement; + + wrapperAnimation.addElement(wrapperEl); + + switch (position) { + case 'top': + wrapperEl.style.setProperty('transform', `translateY(${top})`); + wrapperAnimation.fromTo('opacity', 0.01, 1); + break; + case 'middle': + const topPosition = getOffsetForMiddlePosition(baseEl.clientHeight, wrapperEl.clientHeight); + wrapperEl.style.top = `${topPosition}px`; + wrapperAnimation.fromTo('opacity', 0.01, 1); + break; + default: + wrapperEl.style.setProperty('transform', `translateY(${bottom})`); + wrapperAnimation.fromTo('opacity', 0.01, 1); + break; + } + return baseAnimation.easing('cubic-bezier(.36,.66,.04,1)').duration(400).addAnimation(wrapperAnimation); +}; diff --git a/core/src/components/toast/animations/ionic.leave.ts b/core/src/components/toast/animations/ionic.leave.ts new file mode 100644 index 00000000000..25e930938ff --- /dev/null +++ b/core/src/components/toast/animations/ionic.leave.ts @@ -0,0 +1,19 @@ +import { createAnimation } from '@utils/animation/animation'; +import { getElementRoot } from '@utils/helpers'; + +import type { Animation } from '../../../interface'; + +/** + * Ionic Toast Leave Animation + */ +export const ionicLeaveAnimation = (baseEl: HTMLElement): Animation => { + const baseAnimation = createAnimation(); + const wrapperAnimation = createAnimation(); + + const root = getElementRoot(baseEl); + const wrapperEl = root.querySelector('.toast-wrapper') as HTMLElement; + + wrapperAnimation.addElement(wrapperEl).fromTo('opacity', 0.99, 0); + + return baseAnimation.easing('cubic-bezier(.36,.66,.04,1)').duration(300).addAnimation(wrapperAnimation); +}; diff --git a/core/src/components/toast/toast.tsx b/core/src/components/toast/toast.tsx index a6b498624da..1c785e438e6 100644 --- a/core/src/components/toast/toast.tsx +++ b/core/src/components/toast/toast.tsx @@ -26,6 +26,7 @@ import type { AnimationBuilder, Color, CssClassMap, OverlayInterface, FrameworkD import type { OverlayEventDetail } from '../../utils/overlays-interface'; import type { IonicSafeString } from '../../utils/sanitization'; +import { ionicEnterAnimation } from './animations/ionic.enter'; import { iosEnterAnimation } from './animations/ios.enter'; import { iosLeaveAnimation } from './animations/ios.leave'; import { mdEnterAnimation } from './animations/md.enter'; @@ -388,7 +389,7 @@ export class Toast implements ComponentInterface, OverlayInterface { */ this.lastPresentedPosition = animationPosition; - await present(this, 'toastEnter', iosEnterAnimation, mdEnterAnimation, { + await present(this, 'toastEnter', iosEnterAnimation, mdEnterAnimation, ionicEnterAnimation, { position, top: animationPosition.top, bottom: animationPosition.bottom, diff --git a/core/src/utils/overlays.ts b/core/src/utils/overlays.ts index 9ef2f19f15c..6d15c64f938 100644 --- a/core/src/utils/overlays.ts +++ b/core/src/utils/overlays.ts @@ -5,7 +5,7 @@ import { shouldUseCloseWatcher } from '@utils/hardware-back-button'; import { printIonError, printIonWarning } from '@utils/logging'; import { config } from '../global/config'; -import { getIonMode } from '../global/ionic-global'; +import { getIonMode, getIonTheme } from '../global/ionic-global'; import type { ActionSheetOptions, AlertOptions, @@ -610,6 +610,7 @@ export const present = async ( name: keyof IonicConfig, iosEnterAnimation: AnimationBuilder, mdEnterAnimation: AnimationBuilder, + ionicEnterAnimation: AnimationBuilder, opts?: OverlayPresentOptions ) => { if (overlay.presented) { @@ -664,11 +665,13 @@ export const present = async ( } overlay.willPresentShorthand?.emit(); + const theme = getIonTheme(overlay); const mode = getIonMode(overlay); + + const selectedAnimation = + mode === 'ios' ? iosEnterAnimation : theme === 'ionic' ? ionicEnterAnimation : mdEnterAnimation; // get the user's animation fn if one was provided - const animationBuilder = overlay.enterAnimation - ? overlay.enterAnimation - : config.get(name, mode === 'ios' ? iosEnterAnimation : mdEnterAnimation); + const animationBuilder = overlay.enterAnimation ? overlay.enterAnimation : config.get(name, selectedAnimation); const completed = await overlayAnimation(overlay, animationBuilder, overlay.el, opts); if (completed) {