@ionic/core 8.7.18-dev.11771365900.125bf49c → 8.7.18-dev.11772217280.122eed89

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 (90) hide show
  1. package/components/ion-app.js +17 -12
  2. package/components/ion-checkbox.js +1 -1
  3. package/components/ion-content.js +1 -1
  4. package/components/ion-datetime.js +7 -4
  5. package/components/ion-fab-list.js +10 -3
  6. package/components/ion-img.js +11 -2
  7. package/components/ion-label.js +1 -1
  8. package/components/ion-modal.js +1 -1
  9. package/components/ion-popover.js +1 -1
  10. package/components/ion-radio-group.js +1 -1
  11. package/components/ion-searchbar.js +17 -6
  12. package/components/ion-select-modal.js +1 -1
  13. package/components/ion-select-popover.js +1 -1
  14. package/components/ion-select.js +8 -8
  15. package/components/{p-CJrGvZ1Z.js → p-51c2oSfC.js} +3 -3
  16. package/components/{p-BnpDIASH.js → p-BANeLH_G.js} +2 -2
  17. package/components/{p-D3KEyQ8e.js → p-BRLve2EP.js} +3 -3
  18. package/components/{p-DRHwNkVo.js → p-C6dkQWq2.js} +152 -30
  19. package/components/{p-CUfY9DTD.js → p-Cdm3dKWf.js} +8 -3
  20. package/components/{p-DyFBFrV7.js → p-D--b4514.js} +4 -4
  21. package/components/{p-DPm73mCi.js → p-DGOrLIyS.js} +7 -3
  22. package/components/{p-IHgAGzvW.js → p-iyo0Toaz.js} +326 -17
  23. package/dist/cjs/ion-app_8.cjs.entry.js +22 -13
  24. package/dist/cjs/ion-checkbox.cjs.entry.js +3 -3
  25. package/dist/cjs/ion-datetime_3.cjs.entry.js +6 -3
  26. package/dist/cjs/ion-fab_3.cjs.entry.js +10 -3
  27. package/dist/cjs/ion-img.cjs.entry.js +11 -2
  28. package/dist/cjs/ion-item_8.cjs.entry.js +8 -3
  29. package/dist/cjs/ion-modal.cjs.entry.js +326 -17
  30. package/dist/cjs/ion-popover.cjs.entry.js +152 -30
  31. package/dist/cjs/ion-radio_2.cjs.entry.js +2 -2
  32. package/dist/cjs/ion-searchbar.cjs.entry.js +17 -6
  33. package/dist/collection/components/app/app.js +15 -10
  34. package/dist/collection/components/checkbox/checkbox.js +4 -4
  35. package/dist/collection/components/content/content.js +7 -3
  36. package/dist/collection/components/datetime/datetime.js +6 -3
  37. package/dist/collection/components/fab-list/fab-list.js +10 -3
  38. package/dist/collection/components/img/img.js +11 -2
  39. package/dist/collection/components/label/label.js +8 -3
  40. package/dist/collection/components/modal/modal.ios.css +6 -5
  41. package/dist/collection/components/modal/modal.js +164 -15
  42. package/dist/collection/components/modal/modal.md.css +6 -5
  43. package/dist/collection/components/modal/safe-area-utils.js +165 -0
  44. package/dist/collection/components/popover/animations/ios.enter.js +33 -8
  45. package/dist/collection/components/popover/animations/md.enter.js +26 -6
  46. package/dist/collection/components/popover/utils.js +96 -19
  47. package/dist/collection/components/radio-group/radio-group.ios.css +0 -4
  48. package/dist/collection/components/radio-group/radio-group.md.css +0 -4
  49. package/dist/collection/components/searchbar/searchbar.js +17 -6
  50. package/dist/docs.json +1 -1
  51. package/dist/esm/ion-app_8.entry.js +24 -15
  52. package/dist/esm/ion-checkbox.entry.js +4 -4
  53. package/dist/esm/ion-datetime_3.entry.js +6 -3
  54. package/dist/esm/ion-fab_3.entry.js +10 -3
  55. package/dist/esm/ion-img.entry.js +11 -2
  56. package/dist/esm/ion-item_8.entry.js +8 -3
  57. package/dist/esm/ion-modal.entry.js +326 -17
  58. package/dist/esm/ion-popover.entry.js +152 -30
  59. package/dist/esm/ion-radio_2.entry.js +2 -2
  60. package/dist/esm/ion-searchbar.entry.js +17 -6
  61. package/dist/ionic/ionic.esm.js +1 -1
  62. package/dist/ionic/{p-72af946b.entry.js → p-2ac254dc.entry.js} +1 -1
  63. package/dist/ionic/p-7cad388a.entry.js +4 -0
  64. package/dist/ionic/p-7d218555.entry.js +4 -0
  65. package/dist/ionic/p-8303e3fc.entry.js +4 -0
  66. package/dist/ionic/p-915b3e3d.entry.js +4 -0
  67. package/dist/ionic/{p-eaf3f5ff.entry.js → p-b0ebf9f8.entry.js} +1 -1
  68. package/dist/ionic/p-e4c57008.entry.js +4 -0
  69. package/dist/ionic/p-e5d6d0f0.entry.js +4 -0
  70. package/dist/ionic/p-f1dfee25.entry.js +4 -0
  71. package/dist/ionic/{p-6444c606.entry.js → p-f9f7b9db.entry.js} +1 -1
  72. package/dist/types/components/app/app.d.ts +3 -0
  73. package/dist/types/components/datetime/datetime.d.ts +1 -0
  74. package/dist/types/components/fab-list/fab-list.d.ts +2 -0
  75. package/dist/types/components/img/img.d.ts +2 -0
  76. package/dist/types/components/label/label.d.ts +2 -0
  77. package/dist/types/components/modal/modal.d.ts +35 -0
  78. package/dist/types/components/modal/safe-area-utils.d.ts +66 -0
  79. package/dist/types/components/popover/utils.d.ts +23 -1
  80. package/dist/types/components/searchbar/searchbar.d.ts +3 -0
  81. package/hydrate/index.js +556 -73
  82. package/hydrate/index.mjs +556 -73
  83. package/package.json +1 -1
  84. package/dist/ionic/p-01e27965.entry.js +0 -4
  85. package/dist/ionic/p-3ec563c1.entry.js +0 -4
  86. package/dist/ionic/p-42db6404.entry.js +0 -4
  87. package/dist/ionic/p-46d74291.entry.js +0 -4
  88. package/dist/ionic/p-bc293244.entry.js +0 -4
  89. package/dist/ionic/p-d1f54e28.entry.js +0 -4
  90. package/dist/ionic/p-e338f669.entry.js +0 -4
@@ -16,6 +16,58 @@ require('./index-DkNv4J_i.js');
16
16
  require('./hardware-back-button-VCK4V3mG.js');
17
17
  require('./gesture-controller-dtqlP_q4.js');
18
18
 
19
+ /**
20
+ * Shared per-frame cache for safe-area insets. Avoids creating a temporary
21
+ * DOM element and forcing a synchronous reflow on every call within the same
22
+ * frame (e.g., multiple popovers presenting simultaneously). Invalidated
23
+ * after the next animation frame so values stay fresh across orientation
24
+ * changes and viewport resizes.
25
+ */
26
+ let cachedInsets = null;
27
+ let cacheInvalidationScheduled = false;
28
+ /**
29
+ * Resolves the current --ion-safe-area-* CSS custom property values
30
+ * to actual pixel numbers. A temporary element is needed because
31
+ * getComputedStyle on :root returns the declared value of custom
32
+ * properties (e.g. "env(safe-area-inset-top)") rather than a
33
+ * resolved number. By assigning them to standard padding properties,
34
+ * the browser resolves the value.
35
+ *
36
+ * Results are cached for the current frame to avoid repeated reflows.
37
+ */
38
+ const getSafeAreaInsets = (doc) => {
39
+ if (cachedInsets !== null) {
40
+ return cachedInsets;
41
+ }
42
+ if (doc.body === null) {
43
+ return { top: 0, bottom: 0, left: 0, right: 0 };
44
+ }
45
+ const el = doc.createElement('div');
46
+ el.style.cssText =
47
+ 'position:fixed;visibility:hidden;pointer-events:none;top:0;left:0;' +
48
+ 'padding-top:var(--ion-safe-area-top,0px);' +
49
+ 'padding-bottom:var(--ion-safe-area-bottom,0px);' +
50
+ 'padding-left:var(--ion-safe-area-left,0px);' +
51
+ 'padding-right:var(--ion-safe-area-right,0px);';
52
+ doc.body.appendChild(el);
53
+ const style = getComputedStyle(el);
54
+ const insets = {
55
+ top: parseFloat(style.paddingTop) || 0,
56
+ bottom: parseFloat(style.paddingBottom) || 0,
57
+ left: parseFloat(style.paddingLeft) || 0,
58
+ right: parseFloat(style.paddingRight) || 0,
59
+ };
60
+ el.remove();
61
+ cachedInsets = insets;
62
+ if (!cacheInvalidationScheduled) {
63
+ cacheInvalidationScheduled = true;
64
+ helpers.raf(() => {
65
+ cachedInsets = null;
66
+ cacheInvalidationScheduled = false;
67
+ });
68
+ }
69
+ return insets;
70
+ };
19
71
  /**
20
72
  * Returns the dimensions of the popover
21
73
  * arrow on `ios` mode. If arrow is disabled
@@ -651,8 +703,10 @@ const calculatePopoverCenterAlign = (side, triggerBoundingBox, contentWidth, con
651
703
  * Adjusts popover positioning coordinates
652
704
  * such that popover does not appear offscreen
653
705
  * or overlapping safe area bounds.
706
+ *
707
+ * @internal - This is not part of the public API.
654
708
  */
655
- const calculateWindowAdjustment = (side, coordTop, coordLeft, bodyPadding, bodyWidth, bodyHeight, contentWidth, contentHeight, safeAreaMargin, contentOriginX, contentOriginY, triggerCoordinates, coordArrowTop = 0, coordArrowLeft = 0, arrowHeight = 0) => {
709
+ const calculateWindowAdjustment = (side, coordTop, coordLeft, bodyPadding, bodyWidth, bodyHeight, contentWidth, contentHeight, safeArea, contentOriginX, contentOriginY, triggerCoordinates, coordArrowTop = 0, coordArrowLeft = 0, arrowHeight = 0) => {
656
710
  let arrowTop = coordArrowTop;
657
711
  const arrowLeft = coordArrowLeft;
658
712
  let left = coordLeft;
@@ -662,16 +716,19 @@ const calculateWindowAdjustment = (side, coordTop, coordLeft, bodyPadding, bodyW
662
716
  let originY = contentOriginY;
663
717
  let checkSafeAreaLeft = false;
664
718
  let checkSafeAreaRight = false;
719
+ let checkSafeAreaTop = false;
720
+ let checkSafeAreaBottom = false;
665
721
  const triggerTop = triggerCoordinates
666
722
  ? triggerCoordinates.top + triggerCoordinates.height
667
723
  : bodyHeight / 2 - contentHeight / 2;
668
724
  const triggerHeight = triggerCoordinates ? triggerCoordinates.height : 0;
669
725
  let addPopoverBottomClass = false;
726
+ const hideArrow = false;
670
727
  /**
671
728
  * Adjust popover so it does not
672
729
  * go off the left of the screen.
673
730
  */
674
- if (left < bodyPadding + safeAreaMargin) {
731
+ if (left < bodyPadding + safeArea.left) {
675
732
  left = bodyPadding;
676
733
  checkSafeAreaLeft = true;
677
734
  originX = 'left';
@@ -680,41 +737,58 @@ const calculateWindowAdjustment = (side, coordTop, coordLeft, bodyPadding, bodyW
680
737
  * go off the right of the screen.
681
738
  */
682
739
  }
683
- else if (contentWidth + bodyPadding + left + safeAreaMargin > bodyWidth) {
740
+ else if (contentWidth + bodyPadding + left + safeArea.right > bodyWidth) {
684
741
  checkSafeAreaRight = true;
685
742
  left = bodyWidth - contentWidth - bodyPadding;
686
743
  originX = 'right';
687
744
  }
688
745
  /**
689
746
  * Adjust popover so it does not
690
- * go off the top of the screen.
747
+ * go off the bottom of the screen
748
+ * or overlap the bottom safe area.
691
749
  * If popover is on the left or the right of
692
750
  * the trigger, then we should not adjust top
693
751
  * margins.
694
752
  */
695
- if (triggerTop + triggerHeight + contentHeight > bodyHeight && (side === 'top' || side === 'bottom')) {
696
- if (triggerTop - contentHeight > 0) {
753
+ if (triggerTop + triggerHeight + contentHeight > bodyHeight - safeArea.bottom &&
754
+ (side === 'top' || side === 'bottom')) {
755
+ /**
756
+ * Calculate where the popover top would be if flipped
757
+ * above the trigger. Check whether that position clears
758
+ * the top safe area with room for bodyPadding.
759
+ */
760
+ const idealFlipTop = triggerTop - contentHeight - triggerHeight - (arrowHeight - 1);
761
+ if (idealFlipTop >= safeArea.top + bodyPadding) {
697
762
  /**
698
- * While we strive to align the popover with the trigger
699
- * on smaller screens this is not always possible. As a result,
700
- * we adjust the popover up so that it does not hang
701
- * off the bottom of the screen. However, we do not want to move
702
- * the popover up so much that it goes off the top of the screen.
703
- *
704
- * We chose 12 here so that the popover position looks a bit nicer as
705
- * it is not right up against the edge of the screen.
763
+ * Popover fits above the trigger without overlapping
764
+ * the top safe area. Use the ideal position directly —
765
+ * no safe-area CSS vars needed since it clears both edges.
706
766
  */
707
- top = Math.max(12, triggerTop - contentHeight - triggerHeight - (arrowHeight - 1));
767
+ top = idealFlipTop;
708
768
  arrowTop = top + contentHeight;
709
769
  originY = 'bottom';
710
770
  addPopoverBottomClass = true;
711
- /**
712
- * If not enough room for popover to appear
713
- * above trigger, then cut it off.
714
- */
715
771
  }
716
772
  else {
773
+ /**
774
+ * Can't flip above the trigger. Constrain the bottom
775
+ * edge while keeping the top near the trigger. This
776
+ * creates a scrollable area anchored to the trigger.
777
+ */
717
778
  bottom = bodyPadding;
779
+ checkSafeAreaBottom = true;
780
+ /**
781
+ * When the trigger is near the bottom of the screen,
782
+ * the calculated top may be at or past the bottom
783
+ * constraint, leaving zero visible height. In that
784
+ * case, pull top up to the top safe area so the
785
+ * popover fills the available space between safe areas.
786
+ */
787
+ const bottomEdge = bodyHeight - safeArea.bottom - bodyPadding;
788
+ if (top >= bottomEdge) {
789
+ top = safeArea.top + bodyPadding;
790
+ checkSafeAreaTop = true;
791
+ }
718
792
  }
719
793
  }
720
794
  return {
@@ -725,9 +799,12 @@ const calculateWindowAdjustment = (side, coordTop, coordLeft, bodyPadding, bodyW
725
799
  originY,
726
800
  checkSafeAreaLeft,
727
801
  checkSafeAreaRight,
802
+ checkSafeAreaTop,
803
+ checkSafeAreaBottom,
728
804
  arrowTop,
729
805
  arrowLeft,
730
806
  addPopoverBottomClass,
807
+ hideArrow,
731
808
  };
732
809
  };
733
810
  const shouldShowArrow = (side, didAdjustBounds = false, ev, trigger) => {
@@ -757,6 +834,15 @@ const shouldShowArrow = (side, didAdjustBounds = false, ev, trigger) => {
757
834
  };
758
835
 
759
836
  const POPOVER_IOS_BODY_PADDING = 5;
837
+ /**
838
+ * Minimum edge margin for iOS popovers ensures visual spacing from screen
839
+ * edges on devices without safe areas (e.g., older iPhones without notches).
840
+ * Previously this was a hardcoded `safeAreaMargin = 25` that served dual
841
+ * purpose: safe-area avoidance AND visual spacing. Now that actual safe-area
842
+ * insets are read dynamically, this floor preserves the visual spacing when
843
+ * safe-area values are 0.
844
+ */
845
+ const POPOVER_IOS_MIN_EDGE_MARGIN = 25;
760
846
  /**
761
847
  * iOS Popover Enter Animation
762
848
  */
@@ -782,8 +868,16 @@ const iosEnterAnimation = (baseEl, opts) => {
782
868
  };
783
869
  const results = getPopoverPosition(isRTL, contentWidth, contentHeight, arrowWidth, arrowHeight, reference, side, align, defaultPosition, trigger, ev);
784
870
  const padding = size === 'cover' ? 0 : POPOVER_IOS_BODY_PADDING;
785
- const margin = size === 'cover' ? 0 : 25;
786
- const { originX, originY, top, left, bottom, checkSafeAreaLeft, checkSafeAreaRight, arrowTop, arrowLeft, addPopoverBottomClass, } = calculateWindowAdjustment(side, results.top, results.left, padding, bodyWidth, bodyHeight, contentWidth, contentHeight, margin, results.originX, results.originY, results.referenceCoordinates, results.arrowTop, results.arrowLeft, arrowHeight);
871
+ const rawSafeArea = getSafeAreaInsets(doc);
872
+ const safeArea = size === 'cover'
873
+ ? { top: 0, bottom: 0, left: 0, right: 0 }
874
+ : {
875
+ top: Math.max(rawSafeArea.top, POPOVER_IOS_MIN_EDGE_MARGIN),
876
+ bottom: Math.max(rawSafeArea.bottom, POPOVER_IOS_MIN_EDGE_MARGIN),
877
+ left: Math.max(rawSafeArea.left, POPOVER_IOS_MIN_EDGE_MARGIN),
878
+ right: Math.max(rawSafeArea.right, POPOVER_IOS_MIN_EDGE_MARGIN),
879
+ };
880
+ const { originX, originY, top, left, bottom, checkSafeAreaLeft, checkSafeAreaRight, checkSafeAreaTop, checkSafeAreaBottom, arrowTop, arrowLeft, addPopoverBottomClass} = calculateWindowAdjustment(side, results.top, results.left, padding, bodyWidth, bodyHeight, contentWidth, contentHeight, safeArea, results.originX, results.originY, results.referenceCoordinates, results.arrowTop, results.arrowLeft, arrowHeight);
787
881
  const baseAnimation = animation.createAnimation();
788
882
  const backdropAnimation = animation.createAnimation();
789
883
  const contentAnimation = animation.createAnimation();
@@ -814,10 +908,14 @@ const iosEnterAnimation = (baseEl, opts) => {
814
908
  baseEl.classList.add('popover-bottom');
815
909
  }
816
910
  if (bottom !== undefined) {
817
- contentEl.style.setProperty('bottom', `${bottom}px`);
911
+ let bottomValue = `${bottom}px`;
912
+ if (checkSafeAreaBottom) {
913
+ bottomValue = `${bottom}px + var(--ion-safe-area-bottom, 0px)`;
914
+ }
915
+ contentEl.style.setProperty('bottom', `calc(${bottomValue})`);
818
916
  }
819
- const safeAreaLeft = ' + var(--ion-safe-area-left, 0)';
820
- const safeAreaRight = ' - var(--ion-safe-area-right, 0)';
917
+ const safeAreaLeft = ' + var(--ion-safe-area-left, 0px)';
918
+ const safeAreaRight = ' - var(--ion-safe-area-right, 0px)';
821
919
  let leftValue = `${left}px`;
822
920
  if (checkSafeAreaLeft) {
823
921
  leftValue = `${left}px${safeAreaLeft}`;
@@ -825,7 +923,11 @@ const iosEnterAnimation = (baseEl, opts) => {
825
923
  if (checkSafeAreaRight) {
826
924
  leftValue = `${left}px${safeAreaRight}`;
827
925
  }
828
- contentEl.style.setProperty('top', `calc(${top}px + var(--offset-y, 0))`);
926
+ let topValue = `${top}px`;
927
+ if (checkSafeAreaTop) {
928
+ topValue = `${top}px + var(--ion-safe-area-top, 0px)`;
929
+ }
930
+ contentEl.style.setProperty('top', `calc(${topValue} + var(--offset-y, 0))`);
829
931
  contentEl.style.setProperty('left', `calc(${leftValue} + var(--offset-x, 0))`);
830
932
  contentEl.style.setProperty('transform-origin', `${originY} ${originX}`);
831
933
  if (arrowEl !== null) {
@@ -901,7 +1003,23 @@ const mdEnterAnimation = (baseEl, opts) => {
901
1003
  };
902
1004
  const results = getPopoverPosition(isRTL, contentWidth, contentHeight, 0, 0, reference, side, align, defaultPosition, trigger, ev);
903
1005
  const padding = size === 'cover' ? 0 : POPOVER_MD_BODY_PADDING;
904
- const { originX, originY, top, left, bottom } = calculateWindowAdjustment(side, results.top, results.left, padding, bodyWidth, bodyHeight, contentWidth, contentHeight, 0, results.originX, results.originY, results.referenceCoordinates);
1006
+ // MD mode now applies safe-area insets (previously passed 0, ignoring all safe areas).
1007
+ // This is needed for Android edge-to-edge (API 36+) where system bars overlap content.
1008
+ const safeArea = size === 'cover' ? { top: 0, bottom: 0, left: 0, right: 0 } : getSafeAreaInsets(doc);
1009
+ const { originX, originY, top, left, bottom, checkSafeAreaLeft, checkSafeAreaRight, checkSafeAreaTop, checkSafeAreaBottom, addPopoverBottomClass, } = calculateWindowAdjustment(side, results.top, results.left, padding, bodyWidth, bodyHeight, contentWidth, contentHeight, safeArea, results.originX, results.originY, results.referenceCoordinates);
1010
+ const safeAreaLeftCalc = ' + var(--ion-safe-area-left, 0px)';
1011
+ const safeAreaRightCalc = ' - var(--ion-safe-area-right, 0px)';
1012
+ let leftValue = `${left}px`;
1013
+ if (checkSafeAreaLeft) {
1014
+ leftValue = `${left}px${safeAreaLeftCalc}`;
1015
+ }
1016
+ if (checkSafeAreaRight) {
1017
+ leftValue = `${left}px${safeAreaRightCalc}`;
1018
+ }
1019
+ let topValue = `${top}px`;
1020
+ if (checkSafeAreaTop) {
1021
+ topValue = `${top}px + var(--ion-safe-area-top, 0px)`;
1022
+ }
905
1023
  const baseAnimation = animation.createAnimation();
906
1024
  const backdropAnimation = animation.createAnimation();
907
1025
  const wrapperAnimation = animation.createAnimation();
@@ -918,13 +1036,17 @@ const mdEnterAnimation = (baseEl, opts) => {
918
1036
  contentAnimation
919
1037
  .addElement(contentEl)
920
1038
  .beforeStyles({
921
- top: `calc(${top}px + var(--offset-y, 0px))`,
922
- left: `calc(${left}px + var(--offset-x, 0px))`,
1039
+ top: `calc(${topValue} + var(--offset-y, 0px))`,
1040
+ left: `calc(${leftValue} + var(--offset-x, 0px))`,
923
1041
  'transform-origin': `${originY} ${originX}`,
924
1042
  })
925
1043
  .beforeAddWrite(() => {
926
1044
  if (bottom !== undefined) {
927
- contentEl.style.setProperty('bottom', `${bottom}px`);
1045
+ let bottomValue = `${bottom}px`;
1046
+ if (checkSafeAreaBottom) {
1047
+ bottomValue = `${bottom}px + var(--ion-safe-area-bottom, 0px)`;
1048
+ }
1049
+ contentEl.style.setProperty('bottom', `calc(${bottomValue})`);
928
1050
  }
929
1051
  })
930
1052
  .fromTo('transform', 'scale(0.8)', 'scale(1)');
@@ -936,7 +1058,7 @@ const mdEnterAnimation = (baseEl, opts) => {
936
1058
  if (size === 'cover') {
937
1059
  baseEl.style.setProperty('--width', `${contentWidth}px`);
938
1060
  }
939
- if (originY === 'bottom') {
1061
+ if (addPopoverBottomClass) {
940
1062
  baseEl.classList.add('popover-bottom');
941
1063
  }
942
1064
  })
@@ -165,9 +165,9 @@ Radio.style = {
165
165
  md: radioMdCss
166
166
  };
167
167
 
168
- const radioGroupIosCss = "ion-radio-group{vertical-align:top}.radio-group-wrapper{display:inline}.radio-group-top{line-height:1.5}.radio-group-top .error-text{display:none;color:var(--ion-color-danger, #c5000f)}.radio-group-top .helper-text{display:block;color:var(--ion-color-step-700, var(--ion-text-color-step-300, #4d4d4d))}.ion-touched.ion-invalid .radio-group-top .error-text{display:block}.ion-touched.ion-invalid .radio-group-top .helper-text{display:none}ion-list .radio-group-top{-webkit-padding-start:16px;padding-inline-start:16px;-webkit-padding-end:16px;padding-inline-end:16px}";
168
+ const radioGroupIosCss = "ion-radio-group{vertical-align:top}.radio-group-top{line-height:1.5}.radio-group-top .error-text{display:none;color:var(--ion-color-danger, #c5000f)}.radio-group-top .helper-text{display:block;color:var(--ion-color-step-700, var(--ion-text-color-step-300, #4d4d4d))}.ion-touched.ion-invalid .radio-group-top .error-text{display:block}.ion-touched.ion-invalid .radio-group-top .helper-text{display:none}ion-list .radio-group-top{-webkit-padding-start:16px;padding-inline-start:16px;-webkit-padding-end:16px;padding-inline-end:16px}";
169
169
 
170
- const radioGroupMdCss = "ion-radio-group{vertical-align:top}.radio-group-wrapper{display:inline}.radio-group-top{line-height:1.5}.radio-group-top .error-text{display:none;color:var(--ion-color-danger, #c5000f)}.radio-group-top .helper-text{display:block;color:var(--ion-color-step-700, var(--ion-text-color-step-300, #4d4d4d))}.ion-touched.ion-invalid .radio-group-top .error-text{display:block}.ion-touched.ion-invalid .radio-group-top .helper-text{display:none}ion-list .radio-group-top{-webkit-padding-start:16px;padding-inline-start:16px;-webkit-padding-end:16px;padding-inline-end:16px}";
170
+ const radioGroupMdCss = "ion-radio-group{vertical-align:top}.radio-group-top{line-height:1.5}.radio-group-top .error-text{display:none;color:var(--ion-color-danger, #c5000f)}.radio-group-top .helper-text{display:block;color:var(--ion-color-step-700, var(--ion-text-color-step-300, #4d4d4d))}.ion-touched.ion-invalid .radio-group-top .error-text{display:block}.ion-touched.ion-invalid .radio-group-top .helper-text{display:none}ion-list .radio-group-top{-webkit-padding-start:16px;padding-inline-start:16px;-webkit-padding-end:16px;padding-inline-end:16px}";
171
171
 
172
172
  const RadioGroup = class {
173
173
  constructor(hostRef) {
@@ -107,11 +107,14 @@ const Searchbar = class {
107
107
  * Clears the input field and triggers the control change.
108
108
  */
109
109
  this.onClearInput = async (shouldFocus) => {
110
+ if (this.clearTimeout) {
111
+ clearTimeout(this.clearTimeout);
112
+ }
110
113
  this.ionClear.emit();
111
114
  return new Promise((resolve) => {
112
115
  // setTimeout() fixes https://github.com/ionic-team/ionic-framework/issues/7527
113
116
  // wait for 4 frames
114
- setTimeout(() => {
117
+ this.clearTimeout = setTimeout(() => {
115
118
  const value = this.getValue();
116
119
  if (value !== '') {
117
120
  this.value = '';
@@ -246,10 +249,18 @@ const Searchbar = class {
246
249
  this.originalIonInput = this.ionInput;
247
250
  this.positionElements();
248
251
  this.debounceChanged();
249
- setTimeout(() => {
252
+ this.loadTimeout = setTimeout(() => {
250
253
  this.noAnimate = false;
251
254
  }, 300);
252
255
  }
256
+ disconnectedCallback() {
257
+ if (this.loadTimeout) {
258
+ clearTimeout(this.loadTimeout);
259
+ }
260
+ if (this.clearTimeout) {
261
+ clearTimeout(this.clearTimeout);
262
+ }
263
+ }
253
264
  emitStyle() {
254
265
  this.ionStyle.emit({
255
266
  searchbar: true,
@@ -438,8 +449,8 @@ const Searchbar = class {
438
449
  const clearIcon = this.clearIcon || (mode === 'ios' ? index$1.closeCircle : index$1.closeSharp);
439
450
  const searchIcon = this.searchIcon || (mode === 'ios' ? index$1.searchOutline : index$1.searchSharp);
440
451
  const shouldShowCancelButton = this.shouldShowCancelButton();
441
- const cancelButton = this.showCancelButton !== 'never' && (index.h("button", { key: 'bf574336a561a5bf66c771fb606a9b19adbecb68', "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: '747ad4368bda5044198259d5bc8f7be9e472e5af', "aria-hidden": "true" }, mode === 'md' ? (index.h("ion-icon", { "aria-hidden": "true", mode: mode, icon: this.cancelButtonIcon, lazy: false })) : (cancelButtonText))));
442
- return (index.h(index.Host, { key: 'bcc5b33a2859903ba11bfc5c611c6a2aaa71d06b', role: "search", "aria-disabled": this.disabled ? 'true' : null, class: theme.createColorClasses(this.color, {
452
+ const cancelButton = this.showCancelButton !== 'never' && (index.h("button", { key: '9e9f3a7f6ee66140181f8c3cfa42bbeee75adafc', "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: '5f83fbcc0708830637815e8c0b69356cad381870', "aria-hidden": "true" }, mode === 'md' ? (index.h("ion-icon", { "aria-hidden": "true", mode: mode, icon: this.cancelButtonIcon, lazy: false })) : (cancelButtonText))));
453
+ return (index.h(index.Host, { key: '219c263c1f3cc63e18bc2d2ba5d672b7806eddd0', role: "search", "aria-disabled": this.disabled ? 'true' : null, class: theme.createColorClasses(this.color, {
443
454
  [mode]: true,
444
455
  'searchbar-animated': animated,
445
456
  'searchbar-disabled': this.disabled,
@@ -449,14 +460,14 @@ const Searchbar = class {
449
460
  'searchbar-has-focus': this.focused,
450
461
  'searchbar-should-show-clear': this.shouldShowClearButton(),
451
462
  'searchbar-should-show-cancel': this.shouldShowCancelButton(),
452
- }) }, index.h("div", { key: 'ac0dbf5ddd9c4eb6d714f45c62c44ba3d0bf034d', class: "searchbar-input-container" }, index.h("input", Object.assign({ key: '4b9fdac1b0d5a7a68bf61a6beb00f166434d3e84', "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: '32a889c48b3960560a3873061b4ac5a8a5ce532f', "aria-hidden": "true", mode: mode, icon: searchIcon, lazy: false, class: "searchbar-search-icon" }), index.h("button", { key: '71222df691ef0b95008e619f755224d069ee9388', "aria-label": "reset", type: "button", "no-blur": true, class: "searchbar-clear-button", onPointerDown: (ev) => {
463
+ }) }, index.h("div", { key: '3f1c25442d6d87c0df67f79482a925b80dcc403d', class: "searchbar-input-container" }, index.h("input", Object.assign({ key: 'a386fecafda0fab449ea1488906ba6cfc55e2b5c', "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: '5f81642628335def39a14f0726e0fafdc7203776', "aria-hidden": "true", mode: mode, icon: searchIcon, lazy: false, class: "searchbar-search-icon" }), index.h("button", { key: 'da3073646f2d7f639261b556517fbadabc3c10e9', "aria-label": "reset", type: "button", "no-blur": true, class: "searchbar-clear-button", onPointerDown: (ev) => {
453
464
  /**
454
465
  * This prevents mobile browsers from
455
466
  * blurring the input when the clear
456
467
  * button is activated.
457
468
  */
458
469
  ev.preventDefault();
459
- }, onClick: () => this.onClearInput(true) }, index.h("ion-icon", { key: 'f1c8c0bd8fbb655085875731d3ece387dbe2967e', "aria-hidden": "true", mode: mode, icon: clearIcon, lazy: false, class: "searchbar-clear-icon" }))), mode === 'ios' && cancelButton));
470
+ }, onClick: () => this.onClearInput(true) }, index.h("ion-icon", { key: '065cd6c707ae462c0767df1bab04ce8045f340a4', "aria-hidden": "true", mode: mode, icon: clearIcon, lazy: false, class: "searchbar-clear-icon" }))), mode === 'ios' && cancelButton));
460
471
  }
461
472
  get el() { return index.getElement(this); }
462
473
  static get watchers() { return {
@@ -10,7 +10,7 @@ import { getIonMode } from "../../global/ionic-global";
10
10
  export class App {
11
11
  componentDidLoad() {
12
12
  if (Build.isBrowser) {
13
- rIC(async () => {
13
+ this.rIC(async () => {
14
14
  const isHybrid = isPlatform(window, 'hybrid');
15
15
  if (!config.getBoolean('_testing')) {
16
16
  import('../../utils/tap-click').then((module) => module.startTapClick(config));
@@ -48,6 +48,11 @@ export class App {
48
48
  });
49
49
  }
50
50
  }
51
+ disconnectedCallback() {
52
+ if (this.loadTimeout) {
53
+ clearTimeout(this.loadTimeout);
54
+ }
55
+ }
51
56
  /**
52
57
  * Used to set focus on an element that uses `ion-focusable`.
53
58
  * Do not use this if focusing the element as a result of a keyboard
@@ -64,9 +69,17 @@ export class App {
64
69
  this.focusVisible.setFocus(elements);
65
70
  }
66
71
  }
72
+ rIC(callback) {
73
+ if ('requestIdleCallback' in window) {
74
+ window.requestIdleCallback(callback);
75
+ }
76
+ else {
77
+ this.loadTimeout = setTimeout(callback, 32);
78
+ }
79
+ }
67
80
  render() {
68
81
  const mode = getIonMode(this);
69
- return (h(Host, { key: '9be440c65819e4fa67c2c3c6477ab40b3ad3eed3', class: {
82
+ return (h(Host, { key: '77a7459d3e42b3715a5a7e1cf468e2ebc99e54bb', class: {
70
83
  [mode]: true,
71
84
  'ion-page': true,
72
85
  'force-statusbar-padding': config.getBoolean('_forceStatusbarPadding'),
@@ -136,11 +149,3 @@ const needInputShims = () => {
136
149
  }
137
150
  return false;
138
151
  };
139
- const rIC = (callback) => {
140
- if ('requestIdleCallback' in window) {
141
- window.requestIdleCallback(callback);
142
- }
143
- else {
144
- setTimeout(callback, 32);
145
- }
146
- };
@@ -1,7 +1,7 @@
1
1
  /*!
2
2
  * (C) Ionic http://ionicframework.com - MIT License
3
3
  */
4
- import { Build, Host, h } from "@stencil/core";
4
+ import { Build, Host, h, forceUpdate } from "@stencil/core";
5
5
  import { checkInvalidState } from "../../utils/forms/index";
6
6
  import { inheritAriaAttributes, renderHiddenInput } from "../../utils/helpers";
7
7
  import { createColorClasses, hostContext } from "../../utils/theme";
@@ -201,7 +201,7 @@ export class Checkbox {
201
201
  renderHiddenInput(true, el, name, checked ? value : '', disabled);
202
202
  // The host element must have a checkbox role to ensure proper VoiceOver
203
203
  // support in Safari for accessibility.
204
- return (h(Host, { key: 'ae0fbd4b21accbac132e6b85c513512ad9179394', role: "checkbox", "aria-checked": indeterminate ? 'mixed' : `${checked}`, "aria-describedby": this.hintTextId, "aria-invalid": this.isInvalid ? 'true' : undefined, "aria-labelledby": hasLabelContent ? this.inputLabelId : null, "aria-label": inheritedAttributes['aria-label'] || null, "aria-disabled": disabled ? 'true' : null, "aria-required": required ? 'true' : undefined, tabindex: disabled ? undefined : 0, onKeyDown: this.onKeyDown, onFocus: this.onFocus, onBlur: this.onBlur, onClick: this.onClick, class: createColorClasses(color, {
204
+ return (h(Host, { key: 'fca2e80987d5e2e7f9138eeac3314d883ea1252b', role: "checkbox", "aria-checked": indeterminate ? 'mixed' : `${checked}`, "aria-describedby": this.hintTextId, "aria-invalid": this.isInvalid ? 'true' : undefined, "aria-labelledby": hasLabelContent ? this.inputLabelId : null, "aria-label": inheritedAttributes['aria-label'] || null, "aria-disabled": disabled ? 'true' : null, "aria-required": required ? 'true' : undefined, tabindex: disabled ? undefined : 0, onKeyDown: this.onKeyDown, onFocus: this.onFocus, onBlur: this.onBlur, onClick: this.onClick, class: createColorClasses(color, {
205
205
  [mode]: true,
206
206
  'in-item': hostContext('ion-item', el),
207
207
  'checkbox-checked': checked,
@@ -211,10 +211,10 @@ export class Checkbox {
211
211
  [`checkbox-justify-${justify}`]: justify !== undefined,
212
212
  [`checkbox-alignment-${alignment}`]: alignment !== undefined,
213
213
  [`checkbox-label-placement-${labelPlacement}`]: true,
214
- }) }, h("label", { key: '7a3d7f3c27dde514f2dbf2e34f4629fad33ec3bf', class: "checkbox-wrapper", htmlFor: inputId }, h("input", Object.assign({ key: '4130d77ddf034271fecccda14e101a5a809921b6', type: "checkbox", checked: checked ? true : undefined, disabled: disabled, id: inputId, onChange: this.toggleChecked, required: required }, inheritedAttributes)), h("div", { key: '5daa74f4e62b0947e37764762524001ee42609d9', class: {
214
+ }) }, h("label", { key: '5cf50b6abdb9a3ae1f846f18bd8d5345de0ea2e9', class: "checkbox-wrapper", htmlFor: inputId }, h("input", Object.assign({ key: 'b8c3c6b01f65ff1b723c0de4b5d6f1b62940f481', type: "checkbox", checked: checked ? true : undefined, disabled: disabled, id: inputId, onChange: this.toggleChecked, required: required }, inheritedAttributes)), h("div", { key: '7b29b18b97b8151f454760d73aca1543c8325e41', class: {
215
215
  'label-text-wrapper': true,
216
216
  'label-text-wrapper-hidden': !hasLabelContent,
217
- }, part: "label", id: this.inputLabelId, onClick: this.onDivLabelClick }, h("slot", { key: '23ff66138f8c3a2f56f39113fc842d54b2f7952a' }), this.renderHintText()), h("div", { key: 'ab914d9623c19fc46821d5e62db92f1192ebbe7e', class: "native-wrapper" }, h("svg", { key: '66e3f4f5dcaa9756fb0e9452299954f9ed3dcb7b', class: "checkbox-icon", viewBox: "0 0 24 24", part: "container", "aria-hidden": "true" }, path)))));
217
+ }, part: "label", id: this.inputLabelId, onClick: this.onDivLabelClick }, h("slot", { key: 'ffbce8010274a68b8e67f9c0f19ec012944df484', onSlotchange: () => forceUpdate(this) }), this.renderHintText()), h("div", { key: '8e21fb89c72fbd52df70fa1916367ab5a543f281', class: "native-wrapper" }, h("svg", { key: 'c2519ff1a5c7f69e03ab26197ccfa8718b0951c6', class: "checkbox-icon", viewBox: "0 0 24 24", part: "container", "aria-hidden": "true" }, path)))));
218
218
  }
219
219
  getSVGPath(mode, indeterminate) {
220
220
  let path = indeterminate ? (h("path", { d: "M6 12L18 12", part: "mark" })) : (h("path", { d: "M5.9,12.5l3.8,3.8l8.8-8.8", part: "mark" }));
@@ -131,6 +131,10 @@ export class Content {
131
131
  this.tabsElement = null;
132
132
  this.tabsLoadCallback = undefined;
133
133
  }
134
+ if (this.resizeTimeout) {
135
+ clearTimeout(this.resizeTimeout);
136
+ this.resizeTimeout = null;
137
+ }
134
138
  }
135
139
  /**
136
140
  * Rotating certain devices can update
@@ -369,7 +373,7 @@ export class Content {
369
373
  const forceOverscroll = this.shouldForceOverscroll();
370
374
  const transitionShadow = mode === 'ios';
371
375
  this.resize();
372
- return (h(Host, Object.assign({ key: 'cd8781f848d8dc926fe66f43d43c49564425a507', role: isMainContent ? 'main' : undefined, class: createColorClasses(this.color, {
376
+ return (h(Host, Object.assign({ key: '287f5bb78888b0f66394a35ba2a3ad636d8f3412', role: isMainContent ? 'main' : undefined, class: createColorClasses(this.color, {
373
377
  [mode]: true,
374
378
  'content-sizing': hostContext('ion-popover', this.el),
375
379
  overscroll: forceOverscroll,
@@ -377,12 +381,12 @@ export class Content {
377
381
  }), style: {
378
382
  '--offset-top': `${this.cTop}px`,
379
383
  '--offset-bottom': `${this.cBottom}px`,
380
- } }, inheritedAttributes), h("div", { key: '95b112d7cae30f22ef778ceffb88edb4d941c170', ref: (el) => (this.backgroundContentEl = el), id: "background-content", part: "background" }), fixedSlotPlacement === 'before' ? h("slot", { name: "fixed" }) : null, h("div", { key: '2fdfcbc39fb66f11b6191911f2941c660f4c12e5', class: {
384
+ } }, inheritedAttributes), h("div", { key: '2e70e5e0ab4209478ea711a630058fa0e83af7f8', ref: (el) => (this.backgroundContentEl = el), id: "background-content", part: "background" }), fixedSlotPlacement === 'before' ? h("slot", { name: "fixed" }) : null, h("div", { key: '018a83406173ec7f7725f09b4bd1c0d964874ee4', class: {
381
385
  'inner-scroll': true,
382
386
  'scroll-x': scrollX,
383
387
  'scroll-y': scrollY,
384
388
  overscroll: (scrollX || scrollY) && forceOverscroll,
385
- }, ref: (scrollEl) => (this.scrollEl = scrollEl), onScroll: this.scrollEvents ? (ev) => this.onScroll(ev) : undefined, part: "scroll" }, h("slot", { key: '6bc77e0054ec8e21635a7f2abfe0ca46e0962e03' })), transitionShadow ? (h("div", { class: "transition-effect" }, h("div", { class: "transition-cover" }), h("div", { class: "transition-shadow" }))) : null, fixedSlotPlacement === 'after' ? h("slot", { name: "fixed" }) : null));
389
+ }, ref: (scrollEl) => (this.scrollEl = scrollEl), onScroll: this.scrollEvents ? (ev) => this.onScroll(ev) : undefined, part: "scroll" }, h("slot", { key: '6f028eeb7c9dc7e1b82a8e549e1dd8aaf1d15fca' })), transitionShadow ? (h("div", { class: "transition-effect" }, h("div", { class: "transition-cover" }), h("div", { class: "transition-shadow" }))) : null, fixedSlotPlacement === 'after' ? h("slot", { name: "fixed" }) : null));
386
390
  }
387
391
  static get is() { return "ion-content"; }
388
392
  static get encapsulation() { return "shadow"; }
@@ -884,6 +884,9 @@ export class Datetime {
884
884
  this.clearFocusVisible();
885
885
  this.clearFocusVisible = undefined;
886
886
  }
887
+ if (this.loadTimeout) {
888
+ clearTimeout(this.loadTimeout);
889
+ }
887
890
  }
888
891
  initializeListeners() {
889
892
  this.initializeCalendarListener();
@@ -932,7 +935,7 @@ export class Datetime {
932
935
  *
933
936
  * We schedule this after everything has had a chance to run.
934
937
  */
935
- setTimeout(() => {
938
+ this.loadTimeout = setTimeout(() => {
936
939
  this.ensureReadyIfVisible();
937
940
  }, 100);
938
941
  /**
@@ -1690,7 +1693,7 @@ export class Datetime {
1690
1693
  const hasDatePresentation = presentation === 'date' || presentation === 'date-time' || presentation === 'time-date';
1691
1694
  const hasWheelVariant = hasDatePresentation && preferWheel;
1692
1695
  renderHiddenInput(true, el, name, formatValue(value), disabled);
1693
- return (h(Host, { key: 'efdbc0922670a841bc667ceac392cdc1dedffd01', "aria-disabled": disabled ? 'true' : null, onFocus: this.onFocus, onBlur: this.onBlur, class: Object.assign({}, createColorClasses(color, {
1696
+ return (h(Host, { key: 'b81c3531f4654be3f6948df5f790c0f6ab488000', "aria-disabled": disabled ? 'true' : null, onFocus: this.onFocus, onBlur: this.onBlur, class: Object.assign({}, createColorClasses(color, {
1694
1697
  [mode]: true,
1695
1698
  ['datetime-readonly']: readonly,
1696
1699
  ['datetime-disabled']: disabled,
@@ -1700,7 +1703,7 @@ export class Datetime {
1700
1703
  [`datetime-size-${size}`]: true,
1701
1704
  [`datetime-prefer-wheel`]: hasWheelVariant,
1702
1705
  [`datetime-grid`]: isGridStyle,
1703
- })) }, h("div", { key: '3f8bb75fcb0baff55182ef3aa1b535eacc58d81f', class: "intersection-tracker", ref: (el) => (this.intersectionTrackerRef = el) }), this.renderDatetime(mode)));
1706
+ })) }, h("div", { key: 'f7f39f8e56146186210d0ab70ae95e829573e549', class: "intersection-tracker", ref: (el) => (this.intersectionTrackerRef = el) }), this.renderDatetime(mode)));
1704
1707
  }
1705
1708
  static get is() { return "ion-datetime"; }
1706
1709
  static get encapsulation() { return "shadow"; }
@@ -5,6 +5,7 @@ import { Host, h } from "@stencil/core";
5
5
  import { getIonMode } from "../../global/ionic-global";
6
6
  export class FabList {
7
7
  constructor() {
8
+ this.activateTimeouts = [];
8
9
  /**
9
10
  * If `true`, the fab list will show all fab buttons in the list.
10
11
  */
@@ -15,20 +16,26 @@ export class FabList {
15
16
  this.side = 'bottom';
16
17
  }
17
18
  activatedChanged(activated) {
19
+ this.activateTimeouts.forEach(clearTimeout);
20
+ this.activateTimeouts = [];
18
21
  const fabs = Array.from(this.el.querySelectorAll('ion-fab-button'));
19
22
  // if showing the fabs add a timeout, else show immediately
20
23
  const timeout = activated ? 30 : 0;
21
24
  fabs.forEach((fab, i) => {
22
- setTimeout(() => (fab.show = activated), i * timeout);
25
+ this.activateTimeouts.push(setTimeout(() => (fab.show = activated), i * timeout));
23
26
  });
24
27
  }
28
+ disconnectedCallback() {
29
+ this.activateTimeouts.forEach(clearTimeout);
30
+ this.activateTimeouts = [];
31
+ }
25
32
  render() {
26
33
  const mode = getIonMode(this);
27
- return (h(Host, { key: '64b33366447f66c7f979cfac56307fbb1a6fac1c', class: {
34
+ return (h(Host, { key: '03a8920c29a93c6df4bab14753a78a8a30722ce8', class: {
28
35
  [mode]: true,
29
36
  'fab-list-active': this.activated,
30
37
  [`fab-list-side-${this.side}`]: true,
31
- } }, h("slot", { key: 'd9f474f7f20fd7e813db358fddc720534ca05bb6' })));
38
+ } }, h("slot", { key: 'd9e1541cb044e209bc2dff14080f3b938a2b84ae' })));
32
39
  }
33
40
  static get is() { return "ion-fab-list"; }
34
41
  static get encapsulation() { return "shadow"; }
@@ -26,7 +26,16 @@ export class Img {
26
26
  componentDidLoad() {
27
27
  this.addIO();
28
28
  }
29
+ disconnectedCallback() {
30
+ if (this.loadTimeout) {
31
+ clearTimeout(this.loadTimeout);
32
+ }
33
+ }
29
34
  addIO() {
35
+ if (this.loadTimeout) {
36
+ clearTimeout(this.loadTimeout);
37
+ this.loadTimeout = undefined;
38
+ }
30
39
  if (this.src === undefined) {
31
40
  return;
32
41
  }
@@ -50,7 +59,7 @@ export class Img {
50
59
  }
51
60
  else {
52
61
  // fall back to setTimeout for Safari and IE
53
- setTimeout(() => this.load(), 200);
62
+ this.loadTimeout = setTimeout(() => this.load(), 200);
54
63
  }
55
64
  }
56
65
  load() {
@@ -67,7 +76,7 @@ export class Img {
67
76
  render() {
68
77
  const { loadSrc, alt, onLoad, loadError, inheritedAttributes } = this;
69
78
  const { draggable } = inheritedAttributes;
70
- return (h(Host, { key: 'da600442894427dee1974a28e545613afac69fca', class: getIonMode(this) }, h("img", { key: '16df0c7069af86c0fa7ce5af598bc0f63b4eb71a', decoding: "async", src: loadSrc, alt: alt, onLoad: onLoad, onError: loadError, part: "image", draggable: isDraggable(draggable) })));
79
+ return (h(Host, { key: '9bfaa659b0680189fd29f3acb2fdce7f70be6060', class: getIonMode(this) }, h("img", { key: 'd484beae0d5cbe0406958bfad40cdd6c05625340', decoding: "async", src: loadSrc, alt: alt, onLoad: onLoad, onError: loadError, part: "image", draggable: isDraggable(draggable) })));
71
80
  }
72
81
  static get is() { return "ion-img"; }
73
82
  static get encapsulation() { return "shadow"; }