Skip to content

Commit

Permalink
Added ref forwarding to Flex and Box
Browse files Browse the repository at this point in the history
This would with various helpers and hooks that use refs to somehow affect the component,
say rotate it or draw a bounding box
  • Loading branch information
saitonakamura committed Sep 2, 2021
1 parent 8d3be3a commit 523a04e
Show file tree
Hide file tree
Showing 2 changed files with 165 additions and 147 deletions.
146 changes: 78 additions & 68 deletions src/Box.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,82 +2,84 @@ import React, { useLayoutEffect, useRef, useMemo, useState } from 'react'
import * as THREE from 'three'
import Yoga from 'yoga-layout-prebuilt'
import { ReactThreeFiber, useFrame } from '@react-three/fiber'
import mergeRefs from 'react-merge-refs'

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

/**
* Box container for 3D Objects.
* For containing Boxes use `<Flex />`.
*/
export function Box({
// Non-flex props
children,
centerAnchor,

// flex props
flexDirection,
flexDir,
dir,

alignContent,
alignItems,
alignSelf,
align,

justifyContent,
justify,

flexBasis,
basis,
flexGrow,
grow,

flexShrink,
shrink,

flexWrap,
wrap,

margin,
m,
marginBottom,
marginLeft,
marginRight,
marginTop,
mb,
ml,
mr,
mt,

padding,
p,
paddingBottom,
paddingLeft,
paddingRight,
paddingTop,
pb,
pl,
pr,
pt,

height,
width,

maxHeight,
maxWidth,
minHeight,
minWidth,

// other
...props
}: {
export type BoxProps = {
centerAnchor?: boolean
children: React.ReactNode | ((width: number, height: number, centerAnchor?: boolean) => React.ReactNode)
} & R3FlexProps &
Omit<ReactThreeFiber.Object3DNode<THREE.Group, typeof THREE.Group>, 'children'>) {
Omit<ReactThreeFiber.Object3DNode<THREE.Group, typeof THREE.Group>, 'children'>

function BoxImpl(
{
// Non-flex props
children,
centerAnchor,

// flex props
flexDirection,
flexDir,
dir,

alignContent,
alignItems,
alignSelf,
align,

justifyContent,
justify,

flexBasis,
basis,
flexGrow,
grow,

flexShrink,
shrink,

flexWrap,
wrap,

margin,
m,
marginBottom,
marginLeft,
marginRight,
marginTop,
mb,
ml,
mr,
mt,

padding,
p,
paddingBottom,
paddingLeft,
paddingRight,
paddingTop,
pb,
pl,
pr,
pt,

height,
width,

maxHeight,
maxWidth,
minHeight,
minWidth,

// other
...props
}: BoxProps,
ref: React.Ref<THREE.Group>
) {
// must memoize or the object literal will cause every dependent of flexProps to rerender everytime
const flexProps: R3FlexProps = useMemo(() => {
const _flexProps = {
Expand Down Expand Up @@ -228,10 +230,18 @@ export function Box({
const sharedBoxContext = useMemo<SharedBoxContext>(() => ({ node, size, centerAnchor }), [node, size, centerAnchor])

return (
<group ref={group} {...props}>
<group ref={mergeRefs([group, ref])} {...props}>
<boxContext.Provider value={sharedBoxContext}>
{typeof children === 'function' ? children(size[0], size[1], centerAnchor) : children}
</boxContext.Provider>
</group>
)
}

/**
* Box container for 3D Objects.
* For containing Boxes use `<Flex />`.
*/
export const Box = React.forwardRef<THREE.Group, BoxProps>(BoxImpl)

Box.displayName = 'Box'
166 changes: 87 additions & 79 deletions src/Flex.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import React, { useLayoutEffect, useMemo, useCallback, PropsWithChildren, useRef } from 'react'
import Yoga, { YogaNode } from 'yoga-layout-prebuilt'
import { Vector3, Group, Box3, Object3D } from 'three'
import * as THREE from 'three'
import { useFrame, useThree, ReactThreeFiber } from '@react-three/fiber'
import mergeRefs from 'react-merge-refs'

import {
setYogaProperties,
Expand Down Expand Up @@ -33,86 +34,86 @@ export type FlexProps = PropsWithChildren<
centerAnchor?: boolean
}> &
R3FlexProps &
Omit<ReactThreeFiber.Object3DNode<THREE.Group, typeof Group>, 'children'>
Omit<ReactThreeFiber.Object3DNode<THREE.Group, typeof THREE.Group>, 'children'>
>
interface BoxesItem {
node: YogaNode
group: Group
group: THREE.Group
flexProps: R3FlexProps
centerAnchor: boolean
}

/**
* Flex container. Can contain Boxes
*/
export function Flex({
// Non flex props
size = [1, 1, 1],
yogaDirection = 'ltr',
plane = 'xy',
children,
scaleFactor = 100,
onReflow,
disableSizeRecalc,
centerAnchor: rootCenterAnchor,

// flex props

flexDirection,
flexDir,
dir,

alignContent,
alignItems,
alignSelf,
align,

justifyContent,
justify,

flexBasis,
basis,
flexGrow,
grow,
flexShrink,
shrink,

flexWrap,
wrap,

margin,
m,
marginBottom,
marginLeft,
marginRight,
marginTop,
mb,
ml,
mr,
mt,

padding,
p,
paddingBottom,
paddingLeft,
paddingRight,
paddingTop,
pb,
pl,
pr,
pt,

height,
width,

maxHeight,
maxWidth,
minHeight,
minWidth,

// other
...props
}: FlexProps) {
function FlexImpl(
{
// Non flex props
size = [1, 1, 1],
yogaDirection = 'ltr',
plane = 'xy',
children,
scaleFactor = 100,
onReflow,
disableSizeRecalc,
centerAnchor: rootCenterAnchor,

// flex props

flexDirection,
flexDir,
dir,

alignContent,
alignItems,
alignSelf,
align,

justifyContent,
justify,

flexBasis,
basis,
flexGrow,
grow,
flexShrink,
shrink,

flexWrap,
wrap,

margin,
m,
marginBottom,
marginLeft,
marginRight,
marginTop,
mb,
ml,
mr,
mt,

padding,
p,
paddingBottom,
paddingLeft,
paddingRight,
paddingTop,
pb,
pl,
pr,
pt,

height,
width,

maxHeight,
maxWidth,
minHeight,
minWidth,

// other
...props
}: FlexProps,
ref: React.Ref<THREE.Group>
) {
// must memoize or the object literal will cause every dependent of flexProps to rerender everytime
const flexProps: R3FlexProps = useMemo(() => {
const _flexProps = {
Expand Down Expand Up @@ -217,12 +218,12 @@ export function Flex({
wrap,
])

const rootGroup = useRef<Group>()
const rootGroup = useRef<THREE.Group>()

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

// Common variables for reflow
const boundingBox = useMemo(() => new Box3(), [])
const vec = useMemo(() => new Vector3(), [])
const boundingBox = useMemo(() => new THREE.Box3(), [])
const vec = useMemo(() => new THREE.Vector3(), [])
const mainAxis = plane[0] as Axis
const crossAxis = plane[1] as Axis
const depthAxis = getDepthAxis(plane)
Expand Down Expand Up @@ -356,10 +357,17 @@ export function Flex({
})

return (
<group ref={rootGroup} {...props}>
<group ref={mergeRefs([rootGroup, ref])} {...props}>
<flexContext.Provider value={sharedFlexContext}>
<boxContext.Provider value={sharedBoxContext}>{children}</boxContext.Provider>
</flexContext.Provider>
</group>
)
}

/**
* Flex container. Can contain Boxes
*/
export const Flex = React.forwardRef<THREE.Group, FlexProps>(FlexImpl)

Flex.displayName = 'Flex'

0 comments on commit 523a04e

Please sign in to comment.