directiveComponent.mjs 2.5 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273
  1. // Utilities
  2. import { h, mergeProps, render, resolveComponent } from 'vue';
  3. import { consoleError, isObject } from "../util/index.mjs"; // Types
  4. export function useDirectiveComponent(component, props) {
  5. const concreteComponent = typeof component === 'string' ? resolveComponent(component) : component;
  6. const hook = mountComponent(concreteComponent, props);
  7. return {
  8. mounted: hook,
  9. updated: hook,
  10. unmounted(el) {
  11. render(null, el);
  12. }
  13. };
  14. }
  15. function mountComponent(component, props) {
  16. return function (el, binding, vnode) {
  17. const _props = typeof props === 'function' ? props(binding) : props;
  18. const text = binding.value?.text ?? binding.value ?? _props?.text;
  19. const value = isObject(binding.value) ? binding.value : {};
  20. // Get the children from the props or directive value, or the element's children
  21. const children = () => text ?? el.textContent;
  22. // If vnode.ctx is the same as the instance, then we're bound to a plain element
  23. // and need to find the nearest parent component instance to inherit provides from
  24. const provides = (vnode.ctx === binding.instance.$ ? findComponentParent(vnode, binding.instance.$)?.provides : vnode.ctx?.provides) ?? binding.instance.$.provides;
  25. const node = h(component, mergeProps(_props, value), children);
  26. node.appContext = Object.assign(Object.create(null), binding.instance.$.appContext, {
  27. provides
  28. });
  29. render(node, el);
  30. };
  31. }
  32. function findComponentParent(vnode, root) {
  33. // Walk the tree from root until we find the child vnode
  34. const stack = new Set();
  35. const walk = children => {
  36. for (const child of children) {
  37. if (!child) continue;
  38. if (child === vnode || child.el && vnode.el && child.el === vnode.el) {
  39. return true;
  40. }
  41. stack.add(child);
  42. let result;
  43. if (child.suspense) {
  44. result = walk([child.ssContent]);
  45. } else if (Array.isArray(child.children)) {
  46. result = walk(child.children);
  47. } else if (child.component?.vnode) {
  48. result = walk([child.component?.subTree]);
  49. }
  50. if (result) {
  51. return result;
  52. }
  53. stack.delete(child);
  54. }
  55. return false;
  56. };
  57. if (!walk([root.subTree])) {
  58. consoleError('Could not find original vnode, component will not inherit provides');
  59. return root;
  60. }
  61. // Return the first component parent
  62. const result = Array.from(stack).reverse();
  63. for (const child of result) {
  64. if (child.component) {
  65. return child.component;
  66. }
  67. }
  68. return root;
  69. }
  70. //# sourceMappingURL=directiveComponent.mjs.map