From cd582949f2eebe4685e87ce88481afaaad205b8f Mon Sep 17 00:00:00 2001 From: Jevon <1787176370@qq.com> Date: Sat, 27 Apr 2024 00:02:34 +0800 Subject: [PATCH] feat(runtime-vapor): `v-show` for component (#188) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: 三咲智子 Kevin Deng --- .../__tests__/directives/vShow.spec.ts | 45 ++++++++++++++++++- packages/runtime-vapor/src/directives.ts | 42 ++++++++++++++--- 2 files changed, 80 insertions(+), 7 deletions(-) diff --git a/packages/runtime-vapor/__tests__/directives/vShow.spec.ts b/packages/runtime-vapor/__tests__/directives/vShow.spec.ts index 83f14ed9d..8f627b975 100644 --- a/packages/runtime-vapor/__tests__/directives/vShow.spec.ts +++ b/packages/runtime-vapor/__tests__/directives/vShow.spec.ts @@ -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('
child
') + const t1 = template('') + 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('
child
') + expect(instance.dirs.get(n0)![0].dir).toBe(vShow) + + const btn = host.querySelector('button') + btn?.click() + await nextTick() + expect(host.innerHTML).toBe( + '
child
', + ) + }) }) diff --git a/packages/runtime-vapor/src/directives.ts b/packages/runtime-vapor/src/directives.ts index 3eab22b2c..ab295678b 100644 --- a/packages/runtime-vapor/src/directives.ts +++ b/packages/runtime-vapor/src/directives.ts @@ -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 = Record @@ -62,13 +68,22 @@ export type DirectiveArguments = Array< ] > -export function withDirectives( - node: T, +export function withDirectives( + 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( } } - 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(