Skip to content

Commit

Permalink
feat(custom-element): useShadowRoot() helper
Browse files Browse the repository at this point in the history
close #6113
close #8195
  • Loading branch information
yyx990803 committed Aug 3, 2024
1 parent e181bff commit 5a1a89b
Show file tree
Hide file tree
Showing 4 changed files with 51 additions and 3 deletions.
2 changes: 1 addition & 1 deletion packages/runtime-core/src/component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -417,7 +417,7 @@ export interface ComponentInternalInstance {
* is custom element?
* @internal
*/
isCE?: boolean
isCE?: Element
/**
* custom element specific HMR method
* @internal
Expand Down
20 changes: 20 additions & 0 deletions packages/runtime-dom/__tests__/customElement.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
ref,
render,
renderSlot,
useShadowRoot,
} from '../src'

describe('defineCustomElement', () => {
Expand Down Expand Up @@ -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; }`)
})
})
})
31 changes: 29 additions & 2 deletions packages/runtime-dom/src/apiCustomElement.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {
type VNodeProps,
createVNode,
defineComponent,
getCurrentInstance,
nextTick,
warn,
} from '@vue/runtime-core'
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -247,6 +251,7 @@ export class VueElement extends BaseClass {
this._ob = null
}
render(null, this._root)
this._instance!.isCE = undefined
this._instance = null
}
})
Expand Down Expand Up @@ -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 => {
Expand Down Expand Up @@ -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
}
1 change: 1 addition & 0 deletions packages/runtime-dom/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,7 @@ function normalizeContainer(
export {
defineCustomElement,
defineSSRCustomElement,
useShadowRoot,
VueElement,
type VueElementConstructor,
} from './apiCustomElement'
Expand Down

0 comments on commit 5a1a89b

Please sign in to comment.