1- import { MaybeRef , Ref , computed , customRef , triggerRef , unref } from 'vue' ;
1+ import {
2+ MaybeRef ,
3+ Ref ,
4+ computed ,
5+ customRef ,
6+ triggerRef ,
7+ unref ,
8+ watch ,
9+ isRef ,
10+ } from 'vue' ;
211import { vtkObject } from '@kitware/vtk.js/interfaces' ;
312import { capitalize } from '@kitware/vtk.js/macros' ;
413import { onPausableVTKEvent } from '@/src/composables/onPausableVTKEvent' ;
@@ -52,23 +61,48 @@ export function vtkFieldRef<T extends Maybe<vtkObject>, R>(
5261 factory : GetterSetterFactory < R >
5362) : Ref < R > ;
5463
55- export function vtkFieldRef < T extends Maybe < vtkObject > > (
56- obj : MaybeRef < T > ,
57- fieldNameOrFactory : string | GetterSetterFactory < any >
58- ) : any {
59- let getter : ( ) => any ;
60- let setter : ( v : any ) => boolean | undefined ;
64+ /**
65+ * A customRef wrapper that triggers the ref based on a vtk object modification event,
66+ * using the provided function as a getter. The ref will be read-only.
67+ * @param obj The vtkObject or a Ref to it.
68+ * @param getterFunc A function that retrieves the value from the vtkObject.
69+ */
70+ export function vtkFieldRef < TValue extends Maybe < vtkObject > , R > (
71+ obj : MaybeRef < TValue > ,
72+ getterFunc : ( ) => R
73+ ) : Ref < R > ;
74+
75+ export function vtkFieldRef < TValue extends Maybe < vtkObject > > (
76+ obj : MaybeRef < TValue > ,
77+ config : string | GetterSetterFactory < any > | ( ( ) => any )
78+ ) : Ref < any > {
79+ let _sourceGet : ( ) => any ;
80+ let _finalSetter : ( v : any ) => boolean | undefined ;
6181 let lastValue : any ;
6282 let lastValueIsArray = false ;
6383
64- if ( typeof fieldNameOrFactory === 'string' ) {
65- const getterName = `get${ capitalize ( fieldNameOrFactory ) } ` as keyof T ;
66- const setterName = `set${ capitalize ( fieldNameOrFactory ) } ` as keyof T ;
84+ const updateLastValueAndReturnClonedIfArray = ( rawValue : any ) => {
85+ if ( Array . isArray ( rawValue ) ) {
86+ lastValue = [ ...rawValue ] ;
87+ lastValueIsArray = true ;
88+ } else {
89+ lastValue = rawValue ;
90+ lastValueIsArray = false ;
91+ }
92+ return lastValue ;
93+ } ;
94+
95+ if ( typeof config === 'string' ) {
96+ const fieldName = config ;
97+ const getterName = `get${ capitalize ( fieldName ) } ` as keyof TValue ;
98+ const setterName = `set${ capitalize ( fieldName ) } ` as keyof TValue ;
6799
68- const _getter = computed (
100+ const _objGetter = computed (
69101 ( ) => unref ( obj ) ?. [ getterName ] as ( ( ) => any ) | undefined
70102 ) ;
71- const _setter = computed (
103+ _sourceGet = ( ) => _objGetter . value ?.( ) ;
104+
105+ const _objSetter = computed (
72106 ( ) =>
73107 unref ( obj ) ?. [ setterName ] as ( ( ...args : any [ ] ) => boolean ) | undefined
74108 ) ;
@@ -79,67 +113,54 @@ export function vtkFieldRef<T extends Maybe<vtkObject>>(
79113 return val ? setterName in val : false ;
80114 } ) ;
81115
82- getter = ( ) => {
83- const value = _getter . value ?.( ) ;
84- // create a new reference to trigger update
85- if ( Array . isArray ( value ) ) {
86- lastValue = [ ...value ] ;
87- lastValueIsArray = true ;
88- return lastValue ;
89- }
90- lastValue = value ;
91- lastValueIsArray = false ;
92- return value ;
93- } ;
94-
95- setter = ( v : any ) => {
96- const set = _setter . value ;
116+ _finalSetter = ( v : any ) => {
117+ const set = _objSetter . value ;
97118 if ( ! notNull . value ) return false ;
98119 if ( ! hasSetter . value || ! set )
99- throw new Error ( `No setter for field '${ fieldNameOrFactory } '` ) ;
120+ throw new Error ( `No setter for field '${ fieldName } '` ) ;
100121
101- // handle certain array setters not accepting an array as input
102122 if ( Array . isArray ( v ) && set . length === v . length ) {
103123 return ( set as ArraySetter ) ( ...v ) ;
104124 }
105125 return set ( v ) ;
106126 } ;
107- } else {
108- const originalGetter = fieldNameOrFactory . get ;
109- getter = ( ) => {
110- const value = originalGetter ( ) ;
111- // create a new reference to trigger update
112- if ( Array . isArray ( value ) ) {
113- lastValue = [ ...value ] ;
114- lastValueIsArray = true ;
115- return lastValue ;
116- }
117- lastValue = value ;
118- lastValueIsArray = false ;
119- return value ;
127+ } else if ( typeof config === 'function' ) {
128+ _sourceGet = config ;
129+ _finalSetter = ( ) => {
130+ console . warn (
131+ '[vtkFieldRef] Attempted to set a ref that was defined with a getter function only. This ref is read-only.'
132+ ) ;
133+ return false ;
120134 } ;
121- setter = fieldNameOrFactory . set ;
135+ } else {
136+ _sourceGet = config . get ;
137+ _finalSetter = config . set ;
122138 }
123139
124- let pause : ( ) => void ;
125- let resume : ( ) => void ;
140+ const refGetter = ( ) => {
141+ const rawValue = _sourceGet ( ) ;
142+ return updateLastValueAndReturnClonedIfArray ( rawValue ) ;
143+ } ;
144+
145+ refGetter ( ) ; // Initialize lastValue
146+
147+ let currentPauseListener : ( ) => void = ( ) => { } ;
148+ let currentResumeListener : ( ) => void = ( ) => { } ;
126149
127150 const ref = customRef < any > ( ( track , trigger ) => {
128151 return {
129152 get : ( ) => {
130153 track ( ) ;
131- return getter ( ) ;
154+ return refGetter ( ) ;
132155 } ,
133156 set : ( v ) => {
157+ currentPauseListener ( ) ;
134158 let changed = false ;
135- pause ( ) ;
136-
137159 try {
138- const ret = setter ( v ) ;
139- // in the event a setter returns undefined, assume something changed.
160+ const ret = _finalSetter ( v ) ;
140161 changed = ret === undefined ? true : ret ;
141162 } finally {
142- resume ( ) ;
163+ currentResumeListener ( ) ;
143164 }
144165
145166 if ( changed ) {
@@ -150,36 +171,59 @@ export function vtkFieldRef<T extends Maybe<vtkObject>>(
150171 } ) ;
151172
152173 const onModified = batchForNextTask ( ( ) => {
153- if ( unref ( obj ) ?. isDeleted ( ) ) return ;
154-
155- // Special handling for array values that might have been mutated
156- if ( lastValueIsArray ) {
157- const currentValue = getter ( ) ;
158- if ( Array . isArray ( currentValue ) ) {
159- const previousValue = lastValue ;
160- // Check if array contents changed
161- if (
162- previousValue . length !== currentValue . length ||
163- previousValue . some (
164- ( val : any , idx : number ) => val !== currentValue [ idx ]
165- )
166- ) {
167- // Values changed, trigger the ref
168- triggerRef ( ref ) ;
169- return ;
170- }
174+ const currentVtkObj = unref ( obj ) ;
175+ if ( currentVtkObj ?. isDeleted ( ) ) return ;
176+
177+ const currentSourceValue = _sourceGet ( ) ;
178+ let valueChanged = false ;
179+
180+ if ( lastValueIsArray && Array . isArray ( currentSourceValue ) ) {
181+ if (
182+ lastValue . length !== currentSourceValue . length ||
183+ lastValue . some (
184+ ( val : any , idx : number ) => val !== currentSourceValue [ idx ]
185+ )
186+ ) {
187+ valueChanged = true ;
171188 }
189+ } else if ( lastValue !== currentSourceValue ) {
190+ valueChanged = true ;
172191 }
173192
174- // Always trigger for non-array values
175- triggerRef ( ref ) ;
193+ if ( valueChanged ) {
194+ triggerRef ( ref ) ;
195+ }
176196 } ) ;
177197
178- ( { pause, resume } = onPausableVTKEvent (
179- obj as vtkObject ,
180- 'onModified' ,
181- onModified
182- ) ) ;
198+ const setupVtkListener = ( targetVtkObject : Maybe < vtkObject > ) => {
199+ currentPauseListener ( ) ; // Clean up previous listener's effect
200+
201+ if ( targetVtkObject && ! targetVtkObject . isDeleted ( ) ) {
202+ const { pause : newPause , resume : newResume } = onPausableVTKEvent (
203+ targetVtkObject ,
204+ 'onModified' ,
205+ onModified
206+ ) ;
207+ currentPauseListener = newPause ;
208+ currentResumeListener = newResume ;
209+ } else {
210+ currentPauseListener = ( ) => { } ;
211+ currentResumeListener = ( ) => { } ;
212+ }
213+ } ;
214+
215+ if ( isRef ( obj ) ) {
216+ watch (
217+ obj ,
218+ ( newVtkObj ) => {
219+ setupVtkListener ( newVtkObj ) ;
220+ triggerRef ( ref ) ; // Trigger dependents as the source object has changed
221+ } ,
222+ { immediate : true }
223+ ) ;
224+ } else {
225+ setupVtkListener ( unref ( obj ) ) ;
226+ }
183227
184228 return ref ;
185229}
0 commit comments