1
- import { Fragment , Comment } from 'vue'
1
+ import { Fragment , Comment , cloneVNode } from 'vue'
2
2
import type { VNode } from 'vue'
3
+ import { isArray , isFunction } from './is'
3
4
4
5
export const enum ShapeFlags {
5
6
ELEMENT = 1 ,
@@ -14,6 +15,7 @@ export const enum ShapeFlags {
14
15
COMPONENT_KEPT_ALIVE = 1 << 9 ,
15
16
COMPONENT = ShapeFlags . STATEFUL_COMPONENT | ShapeFlags . FUNCTIONAL_COMPONENT
16
17
}
18
+ type Data = Record < string , any >
17
19
18
20
export const isTextNode = ( vnode : VNode ) =>
19
21
vnode && vnode . shapeFlag & ShapeFlags . TEXT_CHILDREN
@@ -26,7 +28,7 @@ export const isElement = (vnode: VNode) =>
26
28
27
29
export const isSlot = ( vnode : VNode ) =>
28
30
vnode && vnode . shapeFlag & ShapeFlags . SLOTS_CHILDREN
29
- export const isArrayNode = ( vnode : VNode ) =>
31
+ export const isArrayChildrenNode = ( vnode : VNode ) =>
30
32
vnode && vnode . shapeFlag & ShapeFlags . ARRAY_CHILDREN
31
33
export const isFragment = ( vnode : VNode ) => vnode && vnode . type === Fragment
32
34
@@ -50,3 +52,61 @@ export function getAllElements(children: VNode[] | undefined) {
50
52
51
53
return result
52
54
}
55
+
56
+ export const mergeFirstChild = (
57
+ children : VNode [ ] | undefined ,
58
+ extraProps : Data | ( ( vnode ?: VNode ) => Data )
59
+ ) : boolean => {
60
+ if ( ! children ?. length ) return false
61
+
62
+ for ( let i = 0 ; i < children . length ; i ++ ) {
63
+ const vnode = children [ i ]
64
+ if ( isElement ( vnode ) || isComponent ( vnode ) ) {
65
+ const props = isFunction ( extraProps ) ? extraProps ( vnode ) : extraProps
66
+ children [ i ] = cloneVNode ( vnode , props , true )
67
+ return true
68
+ }
69
+ const _children = getArrayChildren ( vnode )
70
+ if ( _children && _children . length ) {
71
+ const result = mergeFirstChild ( _children , extraProps )
72
+ if ( result ) return result
73
+ }
74
+ }
75
+
76
+ return false
77
+ }
78
+
79
+ function getArrayChildren ( vnode : VNode ) : VNode [ ] | undefined {
80
+ if ( isArrayChildrenNode ( vnode ) ) return vnode . children as VNode [ ]
81
+ if ( isArray ( vnode ) ) return vnode
82
+
83
+ return undefined
84
+ }
85
+
86
+ export function getFirstElementFromChildren ( children : VNode [ ] ) {
87
+ for ( const child of children ) {
88
+ const element = getFirstElementFromVnode ( child )
89
+ if ( element ) return element
90
+ }
91
+ return undefined
92
+ }
93
+
94
+ export const getFirstElementFromVnode = (
95
+ vnode : VNode
96
+ ) : HTMLElement | undefined => {
97
+ if ( isElement ( vnode ) ) return vnode . el as HTMLElement
98
+
99
+ if ( isComponent ( vnode ) ) {
100
+ if ( ( vnode . el as Node ) . nodeType === Node . ELEMENT_NODE )
101
+ return vnode . el as HTMLElement
102
+
103
+ if ( vnode . component ?. subTree ) {
104
+ const element = getFirstElementFromVnode ( vnode . component ?. subTree )
105
+ if ( element ) return element
106
+ }
107
+ } else {
108
+ const children = getArrayChildren ( vnode )
109
+ if ( children ) return getFirstElementFromChildren ( children )
110
+ }
111
+ return undefined
112
+ }
0 commit comments