@ionic/core 8.5.8-dev.11748364689.107665ad → 8.5.8-dev.11748388365.11ad9dfe

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.
Files changed (63) hide show
  1. package/components/button.js +23 -5
  2. package/components/ion-datetime.js +27 -51
  3. package/components/ion-input-password-toggle.js +2 -2
  4. package/components/ion-searchbar.js +4 -4
  5. package/components/modal.js +157 -58
  6. package/components/picker-column2.js +3 -3
  7. package/dist/cjs/ion-button_2.cjs.entry.js +19 -3
  8. package/dist/cjs/ion-datetime_3.cjs.entry.js +30 -54
  9. package/dist/cjs/ion-input-password-toggle.cjs.entry.js +2 -2
  10. package/dist/cjs/ion-modal.cjs.entry.js +157 -58
  11. package/dist/cjs/ion-searchbar.cjs.entry.js +4 -4
  12. package/dist/cjs/ionic.cjs.js +1 -1
  13. package/dist/cjs/loader.cjs.js +1 -1
  14. package/dist/collection/components/button/button.js +23 -3
  15. package/dist/collection/components/datetime/datetime.js +27 -51
  16. package/dist/collection/components/input-password-toggle/input-password-toggle.js +2 -2
  17. package/dist/collection/components/modal/animations/ios.enter.js +41 -1
  18. package/dist/collection/components/modal/animations/ios.leave.js +24 -2
  19. package/dist/collection/components/modal/animations/md.enter.js +41 -1
  20. package/dist/collection/components/modal/animations/md.leave.js +24 -2
  21. package/dist/collection/components/modal/gestures/sheet.js +27 -52
  22. package/dist/collection/components/picker-legacy-column/picker-column.js +3 -3
  23. package/dist/collection/components/searchbar/searchbar.js +5 -5
  24. package/dist/docs.json +2 -2
  25. package/dist/esm/ion-button_2.entry.js +20 -4
  26. package/dist/esm/ion-datetime_3.entry.js +30 -54
  27. package/dist/esm/ion-input-password-toggle.entry.js +2 -2
  28. package/dist/esm/ion-modal.entry.js +157 -58
  29. package/dist/esm/ion-searchbar.entry.js +4 -4
  30. package/dist/esm/ionic.js +1 -1
  31. package/dist/esm/loader.js +1 -1
  32. package/dist/esm-es5/ion-button_2.entry.js +1 -1
  33. package/dist/esm-es5/ion-datetime_3.entry.js +1 -1
  34. package/dist/esm-es5/ion-input-password-toggle.entry.js +1 -1
  35. package/dist/esm-es5/ion-modal.entry.js +1 -1
  36. package/dist/esm-es5/ion-searchbar.entry.js +1 -1
  37. package/dist/esm-es5/ionic.js +1 -1
  38. package/dist/esm-es5/loader.js +1 -1
  39. package/dist/html.html-data.json +1 -1
  40. package/dist/ionic/ionic.esm.js +1 -1
  41. package/dist/ionic/p-12914457.entry.js +4 -0
  42. package/dist/ionic/{p-50cb2e8b.system.entry.js → p-3cee5d7c.system.entry.js} +2 -2
  43. package/dist/ionic/p-3d2b2787.system.entry.js +4 -0
  44. package/dist/ionic/{p-7611ede3.system.entry.js → p-3ec227cd.system.entry.js} +1 -1
  45. package/dist/ionic/p-4090ce24.entry.js +4 -0
  46. package/dist/ionic/p-8b8b4530.system.entry.js +4 -0
  47. package/dist/ionic/p-94a18d00.entry.js +4 -0
  48. package/dist/ionic/p-cec902a0.system.entry.js +4 -0
  49. package/dist/ionic/{p-891553d8.entry.js → p-e7f732ef.entry.js} +1 -1
  50. package/dist/ionic/p-f725bf9e.system.js +2 -2
  51. package/dist/ionic/{p-a835aca8.entry.js → p-ffa2125f.entry.js} +1 -1
  52. package/dist/types/components/button/button.d.ts +11 -0
  53. package/dist/types/components/searchbar/searchbar.d.ts +1 -1
  54. package/dist/types/components.d.ts +2 -2
  55. package/hydrate/index.js +211 -121
  56. package/hydrate/index.mjs +211 -121
  57. package/package.json +2 -2
  58. package/dist/ionic/p-451706f5.entry.js +0 -4
  59. package/dist/ionic/p-57957c0f.entry.js +0 -4
  60. package/dist/ionic/p-6b8530f5.system.entry.js +0 -4
  61. package/dist/ionic/p-b5432d15.entry.js +0 -4
  62. package/dist/ionic/p-c1b7d311.system.entry.js +0 -4
  63. package/dist/ionic/p-c8731f25.system.entry.js +0 -4
@@ -687,19 +687,18 @@ const Datetime = class {
687
687
  data.warnIfValueOutOfBounds(valueToProcess, minParts, maxParts);
688
688
  }
689
689
  /**
690
- * If there are multiple values, pick an arbitrary one to clamp to. This way,
691
- * if the values are across months, we always show at least one of them. Note
692
- * that the values don't necessarily have to be in order.
690
+ * If there are multiple values, clamp to the last one.
691
+ * This is because the last value is the one that the user
692
+ * has most recently interacted with.
693
693
  */
694
- const singleValue = Array.isArray(valueToProcess) ? valueToProcess[0] : valueToProcess;
694
+ const singleValue = Array.isArray(valueToProcess) ? valueToProcess[valueToProcess.length - 1] : valueToProcess;
695
695
  const targetValue = data.clampDate(singleValue, minParts, maxParts);
696
696
  const { month, day, year, hour, minute } = targetValue;
697
697
  const ampm = data.parseAmPm(hour);
698
698
  /**
699
- * Since `activeParts` indicates a value that
700
- * been explicitly selected either by the
701
- * user or the app, only update `activeParts`
702
- * if the `value` property is set.
699
+ * Since `activeParts` indicates a value that been explicitly selected
700
+ * either by the user or the app, only update `activeParts` if the
701
+ * `value` property is set.
703
702
  */
704
703
  if (hasValue) {
705
704
  if (Array.isArray(valueToProcess)) {
@@ -724,51 +723,28 @@ const Datetime = class {
724
723
  */
725
724
  this.activeParts = [];
726
725
  }
727
- /**
728
- * Only animate if:
729
- * 1. We're using grid style (wheel style pickers should just jump to new value)
730
- * 2. The month and/or year actually changed, and both are defined (otherwise there's nothing to animate to)
731
- * 3. The calendar body is visible (prevents animation when in collapsed datetime-button, for example)
732
- * 4. The month/year picker is not open (since you wouldn't see the animation anyway)
733
- */
734
726
  const didChangeMonth = (month !== undefined && month !== workingParts.month) || (year !== undefined && year !== workingParts.year);
735
727
  const bodyIsVisible = el.classList.contains('datetime-ready');
736
728
  const { isGridStyle, showMonthAndYear } = this;
737
- let areAllSelectedDatesInSameMonth = true;
738
- if (Array.isArray(valueToProcess)) {
739
- const firstMonth = valueToProcess[0].month;
740
- for (const date of valueToProcess) {
741
- if (date.month !== firstMonth) {
742
- areAllSelectedDatesInSameMonth = false;
743
- break;
744
- }
745
- }
729
+ if (isGridStyle && didChangeMonth && bodyIsVisible && !showMonthAndYear) {
730
+ /**
731
+ * Only animate if:
732
+ * 1. We're using grid style (wheel style pickers should just jump to new value)
733
+ * 2. The month and/or year actually changed, and both are defined (otherwise there's nothing to animate to)
734
+ * 3. The calendar body is visible (prevents animation when in collapsed datetime-button, for example)
735
+ * 4. The month/year picker is not open (since you wouldn't see the animation anyway)
736
+ */
737
+ this.animateToDate(targetValue);
746
738
  }
747
- /**
748
- * If there is more than one date selected
749
- * and the dates aren't all in the same month,
750
- * then we should neither animate to the date
751
- * nor update the working parts because we do
752
- * not know which date the user wants to view.
753
- */
754
- if (areAllSelectedDatesInSameMonth) {
755
- if (isGridStyle && didChangeMonth && bodyIsVisible && !showMonthAndYear) {
756
- this.animateToDate(targetValue);
757
- }
758
- else {
759
- /**
760
- * We only need to do this if we didn't just animate to a new month,
761
- * since that calls prevMonth/nextMonth which calls setWorkingParts for us.
762
- */
763
- this.setWorkingParts({
764
- month,
765
- day,
766
- year,
767
- hour,
768
- minute,
769
- ampm,
770
- });
771
- }
739
+ else {
740
+ this.setWorkingParts({
741
+ month,
742
+ day,
743
+ year,
744
+ hour,
745
+ minute,
746
+ ampm,
747
+ });
772
748
  }
773
749
  };
774
750
  this.animateToDate = async (targetValue) => {
@@ -1763,7 +1739,7 @@ const Datetime = class {
1763
1739
  const hasDatePresentation = presentation === 'date' || presentation === 'date-time' || presentation === 'time-date';
1764
1740
  const hasWheelVariant = hasDatePresentation && preferWheel;
1765
1741
  helpers.renderHiddenInput(true, el, name, data.formatValue(value), disabled);
1766
- return (index$1.h(index$1.Host, { key: 'c3dfea8f46fcbcef38eb9e8a69b1b46a4e4b82fd', "aria-disabled": disabled ? 'true' : null, onFocus: this.onFocus, onBlur: this.onBlur, class: Object.assign({}, theme.createColorClasses(color, {
1742
+ return (index$1.h(index$1.Host, { key: '20e64fb4cfe3fbd74b24038c89e00651d78b500c', "aria-disabled": disabled ? 'true' : null, onFocus: this.onFocus, onBlur: this.onBlur, class: Object.assign({}, theme.createColorClasses(color, {
1767
1743
  [mode]: true,
1768
1744
  ['datetime-readonly']: readonly,
1769
1745
  ['datetime-disabled']: disabled,
@@ -1773,7 +1749,7 @@ const Datetime = class {
1773
1749
  [`datetime-size-${size}`]: true,
1774
1750
  [`datetime-prefer-wheel`]: hasWheelVariant,
1775
1751
  [`datetime-grid`]: isGridStyle,
1776
- })) }, index$1.h("div", { key: '75c91243cf6a51f44b83d7cf7d8c0c96bfd3c83f', class: "intersection-tracker", ref: (el) => (this.intersectionTrackerRef = el) }), this.renderDatetime(mode)));
1752
+ })) }, index$1.h("div", { key: '488fe7bf6b9052e208d65206a580d5d65b945a62', class: "intersection-tracker", ref: (el) => (this.intersectionTrackerRef = el) }), this.renderDatetime(mode)));
1777
1753
  }
1778
1754
  get el() { return index$1.getElement(this); }
1779
1755
  static get watchers() { return {
@@ -2383,7 +2359,7 @@ const PickerColumnCmp = class {
2383
2359
  const colEl = this.optsEl;
2384
2360
  if (colEl) {
2385
2361
  // DOM READ
2386
- // We perfom a DOM read over a rendered item, this needs to happen after the first render or after the the column has changed
2362
+ // We perfom a DOM read over a rendered item, this needs to happen after the first render or after the column has changed
2387
2363
  this.optHeight = colEl.firstElementChild ? colEl.firstElementChild.clientHeight : 0;
2388
2364
  }
2389
2365
  this.refresh(forceRefresh, animated);
@@ -2391,9 +2367,9 @@ const PickerColumnCmp = class {
2391
2367
  render() {
2392
2368
  const col = this.col;
2393
2369
  const mode = ionicGlobal.getIonMode(this);
2394
- return (index$1.h(index$1.Host, { key: '88a3c9397c9ac92dd814074c8ae6ecf8e3420a2c', class: Object.assign({ [mode]: true, 'picker-col': true, 'picker-opts-left': this.col.align === 'left', 'picker-opts-right': this.col.align === 'right' }, theme.getClassMap(col.cssClass)), style: {
2370
+ return (index$1.h(index$1.Host, { key: '10ef653cbae8e1ed92a23fa17f3396082e35951f', class: Object.assign({ [mode]: true, 'picker-col': true, 'picker-opts-left': this.col.align === 'left', 'picker-opts-right': this.col.align === 'right' }, theme.getClassMap(col.cssClass)), style: {
2395
2371
  'max-width': this.col.columnWidth,
2396
- } }, col.prefix && (index$1.h("div", { key: '4491a705d15337e6f45f3cf6fd21af5242474729', class: "picker-prefix", style: { width: col.prefixWidth } }, col.prefix)), index$1.h("div", { key: 'b0dd4b7a7a4c1edc4b73e7fb134ac85264072365', class: "picker-opts", style: { maxWidth: col.optionsWidth }, ref: (el) => (this.optsEl = el) }, col.options.map((o, index) => (index$1.h("button", { "aria-label": o.ariaLabel, class: { 'picker-opt': true, 'picker-opt-disabled': !!o.disabled }, "opt-index": index }, o.text)))), col.suffix && (index$1.h("div", { key: 'c16419ce6481d60fc3ba6b8d102a4edf0ede02aa', class: "picker-suffix", style: { width: col.suffixWidth } }, col.suffix))));
2372
+ } }, col.prefix && (index$1.h("div", { key: '253066708012034206b1d3d466ea48d0a3be3323', class: "picker-prefix", style: { width: col.prefixWidth } }, col.prefix)), index$1.h("div", { key: '15fa07307d4d2b4994504a5bf2de8db05b51cd2c', class: "picker-opts", style: { maxWidth: col.optionsWidth }, ref: (el) => (this.optsEl = el) }, col.options.map((o, index) => (index$1.h("button", { "aria-label": o.ariaLabel, class: { 'picker-opt': true, 'picker-opt-disabled': !!o.disabled }, "opt-index": index }, o.text)))), col.suffix && (index$1.h("div", { key: '4db779830b1e6aa6b324e5a72541d4f610ac1ff5', class: "picker-suffix", style: { width: col.suffixWidth } }, col.suffix))));
2397
2373
  }
2398
2374
  get el() { return index$1.getElement(this); }
2399
2375
  static get watchers() { return {
@@ -71,14 +71,14 @@ const InputPasswordToggle = class {
71
71
  const isPasswordVisible = type === 'text';
72
72
  return (index.h(index.Host, { key: '1a28e078c83e74c72d8bb8189ece93ec2e3fa3d0', class: theme.createColorClasses(color, {
73
73
  [mode]: true,
74
- }) }, index.h("ion-button", { key: '039d1bab764093bb6fe4a34299b0872abda087fd', mode: mode, color: color, fill: "clear", shape: "round", "aria-checked": isPasswordVisible ? 'true' : 'false', "aria-label": "show password", role: "switch", type: "button", onPointerDown: (ev) => {
74
+ }) }, index.h("ion-button", { key: '61591d11d97f3065240e51d8887f831339fd36cf', mode: mode, color: color, fill: "clear", shape: "round", "aria-checked": isPasswordVisible ? 'true' : 'false', "aria-label": isPasswordVisible ? 'Hide password' : 'Show password', role: "switch", type: "button", onPointerDown: (ev) => {
75
75
  /**
76
76
  * This prevents mobile browsers from
77
77
  * blurring the input when the password toggle
78
78
  * button is activated.
79
79
  */
80
80
  ev.preventDefault();
81
- }, onClick: this.togglePasswordVisibility }, index.h("ion-icon", { key: '26477ee97b808c3d79944bf5e33d4e05f1ae0b3f', slot: "icon-only", "aria-hidden": "true", icon: isPasswordVisible ? hidePasswordIcon : showPasswordIcon }))));
81
+ }, onClick: this.togglePasswordVisibility }, index.h("ion-icon", { key: '9d98e6675d5e2adea5c0f56ab67e98b6f57521dd', slot: "icon-only", "aria-hidden": "true", icon: isPasswordVisible ? hidePasswordIcon : showPasswordIcon }))));
82
82
  }
83
83
  get el() { return index.getElement(this); }
84
84
  static get watchers() { return {
@@ -588,7 +588,47 @@ const iosEnterAnimation = (baseEl, opts) => {
588
588
  .addElement(baseEl)
589
589
  .easing('cubic-bezier(0.32,0.72,0,1)')
590
590
  .duration(500)
591
- .addAnimation([wrapperAnimation]);
591
+ .addAnimation([wrapperAnimation])
592
+ .beforeAddWrite(() => {
593
+ if (expandToScroll) {
594
+ // Scroll can only be done when the modal is fully expanded.
595
+ return;
596
+ }
597
+ /**
598
+ * There are some browsers that causes flickering when
599
+ * dragging the content when scroll is enabled at every
600
+ * breakpoint. This is due to the wrapper element being
601
+ * transformed off the screen and having a snap animation.
602
+ *
603
+ * A workaround is to clone the footer element and append
604
+ * it outside of the wrapper element. This way, the footer
605
+ * is still visible and the drag can be done without
606
+ * flickering. The original footer is hidden until the modal
607
+ * is dismissed. This maintains the animation of the footer
608
+ * when the modal is dismissed.
609
+ *
610
+ * The workaround needs to be done before the animation starts
611
+ * so there are no flickering issues.
612
+ */
613
+ const ionFooter = baseEl.querySelector('ion-footer');
614
+ /**
615
+ * This check is needed to prevent more than one footer
616
+ * from being appended to the shadow root.
617
+ * Otherwise, iOS and MD enter animations would append
618
+ * the footer twice.
619
+ */
620
+ const ionFooterAlreadyAppended = baseEl.shadowRoot.querySelector('ion-footer');
621
+ if (ionFooter && !ionFooterAlreadyAppended) {
622
+ const footerHeight = ionFooter.clientHeight;
623
+ const clonedFooter = ionFooter.cloneNode(true);
624
+ baseEl.shadowRoot.appendChild(clonedFooter);
625
+ ionFooter.style.setProperty('display', 'none');
626
+ ionFooter.setAttribute('aria-hidden', 'true');
627
+ // Padding is added to prevent some content from being hidden.
628
+ const page = baseEl.querySelector('.ion-page');
629
+ page.style.setProperty('padding-bottom', `${footerHeight}px`);
630
+ }
631
+ });
592
632
  if (contentAnimation) {
593
633
  baseAnimation.addAnimation(contentAnimation);
594
634
  }
@@ -669,7 +709,7 @@ const createLeaveAnimation$1 = () => {
669
709
  * iOS Modal Leave Animation
670
710
  */
671
711
  const iosLeaveAnimation = (baseEl, opts, duration = 500) => {
672
- const { presentingEl, currentBreakpoint } = opts;
712
+ const { presentingEl, currentBreakpoint, expandToScroll } = opts;
673
713
  const root = helpers.getElementRoot(baseEl);
674
714
  const { wrapperAnimation, backdropAnimation } = currentBreakpoint !== undefined ? createSheetLeaveAnimation(opts) : createLeaveAnimation$1();
675
715
  backdropAnimation.addElement(root.querySelector('ion-backdrop'));
@@ -678,7 +718,29 @@ const iosLeaveAnimation = (baseEl, opts, duration = 500) => {
678
718
  .addElement(baseEl)
679
719
  .easing('cubic-bezier(0.32,0.72,0,1)')
680
720
  .duration(duration)
681
- .addAnimation(wrapperAnimation);
721
+ .addAnimation(wrapperAnimation)
722
+ .beforeAddWrite(() => {
723
+ if (expandToScroll) {
724
+ // Scroll can only be done when the modal is fully expanded.
725
+ return;
726
+ }
727
+ /**
728
+ * If expandToScroll is disabled, we need to swap
729
+ * the visibility to the original, so the footer
730
+ * dismisses with the modal and doesn't stay
731
+ * until the modal is removed from the DOM.
732
+ */
733
+ const ionFooter = baseEl.querySelector('ion-footer');
734
+ if (ionFooter) {
735
+ const clonedFooter = baseEl.shadowRoot.querySelector('ion-footer');
736
+ ionFooter.style.removeProperty('display');
737
+ ionFooter.removeAttribute('aria-hidden');
738
+ clonedFooter.style.setProperty('display', 'none');
739
+ clonedFooter.setAttribute('aria-hidden', 'true');
740
+ const page = baseEl.querySelector('.ion-page');
741
+ page.style.removeProperty('padding-bottom');
742
+ }
743
+ });
682
744
  if (presentingEl) {
683
745
  const isMobile = window.innerWidth < 768;
684
746
  const hasCardModal = presentingEl.tagName === 'ION-MODAL' && presentingEl.presentingElement !== undefined;
@@ -774,7 +836,47 @@ const mdEnterAnimation = (baseEl, opts) => {
774
836
  .addElement(baseEl)
775
837
  .easing('cubic-bezier(0.36,0.66,0.04,1)')
776
838
  .duration(280)
777
- .addAnimation([backdropAnimation, wrapperAnimation]);
839
+ .addAnimation([backdropAnimation, wrapperAnimation])
840
+ .beforeAddWrite(() => {
841
+ if (expandToScroll) {
842
+ // Scroll can only be done when the modal is fully expanded.
843
+ return;
844
+ }
845
+ /**
846
+ * There are some browsers that causes flickering when
847
+ * dragging the content when scroll is enabled at every
848
+ * breakpoint. This is due to the wrapper element being
849
+ * transformed off the screen and having a snap animation.
850
+ *
851
+ * A workaround is to clone the footer element and append
852
+ * it outside of the wrapper element. This way, the footer
853
+ * is still visible and the drag can be done without
854
+ * flickering. The original footer is hidden until the modal
855
+ * is dismissed. This maintains the animation of the footer
856
+ * when the modal is dismissed.
857
+ *
858
+ * The workaround needs to be done before the animation starts
859
+ * so there are no flickering issues.
860
+ */
861
+ const ionFooter = baseEl.querySelector('ion-footer');
862
+ /**
863
+ * This check is needed to prevent more than one footer
864
+ * from being appended to the shadow root.
865
+ * Otherwise, iOS and MD enter animations would append
866
+ * the footer twice.
867
+ */
868
+ const ionFooterAlreadyAppended = baseEl.shadowRoot.querySelector('ion-footer');
869
+ if (ionFooter && !ionFooterAlreadyAppended) {
870
+ const footerHeight = ionFooter.clientHeight;
871
+ const clonedFooter = ionFooter.cloneNode(true);
872
+ baseEl.shadowRoot.appendChild(clonedFooter);
873
+ ionFooter.style.setProperty('display', 'none');
874
+ ionFooter.setAttribute('aria-hidden', 'true');
875
+ // Padding is added to prevent some content from being hidden.
876
+ const page = baseEl.querySelector('.ion-page');
877
+ page.style.setProperty('padding-bottom', `${footerHeight}px`);
878
+ }
879
+ });
778
880
  if (contentAnimation) {
779
881
  baseAnimation.addAnimation(contentAnimation);
780
882
  }
@@ -793,7 +895,7 @@ const createLeaveAnimation = () => {
793
895
  * Md Modal Leave Animation
794
896
  */
795
897
  const mdLeaveAnimation = (baseEl, opts) => {
796
- const { currentBreakpoint } = opts;
898
+ const { currentBreakpoint, expandToScroll } = opts;
797
899
  const root = helpers.getElementRoot(baseEl);
798
900
  const { wrapperAnimation, backdropAnimation } = currentBreakpoint !== undefined ? createSheetLeaveAnimation(opts) : createLeaveAnimation();
799
901
  backdropAnimation.addElement(root.querySelector('ion-backdrop'));
@@ -801,7 +903,29 @@ const mdLeaveAnimation = (baseEl, opts) => {
801
903
  const baseAnimation = animation.createAnimation()
802
904
  .easing('cubic-bezier(0.47,0,0.745,0.715)')
803
905
  .duration(200)
804
- .addAnimation([backdropAnimation, wrapperAnimation]);
906
+ .addAnimation([backdropAnimation, wrapperAnimation])
907
+ .beforeAddWrite(() => {
908
+ if (expandToScroll) {
909
+ // Scroll can only be done when the modal is fully expanded.
910
+ return;
911
+ }
912
+ /**
913
+ * If expandToScroll is disabled, we need to swap
914
+ * the visibility to the original, so the footer
915
+ * dismisses with the modal and doesn't stay
916
+ * until the modal is removed from the DOM.
917
+ */
918
+ const ionFooter = baseEl.querySelector('ion-footer');
919
+ if (ionFooter) {
920
+ const clonedFooter = baseEl.shadowRoot.querySelector('ion-footer');
921
+ ionFooter.style.removeProperty('display');
922
+ ionFooter.removeAttribute('aria-hidden');
923
+ clonedFooter.style.setProperty('display', 'none');
924
+ clonedFooter.setAttribute('aria-hidden', 'true');
925
+ const page = baseEl.querySelector('.ion-page');
926
+ page.style.removeProperty('padding-bottom');
927
+ }
928
+ });
805
929
  return baseAnimation;
806
930
  };
807
931
 
@@ -833,8 +957,6 @@ const createSheetGesture = (baseEl, backdropEl, wrapperEl, initialBreakpoint, ba
833
957
  let offset = 0;
834
958
  let canDismissBlocksGesture = false;
835
959
  let cachedScrollEl = null;
836
- let cachedFooterYPosition = null;
837
- let currentFooterState = null;
838
960
  const canDismissMaxStep = 0.95;
839
961
  const maxBreakpoint = breakpoints[breakpoints.length - 1];
840
962
  const minBreakpoint = breakpoints[0];
@@ -864,38 +986,29 @@ const createSheetGesture = (baseEl, backdropEl, wrapperEl, initialBreakpoint, ba
864
986
  baseEl.classList.add(overlays.FOCUS_TRAP_DISABLE_CLASS);
865
987
  };
866
988
  /**
867
- * Toggles the footer to an absolute position while moving to prevent
868
- * it from shaking while the sheet is being dragged.
869
- * @param footer Whether the footer is in a moving or stationary position.
989
+ * Toggles the visible modal footer when `expandToScroll` is disabled.
990
+ * @param footer The footer to show.
870
991
  */
871
- const swapFooterPosition = (newPosition) => {
872
- var _a, _b;
992
+ const swapFooterVisibility = (footer) => {
873
993
  const originalFooter = baseEl.querySelector('ion-footer');
874
994
  if (!originalFooter) {
875
995
  return;
876
996
  }
877
- currentFooterState = newPosition;
878
- if (newPosition === 'stationary') {
879
- // Reset positioning styles to allow normal document flow
880
- originalFooter.style.removeProperty('position');
881
- originalFooter.style.removeProperty('bottom');
882
- (_a = originalFooter.parentElement) === null || _a === void 0 ? void 0 : _a.style.removeProperty('padding-bottom');
997
+ const clonedFooter = wrapperEl.nextElementSibling;
998
+ const footerToHide = footer === 'original' ? clonedFooter : originalFooter;
999
+ const footerToShow = footer === 'original' ? originalFooter : clonedFooter;
1000
+ footerToShow.style.removeProperty('display');
1001
+ footerToShow.removeAttribute('aria-hidden');
1002
+ const page = baseEl.querySelector('.ion-page');
1003
+ if (footer === 'original') {
1004
+ page.style.removeProperty('padding-bottom');
883
1005
  }
884
1006
  else {
885
- // Add padding to the parent element to prevent content from being hidden
886
- // when the footer is positioned absolutely. This has to be done before we
887
- // make the footer absolutely positioned or we may accidentally cause the
888
- // sheet to scroll.
889
- const footerHeight = originalFooter.clientHeight;
890
- (_b = originalFooter.parentElement) === null || _b === void 0 ? void 0 : _b.style.setProperty('padding-bottom', `${footerHeight}px`);
891
- // Apply positioning styles to keep footer at bottom
892
- originalFooter.style.setProperty('position', 'absolute');
893
- originalFooter.style.setProperty('bottom', '0');
894
- // Also cache the footer Y position, which we use to determine if the
895
- // sheet has been moved below the footer. When that happens, we need to swap
896
- // the position back so it will collapse correctly.
897
- cachedFooterYPosition = originalFooter.getBoundingClientRect().top + window.scrollY;
1007
+ const pagePadding = footerToShow.clientHeight;
1008
+ page.style.setProperty('padding-bottom', `${pagePadding}px`);
898
1009
  }
1010
+ footerToHide.style.setProperty('display', 'none');
1011
+ footerToHide.setAttribute('aria-hidden', 'true');
899
1012
  };
900
1013
  /**
901
1014
  * After the entering animation completes,
@@ -989,11 +1102,12 @@ const createSheetGesture = (baseEl, backdropEl, wrapperEl, initialBreakpoint, ba
989
1102
  }
990
1103
  /**
991
1104
  * If expandToScroll is disabled, we need to swap
992
- * the footer position to moving so that it doesn't shake
993
- * while the sheet is being dragged.
1105
+ * the footer visibility to the original, so if the modal
1106
+ * is dismissed, the footer dismisses with the modal
1107
+ * and doesn't stay on the screen after the modal is gone.
994
1108
  */
995
1109
  if (!expandToScroll) {
996
- swapFooterPosition('moving');
1110
+ swapFooterVisibility('original');
997
1111
  }
998
1112
  /**
999
1113
  * If we are pulling down, then it is possible we are pulling on the content.
@@ -1012,21 +1126,6 @@ const createSheetGesture = (baseEl, backdropEl, wrapperEl, initialBreakpoint, ba
1012
1126
  animation.progressStart(true, 1 - currentBreakpoint);
1013
1127
  };
1014
1128
  const onMove = (detail) => {
1015
- /**
1016
- * If `expandToScroll` is disabled, we need to see if we're currently below
1017
- * the footer element and the footer is in a stationary position. If so,
1018
- * we need to make the stationary the original position so that the footer
1019
- * collapses with the sheet.
1020
- */
1021
- if (!expandToScroll && cachedFooterYPosition !== null && currentFooterState !== null) {
1022
- // Check if we need to swap the footer position
1023
- if (detail.currentY >= cachedFooterYPosition && currentFooterState === 'moving') {
1024
- swapFooterPosition('stationary');
1025
- }
1026
- else if (detail.currentY < cachedFooterYPosition && currentFooterState === 'stationary') {
1027
- swapFooterPosition('moving');
1028
- }
1029
- }
1030
1129
  /**
1031
1130
  * If `expandToScroll` is disabled, and an upwards swipe gesture is done within
1032
1131
  * the scrollable content, we should not allow the swipe gesture to continue.
@@ -1160,6 +1259,14 @@ const createSheetGesture = (baseEl, backdropEl, wrapperEl, initialBreakpoint, ba
1160
1259
  * snapping animation completes.
1161
1260
  */
1162
1261
  gesture.enable(false);
1262
+ /**
1263
+ * If expandToScroll is disabled, we need to swap
1264
+ * the footer visibility to the cloned one so the footer
1265
+ * doesn't flicker when the sheet's height is animated.
1266
+ */
1267
+ if (!expandToScroll && shouldRemainOpen) {
1268
+ swapFooterVisibility('cloned');
1269
+ }
1163
1270
  if (shouldPreventDismiss) {
1164
1271
  handleCanDismiss(baseEl, animation);
1165
1272
  }
@@ -1180,14 +1287,6 @@ const createSheetGesture = (baseEl, backdropEl, wrapperEl, initialBreakpoint, ba
1180
1287
  animation
1181
1288
  .onFinish(() => {
1182
1289
  if (shouldRemainOpen) {
1183
- /**
1184
- * If expandToScroll is disabled, we need to swap
1185
- * the footer position to stationary so that it
1186
- * will act as it would by default
1187
- */
1188
- if (!expandToScroll) {
1189
- swapFooterPosition('stationary');
1190
- }
1191
1290
  /**
1192
1291
  * Once the snapping animation completes,
1193
1292
  * we need to reset the animation to go
@@ -394,8 +394,8 @@ const Searchbar = class {
394
394
  const clearIcon = this.clearIcon || (mode === 'ios' ? index$2.closeCircle : index$2.closeSharp);
395
395
  const searchIcon = this.searchIcon || (mode === 'ios' ? index$2.searchOutline : index$2.searchSharp);
396
396
  const shouldShowCancelButton = this.shouldShowCancelButton();
397
- const cancelButton = this.showCancelButton !== 'never' && (index.h("button", { key: '989f3e84c472ada6e66dd9b249d0d268bf17ce26', "aria-label": cancelButtonText, "aria-hidden": shouldShowCancelButton ? undefined : 'true', type: "button", tabIndex: mode === 'ios' && !shouldShowCancelButton ? -1 : undefined, onMouseDown: this.onCancelSearchbar, onTouchStart: this.onCancelSearchbar, class: "searchbar-cancel-button" }, index.h("div", { key: '7d335d4fde33822dc79d26b748ba2e98db7494bb', "aria-hidden": "true" }, mode === 'md' ? (index.h("ion-icon", { "aria-hidden": "true", mode: mode, icon: this.cancelButtonIcon, lazy: false })) : (cancelButtonText))));
398
- return (index.h(index.Host, { key: 'd1a1972725e949fb102c91487aaa7b9d10c2d718', role: "search", "aria-disabled": this.disabled ? 'true' : null, class: theme.createColorClasses(this.color, {
397
+ const cancelButton = this.showCancelButton !== 'never' && (index.h("button", { key: '4a4bddfaf44acb9ea71d37d11c3fc11450134a5c', "aria-label": cancelButtonText, "aria-hidden": shouldShowCancelButton ? undefined : 'true', type: "button", tabIndex: mode === 'ios' && !shouldShowCancelButton ? -1 : undefined, onMouseDown: this.onCancelSearchbar, onTouchStart: this.onCancelSearchbar, class: "searchbar-cancel-button" }, index.h("div", { key: '24424547e34a80422f617f5597965724a3ef65f0', "aria-hidden": "true" }, mode === 'md' ? (index.h("ion-icon", { "aria-hidden": "true", mode: mode, icon: this.cancelButtonIcon, lazy: false })) : (cancelButtonText))));
398
+ return (index.h(index.Host, { key: '0c0e7334cb2af87081a278e5a3f6be3dc646a073', role: "search", "aria-disabled": this.disabled ? 'true' : null, class: theme.createColorClasses(this.color, {
399
399
  [mode]: true,
400
400
  'searchbar-animated': animated,
401
401
  'searchbar-disabled': this.disabled,
@@ -405,14 +405,14 @@ const Searchbar = class {
405
405
  'searchbar-has-focus': this.focused,
406
406
  'searchbar-should-show-clear': this.shouldShowClearButton(),
407
407
  'searchbar-should-show-cancel': this.shouldShowCancelButton(),
408
- }) }, index.h("div", { key: 'add53640b2994cb6b2bf02792dafe51aba6b1684', class: "searchbar-input-container" }, index.h("input", Object.assign({ key: '160cc36459a4a652e7f41ccd14dcdc782278779e', "aria-label": "search text", disabled: this.disabled, ref: (el) => (this.nativeInput = el), class: "searchbar-input", inputMode: this.inputmode, enterKeyHint: this.enterkeyhint, name: this.name, onInput: this.onInput, onChange: this.onChange, onBlur: this.onBlur, onFocus: this.onFocus, minLength: this.minlength, maxLength: this.maxlength, placeholder: this.placeholder, type: this.type, value: this.getValue(), autoCapitalize: autocapitalize === 'default' ? undefined : autocapitalize, autoComplete: this.autocomplete, autoCorrect: this.autocorrect, spellcheck: this.spellcheck }, this.inheritedAttributes)), mode === 'md' && cancelButton, index.h("ion-icon", { key: '8825fd13af0d2dea451ccc0e00ae7b5021dc01c4', "aria-hidden": "true", mode: mode, icon: searchIcon, lazy: false, class: "searchbar-search-icon" }), index.h("button", { key: '8a7b56da278b9ca5c4f5a4ee9c01924fd5ae29d8', "aria-label": "reset", type: "button", "no-blur": true, class: "searchbar-clear-button", onPointerDown: (ev) => {
408
+ }) }, index.h("div", { key: '859da78b062b9059701eba166be3bc5417f7395b', class: "searchbar-input-container" }, index.h("input", Object.assign({ key: '840fd06a1e4b5a8d7f6d7439d02aef9fcb9720b6', "aria-label": "search text", disabled: this.disabled, ref: (el) => (this.nativeInput = el), class: "searchbar-input", inputMode: this.inputmode, enterKeyHint: this.enterkeyhint, name: this.name, onInput: this.onInput, onChange: this.onChange, onBlur: this.onBlur, onFocus: this.onFocus, minLength: this.minlength, maxLength: this.maxlength, placeholder: this.placeholder, type: this.type, value: this.getValue(), autoCapitalize: autocapitalize === 'default' ? undefined : autocapitalize, autoComplete: this.autocomplete, autoCorrect: this.autocorrect, spellcheck: this.spellcheck }, this.inheritedAttributes)), mode === 'md' && cancelButton, index.h("ion-icon", { key: 'a5aaa6a2204d38433cac283dd47b6197499fa585', "aria-hidden": "true", mode: mode, icon: searchIcon, lazy: false, class: "searchbar-search-icon" }), index.h("button", { key: 'cf62c8969c5c916ac838e122a0ad45b2ae234791', "aria-label": "reset", type: "button", "no-blur": true, class: "searchbar-clear-button", onPointerDown: (ev) => {
409
409
  /**
410
410
  * This prevents mobile browsers from
411
411
  * blurring the input when the clear
412
412
  * button is activated.
413
413
  */
414
414
  ev.preventDefault();
415
- }, onClick: () => this.onClearInput(true) }, index.h("ion-icon", { key: '24c55274516ab012d8c25f03525c6cdb9409e52f', "aria-hidden": "true", mode: mode, icon: clearIcon, lazy: false, class: "searchbar-clear-icon" }))), mode === 'ios' && cancelButton));
415
+ }, onClick: () => this.onClearInput(true) }, index.h("ion-icon", { key: 'b1a75f0964fc2119479e246dc27bef7cc33bd19f', "aria-hidden": "true", mode: mode, icon: clearIcon, lazy: false, class: "searchbar-clear-icon" }))), mode === 'ios' && cancelButton));
416
416
  }
417
417
  get el() { return index.getElement(this); }
418
418
  static get watchers() { return {