Started using styled-components in r3f
This commit is contained in:
parent
16434644dc
commit
b109dca19b
5 changed files with 181 additions and 135 deletions
|
@ -34,6 +34,7 @@
|
||||||
"@types/lodash-es": "^4.17.4",
|
"@types/lodash-es": "^4.17.4",
|
||||||
"@types/node": "^15.6.2",
|
"@types/node": "^15.6.2",
|
||||||
"@types/react": "^17.0.9",
|
"@types/react": "^17.0.9",
|
||||||
|
"@types/styled-components": "^5.1.9",
|
||||||
"typescript": "^4.3.2"
|
"typescript": "^4.3.2"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
@ -50,6 +51,7 @@
|
||||||
"react-shadow": "^19.0.2",
|
"react-shadow": "^19.0.2",
|
||||||
"react-use-measure": "^2.0.4",
|
"react-use-measure": "^2.0.4",
|
||||||
"reakit": "^1.3.8",
|
"reakit": "^1.3.8",
|
||||||
|
"styled-components": "^5.3.0",
|
||||||
"three": "^0.129.0",
|
"three": "^0.129.0",
|
||||||
"zustand": "^3.5.1"
|
"zustand": "^3.5.1"
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,12 +5,32 @@ import {Canvas} from '@react-three/fiber'
|
||||||
import {useEditorStore} from '../store'
|
import {useEditorStore} from '../store'
|
||||||
import {OrbitControls} from '@react-three/drei'
|
import {OrbitControls} from '@react-three/drei'
|
||||||
import shallow from 'zustand/shallow'
|
import shallow from 'zustand/shallow'
|
||||||
import root from 'react-shadow'
|
import root from 'react-shadow/styled-components'
|
||||||
import styles from '../bundle.css.txt'
|
import styles from '../bundle.css.txt'
|
||||||
import UI from './UI'
|
import UI from './UI'
|
||||||
import ProxyManager from './ProxyManager'
|
import ProxyManager from './ProxyManager'
|
||||||
import studio from '@theatre/studio'
|
import studio from '@theatre/studio'
|
||||||
import {useVal} from '@theatre/dataverse-react'
|
import {useVal} from '@theatre/dataverse-react'
|
||||||
|
import styled, {createGlobalStyle} from 'styled-components'
|
||||||
|
|
||||||
|
const GlobalStyle = createGlobalStyle`
|
||||||
|
:host {
|
||||||
|
contain: strict;
|
||||||
|
all: initial;
|
||||||
|
color: white;
|
||||||
|
font: 11px -apple-system, BlinkMacSystemFont, Segoe WPC, Segoe Editor,
|
||||||
|
HelveticaNeue-Light, Ubuntu, Droid Sans, sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
* {
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
font-size: 100%;
|
||||||
|
font: inherit;
|
||||||
|
vertical-align: baseline;
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
const EditorScene = () => {
|
const EditorScene = () => {
|
||||||
const orbitControlsRef = useRef<typeof OrbitControls>()
|
const orbitControlsRef = useRef<typeof OrbitControls>()
|
||||||
|
@ -43,6 +63,27 @@ const EditorScene = () => {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const Wrapper = styled.div<{editorOpen: boolean}>`
|
||||||
|
tab-size: 4;
|
||||||
|
line-height: 1.15; /* 1 */
|
||||||
|
-webkit-text-size-adjust: 100%; /* 2 */
|
||||||
|
margin: 0;
|
||||||
|
|
||||||
|
position: fixed;
|
||||||
|
top: 0px;
|
||||||
|
right: 0px;
|
||||||
|
bottom: 0px;
|
||||||
|
left: 0px;
|
||||||
|
z-index: 50;
|
||||||
|
display: ${(props) => (props.editorOpen ? 'block' : 'none')};
|
||||||
|
`
|
||||||
|
|
||||||
|
const CanvasWrapper = styled.div`
|
||||||
|
display: relative;
|
||||||
|
z-index: 0;
|
||||||
|
height: 100%;
|
||||||
|
`
|
||||||
|
|
||||||
const Editor: VFC = () => {
|
const Editor: VFC = () => {
|
||||||
const [editorObject, sceneSnapshot, initialEditorCamera, createSnapshot] =
|
const [editorObject, sceneSnapshot, initialEditorCamera, createSnapshot] =
|
||||||
useEditorStore(
|
useEditorStore(
|
||||||
|
@ -55,7 +96,7 @@ const Editor: VFC = () => {
|
||||||
shallow,
|
shallow,
|
||||||
)
|
)
|
||||||
|
|
||||||
const editorOpen = useVal(editorObject?.props.isOpen)
|
const editorOpen = !!useVal(editorObject?.props.isOpen)
|
||||||
useLayoutEffect(() => {
|
useLayoutEffect(() => {
|
||||||
if (editorOpen) {
|
if (editorOpen) {
|
||||||
createSnapshot()
|
createSnapshot()
|
||||||
|
@ -66,43 +107,31 @@ const Editor: VFC = () => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<root.div>
|
<root.div>
|
||||||
<div id="react-three-editable-editor-root">
|
<GlobalStyle />
|
||||||
<>
|
<Wrapper id="theatre-plugin-r3f-root" editorOpen={editorOpen}>
|
||||||
|
{sceneSnapshot ? (
|
||||||
<>
|
<>
|
||||||
<div className="relative z-50">
|
<CanvasWrapper>
|
||||||
<div
|
<Canvas
|
||||||
className={`fixed ${editorOpen ? 'block' : 'hidden'} inset-0`}
|
// @ts-ignore
|
||||||
|
colorManagement
|
||||||
|
camera={initialEditorCamera}
|
||||||
|
onCreated={({gl}) => {
|
||||||
|
gl.setClearColor('white')
|
||||||
|
}}
|
||||||
|
shadowMap
|
||||||
|
pixelRatio={window.devicePixelRatio}
|
||||||
|
onPointerMissed={() => studio.__experimental_setSelection([])}
|
||||||
>
|
>
|
||||||
{sceneSnapshot ? (
|
<EditorScene />
|
||||||
<>
|
</Canvas>
|
||||||
<div className="relative z-0 h-full">
|
</CanvasWrapper>
|
||||||
<Canvas
|
|
||||||
// @ts-ignore
|
|
||||||
colorManagement
|
|
||||||
camera={initialEditorCamera}
|
|
||||||
onCreated={({gl}) => {
|
|
||||||
gl.setClearColor('white')
|
|
||||||
}}
|
|
||||||
shadowMap
|
|
||||||
pixelRatio={window.devicePixelRatio}
|
|
||||||
onPointerMissed={() =>
|
|
||||||
studio.__experimental_setSelection([])
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<EditorScene />
|
|
||||||
</Canvas>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<UI />
|
<UI />
|
||||||
</>
|
|
||||||
) : null}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<style type="text/css">{styles}</style>
|
|
||||||
</>
|
</>
|
||||||
</>
|
) : null}
|
||||||
</div>
|
</Wrapper>
|
||||||
|
<style type="text/css">{styles}</style>
|
||||||
</root.div>
|
</root.div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import type {VFC} from 'react'
|
import type {VFC} from 'react'
|
||||||
|
import {useState} from 'react'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import TransformControlsModeSelect from './TransformControlsModeSelect'
|
import TransformControlsModeSelect from './TransformControlsModeSelect'
|
||||||
import {useEditorStore} from '../store'
|
import {useEditorStore} from '../store'
|
||||||
|
@ -13,6 +14,7 @@ import studio from '@theatre/studio'
|
||||||
import {getSelected} from './useSelected'
|
import {getSelected} from './useSelected'
|
||||||
import {useVal} from '@theatre/dataverse-react'
|
import {useVal} from '@theatre/dataverse-react'
|
||||||
import IconButton from './elements/IconButton'
|
import IconButton from './elements/IconButton'
|
||||||
|
import {PortalContext} from 'reakit'
|
||||||
|
|
||||||
const UI: VFC = () => {
|
const UI: VFC = () => {
|
||||||
const [editorObject] = useEditorStore(
|
const [editorObject] = useEditorStore(
|
||||||
|
@ -29,114 +31,121 @@ const UI: VFC = () => {
|
||||||
const viewportShading =
|
const viewportShading =
|
||||||
useVal(editorObject?.props.viewportShading) ?? 'rendered'
|
useVal(editorObject?.props.viewportShading) ?? 'rendered'
|
||||||
|
|
||||||
|
const [wrapper, setWrapper] = useState<null | HTMLDivElement>(null)
|
||||||
|
|
||||||
if (!editorObject) return <></>
|
if (!editorObject) return <></>
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="absolute inset-0 z-50 pointer-events-none">
|
<PortalContext.Provider value={wrapper}>
|
||||||
<div className="flex h-full">
|
<div
|
||||||
<div className="relative flex-1 m-5">
|
className="absolute inset-0 z-50 pointer-events-none"
|
||||||
<div className="flex items-start justify-between">
|
ref={setWrapper}
|
||||||
<div className="flex gap-4">
|
>
|
||||||
<div className="pointer-events-auto">
|
<div className="flex h-full">
|
||||||
<TransformControlsModeSelect
|
<div className="relative flex-1 m-5">
|
||||||
value={transformControlsMode}
|
<div className="flex items-start justify-between">
|
||||||
onChange={(value) =>
|
<div className="flex gap-4">
|
||||||
studio.transaction(({set}) =>
|
<div className="pointer-events-auto">
|
||||||
set(editorObject!.props.transformControlsMode, value),
|
<TransformControlsModeSelect
|
||||||
)
|
value={transformControlsMode}
|
||||||
}
|
onChange={(value) =>
|
||||||
/>
|
studio.transaction(({set}) =>
|
||||||
</div>
|
set(editorObject!.props.transformControlsMode, value),
|
||||||
<div className="pointer-events-auto">
|
|
||||||
<TransformControlsSpaceSelect
|
|
||||||
value={transformControlsSpace}
|
|
||||||
onChange={(space) => {
|
|
||||||
studio.transaction(({set}) => {
|
|
||||||
set(editorObject.props.transformControlsSpace, space)
|
|
||||||
})
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="pointer-events-auto">
|
|
||||||
<ViewportShadingSelect
|
|
||||||
value={viewportShading}
|
|
||||||
onChange={(shading) => {
|
|
||||||
studio.transaction(({set}) => {
|
|
||||||
set(editorObject.props.viewportShading, shading)
|
|
||||||
})
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="pointer-events-auto">
|
|
||||||
<IconButton
|
|
||||||
label="Focus on selected"
|
|
||||||
icon={<RiFocus3Line />}
|
|
||||||
onClick={() => {
|
|
||||||
const orbitControls =
|
|
||||||
useEditorStore.getState().orbitControlsRef?.current
|
|
||||||
const selected = getSelected()
|
|
||||||
|
|
||||||
let focusObject
|
|
||||||
|
|
||||||
if (selected) {
|
|
||||||
focusObject =
|
|
||||||
useEditorStore.getState().editablesSnapshot![selected]
|
|
||||||
.proxyObject
|
|
||||||
}
|
|
||||||
|
|
||||||
if (orbitControls && focusObject) {
|
|
||||||
focusObject.getWorldPosition(
|
|
||||||
// @ts-ignore TODO
|
|
||||||
orbitControls.target as Vector3,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}}
|
/>
|
||||||
/>
|
</div>
|
||||||
</div>
|
<div className="pointer-events-auto">
|
||||||
<div className="pointer-events-auto">
|
<TransformControlsSpaceSelect
|
||||||
<IconButton
|
value={transformControlsSpace}
|
||||||
label="Align object to view"
|
onChange={(space) => {
|
||||||
icon={<GiPocketBow />}
|
studio.transaction(({set}) => {
|
||||||
onClick={() => {
|
set(editorObject.props.transformControlsSpace, space)
|
||||||
const camera = (
|
})
|
||||||
useEditorStore.getState().orbitControlsRef
|
}}
|
||||||
?.current as $FixMe
|
/>
|
||||||
)?.object
|
</div>
|
||||||
|
<div className="pointer-events-auto">
|
||||||
|
<ViewportShadingSelect
|
||||||
|
value={viewportShading}
|
||||||
|
onChange={(shading) => {
|
||||||
|
studio.transaction(({set}) => {
|
||||||
|
set(editorObject.props.viewportShading, shading)
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="pointer-events-auto">
|
||||||
|
<IconButton
|
||||||
|
label="Focus on selected"
|
||||||
|
icon={<RiFocus3Line />}
|
||||||
|
onClick={() => {
|
||||||
|
const orbitControls =
|
||||||
|
useEditorStore.getState().orbitControlsRef?.current
|
||||||
|
const selected = getSelected()
|
||||||
|
|
||||||
const selected = getSelected()
|
let focusObject
|
||||||
|
|
||||||
let proxyObject
|
if (selected) {
|
||||||
|
focusObject =
|
||||||
if (selected) {
|
useEditorStore.getState().editablesSnapshot![selected]
|
||||||
proxyObject =
|
.proxyObject
|
||||||
useEditorStore.getState().editablesSnapshot![selected]
|
|
||||||
.proxyObject
|
|
||||||
|
|
||||||
if (proxyObject && camera) {
|
|
||||||
const direction = new Vector3()
|
|
||||||
const position = camera.position.clone()
|
|
||||||
|
|
||||||
camera.getWorldDirection(direction)
|
|
||||||
proxyObject.position.set(0, 0, 0)
|
|
||||||
proxyObject.lookAt(direction)
|
|
||||||
|
|
||||||
proxyObject.parent!.worldToLocal(position)
|
|
||||||
proxyObject.position.copy(position)
|
|
||||||
|
|
||||||
proxyObject.updateMatrix()
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}}
|
if (orbitControls && focusObject) {
|
||||||
/>
|
focusObject.getWorldPosition(
|
||||||
|
// @ts-ignore TODO
|
||||||
|
orbitControls.target as Vector3,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="pointer-events-auto">
|
||||||
|
<IconButton
|
||||||
|
label="Align object to view"
|
||||||
|
icon={<GiPocketBow />}
|
||||||
|
onClick={() => {
|
||||||
|
const camera = (
|
||||||
|
useEditorStore.getState().orbitControlsRef
|
||||||
|
?.current as $FixMe
|
||||||
|
)?.object
|
||||||
|
|
||||||
|
const selected = getSelected()
|
||||||
|
|
||||||
|
let proxyObject
|
||||||
|
|
||||||
|
if (selected) {
|
||||||
|
proxyObject =
|
||||||
|
useEditorStore.getState().editablesSnapshot![selected]
|
||||||
|
.proxyObject
|
||||||
|
|
||||||
|
if (proxyObject && camera) {
|
||||||
|
const direction = new Vector3()
|
||||||
|
const position = camera.position.clone()
|
||||||
|
|
||||||
|
camera.getWorldDirection(direction)
|
||||||
|
proxyObject.position.set(0, 0, 0)
|
||||||
|
proxyObject.lookAt(direction)
|
||||||
|
|
||||||
|
proxyObject.parent!.worldToLocal(position)
|
||||||
|
proxyObject.position.copy(position)
|
||||||
|
|
||||||
|
proxyObject.updateMatrix()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="absolute right-0 top-0 -z-10">
|
||||||
|
<ReferenceWindow height={referenceWindowSize} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
<div className="absolute right-0 top-0 -z-10">
|
|
||||||
<ReferenceWindow height={referenceWindowSize} />
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</PortalContext.Provider>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@ import type {ReactElement, ReactNode} from 'react'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import type {IconType} from 'react-icons'
|
import type {IconType} from 'react-icons'
|
||||||
import {Group, Button} from 'reakit'
|
import {Group, Button} from 'reakit'
|
||||||
|
import styled from 'styled-components'
|
||||||
import {Tooltip, TooltipReference, useTooltipState} from './Tooltip'
|
import {Tooltip, TooltipReference, useTooltipState} from './Tooltip'
|
||||||
|
|
||||||
interface OptionButtonProps<Option> {
|
interface OptionButtonProps<Option> {
|
||||||
|
@ -12,6 +13,8 @@ interface OptionButtonProps<Option> {
|
||||||
onClick: () => void
|
onClick: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// const Ro = styled(TooltipReference)``
|
||||||
|
|
||||||
function OptionButton<Option>({
|
function OptionButton<Option>({
|
||||||
value,
|
value,
|
||||||
option,
|
option,
|
||||||
|
@ -51,16 +54,17 @@ interface CompactModeSelectProps<Option> {
|
||||||
settingsPanel?: ReactNode
|
settingsPanel?: ReactNode
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const Container = styled(Group)`
|
||||||
|
display: flex;
|
||||||
|
`
|
||||||
|
|
||||||
const CompactModeSelect = <Option extends string | number>({
|
const CompactModeSelect = <Option extends string | number>({
|
||||||
value,
|
value,
|
||||||
onChange,
|
onChange,
|
||||||
options,
|
options,
|
||||||
}: CompactModeSelectProps<Option>) => {
|
}: CompactModeSelectProps<Option>) => {
|
||||||
return (
|
return (
|
||||||
<Group
|
<Container>
|
||||||
// @ts-ignore
|
|
||||||
className="flex"
|
|
||||||
>
|
|
||||||
{options.map(({label, icon, option}) => (
|
{options.map(({label, icon, option}) => (
|
||||||
<OptionButton
|
<OptionButton
|
||||||
key={option}
|
key={option}
|
||||||
|
@ -71,7 +75,7 @@ const CompactModeSelect = <Option extends string | number>({
|
||||||
onClick={() => onChange(option)}
|
onClick={() => onChange(option)}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</Group>
|
</Container>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3802,6 +3802,7 @@ __metadata:
|
||||||
"@types/lodash-es": ^4.17.4
|
"@types/lodash-es": ^4.17.4
|
||||||
"@types/node": ^15.6.2
|
"@types/node": ^15.6.2
|
||||||
"@types/react": ^17.0.9
|
"@types/react": ^17.0.9
|
||||||
|
"@types/styled-components": ^5.1.9
|
||||||
lodash-es: ^4.17.21
|
lodash-es: ^4.17.21
|
||||||
prism-react-renderer: 1.2.1
|
prism-react-renderer: 1.2.1
|
||||||
react: ^17.0.2
|
react: ^17.0.2
|
||||||
|
@ -3811,6 +3812,7 @@ __metadata:
|
||||||
react-shadow: ^19.0.2
|
react-shadow: ^19.0.2
|
||||||
react-use-measure: ^2.0.4
|
react-use-measure: ^2.0.4
|
||||||
reakit: ^1.3.8
|
reakit: ^1.3.8
|
||||||
|
styled-components: ^5.3.0
|
||||||
three: ^0.129.0
|
three: ^0.129.0
|
||||||
typescript: ^4.3.2
|
typescript: ^4.3.2
|
||||||
zustand: ^3.5.1
|
zustand: ^3.5.1
|
||||||
|
|
Loading…
Reference in a new issue