1
1
import type { IConceptJS , INodeJS } from "@modelix/ts-model-api" ;
2
- import { customRef , markRaw } from "vue" ;
2
+ import type { Ref } from "vue" ;
3
+ import { markRaw , shallowRef } from "vue" ;
3
4
import type { Cache } from "./Cache" ;
5
+ import type { Nullable } from "@modelix/model-client" ;
4
6
5
7
export function toReactiveINodeJS (
6
8
node : INodeJS ,
@@ -37,15 +39,19 @@ function unwrapReactiveINodeJS(
37
39
// eslint-disable-next-line @typescript-eslint/no-explicit-any
38
40
type INodeReferenceJS = any ;
39
41
40
- interface TrackAndTrigger {
41
- track : ( ) => void ;
42
- trigger : ( ) => void ;
43
- }
44
-
45
42
export class ReactiveINodeJS implements INodeJS {
46
- private byRoleTrackAndTrigger : Map < string | undefined , TrackAndTrigger > =
43
+ private byRoleRefToProperty : Map < string , Ref < string | undefined > > = new Map ( ) ;
44
+ private byRoleRefToReferenceTargetNode : Map <
45
+ string ,
46
+ Ref < INodeJS | undefined >
47
+ > = new Map ( ) ;
48
+ private byRoleRefToReferenceTargetRef : Map <
49
+ string ,
50
+ Ref < INodeReferenceJS | undefined >
51
+ > = new Map ( ) ;
52
+ private byRoleRefToChildren : Map < string | undefined , Ref < INodeJS [ ] > > =
47
53
new Map ( ) ;
48
- private trackAndTriggerForAllChildren : TrackAndTrigger | undefined ;
54
+ private refForAllChildren : Ref < INodeJS [ ] > | undefined = undefined ;
49
55
50
56
constructor (
51
57
public readonly unreactiveNode : INodeJS ,
@@ -56,6 +62,30 @@ export class ReactiveINodeJS implements INodeJS {
56
62
markRaw ( this ) ;
57
63
}
58
64
65
+ private propertyGetter = ( role : string ) =>
66
+ this . unreactiveNode . getPropertyValue ( role ) ;
67
+
68
+ private referenceTargetRefGetter = ( role : string ) =>
69
+ this . unreactiveNode . getReferenceTargetRef ( role ) ;
70
+
71
+ private referenceTargetNodeGetter = ( role : string ) => {
72
+ const unreactiveTargetNode =
73
+ this . unreactiveNode . getReferenceTargetNode ( role ) ;
74
+ return unreactiveTargetNode
75
+ ? toReactiveINodeJS ( unreactiveTargetNode , this . cache )
76
+ : unreactiveTargetNode ;
77
+ } ;
78
+
79
+ private childrenGetter = ( role : string | undefined ) =>
80
+ this . unreactiveNode
81
+ . getChildren ( role )
82
+ . map ( ( node ) => toReactiveINodeJS ( node , this . cache ) ) ;
83
+
84
+ private allChildrenGetter = ( ) =>
85
+ this . unreactiveNode
86
+ . getAllChildren ( )
87
+ . map ( ( node ) => toReactiveINodeJS ( node , this . cache ) ) ;
88
+
59
89
getConcept ( ) : IConceptJS | undefined {
60
90
return this . unreactiveNode . getConcept ( ) ;
61
91
}
@@ -89,19 +119,19 @@ export class ReactiveINodeJS implements INodeJS {
89
119
}
90
120
91
121
getChildren ( role : string | undefined ) : INodeJS [ ] {
92
- const { track } = this . getOrCreateTrackAndTriggerForRole ( role ) ;
93
- track ( ) ;
94
- return this . unreactiveNode
95
- . getChildren ( role )
96
- . map ( ( node ) => toReactiveINodeJS ( node , this . cache ) ) ;
122
+ const ref = this . getOrCreateRefForRole (
123
+ this . byRoleRefToChildren ,
124
+ role ,
125
+ this . childrenGetter ,
126
+ ) ;
127
+ return ref . value ;
97
128
}
98
129
99
130
getAllChildren ( ) : INodeJS [ ] {
100
- const { track } = this . getOrCreateTrackAndTriggerForAllChildren ( ) ;
101
- track ( ) ;
102
- return this . unreactiveNode
103
- . getAllChildren ( )
104
- . map ( ( node ) => toReactiveINodeJS ( node , this . cache ) ) ;
131
+ if ( this . refForAllChildren == undefined ) {
132
+ this . refForAllChildren = shallowRef ( this . allChildrenGetter ( ) ) ;
133
+ }
134
+ return this . refForAllChildren ! . value ;
105
135
}
106
136
107
137
moveChild ( role : string | undefined , index : number , child : INodeJS ) : void {
@@ -134,18 +164,21 @@ export class ReactiveINodeJS implements INodeJS {
134
164
}
135
165
136
166
getReferenceTargetNode ( role : string ) : INodeJS | undefined {
137
- const { track } = this . getOrCreateTrackAndTriggerForRole ( role ) ;
138
- track ( ) ;
139
- const unreacitveNode = this . unreactiveNode . getReferenceTargetNode ( role ) ;
140
- return unreacitveNode
141
- ? toReactiveINodeJS ( unreacitveNode , this . cache )
142
- : unreacitveNode ;
167
+ const ref = this . getOrCreateRefForRole (
168
+ this . byRoleRefToReferenceTargetNode ,
169
+ role ,
170
+ this . referenceTargetNodeGetter ,
171
+ ) ;
172
+ return ref . value ;
143
173
}
144
174
145
175
getReferenceTargetRef ( role : string ) : INodeReferenceJS | undefined {
146
- const { track } = this . getOrCreateTrackAndTriggerForRole ( role ) ;
147
- track ( ) ;
148
- return this . unreactiveNode . getReferenceTargetRef ( role ) ;
176
+ const ref = this . getOrCreateRefForRole (
177
+ this . byRoleRefToReferenceTargetRef ,
178
+ role ,
179
+ this . referenceTargetRefGetter ,
180
+ ) ;
181
+ return ref . value ;
149
182
}
150
183
151
184
setReferenceTargetNode ( role : string , target : INodeJS | undefined ) : void {
@@ -173,69 +206,57 @@ export class ReactiveINodeJS implements INodeJS {
173
206
}
174
207
175
208
getPropertyValue ( role : string ) : string | undefined {
176
- const { track } = this . getOrCreateTrackAndTriggerForRole ( role ) ;
177
- track ( ) ;
178
- return this . unreactiveNode . getPropertyValue ( role ) ;
209
+ const ref = this . getOrCreateRefForRole ( this . byRoleRefToProperty , role , this . propertyGetter ) ;
210
+ return ref . value ;
179
211
}
180
212
181
213
setPropertyValue ( role : string , value : string | undefined ) : void {
182
214
this . unreactiveNode . setPropertyValue ( role , value ) ;
183
215
}
184
216
185
- private getOrCreateTrackAndTriggerForAllChildren ( ) : TrackAndTrigger {
186
- if ( this . trackAndTriggerForAllChildren === undefined ) {
187
- customRef ( ( track , trigger ) => {
188
- this . trackAndTriggerForAllChildren = {
189
- track ,
190
- trigger ,
191
- } ;
192
- return {
193
- // Getter and setter ar empty for the same reason as in `getOrCreateTrackAndTriggerForRole`
194
- get ( ) { } ,
195
- set ( ) { } ,
196
- } ;
197
- } ) ;
217
+ getOrCreateRefForRole < RoleT , ValueT > (
218
+ byRoleRefs : Map < RoleT , Ref < ValueT > > ,
219
+ role : RoleT ,
220
+ getValue : ( role : RoleT ) => ValueT ,
221
+ ) : Ref < ValueT > {
222
+ const maybeCreatedShallowRef = byRoleRefs . get ( role ) ;
223
+
224
+ if ( maybeCreatedShallowRef != undefined ) {
225
+ return maybeCreatedShallowRef ;
226
+ } else {
227
+ const newRef = shallowRef ( getValue ( role ) ) ;
228
+ byRoleRefs . set ( role , newRef ) ;
229
+ return newRef ;
198
230
}
199
- // `this.trackAndTriggerForAllChildren` will always be set, because the `factory`
200
- // argument of `customRef` will be evaluated immedialty.
201
- return this . trackAndTriggerForAllChildren ! ;
202
231
}
203
232
204
- private getOrCreateTrackAndTriggerForRole (
205
- role : string | undefined ,
206
- ) : TrackAndTrigger {
207
- const existing = this . byRoleTrackAndTrigger . get ( role ) ;
208
- if ( existing !== undefined ) {
209
- return existing ;
233
+ triggerChangeInChild ( role : Nullable < string > ) {
234
+ if ( this . refForAllChildren ) {
235
+ this . refForAllChildren . value = this . allChildrenGetter ( ) ;
236
+ }
237
+
238
+ const normalizedRole = role ?? undefined ;
239
+ const maybeRef = this . byRoleRefToChildren . get ( normalizedRole ) ;
240
+ if ( maybeRef ) {
241
+ maybeRef . value = this . childrenGetter ( normalizedRole ) ;
242
+ }
243
+ }
244
+
245
+ triggerChangeInReference ( role : string ) {
246
+ const maybeTargetNodeRef = this . byRoleRefToReferenceTargetNode . get ( role ) ;
247
+ if ( maybeTargetNodeRef ) {
248
+ maybeTargetNodeRef . value = this . referenceTargetNodeGetter ( role ) ;
249
+ }
250
+ const maybeTargetRefRef = this . byRoleRefToReferenceTargetRef . get ( role ) ;
251
+ if ( maybeTargetRefRef ) {
252
+ maybeTargetRefRef . value = this . referenceTargetRefGetter ( role ) ;
253
+ }
254
+ }
255
+
256
+ triggerChangeInProperty ( role : string ) {
257
+ const maybeRef = this . byRoleRefToProperty . get ( role ) ;
258
+ if ( maybeRef ) {
259
+ maybeRef . value = this . propertyGetter ( role ) ;
210
260
}
211
- let created ;
212
- customRef ( ( track , trigger ) => {
213
- created = {
214
- track,
215
- trigger,
216
- } ;
217
- this . byRoleTrackAndTrigger . set ( role , created ) ;
218
- return {
219
- // The getters and setters will never be called directly
220
- // and therefore the are empty.
221
- // We use `customRef` to get access to a pair of `trigger` and `track`
222
- // to call them directly from outside.
223
- get ( ) { } ,
224
- set ( ) { } ,
225
- } ;
226
- } ) ;
227
- // `created` will always be set, because the `factory`
228
- // argument of `customRef` will be evaluated immedialty.
229
- return created ! ;
230
- }
231
-
232
- triggerChangeInRole ( role : string | undefined | null ) {
233
- const normalizedRole =
234
- role !== undefined && role !== null ? role : undefined ;
235
- this . byRoleTrackAndTrigger . get ( normalizedRole ) ?. trigger ( ) ;
236
- }
237
-
238
- triggerChangeInAllChildren ( ) {
239
- this . trackAndTriggerForAllChildren ?. trigger ( ) ;
240
261
}
241
262
}
0 commit comments