From b8c9d87182641bbeef4584e9fdc4494f754ef594 Mon Sep 17 00:00:00 2001 From: ShaneK Date: Thu, 22 May 2025 07:26:57 -0700 Subject: [PATCH 01/12] chore(sheet): removing code around duplicating the footer --- .../components/modal/animations/ios.enter.ts | 88 +++++++------- .../components/modal/animations/ios.leave.ts | 54 ++++----- .../components/modal/animations/md.enter.ts | 82 ++++++------- .../components/modal/animations/md.leave.ts | 46 ++++---- core/src/components/modal/gestures/sheet.ts | 34 +++--- .../components/modal/test/sheet/index.html | 111 ++++++++++++++++++ 6 files changed, 263 insertions(+), 152 deletions(-) diff --git a/core/src/components/modal/animations/ios.enter.ts b/core/src/components/modal/animations/ios.enter.ts index 3c8924f2889..ec4a313f74f 100644 --- a/core/src/components/modal/animations/ios.enter.ts +++ b/core/src/components/modal/animations/ios.enter.ts @@ -41,50 +41,50 @@ export const iosEnterAnimation = (baseEl: HTMLElement, opts: ModalAnimationOptio .addElement(baseEl) .easing('cubic-bezier(0.32,0.72,0,1)') .duration(500) - .addAnimation([wrapperAnimation]) - .beforeAddWrite(() => { - if (expandToScroll) { - // Scroll can only be done when the modal is fully expanded. - return; - } - - /** - * There are some browsers that causes flickering when - * dragging the content when scroll is enabled at every - * breakpoint. This is due to the wrapper element being - * transformed off the screen and having a snap animation. - * - * A workaround is to clone the footer element and append - * it outside of the wrapper element. This way, the footer - * is still visible and the drag can be done without - * flickering. The original footer is hidden until the modal - * is dismissed. This maintains the animation of the footer - * when the modal is dismissed. - * - * The workaround needs to be done before the animation starts - * so there are no flickering issues. - */ - const ionFooter = baseEl.querySelector('ion-footer'); - /** - * This check is needed to prevent more than one footer - * from being appended to the shadow root. - * Otherwise, iOS and MD enter animations would append - * the footer twice. - */ - const ionFooterAlreadyAppended = baseEl.shadowRoot!.querySelector('ion-footer'); - if (ionFooter && !ionFooterAlreadyAppended) { - const footerHeight = ionFooter.clientHeight; - const clonedFooter = ionFooter.cloneNode(true) as HTMLIonFooterElement; - - baseEl.shadowRoot!.appendChild(clonedFooter); - ionFooter.style.setProperty('display', 'none'); - ionFooter.setAttribute('aria-hidden', 'true'); - - // Padding is added to prevent some content from being hidden. - const page = baseEl.querySelector('.ion-page') as HTMLElement; - page.style.setProperty('padding-bottom', `${footerHeight}px`); - } - }); + .addAnimation([wrapperAnimation]); + // .beforeAddWrite(() => { + // if (expandToScroll) { + // // Scroll can only be done when the modal is fully expanded. + // return; + // } + + // /** + // * There are some browsers that causes flickering when + // * dragging the content when scroll is enabled at every + // * breakpoint. This is due to the wrapper element being + // * transformed off the screen and having a snap animation. + // * + // * A workaround is to clone the footer element and append + // * it outside of the wrapper element. This way, the footer + // * is still visible and the drag can be done without + // * flickering. The original footer is hidden until the modal + // * is dismissed. This maintains the animation of the footer + // * when the modal is dismissed. + // * + // * The workaround needs to be done before the animation starts + // * so there are no flickering issues. + // */ + // const ionFooter = baseEl.querySelector('ion-footer'); + // /** + // * This check is needed to prevent more than one footer + // * from being appended to the shadow root. + // * Otherwise, iOS and MD enter animations would append + // * the footer twice. + // */ + // const ionFooterAlreadyAppended = baseEl.shadowRoot!.querySelector('ion-footer'); + // if (ionFooter && !ionFooterAlreadyAppended) { + // const footerHeight = ionFooter.clientHeight; + // const clonedFooter = ionFooter.cloneNode(true) as HTMLIonFooterElement; + + // baseEl.shadowRoot!.appendChild(clonedFooter); + // ionFooter.style.setProperty('display', 'none'); + // ionFooter.setAttribute('aria-hidden', 'true'); + + // // Padding is added to prevent some content from being hidden. + // const page = baseEl.querySelector('.ion-page') as HTMLElement; + // page.style.setProperty('padding-bottom', `${footerHeight}px`); + // } + // }); if (contentAnimation) { baseAnimation.addAnimation(contentAnimation); diff --git a/core/src/components/modal/animations/ios.leave.ts b/core/src/components/modal/animations/ios.leave.ts index 89ba3ce8427..2e7e071796c 100644 --- a/core/src/components/modal/animations/ios.leave.ts +++ b/core/src/components/modal/animations/ios.leave.ts @@ -32,33 +32,33 @@ export const iosLeaveAnimation = (baseEl: HTMLElement, opts: ModalAnimationOptio .addElement(baseEl) .easing('cubic-bezier(0.32,0.72,0,1)') .duration(duration) - .addAnimation(wrapperAnimation) - .beforeAddWrite(() => { - if (expandToScroll) { - // Scroll can only be done when the modal is fully expanded. - return; - } - - /** - * If expandToScroll is disabled, we need to swap - * the visibility to the original, so the footer - * dismisses with the modal and doesn't stay - * until the modal is removed from the DOM. - */ - const ionFooter = baseEl.querySelector('ion-footer'); - if (ionFooter) { - const clonedFooter = baseEl.shadowRoot!.querySelector('ion-footer')!; - - ionFooter.style.removeProperty('display'); - ionFooter.removeAttribute('aria-hidden'); - - clonedFooter.style.setProperty('display', 'none'); - clonedFooter.setAttribute('aria-hidden', 'true'); - - const page = baseEl.querySelector('.ion-page') as HTMLElement; - page.style.removeProperty('padding-bottom'); - } - }); + .addAnimation(wrapperAnimation); + // .beforeAddWrite(() => { + // if (expandToScroll) { + // // Scroll can only be done when the modal is fully expanded. + // return; + // } + + // /** + // * If expandToScroll is disabled, we need to swap + // * the visibility to the original, so the footer + // * dismisses with the modal and doesn't stay + // * until the modal is removed from the DOM. + // */ + // const ionFooter = baseEl.querySelector('ion-footer'); + // if (ionFooter) { + // const clonedFooter = baseEl.shadowRoot!.querySelector('ion-footer')!; + + // ionFooter.style.removeProperty('display'); + // ionFooter.removeAttribute('aria-hidden'); + + // clonedFooter.style.setProperty('display', 'none'); + // clonedFooter.setAttribute('aria-hidden', 'true'); + + // const page = baseEl.querySelector('.ion-page') as HTMLElement; + // page.style.removeProperty('padding-bottom'); + // } + // }); if (presentingEl) { const isMobile = window.innerWidth < 768; diff --git a/core/src/components/modal/animations/md.enter.ts b/core/src/components/modal/animations/md.enter.ts index fee0efc4f64..88bb4a42eea 100644 --- a/core/src/components/modal/animations/md.enter.ts +++ b/core/src/components/modal/animations/md.enter.ts @@ -43,50 +43,50 @@ export const mdEnterAnimation = (baseEl: HTMLElement, opts: ModalAnimationOption .addElement(baseEl) .easing('cubic-bezier(0.36,0.66,0.04,1)') .duration(280) - .addAnimation([backdropAnimation, wrapperAnimation]) - .beforeAddWrite(() => { - if (expandToScroll) { - // Scroll can only be done when the modal is fully expanded. - return; - } + .addAnimation([backdropAnimation, wrapperAnimation]); + // .beforeAddWrite(() => { + // if (expandToScroll) { + // // Scroll can only be done when the modal is fully expanded. + // return; + // } - /** - * There are some browsers that causes flickering when - * dragging the content when scroll is enabled at every - * breakpoint. This is due to the wrapper element being - * transformed off the screen and having a snap animation. - * - * A workaround is to clone the footer element and append - * it outside of the wrapper element. This way, the footer - * is still visible and the drag can be done without - * flickering. The original footer is hidden until the modal - * is dismissed. This maintains the animation of the footer - * when the modal is dismissed. - * - * The workaround needs to be done before the animation starts - * so there are no flickering issues. - */ - const ionFooter = baseEl.querySelector('ion-footer'); - /** - * This check is needed to prevent more than one footer - * from being appended to the shadow root. - * Otherwise, iOS and MD enter animations would append - * the footer twice. - */ - const ionFooterAlreadyAppended = baseEl.shadowRoot!.querySelector('ion-footer'); - if (ionFooter && !ionFooterAlreadyAppended) { - const footerHeight = ionFooter.clientHeight; - const clonedFooter = ionFooter.cloneNode(true) as HTMLIonFooterElement; + // /** + // * There are some browsers that causes flickering when + // * dragging the content when scroll is enabled at every + // * breakpoint. This is due to the wrapper element being + // * transformed off the screen and having a snap animation. + // * + // * A workaround is to clone the footer element and append + // * it outside of the wrapper element. This way, the footer + // * is still visible and the drag can be done without + // * flickering. The original footer is hidden until the modal + // * is dismissed. This maintains the animation of the footer + // * when the modal is dismissed. + // * + // * The workaround needs to be done before the animation starts + // * so there are no flickering issues. + // */ + // const ionFooter = baseEl.querySelector('ion-footer'); + // /** + // * This check is needed to prevent more than one footer + // * from being appended to the shadow root. + // * Otherwise, iOS and MD enter animations would append + // * the footer twice. + // */ + // const ionFooterAlreadyAppended = baseEl.shadowRoot!.querySelector('ion-footer'); + // if (ionFooter && !ionFooterAlreadyAppended) { + // const footerHeight = ionFooter.clientHeight; + // const clonedFooter = ionFooter.cloneNode(true) as HTMLIonFooterElement; - baseEl.shadowRoot!.appendChild(clonedFooter); - ionFooter.style.setProperty('display', 'none'); - ionFooter.setAttribute('aria-hidden', 'true'); + // baseEl.shadowRoot!.appendChild(clonedFooter); + // ionFooter.style.setProperty('display', 'none'); + // ionFooter.setAttribute('aria-hidden', 'true'); - // Padding is added to prevent some content from being hidden. - const page = baseEl.querySelector('.ion-page') as HTMLElement; - page.style.setProperty('padding-bottom', `${footerHeight}px`); - } - }); + // // Padding is added to prevent some content from being hidden. + // const page = baseEl.querySelector('.ion-page') as HTMLElement; + // page.style.setProperty('padding-bottom', `${footerHeight}px`); + // } + // }); if (contentAnimation) { baseAnimation.addAnimation(contentAnimation); diff --git a/core/src/components/modal/animations/md.leave.ts b/core/src/components/modal/animations/md.leave.ts index e453e9339cd..ce35e9265e1 100644 --- a/core/src/components/modal/animations/md.leave.ts +++ b/core/src/components/modal/animations/md.leave.ts @@ -32,33 +32,33 @@ export const mdLeaveAnimation = (baseEl: HTMLElement, opts: ModalAnimationOption const baseAnimation = createAnimation() .easing('cubic-bezier(0.47,0,0.745,0.715)') .duration(200) - .addAnimation([backdropAnimation, wrapperAnimation]) - .beforeAddWrite(() => { - if (expandToScroll) { - // Scroll can only be done when the modal is fully expanded. - return; - } + .addAnimation([backdropAnimation, wrapperAnimation]); + // .beforeAddWrite(() => { + // if (expandToScroll) { + // // Scroll can only be done when the modal is fully expanded. + // return; + // } - /** - * If expandToScroll is disabled, we need to swap - * the visibility to the original, so the footer - * dismisses with the modal and doesn't stay - * until the modal is removed from the DOM. - */ - const ionFooter = baseEl.querySelector('ion-footer'); - if (ionFooter) { - const clonedFooter = baseEl.shadowRoot!.querySelector('ion-footer')!; + // /** + // * If expandToScroll is disabled, we need to swap + // * the visibility to the original, so the footer + // * dismisses with the modal and doesn't stay + // * until the modal is removed from the DOM. + // */ + // const ionFooter = baseEl.querySelector('ion-footer'); + // if (ionFooter) { + // const clonedFooter = baseEl.shadowRoot!.querySelector('ion-footer')!; - ionFooter.style.removeProperty('display'); - ionFooter.removeAttribute('aria-hidden'); + // ionFooter.style.removeProperty('display'); + // ionFooter.removeAttribute('aria-hidden'); - clonedFooter.style.setProperty('display', 'none'); - clonedFooter.setAttribute('aria-hidden', 'true'); + // clonedFooter.style.setProperty('display', 'none'); + // clonedFooter.setAttribute('aria-hidden', 'true'); - const page = baseEl.querySelector('.ion-page') as HTMLElement; - page.style.removeProperty('padding-bottom'); - } - }); + // const page = baseEl.querySelector('.ion-page') as HTMLElement; + // page.style.removeProperty('padding-bottom'); + // } + // }); return baseAnimation; }; diff --git a/core/src/components/modal/gestures/sheet.ts b/core/src/components/modal/gestures/sheet.ts index 68df1a2ecaf..7098320c7e2 100644 --- a/core/src/components/modal/gestures/sheet.ts +++ b/core/src/components/modal/gestures/sheet.ts @@ -128,23 +128,23 @@ export const createSheetGesture = ( return; } - const clonedFooter = wrapperEl.nextElementSibling as HTMLIonFooterElement; - const footerToHide = footer === 'original' ? clonedFooter : originalFooter; - const footerToShow = footer === 'original' ? originalFooter : clonedFooter; - - footerToShow.style.removeProperty('display'); - footerToShow.removeAttribute('aria-hidden'); - - const page = baseEl.querySelector('.ion-page') as HTMLElement; - if (footer === 'original') { - page.style.removeProperty('padding-bottom'); - } else { - const pagePadding = footerToShow.clientHeight; - page.style.setProperty('padding-bottom', `${pagePadding}px`); - } - - footerToHide.style.setProperty('display', 'none'); - footerToHide.setAttribute('aria-hidden', 'true'); + // const clonedFooter = wrapperEl.nextElementSibling as HTMLIonFooterElement; + // const footerToHide = footer === 'original' ? clonedFooter : originalFooter; + // const footerToShow = footer === 'original' ? originalFooter : clonedFooter; + + // footerToShow.style.removeProperty('display'); + // footerToShow.removeAttribute('aria-hidden'); + + // const page = baseEl.querySelector('.ion-page') as HTMLElement; + // if (footer === 'original') { + // page.style.removeProperty('padding-bottom'); + // } else { + // const pagePadding = footerToShow.clientHeight; + // page.style.setProperty('padding-bottom', `${pagePadding}px`); + // } + + // footerToHide.style.setProperty('display', 'none'); + // footerToHide.setAttribute('aria-hidden', 'true'); }; /** diff --git a/core/src/components/modal/test/sheet/index.html b/core/src/components/modal/test/sheet/index.html index 8dacb81ffc4..17d3e80c2fa 100644 --- a/core/src/components/modal/test/sheet/index.html +++ b/core/src/components/modal/test/sheet/index.html @@ -145,6 +145,61 @@ > Backdrop is inactive + Present Sheet Modal (footer test) + + + +

This is a modal!!

+ + Item + Item + Item + + + The end! +
+ + + Hello world! + + + +
+ +
@@ -233,6 +288,62 @@ modal.remove(); } + function createModalFooterTest(options) { + // create component to open + const element = document.createElement('div'); + element.innerHTML = ` + + +

This is a modal!!

+
+
+ + + + + Dismiss + + + + `; + + let extraOptions = { + initialBreakpoint: 0.25, + breakpoints: [0, 0.25, 0.5, 0.75, 1], + expandToScroll: false, + }; + + if (options) { + extraOptions = { + ...extraOptions, + ...options, + }; + } + + // present the modal + const modalElement = Object.assign(document.createElement('ion-modal'), { + component: element, + ...extraOptions, + }); + + // listen for close event + const button = element.querySelector('ion-button.dismiss-footer-test'); + button.addEventListener('click', () => { + modalElement.dismiss(); + }); + + document.body.appendChild(modalElement); + return modalElement; + } + + async function presentModalFooterTest(options) { + const modal = createModalFooterTest(options); + await modal.present(); + + await modal.onDidDismiss(); + modal.remove(); + } + async function presentCardModal() { const presentingEl = document.querySelectorAll('.ion-page')[1]; const modal = createModal('card', { From a0de1e7eb01e19f3f6d3d5e73d676256c9424131 Mon Sep 17 00:00:00 2001 From: ShaneK Date: Sun, 25 May 2025 09:11:36 -0700 Subject: [PATCH 02/12] feat(sheet): changing the way sheet modal dragging works so the footer is not duplicated, but the position is changed instead --- .../components/modal/animations/ios.enter.ts | 43 ------- .../components/modal/animations/ios.leave.ts | 28 +---- .../components/modal/animations/md.enter.ts | 43 ------- .../components/modal/animations/md.leave.ts | 28 +---- core/src/components/modal/gestures/sheet.ts | 90 ++++++++------ .../components/modal/test/sheet/index.html | 112 ------------------ 6 files changed, 58 insertions(+), 286 deletions(-) diff --git a/core/src/components/modal/animations/ios.enter.ts b/core/src/components/modal/animations/ios.enter.ts index ec4a313f74f..34940062dd2 100644 --- a/core/src/components/modal/animations/ios.enter.ts +++ b/core/src/components/modal/animations/ios.enter.ts @@ -42,49 +42,6 @@ export const iosEnterAnimation = (baseEl: HTMLElement, opts: ModalAnimationOptio .easing('cubic-bezier(0.32,0.72,0,1)') .duration(500) .addAnimation([wrapperAnimation]); - // .beforeAddWrite(() => { - // if (expandToScroll) { - // // Scroll can only be done when the modal is fully expanded. - // return; - // } - - // /** - // * There are some browsers that causes flickering when - // * dragging the content when scroll is enabled at every - // * breakpoint. This is due to the wrapper element being - // * transformed off the screen and having a snap animation. - // * - // * A workaround is to clone the footer element and append - // * it outside of the wrapper element. This way, the footer - // * is still visible and the drag can be done without - // * flickering. The original footer is hidden until the modal - // * is dismissed. This maintains the animation of the footer - // * when the modal is dismissed. - // * - // * The workaround needs to be done before the animation starts - // * so there are no flickering issues. - // */ - // const ionFooter = baseEl.querySelector('ion-footer'); - // /** - // * This check is needed to prevent more than one footer - // * from being appended to the shadow root. - // * Otherwise, iOS and MD enter animations would append - // * the footer twice. - // */ - // const ionFooterAlreadyAppended = baseEl.shadowRoot!.querySelector('ion-footer'); - // if (ionFooter && !ionFooterAlreadyAppended) { - // const footerHeight = ionFooter.clientHeight; - // const clonedFooter = ionFooter.cloneNode(true) as HTMLIonFooterElement; - - // baseEl.shadowRoot!.appendChild(clonedFooter); - // ionFooter.style.setProperty('display', 'none'); - // ionFooter.setAttribute('aria-hidden', 'true'); - - // // Padding is added to prevent some content from being hidden. - // const page = baseEl.querySelector('.ion-page') as HTMLElement; - // page.style.setProperty('padding-bottom', `${footerHeight}px`); - // } - // }); if (contentAnimation) { baseAnimation.addAnimation(contentAnimation); diff --git a/core/src/components/modal/animations/ios.leave.ts b/core/src/components/modal/animations/ios.leave.ts index 2e7e071796c..914652878fa 100644 --- a/core/src/components/modal/animations/ios.leave.ts +++ b/core/src/components/modal/animations/ios.leave.ts @@ -19,7 +19,7 @@ const createLeaveAnimation = () => { * iOS Modal Leave Animation */ export const iosLeaveAnimation = (baseEl: HTMLElement, opts: ModalAnimationOptions, duration = 500): Animation => { - const { presentingEl, currentBreakpoint, expandToScroll } = opts; + const { presentingEl, currentBreakpoint } = opts; const root = getElementRoot(baseEl); const { wrapperAnimation, backdropAnimation } = currentBreakpoint !== undefined ? createSheetLeaveAnimation(opts) : createLeaveAnimation(); @@ -33,32 +33,6 @@ export const iosLeaveAnimation = (baseEl: HTMLElement, opts: ModalAnimationOptio .easing('cubic-bezier(0.32,0.72,0,1)') .duration(duration) .addAnimation(wrapperAnimation); - // .beforeAddWrite(() => { - // if (expandToScroll) { - // // Scroll can only be done when the modal is fully expanded. - // return; - // } - - // /** - // * If expandToScroll is disabled, we need to swap - // * the visibility to the original, so the footer - // * dismisses with the modal and doesn't stay - // * until the modal is removed from the DOM. - // */ - // const ionFooter = baseEl.querySelector('ion-footer'); - // if (ionFooter) { - // const clonedFooter = baseEl.shadowRoot!.querySelector('ion-footer')!; - - // ionFooter.style.removeProperty('display'); - // ionFooter.removeAttribute('aria-hidden'); - - // clonedFooter.style.setProperty('display', 'none'); - // clonedFooter.setAttribute('aria-hidden', 'true'); - - // const page = baseEl.querySelector('.ion-page') as HTMLElement; - // page.style.removeProperty('padding-bottom'); - // } - // }); if (presentingEl) { const isMobile = window.innerWidth < 768; diff --git a/core/src/components/modal/animations/md.enter.ts b/core/src/components/modal/animations/md.enter.ts index 88bb4a42eea..15186212762 100644 --- a/core/src/components/modal/animations/md.enter.ts +++ b/core/src/components/modal/animations/md.enter.ts @@ -44,49 +44,6 @@ export const mdEnterAnimation = (baseEl: HTMLElement, opts: ModalAnimationOption .easing('cubic-bezier(0.36,0.66,0.04,1)') .duration(280) .addAnimation([backdropAnimation, wrapperAnimation]); - // .beforeAddWrite(() => { - // if (expandToScroll) { - // // Scroll can only be done when the modal is fully expanded. - // return; - // } - - // /** - // * There are some browsers that causes flickering when - // * dragging the content when scroll is enabled at every - // * breakpoint. This is due to the wrapper element being - // * transformed off the screen and having a snap animation. - // * - // * A workaround is to clone the footer element and append - // * it outside of the wrapper element. This way, the footer - // * is still visible and the drag can be done without - // * flickering. The original footer is hidden until the modal - // * is dismissed. This maintains the animation of the footer - // * when the modal is dismissed. - // * - // * The workaround needs to be done before the animation starts - // * so there are no flickering issues. - // */ - // const ionFooter = baseEl.querySelector('ion-footer'); - // /** - // * This check is needed to prevent more than one footer - // * from being appended to the shadow root. - // * Otherwise, iOS and MD enter animations would append - // * the footer twice. - // */ - // const ionFooterAlreadyAppended = baseEl.shadowRoot!.querySelector('ion-footer'); - // if (ionFooter && !ionFooterAlreadyAppended) { - // const footerHeight = ionFooter.clientHeight; - // const clonedFooter = ionFooter.cloneNode(true) as HTMLIonFooterElement; - - // baseEl.shadowRoot!.appendChild(clonedFooter); - // ionFooter.style.setProperty('display', 'none'); - // ionFooter.setAttribute('aria-hidden', 'true'); - - // // Padding is added to prevent some content from being hidden. - // const page = baseEl.querySelector('.ion-page') as HTMLElement; - // page.style.setProperty('padding-bottom', `${footerHeight}px`); - // } - // }); if (contentAnimation) { baseAnimation.addAnimation(contentAnimation); diff --git a/core/src/components/modal/animations/md.leave.ts b/core/src/components/modal/animations/md.leave.ts index ce35e9265e1..0caa73e0e84 100644 --- a/core/src/components/modal/animations/md.leave.ts +++ b/core/src/components/modal/animations/md.leave.ts @@ -21,7 +21,7 @@ const createLeaveAnimation = () => { * Md Modal Leave Animation */ export const mdLeaveAnimation = (baseEl: HTMLElement, opts: ModalAnimationOptions): Animation => { - const { currentBreakpoint, expandToScroll } = opts; + const { currentBreakpoint } = opts; const root = getElementRoot(baseEl); const { wrapperAnimation, backdropAnimation } = currentBreakpoint !== undefined ? createSheetLeaveAnimation(opts) : createLeaveAnimation(); @@ -33,32 +33,6 @@ export const mdLeaveAnimation = (baseEl: HTMLElement, opts: ModalAnimationOption .easing('cubic-bezier(0.47,0,0.745,0.715)') .duration(200) .addAnimation([backdropAnimation, wrapperAnimation]); - // .beforeAddWrite(() => { - // if (expandToScroll) { - // // Scroll can only be done when the modal is fully expanded. - // return; - // } - - // /** - // * If expandToScroll is disabled, we need to swap - // * the visibility to the original, so the footer - // * dismisses with the modal and doesn't stay - // * until the modal is removed from the DOM. - // */ - // const ionFooter = baseEl.querySelector('ion-footer'); - // if (ionFooter) { - // const clonedFooter = baseEl.shadowRoot!.querySelector('ion-footer')!; - - // ionFooter.style.removeProperty('display'); - // ionFooter.removeAttribute('aria-hidden'); - - // clonedFooter.style.setProperty('display', 'none'); - // clonedFooter.setAttribute('aria-hidden', 'true'); - - // const page = baseEl.querySelector('.ion-page') as HTMLElement; - // page.style.removeProperty('padding-bottom'); - // } - // }); return baseAnimation; }; diff --git a/core/src/components/modal/gestures/sheet.ts b/core/src/components/modal/gestures/sheet.ts index 7098320c7e2..cd6c686b60f 100644 --- a/core/src/components/modal/gestures/sheet.ts +++ b/core/src/components/modal/gestures/sheet.ts @@ -84,6 +84,8 @@ export const createSheetGesture = ( let offset = 0; let canDismissBlocksGesture = false; let cachedScrollEl: HTMLElement | null = null; + let cachedFooterYPosition: number | null = null; + let currentFooterState: 'moving' | 'stationary' | null = null; const canDismissMaxStep = 0.95; const maxBreakpoint = breakpoints[breakpoints.length - 1]; const minBreakpoint = breakpoints[0]; @@ -118,33 +120,39 @@ export const createSheetGesture = ( }; /** - * Toggles the visible modal footer when `expandToScroll` is disabled. - * @param footer The footer to show. + * Toggles the footer to an absolute position while moving to prevent + * it from shaking while the sheet is being dragged. + * @param footer Whether the footer is in a moving or stationary position. */ - const swapFooterVisibility = (footer: 'original' | 'cloned') => { + const swapFooterPosition = (newPosition: 'moving' | 'stationary') => { const originalFooter = baseEl.querySelector('ion-footer') as HTMLIonFooterElement | null; - if (!originalFooter) { return; } - // const clonedFooter = wrapperEl.nextElementSibling as HTMLIonFooterElement; - // const footerToHide = footer === 'original' ? clonedFooter : originalFooter; - // const footerToShow = footer === 'original' ? originalFooter : clonedFooter; - - // footerToShow.style.removeProperty('display'); - // footerToShow.removeAttribute('aria-hidden'); - - // const page = baseEl.querySelector('.ion-page') as HTMLElement; - // if (footer === 'original') { - // page.style.removeProperty('padding-bottom'); - // } else { - // const pagePadding = footerToShow.clientHeight; - // page.style.setProperty('padding-bottom', `${pagePadding}px`); - // } - - // footerToHide.style.setProperty('display', 'none'); - // footerToHide.setAttribute('aria-hidden', 'true'); + currentFooterState = newPosition; + if (newPosition === 'stationary') { + // Reset positioning styles to allow normal document flow + originalFooter.style.removeProperty('position'); + originalFooter.style.removeProperty('bottom'); + originalFooter.parentElement?.style.removeProperty('padding-bottom'); + } else { + // Add padding to the parent element to prevent content from being hidden + // when the footer is positioned absolutely. This has to be done before we + // make the footer absolutely positioned or we may accidentally cause the + // sheet to scroll. + const footerHeight = originalFooter.clientHeight; + originalFooter.parentElement?.style.setProperty('padding-bottom', `${footerHeight}px`); + + // Apply positioning styles to keep footer at bottom + originalFooter.style.setProperty('position', 'absolute'); + originalFooter.style.setProperty('bottom', '0'); + + // Also cache the footer Y position, which we use to determine if the + // sheet has been moved below the footer. When that happens, we need to swap + // the position back so it will collapse correctly. + cachedFooterYPosition = originalFooter.getBoundingClientRect().top + window.scrollY; + } }; /** @@ -247,12 +255,11 @@ export const createSheetGesture = ( /** * If expandToScroll is disabled, we need to swap - * the footer visibility to the original, so if the modal - * is dismissed, the footer dismisses with the modal - * and doesn't stay on the screen after the modal is gone. + * the footer position to moving so that it doesn't shake + * while the sheet is being dragged. */ if (!expandToScroll) { - swapFooterVisibility('original'); + swapFooterPosition('moving'); } /** @@ -275,6 +282,21 @@ export const createSheetGesture = ( }; const onMove = (detail: GestureDetail) => { + /** + * If `expandToScroll` is disabled, we need to see if we're currently below + * the footer element and the footer is in a stationary position. If so, + * we need to make the stationary the original position so that the footer + * collapses with the sheet. + */ + if (!expandToScroll && cachedFooterYPosition !== null && currentFooterState !== null) { + // Check if we need to swap the footer position + if (detail.currentY >= cachedFooterYPosition && currentFooterState === 'moving') { + swapFooterPosition('stationary'); + } else if (detail.currentY < cachedFooterYPosition && currentFooterState === 'stationary') { + swapFooterPosition('moving'); + } + } + /** * If `expandToScroll` is disabled, and an upwards swipe gesture is done within * the scrollable content, we should not allow the swipe gesture to continue. @@ -431,15 +453,6 @@ export const createSheetGesture = ( */ gesture.enable(false); - /** - * If expandToScroll is disabled, we need to swap - * the footer visibility to the cloned one so the footer - * doesn't flicker when the sheet's height is animated. - */ - if (!expandToScroll && shouldRemainOpen) { - swapFooterVisibility('cloned'); - } - if (shouldPreventDismiss) { handleCanDismiss(baseEl, animation); } else if (!shouldRemainOpen) { @@ -462,6 +475,15 @@ export const createSheetGesture = ( .onFinish( () => { if (shouldRemainOpen) { + /** + * If expandToScroll is disabled, we need to swap + * the footer position to stationary so that it + * will act as it would by default + */ + if (!expandToScroll) { + swapFooterPosition('stationary'); + } + /** * Once the snapping animation completes, * we need to reset the animation to go diff --git a/core/src/components/modal/test/sheet/index.html b/core/src/components/modal/test/sheet/index.html index 17d3e80c2fa..2e366c07eb6 100644 --- a/core/src/components/modal/test/sheet/index.html +++ b/core/src/components/modal/test/sheet/index.html @@ -145,62 +145,6 @@ > Backdrop is inactive - Present Sheet Modal (footer test) - - - -

This is a modal!!

- - Item - Item - Item - - - The end! -
- - - Hello world! - - - -
- - -
@@ -288,62 +232,6 @@ modal.remove(); } - function createModalFooterTest(options) { - // create component to open - const element = document.createElement('div'); - element.innerHTML = ` - - -

This is a modal!!

-
-
- - - - - Dismiss - - - - `; - - let extraOptions = { - initialBreakpoint: 0.25, - breakpoints: [0, 0.25, 0.5, 0.75, 1], - expandToScroll: false, - }; - - if (options) { - extraOptions = { - ...extraOptions, - ...options, - }; - } - - // present the modal - const modalElement = Object.assign(document.createElement('ion-modal'), { - component: element, - ...extraOptions, - }); - - // listen for close event - const button = element.querySelector('ion-button.dismiss-footer-test'); - button.addEventListener('click', () => { - modalElement.dismiss(); - }); - - document.body.appendChild(modalElement); - return modalElement; - } - - async function presentModalFooterTest(options) { - const modal = createModalFooterTest(options); - await modal.present(); - - await modal.onDidDismiss(); - modal.remove(); - } - async function presentCardModal() { const presentingEl = document.querySelectorAll('.ion-page')[1]; const modal = createModal('card', { From 662f2169395179d3421931704fce0dae02d6c44a Mon Sep 17 00:00:00 2001 From: ShaneK Date: Tue, 27 May 2025 11:06:27 -0700 Subject: [PATCH 03/12] WIP --- core/src/components/modal/gestures/sheet.ts | 34 +++++++---- .../components/modal/test/sheet/index.html | 57 ++++++++++++++++++- 2 files changed, 78 insertions(+), 13 deletions(-) diff --git a/core/src/components/modal/gestures/sheet.ts b/core/src/components/modal/gestures/sheet.ts index cd6c686b60f..fcc22d8d572 100644 --- a/core/src/components/modal/gestures/sheet.ts +++ b/core/src/components/modal/gestures/sheet.ts @@ -84,6 +84,7 @@ export const createSheetGesture = ( let offset = 0; let canDismissBlocksGesture = false; let cachedScrollEl: HTMLElement | null = null; + let cachedFooterEl: HTMLIonFooterElement | null = null; let cachedFooterYPosition: number | null = null; let currentFooterState: 'moving' | 'stationary' | null = null; const canDismissMaxStep = 0.95; @@ -125,33 +126,44 @@ export const createSheetGesture = ( * @param footer Whether the footer is in a moving or stationary position. */ const swapFooterPosition = (newPosition: 'moving' | 'stationary') => { - const originalFooter = baseEl.querySelector('ion-footer') as HTMLIonFooterElement | null; - if (!originalFooter) { - return; + if (!cachedFooterEl) { + cachedFooterEl = baseEl.querySelector('ion-footer') as HTMLIonFooterElement | null; + if (!cachedFooterEl) { + return; + } } + const page = baseEl.querySelector('.ion-page') as HTMLElement | null; + currentFooterState = newPosition; if (newPosition === 'stationary') { // Reset positioning styles to allow normal document flow - originalFooter.style.removeProperty('position'); - originalFooter.style.removeProperty('bottom'); - originalFooter.parentElement?.style.removeProperty('padding-bottom'); + cachedFooterEl.style.removeProperty('position'); + cachedFooterEl.style.removeProperty('bottom'); + page?.style.removeProperty('padding-bottom'); + + // Move to page + console.log('Moving footer to page'); + page?.appendChild(cachedFooterEl); } else { // Add padding to the parent element to prevent content from being hidden // when the footer is positioned absolutely. This has to be done before we // make the footer absolutely positioned or we may accidentally cause the // sheet to scroll. - const footerHeight = originalFooter.clientHeight; - originalFooter.parentElement?.style.setProperty('padding-bottom', `${footerHeight}px`); + const footerHeight = cachedFooterEl.clientHeight; + page?.style.setProperty('padding-bottom', `${footerHeight}px`); // Apply positioning styles to keep footer at bottom - originalFooter.style.setProperty('position', 'absolute'); - originalFooter.style.setProperty('bottom', '0'); + cachedFooterEl.style.setProperty('position', 'absolute'); + cachedFooterEl.style.setProperty('bottom', '0'); // Also cache the footer Y position, which we use to determine if the // sheet has been moved below the footer. When that happens, we need to swap // the position back so it will collapse correctly. - cachedFooterYPosition = originalFooter.getBoundingClientRect().top + window.scrollY; + cachedFooterYPosition = cachedFooterEl.getBoundingClientRect().top + window.scrollY; + + console.log('Moving footer to body'); + document.body.appendChild(cachedFooterEl); } }; diff --git a/core/src/components/modal/test/sheet/index.html b/core/src/components/modal/test/sheet/index.html index 2e366c07eb6..1af23413065 100644 --- a/core/src/components/modal/test/sheet/index.html +++ b/core/src/components/modal/test/sheet/index.html @@ -145,8 +145,61 @@ > Backdrop is inactive -
-
+ Present Sheet Modal (footer test) + + + +

This is a modal!!

+
+ + Item + Item + Item + Item + Item + Item + Item + Item + Item + Item + Item + Item + Item + Item + Item + Item + Item + Item + Item + Item + Item + Item + Item + Item + Item + Item + Item + Item + Item + Item + Item + + The end! +
+ + + Hello world! + + + +
+ + + <
From 76861188bae5519c4580b6e0152b7726a978c7a6 Mon Sep 17 00:00:00 2001 From: ShaneK Date: Tue, 27 May 2025 12:12:11 -0700 Subject: [PATCH 04/12] feat(sheet): moving the footer of the sheet modal to the body while dragging to eliminate shaking --- core/src/components/modal/gestures/sheet.ts | 4 +- .../components/modal/test/sheet/index.html | 56 +------------------ core/src/css/core.scss | 6 +- 3 files changed, 8 insertions(+), 58 deletions(-) diff --git a/core/src/components/modal/gestures/sheet.ts b/core/src/components/modal/gestures/sheet.ts index fcc22d8d572..f524d211ef4 100644 --- a/core/src/components/modal/gestures/sheet.ts +++ b/core/src/components/modal/gestures/sheet.ts @@ -138,12 +138,12 @@ export const createSheetGesture = ( currentFooterState = newPosition; if (newPosition === 'stationary') { // Reset positioning styles to allow normal document flow + cachedFooterEl.classList.remove('modal-footer-moving'); cachedFooterEl.style.removeProperty('position'); cachedFooterEl.style.removeProperty('bottom'); page?.style.removeProperty('padding-bottom'); // Move to page - console.log('Moving footer to page'); page?.appendChild(cachedFooterEl); } else { // Add padding to the parent element to prevent content from being hidden @@ -154,6 +154,7 @@ export const createSheetGesture = ( page?.style.setProperty('padding-bottom', `${footerHeight}px`); // Apply positioning styles to keep footer at bottom + cachedFooterEl.classList.add('modal-footer-moving'); cachedFooterEl.style.setProperty('position', 'absolute'); cachedFooterEl.style.setProperty('bottom', '0'); @@ -162,7 +163,6 @@ export const createSheetGesture = ( // the position back so it will collapse correctly. cachedFooterYPosition = cachedFooterEl.getBoundingClientRect().top + window.scrollY; - console.log('Moving footer to body'); document.body.appendChild(cachedFooterEl); } }; diff --git a/core/src/components/modal/test/sheet/index.html b/core/src/components/modal/test/sheet/index.html index 1af23413065..8dacb81ffc4 100644 --- a/core/src/components/modal/test/sheet/index.html +++ b/core/src/components/modal/test/sheet/index.html @@ -145,61 +145,9 @@ > Backdrop is inactive - Present Sheet Modal (footer test) - - - -

This is a modal!!

-
- - Item - Item - Item - Item - Item - Item - Item - Item - Item - Item - Item - Item - Item - Item - Item - Item - Item - Item - Item - Item - Item - Item - Item - Item - Item - Item - Item - Item - Item - Item - Item - - The end! -
- - - Hello world! - - - -
- - <
+
+
diff --git a/core/src/css/core.scss b/core/src/css/core.scss index 1cf0e8bfb7d..cf7560bd348 100644 --- a/core/src/css/core.scss +++ b/core/src/css/core.scss @@ -55,7 +55,8 @@ body.backdrop-no-scroll { */ html.ios ion-modal.modal-card ion-header ion-toolbar:first-of-type, html.ios ion-modal.modal-sheet ion-header ion-toolbar:first-of-type, -html.ios ion-modal ion-footer ion-toolbar:first-of-type { +html.ios ion-modal ion-footer ion-toolbar:first-of-type, +html.ios ion-footer.modal-footer-moving ion-toolbar:first-of-type { padding-top: $modal-sheet-padding-top; } @@ -74,7 +75,8 @@ html.ios ion-modal.modal-sheet ion-header ion-toolbar:last-of-type { * of toolbars while accounting for * safe area values when in landscape. */ -html.ios ion-modal ion-toolbar { +html.ios ion-modal ion-toolbar, +html.ios .modal-footer-moving ion-toolbar { padding-right: calc(var(--ion-safe-area-right) + 8px); padding-left: calc(var(--ion-safe-area-left) + 8px); } From 62326530a2ea5e4ec0d9dee5b0628640f2e3a1d4 Mon Sep 17 00:00:00 2001 From: ShaneK Date: Wed, 28 May 2025 09:37:07 -0700 Subject: [PATCH 05/12] fix(sheet): pinning footer constraints on drag --- core/src/components/modal/gestures/sheet.ts | 41 +++++++++++++++------ 1 file changed, 29 insertions(+), 12 deletions(-) diff --git a/core/src/components/modal/gestures/sheet.ts b/core/src/components/modal/gestures/sheet.ts index f524d211ef4..1f8b39676d7 100644 --- a/core/src/components/modal/gestures/sheet.ts +++ b/core/src/components/modal/gestures/sheet.ts @@ -140,12 +140,19 @@ export const createSheetGesture = ( // Reset positioning styles to allow normal document flow cachedFooterEl.classList.remove('modal-footer-moving'); cachedFooterEl.style.removeProperty('position'); - cachedFooterEl.style.removeProperty('bottom'); + cachedFooterEl.style.removeProperty('width'); + cachedFooterEl.style.removeProperty('height'); + cachedFooterEl.style.removeProperty('top'); + cachedFooterEl.style.removeProperty('left'); page?.style.removeProperty('padding-bottom'); // Move to page page?.appendChild(cachedFooterEl); } else { + // Get both the footer and document body positions + const cachedFooterElRect = cachedFooterEl.getBoundingClientRect(); + const bodyRect = document.body.getBoundingClientRect(); + // Add padding to the parent element to prevent content from being hidden // when the footer is positioned absolutely. This has to be done before we // make the footer absolutely positioned or we may accidentally cause the @@ -155,13 +162,23 @@ export const createSheetGesture = ( // Apply positioning styles to keep footer at bottom cachedFooterEl.classList.add('modal-footer-moving'); + + // Calculate absolute position relative to body + // We need to subtract the body's offsetTop to get true position within document.body + const absoluteTop = cachedFooterElRect.top - bodyRect.top; + const absoluteLeft = cachedFooterElRect.left - bodyRect.left; + + // Capture the footer's current dimensions and hard code them during the drag cachedFooterEl.style.setProperty('position', 'absolute'); - cachedFooterEl.style.setProperty('bottom', '0'); + cachedFooterEl.style.setProperty('width', `${cachedFooterEl.clientWidth}px`); + cachedFooterEl.style.setProperty('height', `${cachedFooterEl.clientHeight}px`); + cachedFooterEl.style.setProperty('top', `${absoluteTop}px`); + cachedFooterEl.style.setProperty('left', `${absoluteLeft}px`); // Also cache the footer Y position, which we use to determine if the // sheet has been moved below the footer. When that happens, we need to swap // the position back so it will collapse correctly. - cachedFooterYPosition = cachedFooterEl.getBoundingClientRect().top + window.scrollY; + cachedFooterYPosition = absoluteTop; document.body.appendChild(cachedFooterEl); } @@ -482,20 +499,20 @@ export const createSheetGesture = ( contentEl.scrollY = true; } + /** + * If expandToScroll is disabled, we need to swap + * the footer position to stationary so that it + * will act as it would by default + */ + if (!expandToScroll) { + swapFooterPosition('stationary'); + } + return new Promise((resolve) => { animation .onFinish( () => { if (shouldRemainOpen) { - /** - * If expandToScroll is disabled, we need to swap - * the footer position to stationary so that it - * will act as it would by default - */ - if (!expandToScroll) { - swapFooterPosition('stationary'); - } - /** * Once the snapping animation completes, * we need to reset the animation to go From 194b475387ebf6f4f8af58d219986a3b7467de90 Mon Sep 17 00:00:00 2001 From: ShaneK Date: Thu, 29 May 2025 06:35:44 -0700 Subject: [PATCH 06/12] fix(modal): improve sheet footer positioning during gestures Co-authored-by: Israel de la Barrera --- core/src/components/modal/gestures/sheet.ts | 22 ++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/core/src/components/modal/gestures/sheet.ts b/core/src/components/modal/gestures/sheet.ts index 1f8b39676d7..9c1ae08b388 100644 --- a/core/src/components/modal/gestures/sheet.ts +++ b/core/src/components/modal/gestures/sheet.ts @@ -179,6 +179,13 @@ export const createSheetGesture = ( // sheet has been moved below the footer. When that happens, we need to swap // the position back so it will collapse correctly. cachedFooterYPosition = absoluteTop; + // If there's a toolbar, we need to combine the toolbar height with the footer position + // because the toolbar moves with the drag handle, so when it starts overlapping the footer, + // we need to account for that. + const toolbar = baseEl.querySelector('ion-toolbar') as HTMLIonToolbarElement | null; + if (toolbar) { + cachedFooterYPosition -= toolbar.clientHeight; + } document.body.appendChild(cachedFooterEl); } @@ -500,11 +507,13 @@ export const createSheetGesture = ( } /** - * If expandToScroll is disabled, we need to swap + * If expandToScroll is disabled and we're animating + * to the close of the sheet, we need to swap * the footer position to stationary so that it - * will act as it would by default + * will collapse correctly. We cannot just always swap + * here or it'll be jittery while animating movement. */ - if (!expandToScroll) { + if (!expandToScroll && snapToBreakpoint === 0) { swapFooterPosition('stationary'); } @@ -513,6 +522,13 @@ export const createSheetGesture = ( .onFinish( () => { if (shouldRemainOpen) { + /** + * If expandToScroll is disabled, we need to swap + * the footer position to stationary so that it + * will act as it would by default + */ + swapFooterPosition('stationary'); + /** * Once the snapping animation completes, * we need to reset the animation to go From 905c409da4e10be5c5bc8e9a85b54ff088c6afae Mon Sep 17 00:00:00 2001 From: ShaneK Date: Thu, 29 May 2025 07:52:12 -0700 Subject: [PATCH 07/12] fix(sheet): adding expand to scroll check in onFinish swap --- core/src/components/modal/gestures/sheet.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/core/src/components/modal/gestures/sheet.ts b/core/src/components/modal/gestures/sheet.ts index 9c1ae08b388..cb380ad156b 100644 --- a/core/src/components/modal/gestures/sheet.ts +++ b/core/src/components/modal/gestures/sheet.ts @@ -527,7 +527,9 @@ export const createSheetGesture = ( * the footer position to stationary so that it * will act as it would by default */ - swapFooterPosition('stationary'); + if (!expandToScroll) { + swapFooterPosition('stationary'); + } /** * Once the snapping animation completes, From fa2c87d15be4caf105681b5a2da66e39757427a5 Mon Sep 17 00:00:00 2001 From: Shane Date: Fri, 30 May 2025 11:26:38 -0700 Subject: [PATCH 08/12] Update core/src/components/modal/gestures/sheet.ts Co-authored-by: Maria Hutt --- core/src/components/modal/gestures/sheet.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/components/modal/gestures/sheet.ts b/core/src/components/modal/gestures/sheet.ts index cb380ad156b..3231dac0f4a 100644 --- a/core/src/components/modal/gestures/sheet.ts +++ b/core/src/components/modal/gestures/sheet.ts @@ -508,7 +508,7 @@ export const createSheetGesture = ( /** * If expandToScroll is disabled and we're animating - * to the close of the sheet, we need to swap + * to close the sheet, we need to swap * the footer position to stationary so that it * will collapse correctly. We cannot just always swap * here or it'll be jittery while animating movement. From 3037d08e69ae06ecf2ecd0d4151202b61674aaad Mon Sep 17 00:00:00 2001 From: Shane Date: Fri, 30 May 2025 11:26:47 -0700 Subject: [PATCH 09/12] Update core/src/components/modal/gestures/sheet.ts Co-authored-by: Maria Hutt --- core/src/components/modal/gestures/sheet.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/components/modal/gestures/sheet.ts b/core/src/components/modal/gestures/sheet.ts index 3231dac0f4a..ecbe0d66663 100644 --- a/core/src/components/modal/gestures/sheet.ts +++ b/core/src/components/modal/gestures/sheet.ts @@ -525,7 +525,7 @@ export const createSheetGesture = ( /** * If expandToScroll is disabled, we need to swap * the footer position to stationary so that it - * will act as it would by default + * will act as it would by default. */ if (!expandToScroll) { swapFooterPosition('stationary'); From 538f4ca58117891246b48895db045513be161c30 Mon Sep 17 00:00:00 2001 From: ShaneK Date: Fri, 30 May 2025 11:35:40 -0700 Subject: [PATCH 10/12] fix(sheet): removing css that's no longer necessary --- core/src/components/modal/modal.ios.scss | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/core/src/components/modal/modal.ios.scss b/core/src/components/modal/modal.ios.scss index dffb778e020..fc5e25e3d19 100644 --- a/core/src/components/modal/modal.ios.scss +++ b/core/src/components/modal/modal.ios.scss @@ -87,16 +87,3 @@ :host(.modal-sheet) .modal-wrapper { @include border-radius(var(--border-radius), var(--border-radius), 0, 0); } - -// iOS Sheet Modal - Scroll at all breakpoints -// -------------------------------------------------- - -/** - * Sheet modals require an additional padding as mentioned in the - * `core.scss` file. However, there's a workaround that requires - * a cloned footer to be added to the modal. This is only necessary - * because the core styles are not being applied to the cloned footer. - */ -:host(.modal-sheet.modal-no-expand-scroll) ion-footer ion-toolbar:first-of-type { - padding-top: $modal-sheet-padding-top; -} From 0f6742c183d07fb27f3b06c5e117e6c70cd42212 Mon Sep 17 00:00:00 2001 From: Shane Date: Tue, 3 Jun 2025 07:17:50 -0700 Subject: [PATCH 11/12] Update sheet.ts Co-authored-by: Brandy Smith --- core/src/components/modal/gestures/sheet.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/components/modal/gestures/sheet.ts b/core/src/components/modal/gestures/sheet.ts index ecbe0d66663..a3f548879f8 100644 --- a/core/src/components/modal/gestures/sheet.ts +++ b/core/src/components/modal/gestures/sheet.ts @@ -123,7 +123,7 @@ export const createSheetGesture = ( /** * Toggles the footer to an absolute position while moving to prevent * it from shaking while the sheet is being dragged. - * @param footer Whether the footer is in a moving or stationary position. + * @param newPosition Whether the footer is in a moving or stationary position. */ const swapFooterPosition = (newPosition: 'moving' | 'stationary') => { if (!cachedFooterEl) { From ee1d693fd7ec6d68dab95242181d83de89182a8d Mon Sep 17 00:00:00 2001 From: ShaneK Date: Tue, 3 Jun 2025 08:29:49 -0700 Subject: [PATCH 12/12] fix(sheet): expand to scroll content animation being applied incorrectly --- core/src/components/modal/animations/md.enter.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/components/modal/animations/md.enter.ts b/core/src/components/modal/animations/md.enter.ts index 15186212762..97dc0a4b200 100644 --- a/core/src/components/modal/animations/md.enter.ts +++ b/core/src/components/modal/animations/md.enter.ts @@ -37,7 +37,7 @@ export const mdEnterAnimation = (baseEl: HTMLElement, opts: ModalAnimationOption // The content animation is only added if scrolling is enabled for // all the breakpoints. - expandToScroll && contentAnimation?.addElement(baseEl.querySelector('.ion-page')!); + !expandToScroll && contentAnimation?.addElement(baseEl.querySelector('.ion-page')!); const baseAnimation = createAnimation() .addElement(baseEl)