@@ -12,6 +12,12 @@ function updateWidget(id, objectState, widgetState) {
1212 Object . assign ( widgetState . data , objectState . refs ) ;
1313}
1414
15+ function updateServerState ( serverState , partialState ) {
16+ for ( const [ key , value ] of Object . entries ( partialState ) ) {
17+ serverState [ key ] = JSON . stringify ( value ) ;
18+ }
19+ }
20+
1521export class DataclassManager {
1622 constructor ( ) {
1723 this . client = null ;
@@ -24,6 +30,9 @@ export class DataclassManager {
2430 this . dataToVue = { } ;
2531 this . pendingClientServerQueue = [ ] ;
2632 this . pendingFlushRequest = 0 ;
33+ this . triggers = { } ;
34+ this . deepReactiveWatchers = { } ;
35+ this . pendingDeepReactives = { } ;
2736 }
2837
2938 connect ( client ) {
@@ -40,19 +49,24 @@ export class DataclassManager {
4049 return ;
4150 }
4251
43- Object . assign ( this . dataStates [ id ] . server , state ) ;
52+ // Capture server state for update comparison
53+ updateServerState ( this . dataStates [ id ] . server , state ) ;
4454 for ( const [ key , value ] of Object . entries ( state ) ) {
4555 if ( this . isDataClass ( id , key ) ) {
4656 await this . handleNestedDataClass ( id , key , value ) ;
4757 } else {
48- this . dataStates [ id ] . refs [ key ] . value = value ;
58+ this . dataStates [ id ] . refs [ key ] . value = this . wrapValue (
59+ id ,
60+ key ,
61+ value ,
62+ ) ;
4963 }
5064 }
5165 } ) ;
5266 }
5367
5468 updateServer ( id , name , value ) {
55- this . pendingClientServerQueue . push ( [ id , name , value ] ) ;
69+ this . pendingClientServerQueue . push ( [ id , name , JSON . stringify ( value ) ] ) ;
5670 this . flushToServer ( ) ;
5771 }
5872
@@ -64,7 +78,8 @@ export class DataclassManager {
6478 const msg = { } ;
6579 let sendingSomething = 0 ;
6680 while ( this . pendingClientServerQueue . length ) {
67- const [ id , name , value ] = this . pendingClientServerQueue . shift ( ) ;
81+ const [ id , name , strValue ] = this . pendingClientServerQueue . shift ( ) ;
82+ const value = JSON . parse ( strValue ) ;
6883 let valueToSend = value ;
6984
7085 // Handle nested dataclass
@@ -86,10 +101,7 @@ export class DataclassManager {
86101 }
87102 }
88103 }
89- if (
90- JSON . stringify ( this . dataStates [ id ] . server [ name ] ) ===
91- JSON . stringify ( valueToSend )
92- ) {
104+ if ( this . dataStates [ id ] . server [ name ] === strValue ) {
93105 continue ;
94106 }
95107 if ( ! msg [ id ] ) {
@@ -116,13 +128,44 @@ export class DataclassManager {
116128 }
117129
118130 isDataClass ( id , name ) {
119- return this . typeDefinitions [
120- this . dataTypes [ id ]
121- ] . dataclass_containers . includes ( name ) ;
131+ return this . typeDefinitions [ this . dataTypes [ id ] ] . dataclass_containers [ name ] ;
122132 }
123133
124134 isClientOnly ( id , name ) {
125- return this . typeDefinitions [ this . dataTypes [ id ] ] ?. client_only . includes ( name ) ;
135+ return this . typeDefinitions [ this . dataTypes [ id ] ] ?. client_only [ name ] ;
136+ }
137+
138+ isDeepReactive ( id , name ) {
139+ return this . typeDefinitions [ this . dataTypes [ id ] ] ?. deep_reactive [ name ] ;
140+ }
141+
142+ wrapValue ( id , name , value ) {
143+ if ( ! this . isDeepReactive ( id , name ) ) {
144+ return value ;
145+ }
146+
147+ const fullKey = `${ id } ::${ name } ` ;
148+ const unwatch = this . deepReactiveWatchers [ fullKey ] ;
149+ if ( unwatch ) {
150+ unwatch ( ) ;
151+ }
152+ this . deepReactiveWatchers [ fullKey ] = null ;
153+
154+ if ( value === null || value === undefined ) {
155+ return value ;
156+ }
157+
158+ const r = reactive ( value ) ;
159+ const trigger = this . triggers [ fullKey ] ;
160+ if ( ! trigger ) {
161+ if ( ! this . pendingDeepReactives [ id ] ) {
162+ this . pendingDeepReactives [ id ] = [ ] ;
163+ }
164+ this . pendingDeepReactives [ id ] . push ( [ fullKey , r ] ) ;
165+ } else {
166+ this . deepReactiveWatchers [ fullKey ] = watch ( r , trigger ) ;
167+ }
168+ return r ;
126169 }
127170
128171 async handleNestedDataClass ( id , key , value ) {
@@ -156,9 +199,9 @@ export class DataclassManager {
156199 }
157200 }
158201 if ( ! this . dataStates [ id ] . refs [ key ] ) {
159- this . dataStates [ id ] . refs [ key ] = ref ( newArray ) ;
202+ this . dataStates [ id ] . refs [ key ] = ref ( this . wrapValue ( id , key , newArray ) ) ;
160203 } else {
161- this . dataStates [ id ] . refs [ key ] . value = newArray ;
204+ this . dataStates [ id ] . refs [ key ] . value = this . wrapValue ( id , key , newArray ) ;
162205 }
163206 } else if ( typeof value === "string" ) {
164207 // direct dataclass
@@ -204,6 +247,18 @@ export class DataclassManager {
204247 }
205248 }
206249
250+ getTrigger ( id , key , refs ) {
251+ const fullKey = `${ id } ::${ key } ` ;
252+ const fn = this . triggers [ fullKey ] ;
253+ if ( fn ) {
254+ return fn ;
255+ }
256+ this . triggers [ fullKey ] = ( ) => {
257+ this . updateServer ( id , key , refs [ key ] . value ) ;
258+ } ;
259+ return this . triggers [ fullKey ] ;
260+ }
261+
207262 async fetchState ( id ) {
208263 const refs = { _id : id } ;
209264 const data = await this . client
@@ -212,7 +267,10 @@ export class DataclassManager {
212267 . call ( "trame.dataclass.state.get" , [ id ] ) ;
213268
214269 this . dataTypes [ id ] = data . definition ;
215- this . dataStates [ id ] = { refs, server : data . state } ;
270+ this . dataStates [ id ] = {
271+ refs,
272+ server : JSON . parse ( JSON . stringify ( data . state ) ) ,
273+ } ;
216274
217275 if ( ! this . typeDefinitions [ data . definition ] ) {
218276 await this . fetchDefinition ( data . definition ) ;
@@ -224,13 +282,18 @@ export class DataclassManager {
224282 refs [ key ] = ref ( null ) ;
225283 await this . handleNestedDataClass ( id , key , value ) ;
226284 } else {
227- refs [ key ] = ref ( value ) ;
285+ refs [ key ] = ref ( this . wrapValue ( id , key , value ) ) ;
228286 }
229287 if ( ! this . isClientOnly ( id , key ) ) {
230- watch (
231- ( ) => refs [ key ] . value ,
232- ( v ) => this . updateServer ( id , key , v ) ,
233- ) ;
288+ const trigger = this . getTrigger ( id , key , refs ) ;
289+ watch ( refs [ key ] , trigger ) ;
290+
291+ const items = this . pendingDeepReactives [ id ] || [ ] ;
292+ while ( items . length ) {
293+ const [ fullKey , r ] = items . pop ( ) ;
294+ const trigger = this . getTrigger ( id , key , refs ) ;
295+ this . deepReactiveWatchers [ fullKey ] = watch ( r , trigger ) ;
296+ }
234297 }
235298 }
236299
@@ -251,7 +314,19 @@ export class DataclassManager {
251314 . getSession ( )
252315 . call ( "trame.dataclass.definition.get" , [ id ] ) ;
253316
254- this . typeDefinitions [ id ] = data ;
317+ this . typeDefinitions [ id ] = {
318+ ...data ,
319+ dataclass_containers : { } ,
320+ client_only : { } ,
321+ deep_reactive : { } ,
322+ } ;
323+ const toDict = [ "dataclass_containers" , "client_only" , "deep_reactive" ] ;
324+ while ( toDict . length ) {
325+ const arrayName = toDict . pop ( ) ;
326+ for ( let i = 0 ; i < data [ arrayName ] . length ; i ++ ) {
327+ this . typeDefinitions [ id ] [ arrayName ] [ data [ arrayName ] [ i ] ] = 1 ;
328+ }
329+ }
255330 }
256331
257332 unlink ( dataId , componentId ) {
0 commit comments