]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
feat(custom-element): useShadowRoot() helper
authorEvan You <evan@vuejs.org>
Sat, 3 Aug 2024 06:19:19 +0000 (14:19 +0800)
committerEvan You <evan@vuejs.org>
Sat, 3 Aug 2024 06:19:19 +0000 (14:19 +0800)
close #6113
close #8195

packages/runtime-core/src/component.ts
packages/runtime-dom/__tests__/customElement.spec.ts
packages/runtime-dom/src/apiCustomElement.ts
packages/runtime-dom/src/index.ts

index 9431ce6c4dd7bca3530d618f826059c00b631d3f..667fa5e713e96f767f51a297594772be6728a00a 100644 (file)
@@ -417,7 +417,7 @@ export interface ComponentInternalInstance {
    * is custom element?
    * @internal
    */
-  isCE?: boolean
+  isCE?: Element
   /**
    * custom element specific HMR method
    * @internal
index 1ed7cc25311de275d01ca084b3d06ef8ce83af36..1762f0cec648d6e6695cdea38676fef6ec533bc6 100644 (file)
@@ -11,6 +11,7 @@ import {
   ref,
   render,
   renderSlot,
+  useShadowRoot,
 } from '../src'
 
 describe('defineCustomElement', () => {
@@ -861,4 +862,23 @@ describe('defineCustomElement', () => {
       )
     })
   })
+
+  describe('useCustomElementRoot', () => {
+    test('should work for style injection', () => {
+      const Foo = defineCustomElement({
+        setup() {
+          const root = useShadowRoot()!
+          const style = document.createElement('style')
+          style.innerHTML = `div { color: red; }`
+          root.appendChild(style)
+          return () => h('div', 'hello')
+        },
+      })
+      customElements.define('my-el', Foo)
+      container.innerHTML = `<my-el></my-el>`
+      const el = container.childNodes[0] as VueElement
+      const style = el.shadowRoot?.querySelector('style')!
+      expect(style.textContent).toBe(`div { color: red; }`)
+    })
+  })
 })
index 476723707912c3e8afa66544764ac19efba3fdf6..7c4d8793ba12968386268ee074408c3088e9a7c5 100644 (file)
@@ -24,6 +24,7 @@ import {
   type VNodeProps,
   createVNode,
   defineComponent,
+  getCurrentInstance,
   nextTick,
   warn,
 } from '@vue/runtime-core'
@@ -191,7 +192,10 @@ export class VueElement extends BaseClass {
   private _numberProps: Record<string, true> | null = null
   private _styles?: HTMLStyleElement[]
   private _ob?: MutationObserver | null = null
-  private _root: Element | ShadowRoot
+  /**
+   * @internal
+   */
+  public _root: Element | ShadowRoot
   private _slots?: Record<string, Node[]>
 
   constructor(
@@ -247,6 +251,7 @@ export class VueElement extends BaseClass {
           this._ob = null
         }
         render(null, this._root)
+        this._instance!.isCE = undefined
         this._instance = null
       }
     })
@@ -395,7 +400,7 @@ export class VueElement extends BaseClass {
     if (!this._instance) {
       vnode.ce = instance => {
         this._instance = instance
-        instance.isCE = true
+        instance.isCE = this
         // HMR
         if (__DEV__) {
           instance.ceReload = newStyles => {
@@ -508,3 +513,25 @@ export class VueElement extends BaseClass {
     }
   }
 }
+
+/**
+ * Retrieve the shadowRoot of the current custom element. Only usable in setup()
+ * of a `defineCustomElement` component.
+ */
+export function useShadowRoot(): ShadowRoot | null {
+  const instance = getCurrentInstance()
+  const el = instance && instance.isCE
+  if (el) {
+    return el.shadowRoot
+  } else if (__DEV__) {
+    if (!instance) {
+      warn(`useCustomElementRoot called without an active component instance.`)
+    } else {
+      warn(
+        `useCustomElementRoot can only be used in components defined via ` +
+          `defineCustomElement.`,
+      )
+    }
+  }
+  return null
+}
index 989a5fd3b80701680b427db88a63c6405f95e44d..7acaa98c9d5f2c53c45506d9813a49721c94bfeb 100644 (file)
@@ -242,6 +242,7 @@ function normalizeContainer(
 export {
   defineCustomElement,
   defineSSRCustomElement,
+  useShadowRoot,
   VueElement,
   type VueElementConstructor,
 } from './apiCustomElement'