1
1
import { IJsonModel , IJsonBorderNode , IJsonRowNode , IJsonTabSetNode , IJsonTabNode } from "./IJsonModel" ;
2
2
3
+ /** Union type representing all possible node types in the JSON model */
3
4
export type JsonNode = IJsonBorderNode | IJsonRowNode | IJsonTabSetNode | IJsonTabNode ;
4
- export type ModelVisitor = ( node : JsonNode , parent : JsonNode | null ) => boolean | void ;
5
+ export interface VisitorResult {
6
+ /** If true, do not process any children of the current node */
7
+ skipChildren ?: boolean ;
8
+ /** If true, remove the current node from its parent's children array */
9
+ delete ?: boolean ;
10
+ }
11
+
12
+ /**
13
+ * Function type for visiting nodes in the model tree
14
+ * @param node The current node being visited
15
+ * @param parent The parent of the current node, or null for top-level nodes
16
+ * @returns
17
+ * - void/true to continue normally
18
+ * - false to stop traversal completely
19
+ * - VisitorResult to control traversal or delete nodes
20
+ */
21
+ export type ModelVisitor = ( node : JsonNode , parent : JsonNode | null ) => void | boolean | VisitorResult ;
5
22
6
23
/**
7
24
* Finds a node by its ID in the model tree
@@ -15,9 +32,9 @@ export function findJsonNodeById(model: IJsonModel, id: string): JsonNode | unde
15
32
walkJsonModel ( model , ( node ) => {
16
33
if ( "id" in node && node . id === id ) {
17
34
result = node ;
18
- return false ; // stop walking
35
+ return { skipChildren : true } ; // found it, no need to check children
19
36
}
20
- return true ;
37
+ return true
21
38
} ) ;
22
39
23
40
return result ;
@@ -27,7 +44,11 @@ export function findJsonNodeById(model: IJsonModel, id: string): JsonNode | unde
27
44
* Walks an IJsonModel tree using a stack-based approach, calling visitor for each node.
28
45
* The walk processes borders first, then traverses the main layout in a depth-first manner.
29
46
* @param model The model to traverse
30
- * @param visitor Callback called for each node in the tree. Return false to stop the traversal.
47
+ * @param visitor Callback called for each node in the tree. The visitor can:
48
+ * - Return false to stop the traversal completely
49
+ * - Return {skipChildren: true} to skip processing children
50
+ * - Return {delete: true} to remove the current node from its parent
51
+ * - Return nothing or true to continue normally
31
52
* The visitor receives the current node and its parent (null for top-level nodes).
32
53
*/
33
54
export function walkJsonModel ( model : IJsonModel , visitor : ModelVisitor ) : void {
@@ -48,13 +69,28 @@ export function walkJsonModel(model: IJsonModel, visitor: ModelVisitor): void {
48
69
while ( stack . length > 0 ) {
49
70
const [ node , parent ] = stack . pop ( ) ! ;
50
71
51
- // Visit current node, stop if visitor returns false
52
- if ( visitor ( node , parent ) === false ) {
72
+ // Visit current node
73
+ const result = visitor ( node , parent ) ;
74
+
75
+ // Handle different return types
76
+ if ( result === false ) {
53
77
break ;
54
78
}
55
79
56
- // Add children to stack in reverse order so they're processed in forward order
57
- if ( "children" in node ) {
80
+ const visitorResult = typeof result === 'object' ? result : undefined ;
81
+
82
+ // Handle deletion if requested
83
+ if ( visitorResult ?. delete && parent && "children" in parent ) {
84
+ const index = parent . children . findIndex ( child => child === node ) ;
85
+ if ( index !== - 1 ) {
86
+ parent . children . splice ( index , 1 ) ;
87
+ continue ;
88
+ }
89
+ }
90
+
91
+ // Skip children if requested or process them
92
+ if ( ! visitorResult ?. skipChildren && "children" in node ) {
93
+ // Add children to stack in reverse order so they're processed in forward order
58
94
for ( let i = node . children . length - 1 ; i >= 0 ; i -- ) {
59
95
stack . push ( [ node . children [ i ] , node ] ) ;
60
96
}
0 commit comments