]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
fix(teleport/css-v-bind): fix css v-bind for teleported content
authorEvan You <yyx990803@gmail.com>
Thu, 10 Nov 2022 06:29:51 +0000 (14:29 +0800)
committerEvan You <yyx990803@gmail.com>
Thu, 10 Nov 2022 06:41:45 +0000 (14:41 +0800)
fix #4605
close #4609 (used tests from this PR)

packages/runtime-core/src/component.ts
packages/runtime-core/src/components/Teleport.ts
packages/runtime-dom/__tests__/helpers/useCssVars.spec.ts
packages/runtime-dom/src/helpers/useCssVars.ts

index 939d84e3705aa7de9fc177817fd593a51df04914..3fdf566bb98db76ba1405fea527d5be042f8510b 100644 (file)
@@ -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()
index e519aa2bb3a3344d24a0129805cfd1048eaaadea..18c4aee86a056b780a40e943b87e9fa38a5ec2c6 100644 (file)
@@ -222,6 +222,8 @@ export const TeleportImpl = {
         }
       }
     }
+
+    updateCssVars(parentComponent, n2)
   },
 
   remove(
@@ -383,6 +385,7 @@ function hydrateTeleport(
         )
       }
     }
+    updateCssVars(parentComponent, vnode)
   }
   return vnode.anchor && nextSibling(vnode.anchor as Node)
 }
@@ -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()
+  }
+}
index fb22529f42daa5233656ba962d735206cd48fdc6..e9dd36596506866d7a2492250df816e8714443f8 100644 (file)
@@ -8,6 +8,7 @@ import {
   nextTick,
   ComponentOptions,
   Suspense,
+  Teleport,
   FunctionalComponent
 } from '@vue/runtime-dom'
 
@@ -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')
+    }
+  })
 })
index 510e81b065b5cfe1efb2ce377def72126c20059a..e0a95c9ca997c08e9f2dac5848889ddf78b5c7db 100644 (file)
@@ -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 })