Skip to content

Commit 5a1a89b

Browse files
committed
feat(custom-element): useShadowRoot() helper
close #6113 close #8195
1 parent e181bff commit 5a1a89b

File tree

4 files changed

+51
-3
lines changed

4 files changed

+51
-3
lines changed

Diff for: packages/runtime-core/src/component.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -417,7 +417,7 @@ export interface ComponentInternalInstance {
417417
* is custom element?
418418
* @internal
419419
*/
420-
isCE?: boolean
420+
isCE?: Element
421421
/**
422422
* custom element specific HMR method
423423
* @internal

Diff for: packages/runtime-dom/__tests__/customElement.spec.ts

+20
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
ref,
1212
render,
1313
renderSlot,
14+
useShadowRoot,
1415
} from '../src'
1516

1617
describe('defineCustomElement', () => {
@@ -861,4 +862,23 @@ describe('defineCustomElement', () => {
861862
)
862863
})
863864
})
865+
866+
describe('useCustomElementRoot', () => {
867+
test('should work for style injection', () => {
868+
const Foo = defineCustomElement({
869+
setup() {
870+
const root = useShadowRoot()!
871+
const style = document.createElement('style')
872+
style.innerHTML = `div { color: red; }`
873+
root.appendChild(style)
874+
return () => h('div', 'hello')
875+
},
876+
})
877+
customElements.define('my-el', Foo)
878+
container.innerHTML = `<my-el></my-el>`
879+
const el = container.childNodes[0] as VueElement
880+
const style = el.shadowRoot?.querySelector('style')!
881+
expect(style.textContent).toBe(`div { color: red; }`)
882+
})
883+
})
864884
})

Diff for: packages/runtime-dom/src/apiCustomElement.ts

+29-2
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import {
2424
type VNodeProps,
2525
createVNode,
2626
defineComponent,
27+
getCurrentInstance,
2728
nextTick,
2829
warn,
2930
} from '@vue/runtime-core'
@@ -191,7 +192,10 @@ export class VueElement extends BaseClass {
191192
private _numberProps: Record<string, true> | null = null
192193
private _styles?: HTMLStyleElement[]
193194
private _ob?: MutationObserver | null = null
194-
private _root: Element | ShadowRoot
195+
/**
196+
* @internal
197+
*/
198+
public _root: Element | ShadowRoot
195199
private _slots?: Record<string, Node[]>
196200

197201
constructor(
@@ -247,6 +251,7 @@ export class VueElement extends BaseClass {
247251
this._ob = null
248252
}
249253
render(null, this._root)
254+
this._instance!.isCE = undefined
250255
this._instance = null
251256
}
252257
})
@@ -395,7 +400,7 @@ export class VueElement extends BaseClass {
395400
if (!this._instance) {
396401
vnode.ce = instance => {
397402
this._instance = instance
398-
instance.isCE = true
403+
instance.isCE = this
399404
// HMR
400405
if (__DEV__) {
401406
instance.ceReload = newStyles => {
@@ -508,3 +513,25 @@ export class VueElement extends BaseClass {
508513
}
509514
}
510515
}
516+
517+
/**
518+
* Retrieve the shadowRoot of the current custom element. Only usable in setup()
519+
* of a `defineCustomElement` component.
520+
*/
521+
export function useShadowRoot(): ShadowRoot | null {
522+
const instance = getCurrentInstance()
523+
const el = instance && instance.isCE
524+
if (el) {
525+
return el.shadowRoot
526+
} else if (__DEV__) {
527+
if (!instance) {
528+
warn(`useCustomElementRoot called without an active component instance.`)
529+
} else {
530+
warn(
531+
`useCustomElementRoot can only be used in components defined via ` +
532+
`defineCustomElement.`,
533+
)
534+
}
535+
}
536+
return null
537+
}

Diff for: packages/runtime-dom/src/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,7 @@ function normalizeContainer(
242242
export {
243243
defineCustomElement,
244244
defineSSRCustomElement,
245+
useShadowRoot,
245246
VueElement,
246247
type VueElementConstructor,
247248
} from './apiCustomElement'

0 commit comments

Comments
 (0)