@@ -84,6 +84,8 @@ export const createSheetGesture = (
84
84
let offset = 0 ;
85
85
let canDismissBlocksGesture = false ;
86
86
let cachedScrollEl : HTMLElement | null = null ;
87
+ let cachedFooterYPosition : number | null = null ;
88
+ let currentFooterState : 'moving' | 'stationary' | null = null ;
87
89
const canDismissMaxStep = 0.95 ;
88
90
const maxBreakpoint = breakpoints [ breakpoints . length - 1 ] ;
89
91
const minBreakpoint = breakpoints [ 0 ] ;
@@ -118,33 +120,39 @@ export const createSheetGesture = (
118
120
} ;
119
121
120
122
/**
121
- * Toggles the visible modal footer when `expandToScroll` is disabled.
122
- * @param footer The footer to show.
123
+ * Toggles the footer to an absolute position while moving to prevent
124
+ * it from shaking while the sheet is being dragged.
125
+ * @param footer Whether the footer is in a moving or stationary position.
123
126
*/
124
- const swapFooterVisibility = ( footer : 'original ' | 'cloned ' ) => {
127
+ const swapFooterPosition = ( newPosition : 'moving ' | 'stationary ' ) => {
125
128
const originalFooter = baseEl . querySelector ( 'ion-footer' ) as HTMLIonFooterElement | null ;
126
-
127
129
if ( ! originalFooter ) {
128
130
return ;
129
131
}
130
132
131
- // const clonedFooter = wrapperEl.nextElementSibling as HTMLIonFooterElement;
132
- // const footerToHide = footer === 'original' ? clonedFooter : originalFooter;
133
- // const footerToShow = footer === 'original' ? originalFooter : clonedFooter;
134
-
135
- // footerToShow.style.removeProperty('display');
136
- // footerToShow.removeAttribute('aria-hidden');
137
-
138
- // const page = baseEl.querySelector('.ion-page') as HTMLElement;
139
- // if (footer === 'original') {
140
- // page.style.removeProperty('padding-bottom');
141
- // } else {
142
- // const pagePadding = footerToShow.clientHeight;
143
- // page.style.setProperty('padding-bottom', `${pagePadding}px`);
144
- // }
145
-
146
- // footerToHide.style.setProperty('display', 'none');
147
- // footerToHide.setAttribute('aria-hidden', 'true');
133
+ currentFooterState = newPosition ;
134
+ if ( newPosition === 'stationary' ) {
135
+ // Reset positioning styles to allow normal document flow
136
+ originalFooter . style . removeProperty ( 'position' ) ;
137
+ originalFooter . style . removeProperty ( 'bottom' ) ;
138
+ originalFooter . parentElement ?. style . removeProperty ( 'padding-bottom' ) ;
139
+ } else {
140
+ // Add padding to the parent element to prevent content from being hidden
141
+ // when the footer is positioned absolutely. This has to be done before we
142
+ // make the footer absolutely positioned or we may accidentally cause the
143
+ // sheet to scroll.
144
+ const footerHeight = originalFooter . clientHeight ;
145
+ originalFooter . parentElement ?. style . setProperty ( 'padding-bottom' , `${ footerHeight } px` ) ;
146
+
147
+ // Apply positioning styles to keep footer at bottom
148
+ originalFooter . style . setProperty ( 'position' , 'absolute' ) ;
149
+ originalFooter . style . setProperty ( 'bottom' , '0' ) ;
150
+
151
+ // Also cache the footer Y position, which we use to determine if the
152
+ // sheet has been moved below the footer. When that happens, we need to swap
153
+ // the position back so it will collapse correctly.
154
+ cachedFooterYPosition = originalFooter . getBoundingClientRect ( ) . top + window . scrollY ;
155
+ }
148
156
} ;
149
157
150
158
/**
@@ -247,12 +255,11 @@ export const createSheetGesture = (
247
255
248
256
/**
249
257
* If expandToScroll is disabled, we need to swap
250
- * the footer visibility to the original, so if the modal
251
- * is dismissed, the footer dismisses with the modal
252
- * and doesn't stay on the screen after the modal is gone.
258
+ * the footer position to moving so that it doesn't shake
259
+ * while the sheet is being dragged.
253
260
*/
254
261
if ( ! expandToScroll ) {
255
- swapFooterVisibility ( 'original ') ;
262
+ swapFooterPosition ( 'moving ') ;
256
263
}
257
264
258
265
/**
@@ -275,6 +282,21 @@ export const createSheetGesture = (
275
282
} ;
276
283
277
284
const onMove = ( detail : GestureDetail ) => {
285
+ /**
286
+ * If `expandToScroll` is disabled, we need to see if we're currently below
287
+ * the footer element and the footer is in a stationary position. If so,
288
+ * we need to make the stationary the original position so that the footer
289
+ * collapses with the sheet.
290
+ */
291
+ if ( ! expandToScroll && cachedFooterYPosition !== null && currentFooterState !== null ) {
292
+ // Check if we need to swap the footer position
293
+ if ( detail . currentY >= cachedFooterYPosition && currentFooterState === 'moving' ) {
294
+ swapFooterPosition ( 'stationary' ) ;
295
+ } else if ( detail . currentY < cachedFooterYPosition && currentFooterState === 'stationary' ) {
296
+ swapFooterPosition ( 'moving' ) ;
297
+ }
298
+ }
299
+
278
300
/**
279
301
* If `expandToScroll` is disabled, and an upwards swipe gesture is done within
280
302
* the scrollable content, we should not allow the swipe gesture to continue.
@@ -431,15 +453,6 @@ export const createSheetGesture = (
431
453
*/
432
454
gesture . enable ( false ) ;
433
455
434
- /**
435
- * If expandToScroll is disabled, we need to swap
436
- * the footer visibility to the cloned one so the footer
437
- * doesn't flicker when the sheet's height is animated.
438
- */
439
- if ( ! expandToScroll && shouldRemainOpen ) {
440
- swapFooterVisibility ( 'cloned' ) ;
441
- }
442
-
443
456
if ( shouldPreventDismiss ) {
444
457
handleCanDismiss ( baseEl , animation ) ;
445
458
} else if ( ! shouldRemainOpen ) {
@@ -462,6 +475,15 @@ export const createSheetGesture = (
462
475
. onFinish (
463
476
( ) => {
464
477
if ( shouldRemainOpen ) {
478
+ /**
479
+ * If expandToScroll is disabled, we need to swap
480
+ * the footer position to stationary so that it
481
+ * will act as it would by default
482
+ */
483
+ if ( ! expandToScroll ) {
484
+ swapFooterPosition ( 'stationary' ) ;
485
+ }
486
+
465
487
/**
466
488
* Once the snapping animation completes,
467
489
* we need to reset the animation to go
0 commit comments