From: Evan You Date: Sun, 2 Feb 2025 04:18:51 +0000 (+0800) Subject: wip(vapor): v-show work on components X-Git-Tag: v3.6.0-alpha.1~16^2~108 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=d51403c1d3a8c8641b3530e3871bfbd9a313d0bd;p=thirdparty%2Fvuejs%2Fcore.git wip(vapor): v-show work on components --- diff --git a/packages/runtime-dom/src/directives/vShow.ts b/packages/runtime-dom/src/directives/vShow.ts index 2303d81973..531c47c8c8 100644 --- a/packages/runtime-dom/src/directives/vShow.ts +++ b/packages/runtime-dom/src/directives/vShow.ts @@ -13,8 +13,8 @@ export const vShowHidden: unique symbol = Symbol('_vsh') */ export interface VShowElement extends HTMLElement { // _vod = vue original display - [vShowOriginalDisplay]: string - [vShowHidden]: boolean + [vShowOriginalDisplay]?: string + [vShowHidden]?: boolean } export const vShow: ObjectDirective & { name?: 'show' } = { @@ -58,7 +58,7 @@ if (__DEV__) { } function setDisplay(el: VShowElement, value: unknown): void { - el.style.display = value ? el[vShowOriginalDisplay] : 'none' + el.style.display = value ? el[vShowOriginalDisplay]! : 'none' el[vShowHidden] = !value } diff --git a/packages/runtime-vapor/__tests__/directives/vShow.spec.ts b/packages/runtime-vapor/__tests__/directives/vShow.spec.ts index 7a22ebffeb..2877e5c144 100644 --- a/packages/runtime-vapor/__tests__/directives/vShow.spec.ts +++ b/packages/runtime-vapor/__tests__/directives/vShow.spec.ts @@ -1,14 +1,13 @@ import { + applyVShow, children, createComponent, + createIf, + defineVaporComponent, on, template, - // @ts-expect-error - vShow, - // @ts-expect-error - withDirectives, } from '../../src' -import { nextTick, ref } from 'vue' +import { type VShowElement, nextTick, ref } from 'vue' import { describe, expect, test } from 'vitest' import { makeRender } from '../_utils' @@ -26,12 +25,12 @@ const createDemo = (defaultValue: boolean) => const n0 = t0() const n1 = children(n0, 0) const n2 = children(n0, 1) - withDirectives(n2, [[vShow, () => visible.value]]) + applyVShow(n2 as VShowElement, () => visible.value) on(n1 as HTMLElement, 'click', () => handleClick) return n0 }) -describe.todo('directive: v-show', () => { +describe('directive: v-show', () => { test('basic', async () => { const { host } = createDemo(true).render() const btn = host.querySelector('button') @@ -56,36 +55,87 @@ describe.todo('directive: v-show', () => { 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 + setup() { + return t0() }, }) const { 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] + setup() { + const n1 = createComponent(Child, null, null, true) + applyVShow(n1, () => visible.value) + return n1 }, }).render() - expect(host.innerHTML).toBe('
child
') + expect(host.innerHTML).toBe('
child
') - const btn = host.querySelector('button') - btn?.click() + visible.value = !visible.value + await nextTick() + expect(host.innerHTML).toBe('
child
') + }) + + test('warn on non-single-element-root component', () => { + const Child = defineVaporComponent({ + setup() { + return document.createTextNode('b') + }, + }) + define({ + setup() { + const n1 = createComponent(Child) + applyVShow(n1, () => true) + return n1 + }, + }).render() + expect( + 'v-show used on component with non-single-element root node', + ).toHaveBeenWarned() + }) + + test('should work on component with dynamic fragment root', async () => { + const t0 = template('
child
') + const t1 = template('child') + const childIf = ref(true) + const visible = ref(true) + + const { component: Child } = define({ + setup() { + return createIf( + () => childIf.value, + () => t0(), + () => t1(), + ) + }, + }) + + const { host } = define({ + setup() { + const n1 = createComponent(Child, null, null, true) + applyVShow(n1, () => visible.value) + return n1 + }, + }).render() + + expect(host.innerHTML).toBe('
child
') + + visible.value = !visible.value + await nextTick() + expect(host.innerHTML).toBe( + '
child
', + ) + + childIf.value = !childIf.value await nextTick() expect(host.innerHTML).toBe( - '
child
', + 'child', ) + + visible.value = !visible.value + await nextTick() + expect(host.innerHTML).toBe('child') }) }) diff --git a/packages/runtime-vapor/src/directives/vShow.ts b/packages/runtime-vapor/src/directives/vShow.ts index 6a21e979c1..ac4c066b71 100644 --- a/packages/runtime-vapor/src/directives/vShow.ts +++ b/packages/runtime-vapor/src/directives/vShow.ts @@ -2,15 +2,55 @@ import { type VShowElement, vShowHidden, vShowOriginalDisplay, + warn, } from '@vue/runtime-dom' import { renderEffect } from '../renderEffect' +import { isVaporComponent } from '../component' +import { type Block, DynamicFragment } from '../block' +import { isArray } from '@vue/shared' -export function applyVShow(el: VShowElement, source: () => any): void { - el[vShowOriginalDisplay] = el.style.display === 'none' ? '' : el.style.display - renderEffect(() => setDisplay(el, source())) +export function applyVShow(target: Block, source: () => any): void { + if (isVaporComponent(target)) { + return applyVShow(target.block, source) + } + + if (isArray(target) && target.length === 1) { + return applyVShow(target[0], source) + } + + if (target instanceof DynamicFragment) { + const update = target.update + target.update = (render, key) => { + update.call(target, render, key) + setDisplay(target, source()) + } + } + + renderEffect(() => setDisplay(target, source())) } -function setDisplay(el: VShowElement, value: unknown): void { - el.style.display = value ? el[vShowOriginalDisplay] : 'none' - el[vShowHidden] = !value +function setDisplay(target: Block, value: unknown): void { + if (isVaporComponent(target)) { + return setDisplay(target, value) + } + if (isArray(target) && target.length === 1) { + return setDisplay(target[0], value) + } + if (target instanceof DynamicFragment) { + return setDisplay(target.nodes, value) + } + if (target instanceof Element) { + const el = target as VShowElement + if (!(vShowOriginalDisplay in el)) { + el[vShowOriginalDisplay] = + el.style.display === 'none' ? '' : el.style.display + } + el.style.display = value ? el[vShowOriginalDisplay]! : 'none' + el[vShowHidden] = !value + } else if (__DEV__) { + warn( + `v-show used on component with non-single-element root node ` + + `and will be ignored.`, + ) + } }