@@ -218,39 +218,68 @@ export const Instances: ForwardRefComponent<InstancesProps, THREE.InstancedMesh>
218
218
)
219
219
} )
220
220
221
- export interface MergedProps extends InstancesProps {
222
- meshes : THREE . Mesh [ ]
223
- children : React . ReactNode
221
+ export interface MergedProps extends Omit < InstancesProps , 'children' > {
222
+ meshes : THREE . Mesh [ ] | Record < string , THREE . Object3D >
223
+ children : (
224
+ ...instances : [ React . FC < InstanceProps > & Record < string , React . FC < InstanceProps > > , ...React . FC < InstanceProps > [ ] ]
225
+ ) => React . ReactNode
224
226
}
225
227
226
- export const Merged : ForwardRefComponent < any , THREE . Group > = React . forwardRef < THREE . Group , any > ( function Merged (
227
- { meshes, children, ...rest } ,
228
- ref
229
- ) {
230
- const instances : React . FC [ ] = [ ]
231
-
232
- if ( Array . isArray ( meshes ) ) {
233
- for ( const mesh of meshes ) {
234
- if ( mesh ?. isMesh ) {
235
- instances . push ( ( props ) => (
236
- < Instances key = { mesh . geometry . uuid } geometry = { mesh . geometry } material = { mesh . material } { ...rest } { ...props } />
237
- ) )
238
- }
239
- }
240
- } else if ( meshes != null && typeof meshes === 'object' ) {
241
- for ( const key in meshes ) {
242
- const mesh = meshes [ key ]
243
- if ( mesh ?. isMesh ) {
244
- instances . push ( ( props ) => (
245
- < Instances key = { mesh . geometry . uuid } geometry = { mesh . geometry } material = { mesh . material } { ...rest } { ...props } />
246
- ) )
247
- }
248
- }
249
- }
228
+ // TODO: make this non-recursive and type-safe
229
+ export const Merged : ForwardRefComponent < MergedProps , THREE . Group > = /* @__PURE__ */ React . forwardRef <
230
+ THREE . Group ,
231
+ MergedProps
232
+ > ( function Merged ( { meshes, children, ...props } , ref ) {
233
+ const isArray = Array . isArray ( meshes )
234
+ // Filter out meshes from collections, which may contain non-meshes
235
+ // @ts -expect-error
236
+ if ( ! isArray ) for ( const key of Object . keys ( meshes ) ) if ( ! meshes [ key ] . isMesh ) delete meshes [ key ]
237
+
238
+ const render = ( args ) =>
239
+ isArray
240
+ ? // @ts -expect-error
241
+ children ( ...args )
242
+ : children (
243
+ // @ts -expect-error
244
+ Object . keys ( meshes )
245
+ // @ts -expect-error
246
+ . filter ( ( key ) => meshes [ key ] . isMesh )
247
+ . reduce ( ( acc , key , i ) => ( { ...acc , [ key ] : args [ i ] } ) , { } )
248
+ )
249
+
250
+ // @ts -expect-error
251
+ const components = ( isArray ? meshes : Object . values ( meshes ) ) . map ( ( { geometry, material } ) => (
252
+ < Instances key = { geometry . uuid } geometry = { geometry } material = { material } { ...props } />
253
+ ) )
250
254
251
- return < group ref = { ref } > { children ( instances ) } </ group >
255
+ return < group ref = { ref } > { renderRecursive ( render , components ) } </ group >
252
256
} )
253
257
258
+ // https://github.com/jamesplease/react-composer
259
+ function renderRecursive (
260
+ render : Function ,
261
+ components : Array < React . ReactElement < { children : any } > | Function > ,
262
+ results : unknown [ ] = [ ]
263
+ ) : React . ReactElement {
264
+ // Once components is exhausted, we can render out the results array.
265
+ if ( ! components [ 0 ] ) {
266
+ return render ( results )
267
+ }
268
+
269
+ // Continue recursion for remaining items.
270
+ // results.concat([value]) ensures [...results, value] instead of [...results, ...value]
271
+ function nextRender ( value ) {
272
+ return renderRecursive ( render , components . slice ( 1 ) , results . concat ( [ value ] ) )
273
+ }
274
+
275
+ // Each props.components entry is either an element or function [element factory]
276
+ return typeof components [ 0 ] === 'function'
277
+ ? // When it is a function, produce an element by invoking it with "render component values".
278
+ components [ 0 ] ( { results, render : nextRender } )
279
+ : // When it is an element, enhance the element's props with the render prop.
280
+ React . cloneElement ( components [ 0 ] , { children : nextRender } )
281
+ }
282
+
254
283
/** Idea and implementation for global instances and instanced attributes by
255
284
/* Matias Gonzalez Fernandez https://x.com/matiNotFound
256
285
/* and Paul Henschel https://x.com/0xca0a
0 commit comments