@ionic/core 8.6.1-nightly.20250610 → 8.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -185,6 +185,7 @@ const ItemSliding = /*@__PURE__*/ proxyCustomElement(class ItemSliding extends H
185
185
  }
186
186
  }
187
187
  async updateOptions() {
188
+ var _a;
188
189
  const options = this.el.querySelectorAll('ion-item-options');
189
190
  let sides = 0;
190
191
  // Reset left and right options in case they were removed
@@ -198,7 +199,7 @@ const ItemSliding = /*@__PURE__*/ proxyCustomElement(class ItemSliding extends H
198
199
  */
199
200
  // eslint-disable-next-line custom-rules/no-component-on-ready-method
200
201
  const option = item.componentOnReady !== undefined ? await item.componentOnReady() : item;
201
- const side = isEndSide(option.side) ? 'end' : 'start';
202
+ const side = isEndSide((_a = option.side) !== null && _a !== void 0 ? _a : option.getAttribute('side')) ? 'end' : 'start';
202
203
  if (side === 'start') {
203
204
  this.leftOptions = option;
204
205
  sides |= 1 /* ItemSide.Start */;
@@ -396,7 +397,7 @@ const ItemSliding = /*@__PURE__*/ proxyCustomElement(class ItemSliding extends H
396
397
  }
397
398
  render() {
398
399
  const mode = getIonMode(this);
399
- return (h(Host, { key: '9880396ad79e06117d572a27f92c4b753d1e26db', class: {
400
+ return (h(Host, { key: 'd812322c9fb5da4ee16e99dc38bfb24cb4590d03', class: {
400
401
  [mode]: true,
401
402
  'item-sliding-active-slide': this.state !== 2 /* SlidingState.Disabled */,
402
403
  'item-sliding-active-options-end': (this.state & 8 /* SlidingState.End */) !== 0,
@@ -827,7 +827,7 @@ const createSheetGesture = (baseEl, backdropEl, wrapperEl, initialBreakpoint, ba
827
827
  let offset = 0;
828
828
  let canDismissBlocksGesture = false;
829
829
  let cachedScrollEl = null;
830
- let cachedFooterEl = null;
830
+ let cachedFooterEls = null;
831
831
  let cachedFooterYPosition = null;
832
832
  let currentFooterState = null;
833
833
  const canDismissMaxStep = 0.95;
@@ -864,60 +864,81 @@ const createSheetGesture = (baseEl, backdropEl, wrapperEl, initialBreakpoint, ba
864
864
  * @param newPosition Whether the footer is in a moving or stationary position.
865
865
  */
866
866
  const swapFooterPosition = (newPosition) => {
867
- if (!cachedFooterEl) {
868
- cachedFooterEl = baseEl.querySelector('ion-footer');
869
- if (!cachedFooterEl) {
867
+ if (!cachedFooterEls) {
868
+ cachedFooterEls = Array.from(baseEl.querySelectorAll('ion-footer'));
869
+ if (!cachedFooterEls.length) {
870
870
  return;
871
871
  }
872
872
  }
873
873
  const page = baseEl.querySelector('.ion-page');
874
874
  currentFooterState = newPosition;
875
875
  if (newPosition === 'stationary') {
876
- // Reset positioning styles to allow normal document flow
877
- cachedFooterEl.classList.remove('modal-footer-moving');
878
- cachedFooterEl.style.removeProperty('position');
879
- cachedFooterEl.style.removeProperty('width');
880
- cachedFooterEl.style.removeProperty('height');
881
- cachedFooterEl.style.removeProperty('top');
882
- cachedFooterEl.style.removeProperty('left');
883
- page === null || page === void 0 ? void 0 : page.style.removeProperty('padding-bottom');
884
- // Move to page
885
- page === null || page === void 0 ? void 0 : page.appendChild(cachedFooterEl);
876
+ cachedFooterEls.forEach((cachedFooterEl) => {
877
+ // Reset positioning styles to allow normal document flow
878
+ cachedFooterEl.classList.remove('modal-footer-moving');
879
+ cachedFooterEl.style.removeProperty('position');
880
+ cachedFooterEl.style.removeProperty('width');
881
+ cachedFooterEl.style.removeProperty('height');
882
+ cachedFooterEl.style.removeProperty('top');
883
+ cachedFooterEl.style.removeProperty('left');
884
+ page === null || page === void 0 ? void 0 : page.style.removeProperty('padding-bottom');
885
+ // Move to page
886
+ page === null || page === void 0 ? void 0 : page.appendChild(cachedFooterEl);
887
+ });
886
888
  }
887
889
  else {
888
- // Get both the footer and document body positions
889
- const cachedFooterElRect = cachedFooterEl.getBoundingClientRect();
890
- const bodyRect = document.body.getBoundingClientRect();
891
- // Add padding to the parent element to prevent content from being hidden
892
- // when the footer is positioned absolutely. This has to be done before we
893
- // make the footer absolutely positioned or we may accidentally cause the
894
- // sheet to scroll.
895
- const footerHeight = cachedFooterEl.clientHeight;
896
- page === null || page === void 0 ? void 0 : page.style.setProperty('padding-bottom', `${footerHeight}px`);
897
- // Apply positioning styles to keep footer at bottom
898
- cachedFooterEl.classList.add('modal-footer-moving');
899
- // Calculate absolute position relative to body
900
- // We need to subtract the body's offsetTop to get true position within document.body
901
- const absoluteTop = cachedFooterElRect.top - bodyRect.top;
902
- const absoluteLeft = cachedFooterElRect.left - bodyRect.left;
903
- // Capture the footer's current dimensions and hard code them during the drag
904
- cachedFooterEl.style.setProperty('position', 'absolute');
905
- cachedFooterEl.style.setProperty('width', `${cachedFooterEl.clientWidth}px`);
906
- cachedFooterEl.style.setProperty('height', `${cachedFooterEl.clientHeight}px`);
907
- cachedFooterEl.style.setProperty('top', `${absoluteTop}px`);
908
- cachedFooterEl.style.setProperty('left', `${absoluteLeft}px`);
909
- // Also cache the footer Y position, which we use to determine if the
910
- // sheet has been moved below the footer. When that happens, we need to swap
911
- // the position back so it will collapse correctly.
912
- cachedFooterYPosition = absoluteTop;
913
- // If there's a toolbar, we need to combine the toolbar height with the footer position
914
- // because the toolbar moves with the drag handle, so when it starts overlapping the footer,
915
- // we need to account for that.
916
- const toolbar = baseEl.querySelector('ion-toolbar');
917
- if (toolbar) {
918
- cachedFooterYPosition -= toolbar.clientHeight;
919
- }
920
- document.body.appendChild(cachedFooterEl);
890
+ let footerHeights = 0;
891
+ cachedFooterEls.forEach((cachedFooterEl, index) => {
892
+ // Get both the footer and document body positions
893
+ const cachedFooterElRect = cachedFooterEl.getBoundingClientRect();
894
+ const bodyRect = document.body.getBoundingClientRect();
895
+ // Calculate the total height of all footers
896
+ // so we can add padding to the page element
897
+ footerHeights += cachedFooterEl.clientHeight;
898
+ // Calculate absolute position relative to body
899
+ // We need to subtract the body's offsetTop to get true position within document.body
900
+ const absoluteTop = cachedFooterElRect.top - bodyRect.top;
901
+ const absoluteLeft = cachedFooterElRect.left - bodyRect.left;
902
+ // Capture the footer's current dimensions and store them in CSS variables for
903
+ // later use when applying absolute positioning.
904
+ cachedFooterEl.style.setProperty('--pinned-width', `${cachedFooterEl.clientWidth}px`);
905
+ cachedFooterEl.style.setProperty('--pinned-height', `${cachedFooterEl.clientHeight}px`);
906
+ cachedFooterEl.style.setProperty('--pinned-top', `${absoluteTop}px`);
907
+ cachedFooterEl.style.setProperty('--pinned-left', `${absoluteLeft}px`);
908
+ // Only cache the first footer's Y position
909
+ // This is used to determine if the sheet has been moved below the footer
910
+ // and needs to be swapped back to stationary so it collapses correctly.
911
+ if (index === 0) {
912
+ cachedFooterYPosition = absoluteTop;
913
+ // If there's a header, we need to combine the header height with the footer position
914
+ // because the header moves with the drag handle, so when it starts overlapping the footer,
915
+ // we need to account for that.
916
+ const header = baseEl.querySelector('ion-header');
917
+ if (header) {
918
+ cachedFooterYPosition -= header.clientHeight;
919
+ }
920
+ }
921
+ });
922
+ // Apply the pinning of styles after we've calculated everything
923
+ // so that we don't cause layouts to shift while calculating the footer positions.
924
+ // Otherwise, with multiple footers we'll end up capturing the wrong positions.
925
+ cachedFooterEls.forEach((cachedFooterEl) => {
926
+ // Add padding to the parent element to prevent content from being hidden
927
+ // when the footer is positioned absolutely. This has to be done before we
928
+ // make the footer absolutely positioned or we may accidentally cause the
929
+ // sheet to scroll.
930
+ page === null || page === void 0 ? void 0 : page.style.setProperty('padding-bottom', `${footerHeights}px`);
931
+ // Apply positioning styles to keep footer at bottom
932
+ cachedFooterEl.classList.add('modal-footer-moving');
933
+ // Apply our preserved styles to pin the footer
934
+ cachedFooterEl.style.setProperty('position', 'absolute');
935
+ cachedFooterEl.style.setProperty('width', 'var(--pinned-width)');
936
+ cachedFooterEl.style.setProperty('height', 'var(--pinned-height)');
937
+ cachedFooterEl.style.setProperty('top', 'var(--pinned-top)');
938
+ cachedFooterEl.style.setProperty('left', 'var(--pinned-left)');
939
+ // Move the element to the body when everything else is done
940
+ document.body.appendChild(cachedFooterEl);
941
+ });
921
942
  }
922
943
  };
923
944
  /**
@@ -1109,6 +1130,14 @@ const createSheetGesture = (baseEl, backdropEl, wrapperEl, initialBreakpoint, ba
1109
1130
  * is not scrolled to the top.
1110
1131
  */
1111
1132
  if (!expandToScroll && detail.deltaY <= 0 && cachedScrollEl && cachedScrollEl.scrollTop > 0) {
1133
+ /**
1134
+ * If expand to scroll is disabled, we need to make sure we swap the footer position
1135
+ * back to stationary so that it will collapse correctly if the modal is dismissed without
1136
+ * dragging (e.g. through a dismiss button).
1137
+ * This can cause issues if the user has a modal with content that can be dragged, as we'll
1138
+ * swap to moving on drag and if we don't swap back here then the footer will get stuck.
1139
+ */
1140
+ swapFooterPosition('stationary');
1112
1141
  return;
1113
1142
  }
1114
1143
  /**
@@ -282,6 +282,7 @@ const ItemSliding = class {
282
282
  }
283
283
  }
284
284
  async updateOptions() {
285
+ var _a;
285
286
  const options = this.el.querySelectorAll('ion-item-options');
286
287
  let sides = 0;
287
288
  // Reset left and right options in case they were removed
@@ -295,7 +296,7 @@ const ItemSliding = class {
295
296
  */
296
297
  // eslint-disable-next-line custom-rules/no-component-on-ready-method
297
298
  const option = item.componentOnReady !== undefined ? await item.componentOnReady() : item;
298
- const side = helpers.isEndSide(option.side) ? 'end' : 'start';
299
+ const side = helpers.isEndSide((_a = option.side) !== null && _a !== void 0 ? _a : option.getAttribute('side')) ? 'end' : 'start';
299
300
  if (side === 'start') {
300
301
  this.leftOptions = option;
301
302
  sides |= 1 /* ItemSide.Start */;
@@ -493,7 +494,7 @@ const ItemSliding = class {
493
494
  }
494
495
  render() {
495
496
  const mode = index.getIonMode(this);
496
- return (index.h(index.Host, { key: '9880396ad79e06117d572a27f92c4b753d1e26db', class: {
497
+ return (index.h(index.Host, { key: 'd812322c9fb5da4ee16e99dc38bfb24cb4590d03', class: {
497
498
  [mode]: true,
498
499
  'item-sliding-active-slide': this.state !== 2 /* SlidingState.Disabled */,
499
500
  'item-sliding-active-options-end': (this.state & 8 /* SlidingState.End */) !== 0,
@@ -829,7 +829,7 @@ const createSheetGesture = (baseEl, backdropEl, wrapperEl, initialBreakpoint, ba
829
829
  let offset = 0;
830
830
  let canDismissBlocksGesture = false;
831
831
  let cachedScrollEl = null;
832
- let cachedFooterEl = null;
832
+ let cachedFooterEls = null;
833
833
  let cachedFooterYPosition = null;
834
834
  let currentFooterState = null;
835
835
  const canDismissMaxStep = 0.95;
@@ -866,60 +866,81 @@ const createSheetGesture = (baseEl, backdropEl, wrapperEl, initialBreakpoint, ba
866
866
  * @param newPosition Whether the footer is in a moving or stationary position.
867
867
  */
868
868
  const swapFooterPosition = (newPosition) => {
869
- if (!cachedFooterEl) {
870
- cachedFooterEl = baseEl.querySelector('ion-footer');
871
- if (!cachedFooterEl) {
869
+ if (!cachedFooterEls) {
870
+ cachedFooterEls = Array.from(baseEl.querySelectorAll('ion-footer'));
871
+ if (!cachedFooterEls.length) {
872
872
  return;
873
873
  }
874
874
  }
875
875
  const page = baseEl.querySelector('.ion-page');
876
876
  currentFooterState = newPosition;
877
877
  if (newPosition === 'stationary') {
878
- // Reset positioning styles to allow normal document flow
879
- cachedFooterEl.classList.remove('modal-footer-moving');
880
- cachedFooterEl.style.removeProperty('position');
881
- cachedFooterEl.style.removeProperty('width');
882
- cachedFooterEl.style.removeProperty('height');
883
- cachedFooterEl.style.removeProperty('top');
884
- cachedFooterEl.style.removeProperty('left');
885
- page === null || page === void 0 ? void 0 : page.style.removeProperty('padding-bottom');
886
- // Move to page
887
- page === null || page === void 0 ? void 0 : page.appendChild(cachedFooterEl);
878
+ cachedFooterEls.forEach((cachedFooterEl) => {
879
+ // Reset positioning styles to allow normal document flow
880
+ cachedFooterEl.classList.remove('modal-footer-moving');
881
+ cachedFooterEl.style.removeProperty('position');
882
+ cachedFooterEl.style.removeProperty('width');
883
+ cachedFooterEl.style.removeProperty('height');
884
+ cachedFooterEl.style.removeProperty('top');
885
+ cachedFooterEl.style.removeProperty('left');
886
+ page === null || page === void 0 ? void 0 : page.style.removeProperty('padding-bottom');
887
+ // Move to page
888
+ page === null || page === void 0 ? void 0 : page.appendChild(cachedFooterEl);
889
+ });
888
890
  }
889
891
  else {
890
- // Get both the footer and document body positions
891
- const cachedFooterElRect = cachedFooterEl.getBoundingClientRect();
892
- const bodyRect = document.body.getBoundingClientRect();
893
- // Add padding to the parent element to prevent content from being hidden
894
- // when the footer is positioned absolutely. This has to be done before we
895
- // make the footer absolutely positioned or we may accidentally cause the
896
- // sheet to scroll.
897
- const footerHeight = cachedFooterEl.clientHeight;
898
- page === null || page === void 0 ? void 0 : page.style.setProperty('padding-bottom', `${footerHeight}px`);
899
- // Apply positioning styles to keep footer at bottom
900
- cachedFooterEl.classList.add('modal-footer-moving');
901
- // Calculate absolute position relative to body
902
- // We need to subtract the body's offsetTop to get true position within document.body
903
- const absoluteTop = cachedFooterElRect.top - bodyRect.top;
904
- const absoluteLeft = cachedFooterElRect.left - bodyRect.left;
905
- // Capture the footer's current dimensions and hard code them during the drag
906
- cachedFooterEl.style.setProperty('position', 'absolute');
907
- cachedFooterEl.style.setProperty('width', `${cachedFooterEl.clientWidth}px`);
908
- cachedFooterEl.style.setProperty('height', `${cachedFooterEl.clientHeight}px`);
909
- cachedFooterEl.style.setProperty('top', `${absoluteTop}px`);
910
- cachedFooterEl.style.setProperty('left', `${absoluteLeft}px`);
911
- // Also cache the footer Y position, which we use to determine if the
912
- // sheet has been moved below the footer. When that happens, we need to swap
913
- // the position back so it will collapse correctly.
914
- cachedFooterYPosition = absoluteTop;
915
- // If there's a toolbar, we need to combine the toolbar height with the footer position
916
- // because the toolbar moves with the drag handle, so when it starts overlapping the footer,
917
- // we need to account for that.
918
- const toolbar = baseEl.querySelector('ion-toolbar');
919
- if (toolbar) {
920
- cachedFooterYPosition -= toolbar.clientHeight;
921
- }
922
- document.body.appendChild(cachedFooterEl);
892
+ let footerHeights = 0;
893
+ cachedFooterEls.forEach((cachedFooterEl, index) => {
894
+ // Get both the footer and document body positions
895
+ const cachedFooterElRect = cachedFooterEl.getBoundingClientRect();
896
+ const bodyRect = document.body.getBoundingClientRect();
897
+ // Calculate the total height of all footers
898
+ // so we can add padding to the page element
899
+ footerHeights += cachedFooterEl.clientHeight;
900
+ // Calculate absolute position relative to body
901
+ // We need to subtract the body's offsetTop to get true position within document.body
902
+ const absoluteTop = cachedFooterElRect.top - bodyRect.top;
903
+ const absoluteLeft = cachedFooterElRect.left - bodyRect.left;
904
+ // Capture the footer's current dimensions and store them in CSS variables for
905
+ // later use when applying absolute positioning.
906
+ cachedFooterEl.style.setProperty('--pinned-width', `${cachedFooterEl.clientWidth}px`);
907
+ cachedFooterEl.style.setProperty('--pinned-height', `${cachedFooterEl.clientHeight}px`);
908
+ cachedFooterEl.style.setProperty('--pinned-top', `${absoluteTop}px`);
909
+ cachedFooterEl.style.setProperty('--pinned-left', `${absoluteLeft}px`);
910
+ // Only cache the first footer's Y position
911
+ // This is used to determine if the sheet has been moved below the footer
912
+ // and needs to be swapped back to stationary so it collapses correctly.
913
+ if (index === 0) {
914
+ cachedFooterYPosition = absoluteTop;
915
+ // If there's a header, we need to combine the header height with the footer position
916
+ // because the header moves with the drag handle, so when it starts overlapping the footer,
917
+ // we need to account for that.
918
+ const header = baseEl.querySelector('ion-header');
919
+ if (header) {
920
+ cachedFooterYPosition -= header.clientHeight;
921
+ }
922
+ }
923
+ });
924
+ // Apply the pinning of styles after we've calculated everything
925
+ // so that we don't cause layouts to shift while calculating the footer positions.
926
+ // Otherwise, with multiple footers we'll end up capturing the wrong positions.
927
+ cachedFooterEls.forEach((cachedFooterEl) => {
928
+ // Add padding to the parent element to prevent content from being hidden
929
+ // when the footer is positioned absolutely. This has to be done before we
930
+ // make the footer absolutely positioned or we may accidentally cause the
931
+ // sheet to scroll.
932
+ page === null || page === void 0 ? void 0 : page.style.setProperty('padding-bottom', `${footerHeights}px`);
933
+ // Apply positioning styles to keep footer at bottom
934
+ cachedFooterEl.classList.add('modal-footer-moving');
935
+ // Apply our preserved styles to pin the footer
936
+ cachedFooterEl.style.setProperty('position', 'absolute');
937
+ cachedFooterEl.style.setProperty('width', 'var(--pinned-width)');
938
+ cachedFooterEl.style.setProperty('height', 'var(--pinned-height)');
939
+ cachedFooterEl.style.setProperty('top', 'var(--pinned-top)');
940
+ cachedFooterEl.style.setProperty('left', 'var(--pinned-left)');
941
+ // Move the element to the body when everything else is done
942
+ document.body.appendChild(cachedFooterEl);
943
+ });
923
944
  }
924
945
  };
925
946
  /**
@@ -1111,6 +1132,14 @@ const createSheetGesture = (baseEl, backdropEl, wrapperEl, initialBreakpoint, ba
1111
1132
  * is not scrolled to the top.
1112
1133
  */
1113
1134
  if (!expandToScroll && detail.deltaY <= 0 && cachedScrollEl && cachedScrollEl.scrollTop > 0) {
1135
+ /**
1136
+ * If expand to scroll is disabled, we need to make sure we swap the footer position
1137
+ * back to stationary so that it will collapse correctly if the modal is dismissed without
1138
+ * dragging (e.g. through a dismiss button).
1139
+ * This can cause issues if the user has a modal with content that can be dragged, as we'll
1140
+ * swap to moving on drag and if we don't swap back here then the footer will get stuck.
1141
+ */
1142
+ swapFooterPosition('stationary');
1114
1143
  return;
1115
1144
  }
1116
1145
  /**
@@ -179,6 +179,7 @@ export class ItemSliding {
179
179
  }
180
180
  }
181
181
  async updateOptions() {
182
+ var _a;
182
183
  const options = this.el.querySelectorAll('ion-item-options');
183
184
  let sides = 0;
184
185
  // Reset left and right options in case they were removed
@@ -192,7 +193,7 @@ export class ItemSliding {
192
193
  */
193
194
  // eslint-disable-next-line custom-rules/no-component-on-ready-method
194
195
  const option = item.componentOnReady !== undefined ? await item.componentOnReady() : item;
195
- const side = isEndSide(option.side) ? 'end' : 'start';
196
+ const side = isEndSide((_a = option.side) !== null && _a !== void 0 ? _a : option.getAttribute('side')) ? 'end' : 'start';
196
197
  if (side === 'start') {
197
198
  this.leftOptions = option;
198
199
  sides |= 1 /* ItemSide.Start */;
@@ -390,7 +391,7 @@ export class ItemSliding {
390
391
  }
391
392
  render() {
392
393
  const mode = getIonMode(this);
393
- return (h(Host, { key: '9880396ad79e06117d572a27f92c4b753d1e26db', class: {
394
+ return (h(Host, { key: 'd812322c9fb5da4ee16e99dc38bfb24cb4590d03', class: {
394
395
  [mode]: true,
395
396
  'item-sliding-active-slide': this.state !== 2 /* SlidingState.Disabled */,
396
397
  'item-sliding-active-options-end': (this.state & 8 /* SlidingState.End */) !== 0,
@@ -35,7 +35,7 @@ export const createSheetGesture = (baseEl, backdropEl, wrapperEl, initialBreakpo
35
35
  let offset = 0;
36
36
  let canDismissBlocksGesture = false;
37
37
  let cachedScrollEl = null;
38
- let cachedFooterEl = null;
38
+ let cachedFooterEls = null;
39
39
  let cachedFooterYPosition = null;
40
40
  let currentFooterState = null;
41
41
  const canDismissMaxStep = 0.95;
@@ -72,60 +72,81 @@ export const createSheetGesture = (baseEl, backdropEl, wrapperEl, initialBreakpo
72
72
  * @param newPosition Whether the footer is in a moving or stationary position.
73
73
  */
74
74
  const swapFooterPosition = (newPosition) => {
75
- if (!cachedFooterEl) {
76
- cachedFooterEl = baseEl.querySelector('ion-footer');
77
- if (!cachedFooterEl) {
75
+ if (!cachedFooterEls) {
76
+ cachedFooterEls = Array.from(baseEl.querySelectorAll('ion-footer'));
77
+ if (!cachedFooterEls.length) {
78
78
  return;
79
79
  }
80
80
  }
81
81
  const page = baseEl.querySelector('.ion-page');
82
82
  currentFooterState = newPosition;
83
83
  if (newPosition === 'stationary') {
84
- // Reset positioning styles to allow normal document flow
85
- cachedFooterEl.classList.remove('modal-footer-moving');
86
- cachedFooterEl.style.removeProperty('position');
87
- cachedFooterEl.style.removeProperty('width');
88
- cachedFooterEl.style.removeProperty('height');
89
- cachedFooterEl.style.removeProperty('top');
90
- cachedFooterEl.style.removeProperty('left');
91
- page === null || page === void 0 ? void 0 : page.style.removeProperty('padding-bottom');
92
- // Move to page
93
- page === null || page === void 0 ? void 0 : page.appendChild(cachedFooterEl);
84
+ cachedFooterEls.forEach((cachedFooterEl) => {
85
+ // Reset positioning styles to allow normal document flow
86
+ cachedFooterEl.classList.remove('modal-footer-moving');
87
+ cachedFooterEl.style.removeProperty('position');
88
+ cachedFooterEl.style.removeProperty('width');
89
+ cachedFooterEl.style.removeProperty('height');
90
+ cachedFooterEl.style.removeProperty('top');
91
+ cachedFooterEl.style.removeProperty('left');
92
+ page === null || page === void 0 ? void 0 : page.style.removeProperty('padding-bottom');
93
+ // Move to page
94
+ page === null || page === void 0 ? void 0 : page.appendChild(cachedFooterEl);
95
+ });
94
96
  }
95
97
  else {
96
- // Get both the footer and document body positions
97
- const cachedFooterElRect = cachedFooterEl.getBoundingClientRect();
98
- const bodyRect = document.body.getBoundingClientRect();
99
- // Add padding to the parent element to prevent content from being hidden
100
- // when the footer is positioned absolutely. This has to be done before we
101
- // make the footer absolutely positioned or we may accidentally cause the
102
- // sheet to scroll.
103
- const footerHeight = cachedFooterEl.clientHeight;
104
- page === null || page === void 0 ? void 0 : page.style.setProperty('padding-bottom', `${footerHeight}px`);
105
- // Apply positioning styles to keep footer at bottom
106
- cachedFooterEl.classList.add('modal-footer-moving');
107
- // Calculate absolute position relative to body
108
- // We need to subtract the body's offsetTop to get true position within document.body
109
- const absoluteTop = cachedFooterElRect.top - bodyRect.top;
110
- const absoluteLeft = cachedFooterElRect.left - bodyRect.left;
111
- // Capture the footer's current dimensions and hard code them during the drag
112
- cachedFooterEl.style.setProperty('position', 'absolute');
113
- cachedFooterEl.style.setProperty('width', `${cachedFooterEl.clientWidth}px`);
114
- cachedFooterEl.style.setProperty('height', `${cachedFooterEl.clientHeight}px`);
115
- cachedFooterEl.style.setProperty('top', `${absoluteTop}px`);
116
- cachedFooterEl.style.setProperty('left', `${absoluteLeft}px`);
117
- // Also cache the footer Y position, which we use to determine if the
118
- // sheet has been moved below the footer. When that happens, we need to swap
119
- // the position back so it will collapse correctly.
120
- cachedFooterYPosition = absoluteTop;
121
- // If there's a toolbar, we need to combine the toolbar height with the footer position
122
- // because the toolbar moves with the drag handle, so when it starts overlapping the footer,
123
- // we need to account for that.
124
- const toolbar = baseEl.querySelector('ion-toolbar');
125
- if (toolbar) {
126
- cachedFooterYPosition -= toolbar.clientHeight;
127
- }
128
- document.body.appendChild(cachedFooterEl);
98
+ let footerHeights = 0;
99
+ cachedFooterEls.forEach((cachedFooterEl, index) => {
100
+ // Get both the footer and document body positions
101
+ const cachedFooterElRect = cachedFooterEl.getBoundingClientRect();
102
+ const bodyRect = document.body.getBoundingClientRect();
103
+ // Calculate the total height of all footers
104
+ // so we can add padding to the page element
105
+ footerHeights += cachedFooterEl.clientHeight;
106
+ // Calculate absolute position relative to body
107
+ // We need to subtract the body's offsetTop to get true position within document.body
108
+ const absoluteTop = cachedFooterElRect.top - bodyRect.top;
109
+ const absoluteLeft = cachedFooterElRect.left - bodyRect.left;
110
+ // Capture the footer's current dimensions and store them in CSS variables for
111
+ // later use when applying absolute positioning.
112
+ cachedFooterEl.style.setProperty('--pinned-width', `${cachedFooterEl.clientWidth}px`);
113
+ cachedFooterEl.style.setProperty('--pinned-height', `${cachedFooterEl.clientHeight}px`);
114
+ cachedFooterEl.style.setProperty('--pinned-top', `${absoluteTop}px`);
115
+ cachedFooterEl.style.setProperty('--pinned-left', `${absoluteLeft}px`);
116
+ // Only cache the first footer's Y position
117
+ // This is used to determine if the sheet has been moved below the footer
118
+ // and needs to be swapped back to stationary so it collapses correctly.
119
+ if (index === 0) {
120
+ cachedFooterYPosition = absoluteTop;
121
+ // If there's a header, we need to combine the header height with the footer position
122
+ // because the header moves with the drag handle, so when it starts overlapping the footer,
123
+ // we need to account for that.
124
+ const header = baseEl.querySelector('ion-header');
125
+ if (header) {
126
+ cachedFooterYPosition -= header.clientHeight;
127
+ }
128
+ }
129
+ });
130
+ // Apply the pinning of styles after we've calculated everything
131
+ // so that we don't cause layouts to shift while calculating the footer positions.
132
+ // Otherwise, with multiple footers we'll end up capturing the wrong positions.
133
+ cachedFooterEls.forEach((cachedFooterEl) => {
134
+ // Add padding to the parent element to prevent content from being hidden
135
+ // when the footer is positioned absolutely. This has to be done before we
136
+ // make the footer absolutely positioned or we may accidentally cause the
137
+ // sheet to scroll.
138
+ page === null || page === void 0 ? void 0 : page.style.setProperty('padding-bottom', `${footerHeights}px`);
139
+ // Apply positioning styles to keep footer at bottom
140
+ cachedFooterEl.classList.add('modal-footer-moving');
141
+ // Apply our preserved styles to pin the footer
142
+ cachedFooterEl.style.setProperty('position', 'absolute');
143
+ cachedFooterEl.style.setProperty('width', 'var(--pinned-width)');
144
+ cachedFooterEl.style.setProperty('height', 'var(--pinned-height)');
145
+ cachedFooterEl.style.setProperty('top', 'var(--pinned-top)');
146
+ cachedFooterEl.style.setProperty('left', 'var(--pinned-left)');
147
+ // Move the element to the body when everything else is done
148
+ document.body.appendChild(cachedFooterEl);
149
+ });
129
150
  }
130
151
  };
131
152
  /**
@@ -317,6 +338,14 @@ export const createSheetGesture = (baseEl, backdropEl, wrapperEl, initialBreakpo
317
338
  * is not scrolled to the top.
318
339
  */
319
340
  if (!expandToScroll && detail.deltaY <= 0 && cachedScrollEl && cachedScrollEl.scrollTop > 0) {
341
+ /**
342
+ * If expand to scroll is disabled, we need to make sure we swap the footer position
343
+ * back to stationary so that it will collapse correctly if the modal is dismissed without
344
+ * dragging (e.g. through a dismiss button).
345
+ * This can cause issues if the user has a modal with content that can be dragged, as we'll
346
+ * swap to moving on drag and if we don't swap back here then the footer will get stuck.
347
+ */
348
+ swapFooterPosition('stationary');
320
349
  return;
321
350
  }
322
351
  /**
package/dist/docs.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "timestamp": "2025-06-10T06:09:50",
2
+ "timestamp": "2025-06-11T15:33:22",
3
3
  "compiler": {
4
4
  "name": "@stencil/core",
5
5
  "version": "4.33.1",
@@ -280,6 +280,7 @@ const ItemSliding = class {
280
280
  }
281
281
  }
282
282
  async updateOptions() {
283
+ var _a;
283
284
  const options = this.el.querySelectorAll('ion-item-options');
284
285
  let sides = 0;
285
286
  // Reset left and right options in case they were removed
@@ -293,7 +294,7 @@ const ItemSliding = class {
293
294
  */
294
295
  // eslint-disable-next-line custom-rules/no-component-on-ready-method
295
296
  const option = item.componentOnReady !== undefined ? await item.componentOnReady() : item;
296
- const side = isEndSide(option.side) ? 'end' : 'start';
297
+ const side = isEndSide((_a = option.side) !== null && _a !== void 0 ? _a : option.getAttribute('side')) ? 'end' : 'start';
297
298
  if (side === 'start') {
298
299
  this.leftOptions = option;
299
300
  sides |= 1 /* ItemSide.Start */;
@@ -491,7 +492,7 @@ const ItemSliding = class {
491
492
  }
492
493
  render() {
493
494
  const mode = getIonMode(this);
494
- return (h(Host, { key: '9880396ad79e06117d572a27f92c4b753d1e26db', class: {
495
+ return (h(Host, { key: 'd812322c9fb5da4ee16e99dc38bfb24cb4590d03', class: {
495
496
  [mode]: true,
496
497
  'item-sliding-active-slide': this.state !== 2 /* SlidingState.Disabled */,
497
498
  'item-sliding-active-options-end': (this.state & 8 /* SlidingState.End */) !== 0,