Skip to content

Commit 523a04e

Browse files
committed
Added ref forwarding to Flex and Box
This would with various helpers and hooks that use refs to somehow affect the component, say rotate it or draw a bounding box
1 parent 8d3be3a commit 523a04e

File tree

2 files changed

+165
-147
lines changed

2 files changed

+165
-147
lines changed

src/Box.tsx

Lines changed: 78 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -2,82 +2,84 @@ import React, { useLayoutEffect, useRef, useMemo, useState } from 'react'
22
import * as THREE from 'three'
33
import Yoga from 'yoga-layout-prebuilt'
44
import { ReactThreeFiber, useFrame } from '@react-three/fiber'
5+
import mergeRefs from 'react-merge-refs'
56

67
import { setYogaProperties, rmUndefFromObj } from './util'
78
import { boxContext, flexContext, SharedBoxContext } from './context'
89
import { R3FlexProps } from './props'
910
import { useReflow, useContext } from './hooks'
1011

11-
/**
12-
* Box container for 3D Objects.
13-
* For containing Boxes use `<Flex />`.
14-
*/
15-
export function Box({
16-
// Non-flex props
17-
children,
18-
centerAnchor,
19-
20-
// flex props
21-
flexDirection,
22-
flexDir,
23-
dir,
24-
25-
alignContent,
26-
alignItems,
27-
alignSelf,
28-
align,
29-
30-
justifyContent,
31-
justify,
32-
33-
flexBasis,
34-
basis,
35-
flexGrow,
36-
grow,
37-
38-
flexShrink,
39-
shrink,
40-
41-
flexWrap,
42-
wrap,
43-
44-
margin,
45-
m,
46-
marginBottom,
47-
marginLeft,
48-
marginRight,
49-
marginTop,
50-
mb,
51-
ml,
52-
mr,
53-
mt,
54-
55-
padding,
56-
p,
57-
paddingBottom,
58-
paddingLeft,
59-
paddingRight,
60-
paddingTop,
61-
pb,
62-
pl,
63-
pr,
64-
pt,
65-
66-
height,
67-
width,
68-
69-
maxHeight,
70-
maxWidth,
71-
minHeight,
72-
minWidth,
73-
74-
// other
75-
...props
76-
}: {
12+
export type BoxProps = {
7713
centerAnchor?: boolean
7814
children: React.ReactNode | ((width: number, height: number, centerAnchor?: boolean) => React.ReactNode)
7915
} & R3FlexProps &
80-
Omit<ReactThreeFiber.Object3DNode<THREE.Group, typeof THREE.Group>, 'children'>) {
16+
Omit<ReactThreeFiber.Object3DNode<THREE.Group, typeof THREE.Group>, 'children'>
17+
18+
function BoxImpl(
19+
{
20+
// Non-flex props
21+
children,
22+
centerAnchor,
23+
24+
// flex props
25+
flexDirection,
26+
flexDir,
27+
dir,
28+
29+
alignContent,
30+
alignItems,
31+
alignSelf,
32+
align,
33+
34+
justifyContent,
35+
justify,
36+
37+
flexBasis,
38+
basis,
39+
flexGrow,
40+
grow,
41+
42+
flexShrink,
43+
shrink,
44+
45+
flexWrap,
46+
wrap,
47+
48+
margin,
49+
m,
50+
marginBottom,
51+
marginLeft,
52+
marginRight,
53+
marginTop,
54+
mb,
55+
ml,
56+
mr,
57+
mt,
58+
59+
padding,
60+
p,
61+
paddingBottom,
62+
paddingLeft,
63+
paddingRight,
64+
paddingTop,
65+
pb,
66+
pl,
67+
pr,
68+
pt,
69+
70+
height,
71+
width,
72+
73+
maxHeight,
74+
maxWidth,
75+
minHeight,
76+
minWidth,
77+
78+
// other
79+
...props
80+
}: BoxProps,
81+
ref: React.Ref<THREE.Group>
82+
) {
8183
// must memoize or the object literal will cause every dependent of flexProps to rerender everytime
8284
const flexProps: R3FlexProps = useMemo(() => {
8385
const _flexProps = {
@@ -228,10 +230,18 @@ export function Box({
228230
const sharedBoxContext = useMemo<SharedBoxContext>(() => ({ node, size, centerAnchor }), [node, size, centerAnchor])
229231

230232
return (
231-
<group ref={group} {...props}>
233+
<group ref={mergeRefs([group, ref])} {...props}>
232234
<boxContext.Provider value={sharedBoxContext}>
233235
{typeof children === 'function' ? children(size[0], size[1], centerAnchor) : children}
234236
</boxContext.Provider>
235237
</group>
236238
)
237239
}
240+
241+
/**
242+
* Box container for 3D Objects.
243+
* For containing Boxes use `<Flex />`.
244+
*/
245+
export const Box = React.forwardRef<THREE.Group, BoxProps>(BoxImpl)
246+
247+
Box.displayName = 'Box'

src/Flex.tsx

Lines changed: 87 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import React, { useLayoutEffect, useMemo, useCallback, PropsWithChildren, useRef } from 'react'
22
import Yoga, { YogaNode } from 'yoga-layout-prebuilt'
3-
import { Vector3, Group, Box3, Object3D } from 'three'
3+
import * as THREE from 'three'
44
import { useFrame, useThree, ReactThreeFiber } from '@react-three/fiber'
5+
import mergeRefs from 'react-merge-refs'
56

67
import {
78
setYogaProperties,
@@ -33,86 +34,86 @@ export type FlexProps = PropsWithChildren<
3334
centerAnchor?: boolean
3435
}> &
3536
R3FlexProps &
36-
Omit<ReactThreeFiber.Object3DNode<THREE.Group, typeof Group>, 'children'>
37+
Omit<ReactThreeFiber.Object3DNode<THREE.Group, typeof THREE.Group>, 'children'>
3738
>
3839
interface BoxesItem {
3940
node: YogaNode
40-
group: Group
41+
group: THREE.Group
4142
flexProps: R3FlexProps
4243
centerAnchor: boolean
4344
}
4445

45-
/**
46-
* Flex container. Can contain Boxes
47-
*/
48-
export function Flex({
49-
// Non flex props
50-
size = [1, 1, 1],
51-
yogaDirection = 'ltr',
52-
plane = 'xy',
53-
children,
54-
scaleFactor = 100,
55-
onReflow,
56-
disableSizeRecalc,
57-
centerAnchor: rootCenterAnchor,
58-
59-
// flex props
60-
61-
flexDirection,
62-
flexDir,
63-
dir,
64-
65-
alignContent,
66-
alignItems,
67-
alignSelf,
68-
align,
69-
70-
justifyContent,
71-
justify,
72-
73-
flexBasis,
74-
basis,
75-
flexGrow,
76-
grow,
77-
flexShrink,
78-
shrink,
79-
80-
flexWrap,
81-
wrap,
82-
83-
margin,
84-
m,
85-
marginBottom,
86-
marginLeft,
87-
marginRight,
88-
marginTop,
89-
mb,
90-
ml,
91-
mr,
92-
mt,
93-
94-
padding,
95-
p,
96-
paddingBottom,
97-
paddingLeft,
98-
paddingRight,
99-
paddingTop,
100-
pb,
101-
pl,
102-
pr,
103-
pt,
104-
105-
height,
106-
width,
107-
108-
maxHeight,
109-
maxWidth,
110-
minHeight,
111-
minWidth,
112-
113-
// other
114-
...props
115-
}: FlexProps) {
46+
function FlexImpl(
47+
{
48+
// Non flex props
49+
size = [1, 1, 1],
50+
yogaDirection = 'ltr',
51+
plane = 'xy',
52+
children,
53+
scaleFactor = 100,
54+
onReflow,
55+
disableSizeRecalc,
56+
centerAnchor: rootCenterAnchor,
57+
58+
// flex props
59+
60+
flexDirection,
61+
flexDir,
62+
dir,
63+
64+
alignContent,
65+
alignItems,
66+
alignSelf,
67+
align,
68+
69+
justifyContent,
70+
justify,
71+
72+
flexBasis,
73+
basis,
74+
flexGrow,
75+
grow,
76+
flexShrink,
77+
shrink,
78+
79+
flexWrap,
80+
wrap,
81+
82+
margin,
83+
m,
84+
marginBottom,
85+
marginLeft,
86+
marginRight,
87+
marginTop,
88+
mb,
89+
ml,
90+
mr,
91+
mt,
92+
93+
padding,
94+
p,
95+
paddingBottom,
96+
paddingLeft,
97+
paddingRight,
98+
paddingTop,
99+
pb,
100+
pl,
101+
pr,
102+
pt,
103+
104+
height,
105+
width,
106+
107+
maxHeight,
108+
maxWidth,
109+
minHeight,
110+
minWidth,
111+
112+
// other
113+
...props
114+
}: FlexProps,
115+
ref: React.Ref<THREE.Group>
116+
) {
116117
// must memoize or the object literal will cause every dependent of flexProps to rerender everytime
117118
const flexProps: R3FlexProps = useMemo(() => {
118119
const _flexProps = {
@@ -217,12 +218,12 @@ export function Flex({
217218
wrap,
218219
])
219220

220-
const rootGroup = useRef<Group>()
221+
const rootGroup = useRef<THREE.Group>()
221222

222223
// Keeps track of the yoga nodes of the children and the related wrapper groups
223224
const boxesRef = useRef<BoxesItem[]>([])
224225
const registerBox = useCallback(
225-
(node: YogaNode, group: Group, flexProps: R3FlexProps, centerAnchor: boolean = false) => {
226+
(node: YogaNode, group: THREE.Group, flexProps: R3FlexProps, centerAnchor: boolean = false) => {
226227
const i = boxesRef.current.findIndex((b) => b.node === node)
227228
if (i !== -1) {
228229
boxesRef.current.splice(i, 1)
@@ -258,8 +259,8 @@ export function Flex({
258259
}, [children, flexProps, requestReflow])
259260

260261
// Common variables for reflow
261-
const boundingBox = useMemo(() => new Box3(), [])
262-
const vec = useMemo(() => new Vector3(), [])
262+
const boundingBox = useMemo(() => new THREE.Box3(), [])
263+
const vec = useMemo(() => new THREE.Vector3(), [])
263264
const mainAxis = plane[0] as Axis
264265
const crossAxis = plane[1] as Axis
265266
const depthAxis = getDepthAxis(plane)
@@ -356,10 +357,17 @@ export function Flex({
356357
})
357358

358359
return (
359-
<group ref={rootGroup} {...props}>
360+
<group ref={mergeRefs([rootGroup, ref])} {...props}>
360361
<flexContext.Provider value={sharedFlexContext}>
361362
<boxContext.Provider value={sharedBoxContext}>{children}</boxContext.Provider>
362363
</flexContext.Provider>
363364
</group>
364365
)
365366
}
367+
368+
/**
369+
* Flex container. Can contain Boxes
370+
*/
371+
export const Flex = React.forwardRef<THREE.Group, FlexProps>(FlexImpl)
372+
373+
Flex.displayName = 'Flex'

0 commit comments

Comments
 (0)