notch-controller-09b7f358.js 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. /*!
  2. * (C) Ionic http://ionicframework.com - MIT License
  3. */
  4. 'use strict';
  5. const index = require('./index-c8d52405.js');
  6. const helpers = require('./helpers-8a48fdea.js');
  7. /**
  8. * A utility to calculate the size of an outline notch
  9. * width relative to the content passed. This is used in
  10. * components such as `ion-select` with `fill="outline"`
  11. * where we need to pass slotted HTML content. This is not
  12. * needed when rendering plaintext content because we can
  13. * render the plaintext again hidden with `opacity: 0` inside
  14. * of the notch. As a result we can rely on the intrinsic size
  15. * of the element to correctly compute the notch width. We
  16. * cannot do this with slotted content because we cannot project
  17. * it into 2 places at once.
  18. *
  19. * @internal
  20. * @param el: The host element
  21. * @param getNotchSpacerEl: A function that returns a reference to the notch spacer element inside of the component template.
  22. * @param getLabelSlot: A function that returns a reference to the slotted content.
  23. */
  24. const createNotchController = (el, getNotchSpacerEl, getLabelSlot) => {
  25. let notchVisibilityIO;
  26. const needsExplicitNotchWidth = () => {
  27. const notchSpacerEl = getNotchSpacerEl();
  28. if (
  29. /**
  30. * If the notch is not being used
  31. * then we do not need to set the notch width.
  32. */
  33. notchSpacerEl === undefined ||
  34. /**
  35. * If either the label property is being
  36. * used or the label slot is not defined,
  37. * then we do not need to estimate the notch width.
  38. */
  39. el.label !== undefined ||
  40. getLabelSlot() === null) {
  41. return false;
  42. }
  43. return true;
  44. };
  45. const calculateNotchWidth = () => {
  46. if (needsExplicitNotchWidth()) {
  47. /**
  48. * Run this the frame after
  49. * the browser has re-painted the host element.
  50. * Otherwise, the label element may have a width
  51. * of 0 and the IntersectionObserver will be used.
  52. */
  53. helpers.raf(() => {
  54. setNotchWidth();
  55. });
  56. }
  57. };
  58. /**
  59. * When using a label prop we can render
  60. * the label value inside of the notch and
  61. * let the browser calculate the size of the notch.
  62. * However, we cannot render the label slot in multiple
  63. * places so we need to manually calculate the notch dimension
  64. * based on the size of the slotted content.
  65. *
  66. * This function should only be used to set the notch width
  67. * on slotted label content. The notch width for label prop
  68. * content is automatically calculated based on the
  69. * intrinsic size of the label text.
  70. */
  71. const setNotchWidth = () => {
  72. const notchSpacerEl = getNotchSpacerEl();
  73. if (notchSpacerEl === undefined) {
  74. return;
  75. }
  76. if (!needsExplicitNotchWidth()) {
  77. notchSpacerEl.style.removeProperty('width');
  78. return;
  79. }
  80. const width = getLabelSlot().scrollWidth;
  81. if (
  82. /**
  83. * If the computed width of the label is 0
  84. * and notchSpacerEl's offsetParent is null
  85. * then that means the element is hidden.
  86. * As a result, we need to wait for the element
  87. * to become visible before setting the notch width.
  88. *
  89. * We do not check el.offsetParent because
  90. * that can be null if the host element has
  91. * position: fixed applied to it.
  92. * notchSpacerEl does not have position: fixed.
  93. */
  94. width === 0 &&
  95. notchSpacerEl.offsetParent === null &&
  96. index.win !== undefined &&
  97. 'IntersectionObserver' in index.win) {
  98. /**
  99. * If there is an IO already attached
  100. * then that will update the notch
  101. * once the element becomes visible.
  102. * As a result, there is no need to create
  103. * another one.
  104. */
  105. if (notchVisibilityIO !== undefined) {
  106. return;
  107. }
  108. const io = (notchVisibilityIO = new IntersectionObserver((ev) => {
  109. /**
  110. * If the element is visible then we
  111. * can try setting the notch width again.
  112. */
  113. if (ev[0].intersectionRatio === 1) {
  114. setNotchWidth();
  115. io.disconnect();
  116. notchVisibilityIO = undefined;
  117. }
  118. },
  119. /**
  120. * Set the root to be the host element
  121. * This causes the IO callback
  122. * to be fired in WebKit as soon as the element
  123. * is visible. If we used the default root value
  124. * then WebKit would only fire the IO callback
  125. * after any animations (such as a modal transition)
  126. * finished, and there would potentially be a flicker.
  127. */
  128. { threshold: 0.01, root: el }));
  129. io.observe(notchSpacerEl);
  130. return;
  131. }
  132. /**
  133. * If the element is visible then we can set the notch width.
  134. * The notch is only visible when the label is scaled,
  135. * which is why we multiply the width by 0.75 as this is
  136. * the same amount the label element is scaled by in the host CSS.
  137. * (See $form-control-label-stacked-scale in ionic.globals.scss).
  138. */
  139. notchSpacerEl.style.setProperty('width', `${width * 0.75}px`);
  140. };
  141. const destroy = () => {
  142. if (notchVisibilityIO) {
  143. notchVisibilityIO.disconnect();
  144. notchVisibilityIO = undefined;
  145. }
  146. };
  147. return {
  148. calculateNotchWidth,
  149. destroy,
  150. };
  151. };
  152. exports.createNotchController = createNotchController;