244 lines
6.8 KiB
TypeScript
244 lines
6.8 KiB
TypeScript
|
import type {VFC} from 'react'
|
||
|
import React, {
|
||
|
useEffect,
|
||
|
useLayoutEffect,
|
||
|
useRef,
|
||
|
useState,
|
||
|
Suspense,
|
||
|
} from 'react'
|
||
|
import {Canvas} from '@react-three/fiber'
|
||
|
import {useEditorStore} from '../store'
|
||
|
import {OrbitControls, Environment} from '@react-three/drei'
|
||
|
import shallow from 'zustand/shallow'
|
||
|
import root from 'react-shadow'
|
||
|
import styles from '../bundle.css.txt'
|
||
|
import UI from './UI'
|
||
|
import ProxyManager from './ProxyManager'
|
||
|
import {
|
||
|
Button,
|
||
|
Heading,
|
||
|
Code,
|
||
|
PortalManager,
|
||
|
Modal,
|
||
|
ModalHeader,
|
||
|
ModalFooter,
|
||
|
ModalBody,
|
||
|
IdProvider,
|
||
|
} from './elements'
|
||
|
import studio from '@theatre/studio'
|
||
|
|
||
|
const EditorScene = () => {
|
||
|
const orbitControlsRef = useRef<typeof OrbitControls>()
|
||
|
|
||
|
const [
|
||
|
selectedHdr,
|
||
|
useHdrAsBackground,
|
||
|
showGrid,
|
||
|
showAxes,
|
||
|
helpersRoot,
|
||
|
setOrbitControlsRef,
|
||
|
] = useEditorStore(
|
||
|
(state) => [
|
||
|
state.selectedHdr,
|
||
|
state.useHdrAsBackground,
|
||
|
state.showGrid,
|
||
|
state.showAxes,
|
||
|
state.helpersRoot,
|
||
|
state.setOrbitControlsRef,
|
||
|
],
|
||
|
shallow,
|
||
|
)
|
||
|
|
||
|
useEffect(() => {
|
||
|
setOrbitControlsRef(orbitControlsRef)
|
||
|
}, [setOrbitControlsRef])
|
||
|
|
||
|
return (
|
||
|
<>
|
||
|
<Suspense fallback={null}>
|
||
|
{selectedHdr && (
|
||
|
<Environment
|
||
|
// @ts-ignore
|
||
|
files={selectedHdr}
|
||
|
path=""
|
||
|
background={useHdrAsBackground}
|
||
|
/>
|
||
|
)}
|
||
|
</Suspense>
|
||
|
{showGrid && <gridHelper args={[1000, 1000, 0x444444, 0x888888]} />}
|
||
|
{showAxes && <axesHelper args={[500]} />}
|
||
|
{/* @ts-ignore */}
|
||
|
<OrbitControls ref={orbitControlsRef} />
|
||
|
<primitive object={helpersRoot}></primitive>
|
||
|
<ProxyManager orbitControlsRef={orbitControlsRef} />
|
||
|
</>
|
||
|
)
|
||
|
}
|
||
|
|
||
|
const Editor: VFC = () => {
|
||
|
const [
|
||
|
sceneSnapshot,
|
||
|
editorOpen,
|
||
|
initialState,
|
||
|
initialEditorCamera,
|
||
|
setEditorOpen,
|
||
|
createSnapshot,
|
||
|
isPersistedStateDifferentThanInitial,
|
||
|
applyPersistedState,
|
||
|
] = useEditorStore(
|
||
|
(state) => [
|
||
|
state.sceneSnapshot,
|
||
|
state.editorOpen,
|
||
|
state.initialState,
|
||
|
state.initialEditorCamera,
|
||
|
state.setEditorOpen,
|
||
|
state.createSnapshot,
|
||
|
state.isPersistedStateDifferentThanInitial,
|
||
|
state.applyPersistedState,
|
||
|
],
|
||
|
shallow,
|
||
|
)
|
||
|
|
||
|
const [stateMismatch, setStateMismatch] = useState(false)
|
||
|
|
||
|
useLayoutEffect(() => {
|
||
|
if (initialState) {
|
||
|
setStateMismatch(isPersistedStateDifferentThanInitial())
|
||
|
} else {
|
||
|
applyPersistedState()
|
||
|
}
|
||
|
}, [applyPersistedState, initialState, isPersistedStateDifferentThanInitial])
|
||
|
|
||
|
return (
|
||
|
<root.div>
|
||
|
<div id="react-three-editable-editor-root">
|
||
|
<PortalManager>
|
||
|
<IdProvider>
|
||
|
<div className="relative z-50">
|
||
|
<div
|
||
|
className={`fixed ${editorOpen ? 'block' : 'hidden'} inset-0`}
|
||
|
>
|
||
|
{sceneSnapshot ? (
|
||
|
<>
|
||
|
<div className="relative z-0 h-full">
|
||
|
<Canvas
|
||
|
// @ts-ignore
|
||
|
colorManagement
|
||
|
camera={initialEditorCamera}
|
||
|
onCreated={({gl}) => {
|
||
|
gl.setClearColor('white')
|
||
|
}}
|
||
|
shadowMap
|
||
|
pixelRatio={window.devicePixelRatio}
|
||
|
onPointerMissed={() =>
|
||
|
studio.__experimental_setSelection([])
|
||
|
}
|
||
|
>
|
||
|
<EditorScene />
|
||
|
</Canvas>
|
||
|
</div>
|
||
|
|
||
|
<UI />
|
||
|
</>
|
||
|
) : (
|
||
|
<div className="flex justify-center items-center bg-white h-screen">
|
||
|
<div className="flex flex-col gap-5 items-center ">
|
||
|
<Heading className="text-2xl mb-4">
|
||
|
No canvas connected
|
||
|
</Heading>
|
||
|
<div>
|
||
|
Please use <Code>configure()</Code> and{' '}
|
||
|
<Code>bind()</Code> to connect a canvas to React Three
|
||
|
Editable.
|
||
|
</div>
|
||
|
<Code block>
|
||
|
{`import React from 'react';
|
||
|
import { Canvas } from '@react-three/fiber';
|
||
|
import { configure, editable as e } from 'react-three-editable';
|
||
|
|
||
|
const bind = configure({
|
||
|
localStorageNamespace: "MyProject"
|
||
|
});
|
||
|
|
||
|
const MyComponent = () => (
|
||
|
<Canvas onCreated={bind()}>
|
||
|
<e.mesh uniqueName="My First Editable Object">
|
||
|
<sphereBufferGeometry />
|
||
|
<meshStandardMaterial color="rebeccapurple" />
|
||
|
</e.mesh>
|
||
|
</Canvas>
|
||
|
);`}
|
||
|
</Code>
|
||
|
<div>
|
||
|
For more details, please consult the{' '}
|
||
|
<a
|
||
|
className="rounded-md font-medium text-green-600 hover:text-green-500"
|
||
|
href="https://github.com/AndrewPrifer/react-three-editable"
|
||
|
rel="noreferrer"
|
||
|
target="_blank"
|
||
|
>
|
||
|
documentation
|
||
|
</a>
|
||
|
.
|
||
|
</div>
|
||
|
<Button
|
||
|
className=""
|
||
|
onClick={() => {
|
||
|
setEditorOpen(false)
|
||
|
}}
|
||
|
>
|
||
|
Close
|
||
|
</Button>
|
||
|
</div>
|
||
|
</div>
|
||
|
)}
|
||
|
</div>
|
||
|
{editorOpen || (
|
||
|
<Button
|
||
|
className="fixed bottom-5 left-5"
|
||
|
onClick={() => {
|
||
|
if (!sceneSnapshot) {
|
||
|
createSnapshot()
|
||
|
}
|
||
|
setEditorOpen(true)
|
||
|
}}
|
||
|
>
|
||
|
Editor
|
||
|
</Button>
|
||
|
)}
|
||
|
</div>
|
||
|
<Modal visible={stateMismatch}>
|
||
|
<ModalHeader>Saved state found</ModalHeader>
|
||
|
<ModalBody>
|
||
|
Would you like to use initial state or saved state?
|
||
|
</ModalBody>
|
||
|
<ModalFooter>
|
||
|
<Button
|
||
|
className="flex-1"
|
||
|
onClick={() => {
|
||
|
applyPersistedState()
|
||
|
setStateMismatch(false)
|
||
|
}}
|
||
|
>
|
||
|
Saved
|
||
|
</Button>
|
||
|
<Button
|
||
|
className="flex-1"
|
||
|
onClick={() => {
|
||
|
setStateMismatch(false)
|
||
|
}}
|
||
|
>
|
||
|
Initial
|
||
|
</Button>
|
||
|
</ModalFooter>
|
||
|
</Modal>
|
||
|
<style type="text/css">{styles}</style>
|
||
|
</IdProvider>
|
||
|
</PortalManager>
|
||
|
</div>
|
||
|
</root.div>
|
||
|
)
|
||
|
}
|
||
|
|
||
|
export default Editor
|