Skip to content

Commit

Permalink
fix(teleport/css-v-bind): fix css v-bind for teleported content
Browse files Browse the repository at this point in the history
fix #4605
close #4609 (used tests from this PR)
  • Loading branch information
yyx990803 committed Nov 10, 2022
1 parent 9ca8b7c commit 42239cf
Show file tree
Hide file tree
Showing 4 changed files with 94 additions and 2 deletions.
6 changes: 6 additions & 0 deletions packages/runtime-core/src/component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -458,6 +458,12 @@ export interface ComponentInternalInstance {
* @internal
*/
n?: () => Promise<void>
/**
* `updateTeleportCssVars`
* For updating css vars on contained teleports
* @internal
*/
ut?: (vars?: Record<string, string>) => void
}

const emptyAppContext = createAppContext()
Expand Down
20 changes: 20 additions & 0 deletions packages/runtime-core/src/components/Teleport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,8 @@ export const TeleportImpl = {
}
}
}

updateCssVars(parentComponent, n2)
},

remove(
Expand Down Expand Up @@ -383,6 +385,7 @@ function hydrateTeleport(
)
}
}
updateCssVars(parentComponent, vnode)
}
return vnode.anchor && nextSibling(vnode.anchor as Node)
}
Expand All @@ -392,3 +395,20 @@ export const Teleport = TeleportImpl as unknown as {
__isTeleport: true
new (): { $props: VNodeProps & TeleportProps }
}

function updateCssVars(
parentComponent: ComponentInternalInstance | null,
vnode: VNode
) {
// presence of .ut method indicates owner component uses css vars.
// code path here can assume browser environment.
if (parentComponent && parentComponent.ut) {
let node = (vnode.children as VNode[])[0].el!
while (node !== vnode.targetAnchor) {
if (node.nodeType === 1)
node.setAttribute('data-v-owner', parentComponent.uid)
node = node.nextSibling
}
parentComponent.ut()
}
}
55 changes: 55 additions & 0 deletions packages/runtime-dom/__tests__/helpers/useCssVars.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
nextTick,
ComponentOptions,
Suspense,
Teleport,
FunctionalComponent
} from '@vue/runtime-dom'

Expand Down Expand Up @@ -196,4 +197,58 @@ describe('useCssVars', () => {
expect((c as HTMLElement).style.getPropertyValue(`--color`)).toBe('red')
}
})

test('with teleport', async () => {
document.body.innerHTML = ''
const state = reactive({ color: 'red' })
const root = document.createElement('div')
const target = document.body

const App = {
setup() {
useCssVars(() => state)
return () => [h(Teleport, { to: target }, [h('div')])]
}
}

render(h(App), root)
await nextTick()
for (const c of [].slice.call(target.children as any)) {
expect((c as HTMLElement).style.getPropertyValue(`--color`)).toBe('red')
}
})

test('with teleport(change subTree)', async () => {
document.body.innerHTML = ''
const state = reactive({ color: 'red' })
const root = document.createElement('div')
const target = document.body
const toggle = ref(false)

const App = {
setup() {
useCssVars(() => state)
return () => [
h(Teleport, { to: target }, [
h('div'),
toggle.value ? h('div') : null
])
]
}
}

render(h(App), root)
await nextTick()
expect(target.children.length).toBe(1)
for (const c of [].slice.call(target.children as any)) {
expect((c as HTMLElement).style.getPropertyValue(`--color`)).toBe('red')
}

toggle.value = true
await nextTick()
expect(target.children.length).toBe(2)
for (const c of [].slice.call(target.children as any)) {
expect((c as HTMLElement).style.getPropertyValue(`--color`)).toBe('red')
}
})
})
15 changes: 13 additions & 2 deletions packages/runtime-dom/src/helpers/useCssVars.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,20 @@ export function useCssVars(getter: (ctx: any) => Record<string, string>) {
return
}

const setVars = () =>
setVarsOnVNode(instance.subTree, getter(instance.proxy!))
const updateTeleports = (instance.ut = (vars = getter(instance.proxy)) => {
Array.from(
document.querySelectorAll(`[data-v-owner="${instance.uid}"]`)
).forEach(node => setVarsOnNode(node, vars))
})

const setVars = () => {
const vars = getter(instance.proxy)
setVarsOnVNode(instance.subTree, vars)
updateTeleports(vars)
}

watchPostEffect(setVars)

onMounted(() => {
const ob = new MutationObserver(setVars)
ob.observe(instance.subTree.el!.parentNode, { childList: true })
Expand Down

0 comments on commit 42239cf

Please sign in to comment.