Added support for multiple sheets in r3f

This commit is contained in:
Aria Minaei 2021-09-05 23:21:18 +02:00
parent 1452c9ebbe
commit 2605401e06
4 changed files with 35 additions and 21 deletions

View file

@ -1,12 +1,26 @@
import React, {useLayoutEffect} from 'react' import React, {
createContext,
useContext,
useLayoutEffect,
useState,
} from 'react'
import {useThree} from '@react-three/fiber' import {useThree} from '@react-three/fiber'
import type {ISheet} from '@theatre/core' import type {ISheet} from '@theatre/core'
import {bindToCanvas} from './store' import {bindToCanvas} from './store'
const ctx = createContext<{sheet: ISheet | undefined} | undefined>(undefined)
export const useWrapperContext = ():
| {sheet: ISheet | undefined}
| undefined => {
return useContext(ctx)
}
const Wrapper: React.FC<{ const Wrapper: React.FC<{
getSheet: () => ISheet getSheet: () => ISheet
}> = (props) => { }> = (props) => {
const {scene, gl} = useThree((s) => ({scene: s.scene, gl: s.gl})) const {scene, gl} = useThree((s) => ({scene: s.scene, gl: s.gl}))
const [sheet, setSheet] = useState<ISheet | undefined>(undefined)
useLayoutEffect(() => { useLayoutEffect(() => {
const sheet = props.getSheet() const sheet = props.getSheet()
@ -15,10 +29,11 @@ const Wrapper: React.FC<{
`getSheet() in <Wrapper getSheet={getSheet}> has returned an invalid value`, `getSheet() in <Wrapper getSheet={getSheet}> has returned an invalid value`,
) )
} }
bindToCanvas({sheet, gl, scene}) setSheet(sheet)
bindToCanvas({gl, scene})
}, [scene, gl]) }, [scene, gl])
return <>{props.children}</> return <ctx.Provider value={{sheet}}>{props.children}</ctx.Provider>
} }
export default Wrapper export default Wrapper

View file

@ -18,6 +18,7 @@ import mergeRefs from 'react-merge-refs'
import type {$FixMe} from '@theatre/shared/utils/types' import type {$FixMe} from '@theatre/shared/utils/types'
import type {ISheetObject} from '@theatre/core' import type {ISheetObject} from '@theatre/core'
import useInvalidate from './useInvalidate' import useInvalidate from './useInvalidate'
import {useWrapperContext} from '../Wrapper'
interface Elements { interface Elements {
group: Group group: Group
@ -50,7 +51,14 @@ const editable = <
({uniqueName, visible, editableType, ...props}: Props, ref) => { ({uniqueName, visible, editableType, ...props}: Props, ref) => {
const objectRef = useRef<Elements[U]>() const objectRef = useRef<Elements[U]>()
const sheet = useEditorStore((state) => state.sheet) const wrapperContext = useWrapperContext()
if (!wrapperContext) {
throw new Error(
`Editable components must be a descendent of a <Wrapper>`,
)
}
const {sheet} = wrapperContext
const [sheetObject, setSheetObject] = useState< const [sheetObject, setSheetObject] = useState<
undefined | ISheetObject<$FixMe> undefined | ISheetObject<$FixMe>

View file

@ -1,6 +1,5 @@
import {useLayoutEffect, useRef, useState} from 'react' import {useLayoutEffect, useRef, useState} from 'react'
import {useEditorStore} from '../store' import {allRegisteredObjects} from '../store'
import shallow from 'zustand/shallow'
import studio from '@theatre/studio' import studio from '@theatre/studio'
import type {ISheetObject} from '@theatre/core' import type {ISheetObject} from '@theatre/core'
@ -9,13 +8,12 @@ export function useSelected(): undefined | string {
const stateRef = useRef(state) const stateRef = useRef(state)
stateRef.current = state stateRef.current = state
const sheet = useEditorStore((state) => state.sheet, shallow)
useLayoutEffect(() => { useLayoutEffect(() => {
const setFromStudio = (selection: typeof studio.selection) => { const setFromStudio = (selection: typeof studio.selection) => {
const item = selection.find( const item = selection.find(
(s): s is ISheetObject => (s): s is ISheetObject =>
s.type === 'Theatre_SheetObject_PublicAPI' && s.sheet === sheet, s.type === 'Theatre_SheetObject_PublicAPI' &&
allRegisteredObjects.has(s),
) )
if (!item) { if (!item) {
set(undefined) set(undefined)
@ -25,17 +23,15 @@ export function useSelected(): undefined | string {
} }
setFromStudio(studio.selection) setFromStudio(studio.selection)
return studio.onSelectionChange(setFromStudio) return studio.onSelectionChange(setFromStudio)
}, [sheet]) }, [])
return state return state
} }
export function getSelected(): undefined | string { export function getSelected(): undefined | string {
const sheet = useEditorStore.getState().sheet
if (!sheet) return undefined
const item = studio.selection.find( const item = studio.selection.find(
(s): s is ISheetObject => (s): s is ISheetObject =>
s.type === 'Theatre_SheetObject_PublicAPI' && s.sheet === sheet, s.type === 'Theatre_SheetObject_PublicAPI' && allRegisteredObjects.has(s),
) )
if (!item) { if (!item) {
return undefined return undefined

View file

@ -2,7 +2,7 @@ import type {StateCreator} from 'zustand'
import create from 'zustand' import create from 'zustand'
import type {Object3D, Scene, WebGLRenderer} from 'three' import type {Object3D, Scene, WebGLRenderer} from 'three'
import {Group} from 'three' import {Group} from 'three'
import type {ISheet, ISheetObject} from '@theatre/core' import type {ISheetObject} from '@theatre/core'
import {types} from '@theatre/core' import {types} from '@theatre/core'
export type EditableType = export type EditableType =
@ -134,7 +134,6 @@ export interface EditableState {
} }
export type EditorStore = { export type EditorStore = {
sheet: ISheet | null
sheetObjects: {[uniqueName in string]?: BaseSheetObjectType} sheetObjects: {[uniqueName in string]?: BaseSheetObjectType}
scene: Scene | null scene: Scene | null
gl: WebGLRenderer | null gl: WebGLRenderer | null
@ -150,7 +149,6 @@ export type EditorStore = {
scene: Scene, scene: Scene,
gl: WebGLRenderer, gl: WebGLRenderer,
allowImplicitInstancing: boolean, allowImplicitInstancing: boolean,
sheet: ISheet,
) => void ) => void
addEditable: <T extends EditableType>(type: T, uniqueName: string) => void addEditable: <T extends EditableType>(type: T, uniqueName: string) => void
@ -178,12 +176,11 @@ const config: StateCreator<EditorStore> = (set, get) => {
editablesSnapshot: null, editablesSnapshot: null,
initialEditorCamera: {}, initialEditorCamera: {},
init: (scene, gl, allowImplicitInstancing, sheet) => { init: (scene, gl, allowImplicitInstancing) => {
set({ set({
scene, scene,
gl, gl,
allowImplicitInstancing, allowImplicitInstancing,
sheet,
}) })
}, },
@ -266,17 +263,15 @@ export const useEditorStore = create<EditorStore>(config)
export type BindFunction = (options: { export type BindFunction = (options: {
allowImplicitInstancing?: boolean allowImplicitInstancing?: boolean
sheet: ISheet
gl: WebGLRenderer gl: WebGLRenderer
scene: Scene scene: Scene
}) => void }) => void
export const bindToCanvas: BindFunction = ({ export const bindToCanvas: BindFunction = ({
allowImplicitInstancing = false, allowImplicitInstancing = false,
sheet,
gl, gl,
scene, scene,
}) => { }) => {
const init = useEditorStore.getState().init const init = useEditorStore.getState().init
init(scene, gl, allowImplicitInstancing, sheet) init(scene, gl, allowImplicitInstancing)
} }