@ionic/core 8.6.1-dev.11749562115.1e681558 → 8.6.1-dev.11749572912.152b544d
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.
- package/components/ion-item-sliding.js +2 -3
- package/components/modal.js +78 -47
- package/dist/cjs/ion-item-option_3.cjs.entry.js +2 -3
- package/dist/cjs/ion-modal.cjs.entry.js +78 -47
- package/dist/collection/components/item-sliding/item-sliding.js +2 -3
- package/dist/collection/components/modal/gestures/sheet.js +78 -47
- package/dist/collection/utils/test/playwright/page/utils/set-content.js +2 -15
- package/dist/docs.json +1 -1
- package/dist/esm/ion-item-option_3.entry.js +2 -3
- package/dist/esm/ion-modal.entry.js +78 -47
- package/dist/ionic/ionic.esm.js +1 -1
- package/dist/ionic/{p-a92bfa13.entry.js → p-66510682.entry.js} +1 -1
- package/dist/ionic/p-c045d121.entry.js +4 -0
- package/dist/types/utils/test/playwright/playwright-declarations.d.ts +0 -5
- package/hydrate/index.js +80 -50
- package/hydrate/index.mjs +80 -50
- package/package.json +1 -1
- package/dist/ionic/p-90f87086.entry.js +0 -4
|
@@ -185,7 +185,6 @@ const ItemSliding = /*@__PURE__*/ proxyCustomElement(class ItemSliding extends H
|
|
|
185
185
|
}
|
|
186
186
|
}
|
|
187
187
|
async updateOptions() {
|
|
188
|
-
var _a;
|
|
189
188
|
const options = this.el.querySelectorAll('ion-item-options');
|
|
190
189
|
let sides = 0;
|
|
191
190
|
// Reset left and right options in case they were removed
|
|
@@ -199,7 +198,7 @@ const ItemSliding = /*@__PURE__*/ proxyCustomElement(class ItemSliding extends H
|
|
|
199
198
|
*/
|
|
200
199
|
// eslint-disable-next-line custom-rules/no-component-on-ready-method
|
|
201
200
|
const option = item.componentOnReady !== undefined ? await item.componentOnReady() : item;
|
|
202
|
-
const side = isEndSide(
|
|
201
|
+
const side = isEndSide(option.side) ? 'end' : 'start';
|
|
203
202
|
if (side === 'start') {
|
|
204
203
|
this.leftOptions = option;
|
|
205
204
|
sides |= 1 /* ItemSide.Start */;
|
|
@@ -397,7 +396,7 @@ const ItemSliding = /*@__PURE__*/ proxyCustomElement(class ItemSliding extends H
|
|
|
397
396
|
}
|
|
398
397
|
render() {
|
|
399
398
|
const mode = getIonMode(this);
|
|
400
|
-
return (h(Host, { key: '
|
|
399
|
+
return (h(Host, { key: '9880396ad79e06117d572a27f92c4b753d1e26db', class: {
|
|
401
400
|
[mode]: true,
|
|
402
401
|
'item-sliding-active-slide': this.state !== 2 /* SlidingState.Disabled */,
|
|
403
402
|
'item-sliding-active-options-end': (this.state & 8 /* SlidingState.End */) !== 0,
|
package/components/modal.js
CHANGED
|
@@ -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
|
|
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 (!
|
|
868
|
-
|
|
869
|
-
if (!
|
|
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
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
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
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
}
|
|
920
|
-
|
|
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 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
|
+
}
|
|
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,16 @@ 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
|
+
if (!expandToScroll) {
|
|
1141
|
+
swapFooterPosition('stationary');
|
|
1142
|
+
}
|
|
1112
1143
|
return;
|
|
1113
1144
|
}
|
|
1114
1145
|
/**
|
|
@@ -282,7 +282,6 @@ const ItemSliding = class {
|
|
|
282
282
|
}
|
|
283
283
|
}
|
|
284
284
|
async updateOptions() {
|
|
285
|
-
var _a;
|
|
286
285
|
const options = this.el.querySelectorAll('ion-item-options');
|
|
287
286
|
let sides = 0;
|
|
288
287
|
// Reset left and right options in case they were removed
|
|
@@ -296,7 +295,7 @@ const ItemSliding = class {
|
|
|
296
295
|
*/
|
|
297
296
|
// eslint-disable-next-line custom-rules/no-component-on-ready-method
|
|
298
297
|
const option = item.componentOnReady !== undefined ? await item.componentOnReady() : item;
|
|
299
|
-
const side = helpers.isEndSide(
|
|
298
|
+
const side = helpers.isEndSide(option.side) ? 'end' : 'start';
|
|
300
299
|
if (side === 'start') {
|
|
301
300
|
this.leftOptions = option;
|
|
302
301
|
sides |= 1 /* ItemSide.Start */;
|
|
@@ -494,7 +493,7 @@ const ItemSliding = class {
|
|
|
494
493
|
}
|
|
495
494
|
render() {
|
|
496
495
|
const mode = index.getIonMode(this);
|
|
497
|
-
return (index.h(index.Host, { key: '
|
|
496
|
+
return (index.h(index.Host, { key: '9880396ad79e06117d572a27f92c4b753d1e26db', class: {
|
|
498
497
|
[mode]: true,
|
|
499
498
|
'item-sliding-active-slide': this.state !== 2 /* SlidingState.Disabled */,
|
|
500
499
|
'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
|
|
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 (!
|
|
870
|
-
|
|
871
|
-
if (!
|
|
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
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
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
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
}
|
|
922
|
-
|
|
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 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
|
+
}
|
|
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,16 @@ 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
|
+
if (!expandToScroll) {
|
|
1143
|
+
swapFooterPosition('stationary');
|
|
1144
|
+
}
|
|
1114
1145
|
return;
|
|
1115
1146
|
}
|
|
1116
1147
|
/**
|
|
@@ -179,7 +179,6 @@ export class ItemSliding {
|
|
|
179
179
|
}
|
|
180
180
|
}
|
|
181
181
|
async updateOptions() {
|
|
182
|
-
var _a;
|
|
183
182
|
const options = this.el.querySelectorAll('ion-item-options');
|
|
184
183
|
let sides = 0;
|
|
185
184
|
// Reset left and right options in case they were removed
|
|
@@ -193,7 +192,7 @@ export class ItemSliding {
|
|
|
193
192
|
*/
|
|
194
193
|
// eslint-disable-next-line custom-rules/no-component-on-ready-method
|
|
195
194
|
const option = item.componentOnReady !== undefined ? await item.componentOnReady() : item;
|
|
196
|
-
const side = isEndSide(
|
|
195
|
+
const side = isEndSide(option.side) ? 'end' : 'start';
|
|
197
196
|
if (side === 'start') {
|
|
198
197
|
this.leftOptions = option;
|
|
199
198
|
sides |= 1 /* ItemSide.Start */;
|
|
@@ -391,7 +390,7 @@ export class ItemSliding {
|
|
|
391
390
|
}
|
|
392
391
|
render() {
|
|
393
392
|
const mode = getIonMode(this);
|
|
394
|
-
return (h(Host, { key: '
|
|
393
|
+
return (h(Host, { key: '9880396ad79e06117d572a27f92c4b753d1e26db', class: {
|
|
395
394
|
[mode]: true,
|
|
396
395
|
'item-sliding-active-slide': this.state !== 2 /* SlidingState.Disabled */,
|
|
397
396
|
'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
|
|
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 (!
|
|
76
|
-
|
|
77
|
-
if (!
|
|
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
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
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
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
}
|
|
128
|
-
|
|
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 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
|
+
}
|
|
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,16 @@ 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
|
+
if (!expandToScroll) {
|
|
349
|
+
swapFooterPosition('stationary');
|
|
350
|
+
}
|
|
320
351
|
return;
|
|
321
352
|
}
|
|
322
353
|
/**
|
|
@@ -30,20 +30,6 @@ export const setContent = async (page, html, testInfo, options) => {
|
|
|
30
30
|
palette = options.palette;
|
|
31
31
|
}
|
|
32
32
|
const baseUrl = process.env.PLAYWRIGHT_TEST_BASE_URL;
|
|
33
|
-
// The Ionic bundle is included locally by default unless the test
|
|
34
|
-
// config passes in the importIonicFromCDN option. This is useful
|
|
35
|
-
// when testing with the CDN version of Ionic.
|
|
36
|
-
let ionicImports = `
|
|
37
|
-
<link href="${baseUrl}/css/ionic.bundle.css" rel="stylesheet" />
|
|
38
|
-
<script type="module" src="${baseUrl}/dist/ionic/ionic.esm.js"></script>
|
|
39
|
-
`;
|
|
40
|
-
if (options === null || options === void 0 ? void 0 : options.importIonicFromCDN) {
|
|
41
|
-
ionicImports = `
|
|
42
|
-
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@ionic/core/css/ionic.bundle.css" />
|
|
43
|
-
<script type="module" src="https://cdn.jsdelivr.net/npm/@ionic/core/dist/ionic/ionic.esm.js"></script>
|
|
44
|
-
<script nomodule src="https://cdn.jsdelivr.net/npm/@ionic/core/dist/ionic/ionic.js"></script>
|
|
45
|
-
`;
|
|
46
|
-
}
|
|
47
33
|
const output = `
|
|
48
34
|
<!DOCTYPE html>
|
|
49
35
|
<html dir="${direction}" lang="en">
|
|
@@ -51,10 +37,11 @@ export const setContent = async (page, html, testInfo, options) => {
|
|
|
51
37
|
<title>Ionic Playwright Test</title>
|
|
52
38
|
<meta charset="UTF-8" />
|
|
53
39
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0" />
|
|
40
|
+
<link href="${baseUrl}/css/ionic.bundle.css" rel="stylesheet" />
|
|
54
41
|
<link href="${baseUrl}/scripts/testing/styles.css" rel="stylesheet" />
|
|
55
42
|
${palette !== 'light' ? `<link href="${baseUrl}/css/palettes/${palette}.always.css" rel="stylesheet" />` : ''}
|
|
56
43
|
<script src="${baseUrl}/scripts/testing/scripts.js"></script>
|
|
57
|
-
${
|
|
44
|
+
<script type="module" src="${baseUrl}/dist/ionic/ionic.esm.js"></script>
|
|
58
45
|
<script>
|
|
59
46
|
window.Ionic = {
|
|
60
47
|
config: {
|
package/dist/docs.json
CHANGED