feat(runtime-vapor): `v-show` for component (#188)

Co-authored-by: 三咲智子 Kevin Deng <sxzz@sxzz.moe>
This commit is contained in:
Jevon 2024-04-27 00:02:34 +08:00 committed by GitHub
parent 464b498f13
commit cd582949f2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 80 additions and 7 deletions

View File

@ -1,4 +1,11 @@
import { children, on, template, vShow, withDirectives } from '../../src'
import {
children,
createComponent,
on,
template,
vShow,
withDirectives,
} from '../../src'
import { nextTick, ref } from 'vue'
import { describe, expect, test } from 'vitest'
import { makeRender } from '../_utils'
@ -43,4 +50,40 @@ describe('directive: v-show', () => {
await nextTick()
expect(h1?.style.display).toBe('')
})
test('should work on component', async () => {
const t0 = template('<div>child</div>')
const t1 = template('<button>toggle</button>')
const n0 = t0()
const visible = ref(true)
function handleClick() {
visible.value = !visible.value
}
const { component: Child } = define({
render() {
return n0
},
})
const { instance, host } = define({
render() {
const n1 = t1()
const n2 = createComponent(Child, [], null, null, true)
withDirectives(n2, [[vShow, () => visible.value]])
on(n1 as HTMLElement, 'click', () => handleClick)
return [n1, n2]
},
}).render()
expect(host.innerHTML).toBe('<button>toggle</button><div>child</div>')
expect(instance.dirs.get(n0)![0].dir).toBe(vShow)
const btn = host.querySelector('button')
btn?.click()
await nextTick()
expect(host.innerHTML).toBe(
'<button>toggle</button><div style="display: none;">child</div>',
)
})
})

View File

@ -1,8 +1,14 @@
import { isFunction } from '@vue/shared'
import { type ComponentInternalInstance, currentInstance } from './component'
import {
type ComponentInternalInstance,
currentInstance,
isVaporComponent,
} from './component'
import { pauseTracking, resetTracking, traverse } from '@vue/reactivity'
import { VaporErrorCodes, callWithAsyncErrorHandling } from './errorHandling'
import { renderEffect } from './renderEffect'
import { warn } from './warning'
import { normalizeBlock } from './dom/element'
export type DirectiveModifiers<M extends string = string> = Record<M, boolean>
@ -62,13 +68,22 @@ export type DirectiveArguments = Array<
]
>
export function withDirectives<T extends Node>(
node: T,
export function withDirectives<T extends ComponentInternalInstance | Node>(
nodeOrComponent: T,
directives: DirectiveArguments,
): T {
if (!currentInstance) {
// TODO warning
return node
__DEV__ && warn(`withDirectives can only be used inside render functions.`)
return nodeOrComponent
}
let node: Node
if (isVaporComponent(nodeOrComponent)) {
const root = getComponentNode(nodeOrComponent)
if (!root) return nodeOrComponent
node = root
} else {
node = nodeOrComponent
}
const instance = currentInstance
@ -109,7 +124,22 @@ export function withDirectives<T extends Node>(
}
}
return node
return nodeOrComponent
}
function getComponentNode(component: ComponentInternalInstance) {
if (!component.block) return
const nodes = normalizeBlock(component.block)
if (nodes.length !== 1) {
warn(
`Runtime directive used on component with non-element root node. ` +
`The directives will not function as intended.`,
)
return
}
return nodes[0]
}
export function invokeDirectiveHook(