1+ import Base from '../../core/Base.mjs' ;
2+ import ComponentManager from '../../manager/Component.mjs' ;
3+
4+ /**
5+ * A singleton utility class responsible for recursively building VDOM and VNode trees.
6+ * It can expand component references within a tree structure into their full VDOM/VNode representations,
7+ * supporting selective (asymmetric) tree expansion for optimized updates.
8+ * @class Neo.util.vdom.TreeBuilder
9+ * @extends Neo.core.Base
10+ * @singleton
11+ */
12+ class TreeBuilder extends Base {
13+ static config = {
14+ /**
15+ * @member {String} className='Neo.util.vdom.TreeBuilder'
16+ * @protected
17+ */
18+ className : 'Neo.util.vdom.TreeBuilder' ,
19+ /**
20+ * @member {Boolean} singleton=true
21+ * @protected
22+ */
23+ singleton : true
24+ }
25+
26+ /**
27+ * Copies a given vdom tree and replaces child component references with the vdom of their matching components
28+ * @param {Object } vdom
29+ * @param {Number } [depth=-1]
30+ * The component replacement depth.
31+ * -1 will parse the full tree, 1 top level only, 2 include children, 3 include grandchildren
32+ * @param {Set<String>|null } [mergedChildIds=null] A set of component IDs to selectively expand.
33+ * @returns {Object }
34+ */
35+ getVdomTree ( vdom , depth = - 1 , mergedChildIds = null ) {
36+ if ( ! Neo . isObject ( vdom ) ) {
37+ return vdom ;
38+ }
39+
40+ let output = { ...vdom } , // shallow copy
41+ childDepth ;
42+
43+ if ( vdom . cn ) {
44+ output . cn = [ ] ;
45+
46+ vdom . cn . forEach ( item => {
47+ let currentItem = item ;
48+
49+ if ( currentItem . componentId ) {
50+ // Expand if:
51+ // 1. We are not at the depth limit (depth > 1 or depth === -1).
52+ // 2. The child component is part of the set of components whose updates were merged into this one.
53+ if ( ( depth === - 1 || depth > 1 ) && mergedChildIds ?. has ( currentItem . componentId ) ) {
54+ const component = ComponentManager . get ( currentItem . componentId ) ;
55+ if ( component ?. vdom ) {
56+ currentItem = component . vdom ;
57+ }
58+ }
59+ }
60+
61+ if ( item . componentId ) { // check original item
62+ childDepth = ( depth === - 1 ) ? - 1 : Math . max ( 0 , depth - 1 ) ;
63+ } else {
64+ childDepth = depth ;
65+ }
66+
67+ output . cn . push ( this . getVdomTree ( currentItem , childDepth , mergedChildIds ) ) ;
68+ } ) ;
69+ }
70+
71+ return output
72+ }
73+
74+ /**
75+ * Copies a given vnode tree and replaces child component references with the vnode of their matching components
76+ * @param {Object } vnode
77+ * @param {Number } [depth=-1]
78+ * The component replacement depth.
79+ * -1 will parse the full tree, 1 top level only, 2 include children, 3 include grandchildren
80+ * @param {Set<String>|null } [mergedChildIds=null] A set of component IDs to selectively expand.
81+ * @returns {Object }
82+ */
83+ getVnodeTree ( vnode , depth = - 1 , mergedChildIds = null ) {
84+ let output = { ...vnode } , // shallow copy
85+ childDepth , component ;
86+
87+ if ( vnode . childNodes ) {
88+ output . childNodes = [ ] ;
89+
90+ vnode . childNodes . forEach ( item => {
91+ let currentItem = item ;
92+
93+ if ( currentItem . componentId ) {
94+ // Expand if:
95+ // 1. We are not at the depth limit (depth > 1 or depth === -1).
96+ // 2. The child component is part of the set of components whose updates were merged into this one.
97+ if ( ( depth === - 1 || depth > 1 ) && mergedChildIds ?. has ( currentItem . componentId ) ) {
98+ component = ComponentManager . get ( currentItem . componentId ) ;
99+
100+ // keep references in case there is no vnode (e.g. component not mounted yet)
101+ if ( component ?. vnode ) {
102+ currentItem = component . vnode ;
103+ }
104+ }
105+ }
106+
107+ if ( item . componentId ) { // check original item
108+ childDepth = ( depth === - 1 ) ? - 1 : Math . max ( 0 , depth - 1 ) ;
109+ } else {
110+ childDepth = depth ;
111+ }
112+
113+ output . childNodes . push ( this . getVnodeTree ( currentItem , childDepth , mergedChildIds ) ) ;
114+ } ) ;
115+ }
116+
117+ return output
118+ }
119+ }
120+
121+ export default Neo . setupClass ( TreeBuilder ) ;
0 commit comments