1+ /* eslint-disable react/no-unused-state,@typescript-eslint/consistent-type-assertions */
12import React , {
23 ComponentType ,
34 PropsWithChildren ,
@@ -87,8 +88,15 @@ export type TreeState<
8788 TData extends NodeData
8889> = Readonly < {
8990 component : ComponentType < TNodeComponentProps > ;
90- methods : OverridableMethods ;
9191 order ?: ReadonlyArray < string | symbol > ;
92+ computeTree : TreeComputer <
93+ TNodeComponentProps ,
94+ TNodeRecord ,
95+ TUpdateOptions ,
96+ TData ,
97+ any ,
98+ any
99+ > ;
92100 records : Readonly < Record < string , TNodeRecord | undefined > > ;
93101 treeData ?: any ;
94102 recomputeTree : ( options ?: TUpdateOptions ) => Promise < void > ;
@@ -126,17 +134,81 @@ export const Row = <TData extends NodeData>({
126134 />
127135) ;
128136
129- const computeTree = (
130- { treeWalker} : TreeProps < any , any > ,
131- state : TreeState < any , NodeRecord < any > , any , any > ,
132- options : UpdateOptions = { } ,
137+ export type TreeCreatorOptions <
138+ TNodeComponentProps extends NodeComponentProps < TData > ,
139+ TNodeRecord extends NodeRecord < TData > ,
140+ TUpdateOptions extends UpdateOptions ,
141+ TData extends NodeData ,
142+ TState extends TreeState <
143+ TNodeComponentProps ,
144+ TNodeRecord ,
145+ TUpdateOptions ,
146+ TData
147+ >
148+ > = Readonly < {
149+ createRecord : ( data : TData , state : TState ) => TNodeRecord ;
150+ shouldUpdateRecords : ( options : TUpdateOptions ) => boolean ;
151+ updateRecord : (
152+ record : TNodeRecord ,
153+ recordId : string | symbol ,
154+ options : TUpdateOptions ,
155+ ) => void ;
156+ updateRecordOnWalk : ( record : TNodeRecord , options : TUpdateOptions ) => void ;
157+ } > ;
158+
159+ export type TreeComputer <
160+ TNodeComponentProps extends NodeComponentProps < TData > ,
161+ TNodeRecord extends NodeRecord < TData > ,
162+ TUpdateOptions extends UpdateOptions ,
163+ TData extends NodeData ,
164+ TProps extends TreeProps < TNodeComponentProps , TData > ,
165+ TState extends TreeState <
166+ TNodeComponentProps ,
167+ TNodeRecord ,
168+ TUpdateOptions ,
169+ TData
170+ >
171+ > = (
172+ props : TProps ,
173+ state : TState ,
174+ options ?: TUpdateOptions ,
175+ ) => Pick < TState , 'order' | 'records' > ;
176+
177+ export const createTreeComputer = <
178+ TNodeComponentProps extends NodeComponentProps < TData > ,
179+ TNodeRecord extends NodeRecord < TData > ,
180+ TUpdateOptions extends UpdateOptions ,
181+ TData extends NodeData ,
182+ TProps extends TreeProps < TNodeComponentProps , TData > ,
183+ TState extends TreeState <
184+ TNodeComponentProps ,
185+ TNodeRecord ,
186+ TUpdateOptions ,
187+ TData
188+ >
189+ > ( {
190+ createRecord,
191+ shouldUpdateRecords,
192+ updateRecord,
193+ updateRecordOnWalk,
194+ } : TreeCreatorOptions <
195+ TNodeComponentProps ,
196+ TNodeRecord ,
197+ TUpdateOptions ,
198+ TData ,
199+ TState
200+ > ) : TreeComputer <
201+ TNodeComponentProps ,
202+ TNodeRecord ,
203+ TUpdateOptions ,
204+ TData ,
205+ TProps ,
206+ TState
207+ > => (
208+ { treeWalker} ,
209+ state ,
210+ options = { } as TUpdateOptions ,
133211) : Pick < TreeState < any , any , any , any > , 'order' | 'records' > => {
134- const {
135- constructRecord,
136- shouldUpdateRecords,
137- updateRecord,
138- updateRecordDuringTreeWalk,
139- } = state . methods ;
140212 const order : Array < string | symbol > = [ ] ;
141213 const records = { ...state . records } ;
142214 const iter = treeWalker ( options . refreshNodes ?? false ) ;
@@ -170,10 +242,10 @@ const computeTree = (
170242 const record = records [ id as string ] ;
171243
172244 if ( ! record ) {
173- records [ id as string ] = constructRecord ( value , state ) ;
245+ records [ id as string ] = createRecord ( value , state ) ;
174246 } else {
175247 record . data = value ;
176- updateRecordDuringTreeWalk ( record , options ) ;
248+ updateRecordOnWalk ( record , options ) ;
177249 }
178250 }
179251
@@ -192,7 +264,7 @@ const computeTree = (
192264 } ;
193265} ;
194266
195- abstract class Tree <
267+ class Tree <
196268 TNodeComponentProps extends NodeComponentProps < TData > ,
197269 TNodeRecord extends NodeRecord < TData > ,
198270 TUpdateOptions extends UpdateOptions ,
@@ -215,7 +287,7 @@ abstract class Tree<
215287 state : TreeState < any , any , any , any > ,
216288 ) : Partial < TreeState < any , any , any , any > > {
217289 const { children : component , itemData : treeData , treeWalker} = props ;
218- const { treeWalker : oldTreeWalker , order } = state ;
290+ const { computeTree , order , treeWalker : oldTreeWalker } = state ;
219291
220292 return {
221293 component,
@@ -226,68 +298,23 @@ abstract class Tree<
226298 } ;
227299 }
228300
229- protected static constructRecord (
230- data : NodeData ,
231- { recomputeTree} : TreeState < any , any , any , any > ,
232- ) : NodeRecord < NodeData > {
233- const record = {
234- data,
235- isOpen : data . isOpenByDefault ,
236- async toggle ( ) : Promise < void > {
237- record . isOpen = ! record . isOpen ;
238- await recomputeTree ( { refreshNodes : record . isOpen } ) ;
239- } ,
240- } ;
241-
242- return record ;
243- }
244-
245- protected static shouldUpdateRecords ( {
246- opennessState,
247- useDefaultOpenness = false ,
248- } : UpdateOptions ) : boolean {
249- return ! ! opennessState || useDefaultOpenness ;
250- }
251-
252- protected static updateRecord (
253- record : NodeRecord < NodeData > ,
254- recordId : string ,
255- { opennessState, useDefaultOpenness = false } : UpdateOptions ,
256- ) : void {
257- record . isOpen = useDefaultOpenness
258- ? record . data . isOpenByDefault
259- : opennessState ?. [ recordId ] ?? record . isOpen ;
260- }
261-
262- protected static updateRecordDuringTreeWalk (
263- record : NodeRecord < NodeData > ,
264- { useDefaultOpenness = false } : UpdateOptions ,
265- ) : void {
266- if ( useDefaultOpenness ) {
267- record . isOpen = record . data . isOpenByDefault ;
268- }
269- }
270-
271301 protected readonly list : React . RefObject < TListComponent > = React . createRef ( ) ;
272302
273- protected constructor ( props : TProps , context : any ) {
303+ public constructor ( props : TProps , context : any ) {
274304 super ( props , context ) ;
275305
276- this . state = this . constructState ( {
306+ this . state = {
277307 component : props . children ,
278- // Remembering the current constructor to use overridable static methods
279- // in computeTree function
280- methods : ( this . constructor as unknown ) as OverridableMethods ,
281308 recomputeTree : this . recomputeTree . bind ( this ) ,
282309 records : { } ,
283310 treeWalker : props . treeWalker ,
284- } ) ;
311+ } as TState ;
285312 }
286313
287314 public async recomputeTree ( options ?: TUpdateOptions ) : Promise < void > {
288315 return new Promise ( ( resolve ) => {
289316 this . setState < never > (
290- ( prevState ) => computeTree ( this . props , prevState , options ) ,
317+ ( prevState ) => prevState . computeTree ( this . props , prevState , options ) ,
291318 resolve ,
292319 ) ;
293320 } ) ;
@@ -301,12 +328,6 @@ abstract class Tree<
301328 // eslint-disable-next-line react/destructuring-assignment
302329 this . list . current ?. scrollToItem ( this . state . order ! . indexOf ( id ) , align ) ;
303330 }
304-
305- public abstract render ( ) : React . ReactNode ;
306-
307- protected abstract constructState (
308- state : TreeState < any , any , any , any > ,
309- ) : TState ;
310331}
311332
312333export default Tree ;
0 commit comments