input.utils.js 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. /*!
  2. * (C) Ionic http://ionicframework.com - MIT License
  3. */
  4. import { w as win } from './index6.js';
  5. import { r as raf } from './helpers.js';
  6. import { p as printIonError } from './index4.js';
  7. /**
  8. * Used to update a scoped component that uses emulated slots. This fires when
  9. * content is passed into the slot or when the content inside of a slot changes.
  10. * This is not needed for components using native slots in the Shadow DOM.
  11. * @internal
  12. * @param el The host element to observe
  13. * @param slotName mutationCallback will fire when nodes on these slot(s) change
  14. * @param mutationCallback The callback to fire whenever the slotted content changes
  15. */
  16. const createSlotMutationController = (el, slotName, mutationCallback) => {
  17. let hostMutationObserver;
  18. let slottedContentMutationObserver;
  19. if (win !== undefined && 'MutationObserver' in win) {
  20. const slots = Array.isArray(slotName) ? slotName : [slotName];
  21. hostMutationObserver = new MutationObserver((entries) => {
  22. for (const entry of entries) {
  23. for (const node of entry.addedNodes) {
  24. /**
  25. * Check to see if the added node
  26. * is our slotted content.
  27. */
  28. if (node.nodeType === Node.ELEMENT_NODE && slots.includes(node.slot)) {
  29. /**
  30. * If so, we want to watch the slotted
  31. * content itself for changes. This lets us
  32. * detect when content inside of the slot changes.
  33. */
  34. mutationCallback();
  35. /**
  36. * Adding the listener in an raf
  37. * waits until Stencil moves the slotted element
  38. * into the correct place in the event that
  39. * slotted content is being added.
  40. */
  41. raf(() => watchForSlotChange(node));
  42. return;
  43. }
  44. }
  45. }
  46. });
  47. hostMutationObserver.observe(el, {
  48. childList: true,
  49. /**
  50. * This fixes an issue with the `ion-input` and
  51. * `ion-textarea` not re-rendering in some cases
  52. * when using the label slot functionality.
  53. *
  54. * HTML element patches in Stencil that are enabled
  55. * by the `experimentalSlotFixes` flag in Stencil v4
  56. * result in DOM manipulations that won't trigger
  57. * the current mutation observer configuration and
  58. * callback.
  59. */
  60. subtree: true,
  61. });
  62. }
  63. /**
  64. * Listen for changes inside of the slotted content.
  65. * We can listen for subtree changes here to be
  66. * informed of text within the slotted content
  67. * changing. Doing this on the host is possible
  68. * but it is much more expensive to do because
  69. * it also listens for changes to the internals
  70. * of the component.
  71. */
  72. const watchForSlotChange = (slottedEl) => {
  73. var _a;
  74. if (slottedContentMutationObserver) {
  75. slottedContentMutationObserver.disconnect();
  76. slottedContentMutationObserver = undefined;
  77. }
  78. slottedContentMutationObserver = new MutationObserver((entries) => {
  79. mutationCallback();
  80. for (const entry of entries) {
  81. for (const node of entry.removedNodes) {
  82. /**
  83. * If the element was removed then we
  84. * need to destroy the MutationObserver
  85. * so the element can be garbage collected.
  86. */
  87. if (node.nodeType === Node.ELEMENT_NODE && node.slot === slotName) {
  88. destroySlottedContentObserver();
  89. }
  90. }
  91. }
  92. });
  93. /**
  94. * Listen for changes inside of the element
  95. * as well as anything deep in the tree.
  96. * We listen on the parentElement so that we can
  97. * detect when slotted element itself is removed.
  98. */
  99. slottedContentMutationObserver.observe((_a = slottedEl.parentElement) !== null && _a !== void 0 ? _a : slottedEl, { subtree: true, childList: true });
  100. };
  101. const destroy = () => {
  102. if (hostMutationObserver) {
  103. hostMutationObserver.disconnect();
  104. hostMutationObserver = undefined;
  105. }
  106. destroySlottedContentObserver();
  107. };
  108. const destroySlottedContentObserver = () => {
  109. if (slottedContentMutationObserver) {
  110. slottedContentMutationObserver.disconnect();
  111. slottedContentMutationObserver = undefined;
  112. }
  113. };
  114. return {
  115. destroy,
  116. };
  117. };
  118. const getCounterText = (value, maxLength, counterFormatter) => {
  119. const valueLength = value == null ? 0 : value.toString().length;
  120. const defaultCounterText = defaultCounterFormatter(valueLength, maxLength);
  121. /**
  122. * If developers did not pass a custom formatter,
  123. * use the default one.
  124. */
  125. if (counterFormatter === undefined) {
  126. return defaultCounterText;
  127. }
  128. /**
  129. * Otherwise, try to use the custom formatter
  130. * and fallback to the default formatter if
  131. * there was an error.
  132. */
  133. try {
  134. return counterFormatter(valueLength, maxLength);
  135. }
  136. catch (e) {
  137. printIonError('[ion-input] - Exception in provided `counterFormatter`:', e);
  138. return defaultCounterText;
  139. }
  140. };
  141. const defaultCounterFormatter = (length, maxlength) => {
  142. return `${length} / ${maxlength}`;
  143. };
  144. export { createSlotMutationController as c, getCounterText as g };