Skip to content

Commit cd58294

Browse files
Jevon617sxzz
andauthored
feat(runtime-vapor): v-show for component (#188)
Co-authored-by: 三咲智子 Kevin Deng <sxzz@sxzz.moe>
1 parent 464b498 commit cd58294

File tree

2 files changed

+80
-7
lines changed

2 files changed

+80
-7
lines changed

packages/runtime-vapor/__tests__/directives/vShow.spec.ts

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,11 @@
1-
import { children, on, template, vShow, withDirectives } from '../../src'
1+
import {
2+
children,
3+
createComponent,
4+
on,
5+
template,
6+
vShow,
7+
withDirectives,
8+
} from '../../src'
29
import { nextTick, ref } from 'vue'
310
import { describe, expect, test } from 'vitest'
411
import { makeRender } from '../_utils'
@@ -43,4 +50,40 @@ describe('directive: v-show', () => {
4350
await nextTick()
4451
expect(h1?.style.display).toBe('')
4552
})
53+
54+
test('should work on component', async () => {
55+
const t0 = template('<div>child</div>')
56+
const t1 = template('<button>toggle</button>')
57+
const n0 = t0()
58+
const visible = ref(true)
59+
60+
function handleClick() {
61+
visible.value = !visible.value
62+
}
63+
const { component: Child } = define({
64+
render() {
65+
return n0
66+
},
67+
})
68+
69+
const { instance, host } = define({
70+
render() {
71+
const n1 = t1()
72+
const n2 = createComponent(Child, [], null, null, true)
73+
withDirectives(n2, [[vShow, () => visible.value]])
74+
on(n1 as HTMLElement, 'click', () => handleClick)
75+
return [n1, n2]
76+
},
77+
}).render()
78+
79+
expect(host.innerHTML).toBe('<button>toggle</button><div>child</div>')
80+
expect(instance.dirs.get(n0)![0].dir).toBe(vShow)
81+
82+
const btn = host.querySelector('button')
83+
btn?.click()
84+
await nextTick()
85+
expect(host.innerHTML).toBe(
86+
'<button>toggle</button><div style="display: none;">child</div>',
87+
)
88+
})
4689
})

packages/runtime-vapor/src/directives.ts

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,14 @@
11
import { isFunction } from '@vue/shared'
2-
import { type ComponentInternalInstance, currentInstance } from './component'
2+
import {
3+
type ComponentInternalInstance,
4+
currentInstance,
5+
isVaporComponent,
6+
} from './component'
37
import { pauseTracking, resetTracking, traverse } from '@vue/reactivity'
48
import { VaporErrorCodes, callWithAsyncErrorHandling } from './errorHandling'
59
import { renderEffect } from './renderEffect'
10+
import { warn } from './warning'
11+
import { normalizeBlock } from './dom/element'
612

713
export type DirectiveModifiers<M extends string = string> = Record<M, boolean>
814

@@ -62,13 +68,22 @@ export type DirectiveArguments = Array<
6268
]
6369
>
6470

65-
export function withDirectives<T extends Node>(
66-
node: T,
71+
export function withDirectives<T extends ComponentInternalInstance | Node>(
72+
nodeOrComponent: T,
6773
directives: DirectiveArguments,
6874
): T {
6975
if (!currentInstance) {
70-
// TODO warning
71-
return node
76+
__DEV__ && warn(`withDirectives can only be used inside render functions.`)
77+
return nodeOrComponent
78+
}
79+
80+
let node: Node
81+
if (isVaporComponent(nodeOrComponent)) {
82+
const root = getComponentNode(nodeOrComponent)
83+
if (!root) return nodeOrComponent
84+
node = root
85+
} else {
86+
node = nodeOrComponent
7287
}
7388

7489
const instance = currentInstance
@@ -109,7 +124,22 @@ export function withDirectives<T extends Node>(
109124
}
110125
}
111126

112-
return node
127+
return nodeOrComponent
128+
}
129+
130+
function getComponentNode(component: ComponentInternalInstance) {
131+
if (!component.block) return
132+
133+
const nodes = normalizeBlock(component.block)
134+
if (nodes.length !== 1) {
135+
warn(
136+
`Runtime directive used on component with non-element root node. ` +
137+
`The directives will not function as intended.`,
138+
)
139+
return
140+
}
141+
142+
return nodes[0]
113143
}
114144

115145
export function invokeDirectiveHook(

0 commit comments

Comments
 (0)