framework-delegate.js 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. /*!
  2. * (C) Ionic http://ionicframework.com - MIT License
  3. */
  4. import { c as componentOnReady } from './helpers.js';
  5. // TODO(FW-2832): types
  6. const attachComponent = async (delegate, container, component, cssClasses, componentProps, inline) => {
  7. var _a;
  8. if (delegate) {
  9. return delegate.attachViewToDom(container, component, componentProps, cssClasses);
  10. }
  11. if (!inline && typeof component !== 'string' && !(component instanceof HTMLElement)) {
  12. throw new Error('framework delegate is missing');
  13. }
  14. const el = typeof component === 'string' ? (_a = container.ownerDocument) === null || _a === void 0 ? void 0 : _a.createElement(component) : component;
  15. if (cssClasses) {
  16. cssClasses.forEach((c) => el.classList.add(c));
  17. }
  18. if (componentProps) {
  19. Object.assign(el, componentProps);
  20. }
  21. container.appendChild(el);
  22. await new Promise((resolve) => componentOnReady(el, resolve));
  23. return el;
  24. };
  25. const detachComponent = (delegate, element) => {
  26. if (element) {
  27. if (delegate) {
  28. const container = element.parentElement;
  29. return delegate.removeViewFromDom(container, element);
  30. }
  31. element.remove();
  32. }
  33. return Promise.resolve();
  34. };
  35. const CoreDelegate = () => {
  36. let BaseComponent;
  37. let Reference;
  38. const attachViewToDom = async (parentElement, userComponent, userComponentProps = {}, cssClasses = []) => {
  39. var _a, _b;
  40. BaseComponent = parentElement;
  41. let ChildComponent;
  42. /**
  43. * If passing in a component via the `component` props
  44. * we need to append it inside of our overlay component.
  45. */
  46. if (userComponent) {
  47. /**
  48. * If passing in the tag name, create
  49. * the element otherwise just get a reference
  50. * to the component.
  51. */
  52. const el = typeof userComponent === 'string' ? (_a = BaseComponent.ownerDocument) === null || _a === void 0 ? void 0 : _a.createElement(userComponent) : userComponent;
  53. /**
  54. * Add any css classes passed in
  55. * via the cssClasses prop on the overlay.
  56. */
  57. cssClasses.forEach((c) => el.classList.add(c));
  58. /**
  59. * Add any props passed in
  60. * via the componentProps prop on the overlay.
  61. */
  62. Object.assign(el, userComponentProps);
  63. /**
  64. * Finally, append the component
  65. * inside of the overlay component.
  66. */
  67. BaseComponent.appendChild(el);
  68. ChildComponent = el;
  69. await new Promise((resolve) => componentOnReady(el, resolve));
  70. }
  71. else if (BaseComponent.children.length > 0 &&
  72. (BaseComponent.tagName === 'ION-MODAL' || BaseComponent.tagName === 'ION-POPOVER')) {
  73. /**
  74. * The delegate host wrapper el is only needed for modals and popovers
  75. * because they allow the dev to provide custom content to the overlay.
  76. */
  77. const root = (ChildComponent = BaseComponent.children[0]);
  78. if (!root.classList.contains('ion-delegate-host')) {
  79. /**
  80. * If the root element is not a delegate host, it means
  81. * that the overlay has not been presented yet and we need
  82. * to create the containing element with the specified classes.
  83. */
  84. const el = (_b = BaseComponent.ownerDocument) === null || _b === void 0 ? void 0 : _b.createElement('div');
  85. // Add a class to track if the root element was created by the delegate.
  86. el.classList.add('ion-delegate-host');
  87. cssClasses.forEach((c) => el.classList.add(c));
  88. // Move each child from the original template to the new parent element.
  89. el.append(...BaseComponent.children);
  90. // Append the new parent element to the original parent element.
  91. BaseComponent.appendChild(el);
  92. /**
  93. * Update the ChildComponent to be the
  94. * newly created div in the event that one
  95. * does not already exist.
  96. */
  97. ChildComponent = el;
  98. }
  99. }
  100. /**
  101. * Get the root of the app and
  102. * add the overlay there.
  103. */
  104. const app = document.querySelector('ion-app') || document.body;
  105. /**
  106. * Create a placeholder comment so that
  107. * we can return this component to where
  108. * it was previously.
  109. */
  110. Reference = document.createComment('ionic teleport');
  111. BaseComponent.parentNode.insertBefore(Reference, BaseComponent);
  112. app.appendChild(BaseComponent);
  113. /**
  114. * We return the child component rather than the overlay
  115. * reference itself since modal and popover will
  116. * use this to wait for any Ionic components in the child view
  117. * to be ready (i.e. componentOnReady) when using the
  118. * lazy loaded component bundle.
  119. *
  120. * However, we fall back to returning BaseComponent
  121. * in the event that a modal or popover is presented
  122. * with no child content.
  123. */
  124. return ChildComponent !== null && ChildComponent !== void 0 ? ChildComponent : BaseComponent;
  125. };
  126. const removeViewFromDom = () => {
  127. /**
  128. * Return component to where it was previously in the DOM.
  129. */
  130. if (BaseComponent && Reference) {
  131. Reference.parentNode.insertBefore(BaseComponent, Reference);
  132. Reference.remove();
  133. }
  134. return Promise.resolve();
  135. };
  136. return { attachViewToDom, removeViewFromDom };
  137. };
  138. export { CoreDelegate as C, attachComponent as a, detachComponent as d };