diff --git a/compatibility-tests/test-cra-react18/package.json b/compatibility-tests/test-cra-react18/package.json index bab4a91..152c9b3 100644 --- a/compatibility-tests/test-cra-react18/package.json +++ b/compatibility-tests/test-cra-react18/package.json @@ -15,6 +15,7 @@ "@testing-library/user-event": "^12.1.10", "@theatre/core": "^0.0.1-COMPAT.1", "@theatre/studio": "^0.0.1-COMPAT.1", + "@theatre/r3f": "^0.0.1-COMPAT.1", "react-scripts": "^5.0.1", "three": ">0.132.0", "web-vitals": "^1.0.1" diff --git a/compatibility-tests/test-cra-react18/src/App.js b/compatibility-tests/test-cra-react18/src/App.js index 1533aae..5a338c6 100644 --- a/compatibility-tests/test-cra-react18/src/App.js +++ b/compatibility-tests/test-cra-react18/src/App.js @@ -1,6 +1,154 @@ -import React from 'react' +import {getProject} from '@theatre/core' +import * as THREE from 'three' +import React, {useState, useEffect, useRef} from 'react' +import {useFrame, Canvas} from '@react-three/fiber' +import {Shadow, softShadows} from '@react-three/drei' +import studio from '@theatre/studio' +import {editable as e, SheetProvider} from '@theatre/r3f' +import extension from '@theatre/r3f/dist/extension' -export default function App({obj}) { - console.log(obj) - return
hi
+if (process.env.NODE_ENV === 'development') { + studio.extend(extension) + studio.initialize() } + +// Soft shadows are expensive, comment and refresh when it's too slow +softShadows() + +// credit: https://codesandbox.io/s/camera-pan-nsb7f + +function Button() { + const vec = new THREE.Vector3() + const light = useRef(undefined) + const [active, setActive] = useState(false) + const [zoom, set] = useState(true) + useEffect( + () => void (document.body.style.cursor = active ? 'pointer' : 'auto'), + [active], + ) + + useFrame((state) => { + const step = 0.1 + const camera = state.camera + camera.fov = THREE.MathUtils.lerp(camera.fov, zoom ? 10 : 42, step) + camera.position.lerp( + vec.set(zoom ? 25 : 10, zoom ? 1 : 5, zoom ? 0 : 10), + step, + ) + state.camera.lookAt(0, 0, 0) + state.camera.updateProjectionMatrix() + + light.current.position.lerp( + vec.set(zoom ? 4 : 0, zoom ? 3 : 8, zoom ? 3 : 5), + step, + ) + }) + + return ( + set(!zoom)} + onPointerOver={() => setActive(true)} + onPointerOut={() => setActive(false)} + uniqueName="The Button" + > + + + + + + ) +} + +function Plane({color, uniqueName, ...props}) { + return ( + + + + + ) +} + +function App() { + return ( +
+ + + {/* @ts-ignore */} + + + + + + + + + + +
+ ) +} + +export default App diff --git a/compatibility-tests/test-parcel1-react17/.gitignore b/compatibility-tests/test-parcel1-react17/.gitignore new file mode 100644 index 0000000..6ac9863 --- /dev/null +++ b/compatibility-tests/test-parcel1-react17/.gitignore @@ -0,0 +1,3 @@ +/.cache +/dist +/package-lock.json \ No newline at end of file diff --git a/compatibility-tests/test-parcel1-react17/index.html b/compatibility-tests/test-parcel1-react17/index.html new file mode 100644 index 0000000..4a8bbbd --- /dev/null +++ b/compatibility-tests/test-parcel1-react17/index.html @@ -0,0 +1,13 @@ + + + + + + + Theatre.js Example - DOM + + +
+ + + diff --git a/compatibility-tests/test-parcel1-react17/package.json b/compatibility-tests/test-parcel1-react17/package.json new file mode 100644 index 0000000..06a5670 --- /dev/null +++ b/compatibility-tests/test-parcel1-react17/package.json @@ -0,0 +1,17 @@ +{ + "name": "@compat/parcel1-react18", + "scripts": { + "dev": "parcel serve ./index.html" + }, + "dependencies": { + "parcel-bundler": "^1.12.5", + "@react-three/drei": "^7.3.1", + "@react-three/fiber": "^7.0.6", + "@theatre/core": "^0.0.1-COMPAT.1", + "@theatre/r3f": "^0.0.1-COMPAT.1", + "@theatre/studio": "^0.0.1-COMPAT.1", + "react": "^17.0.2", + "react-dom": "^17.0.2", + "three": "^0.137.0" + } +} diff --git a/compatibility-tests/test-parcel1-react17/src/App.js b/compatibility-tests/test-parcel1-react17/src/App.js new file mode 100644 index 0000000..5a338c6 --- /dev/null +++ b/compatibility-tests/test-parcel1-react17/src/App.js @@ -0,0 +1,154 @@ +import {getProject} from '@theatre/core' +import * as THREE from 'three' +import React, {useState, useEffect, useRef} from 'react' +import {useFrame, Canvas} from '@react-three/fiber' +import {Shadow, softShadows} from '@react-three/drei' +import studio from '@theatre/studio' +import {editable as e, SheetProvider} from '@theatre/r3f' +import extension from '@theatre/r3f/dist/extension' + +if (process.env.NODE_ENV === 'development') { + studio.extend(extension) + studio.initialize() +} + +// Soft shadows are expensive, comment and refresh when it's too slow +softShadows() + +// credit: https://codesandbox.io/s/camera-pan-nsb7f + +function Button() { + const vec = new THREE.Vector3() + const light = useRef(undefined) + const [active, setActive] = useState(false) + const [zoom, set] = useState(true) + useEffect( + () => void (document.body.style.cursor = active ? 'pointer' : 'auto'), + [active], + ) + + useFrame((state) => { + const step = 0.1 + const camera = state.camera + camera.fov = THREE.MathUtils.lerp(camera.fov, zoom ? 10 : 42, step) + camera.position.lerp( + vec.set(zoom ? 25 : 10, zoom ? 1 : 5, zoom ? 0 : 10), + step, + ) + state.camera.lookAt(0, 0, 0) + state.camera.updateProjectionMatrix() + + light.current.position.lerp( + vec.set(zoom ? 4 : 0, zoom ? 3 : 8, zoom ? 3 : 5), + step, + ) + }) + + return ( + set(!zoom)} + onPointerOver={() => setActive(true)} + onPointerOut={() => setActive(false)} + uniqueName="The Button" + > + + + + + + ) +} + +function Plane({color, uniqueName, ...props}) { + return ( + + + + + ) +} + +function App() { + return ( +
+ + + {/* @ts-ignore */} + + + + + + + + + + +
+ ) +} + +export default App diff --git a/compatibility-tests/test-parcel1-react17/src/index.js b/compatibility-tests/test-parcel1-react17/src/index.js new file mode 100644 index 0000000..b2d2250 --- /dev/null +++ b/compatibility-tests/test-parcel1-react17/src/index.js @@ -0,0 +1,13 @@ +import ReactDOM from 'react-dom' +import '@theatre/studio' +import React from 'react' +import App from './App' + +console.log('hj') + +ReactDOM.render( + + + , + document.getElementById('root'), +) diff --git a/compatibility-tests/test-vite-react18/package.json b/compatibility-tests/test-vite-react18/package.json index ae9fc28..877b0dd 100644 --- a/compatibility-tests/test-vite-react18/package.json +++ b/compatibility-tests/test-vite-react18/package.json @@ -8,10 +8,14 @@ "preview": "vite preview" }, "dependencies": { + "@react-three/drei": "^9.11.3", + "@react-three/fiber": "^8.0.19", "@theatre/core": "^0.0.1-COMPAT.1", + "@theatre/r3f": "^0.0.1-COMPAT.1", "@theatre/studio": "^0.0.1-COMPAT.1", "react": "^18.0.0", - "react-dom": "^18.0.0" + "react-dom": "^18.0.0", + "three": "^0.141.0" }, "devDependencies": { "@types/react": "^18.0.0", diff --git a/compatibility-tests/test-vite-react18/src/App.tsx b/compatibility-tests/test-vite-react18/src/App.tsx index 1533aae..5a338c6 100644 --- a/compatibility-tests/test-vite-react18/src/App.tsx +++ b/compatibility-tests/test-vite-react18/src/App.tsx @@ -1,6 +1,154 @@ -import React from 'react' +import {getProject} from '@theatre/core' +import * as THREE from 'three' +import React, {useState, useEffect, useRef} from 'react' +import {useFrame, Canvas} from '@react-three/fiber' +import {Shadow, softShadows} from '@react-three/drei' +import studio from '@theatre/studio' +import {editable as e, SheetProvider} from '@theatre/r3f' +import extension from '@theatre/r3f/dist/extension' -export default function App({obj}) { - console.log(obj) - return
hi
+if (process.env.NODE_ENV === 'development') { + studio.extend(extension) + studio.initialize() } + +// Soft shadows are expensive, comment and refresh when it's too slow +softShadows() + +// credit: https://codesandbox.io/s/camera-pan-nsb7f + +function Button() { + const vec = new THREE.Vector3() + const light = useRef(undefined) + const [active, setActive] = useState(false) + const [zoom, set] = useState(true) + useEffect( + () => void (document.body.style.cursor = active ? 'pointer' : 'auto'), + [active], + ) + + useFrame((state) => { + const step = 0.1 + const camera = state.camera + camera.fov = THREE.MathUtils.lerp(camera.fov, zoom ? 10 : 42, step) + camera.position.lerp( + vec.set(zoom ? 25 : 10, zoom ? 1 : 5, zoom ? 0 : 10), + step, + ) + state.camera.lookAt(0, 0, 0) + state.camera.updateProjectionMatrix() + + light.current.position.lerp( + vec.set(zoom ? 4 : 0, zoom ? 3 : 8, zoom ? 3 : 5), + step, + ) + }) + + return ( + set(!zoom)} + onPointerOver={() => setActive(true)} + onPointerOut={() => setActive(false)} + uniqueName="The Button" + > + + + + + + ) +} + +function Plane({color, uniqueName, ...props}) { + return ( + + + + + ) +} + +function App() { + return ( +
+ + + {/* @ts-ignore */} + + + + + + + + + + +
+ ) +} + +export default App diff --git a/compatibility-tests/test-vite-react18/src/main.tsx b/compatibility-tests/test-vite-react18/src/main.tsx index 69e4852..7cfe000 100644 --- a/compatibility-tests/test-vite-react18/src/main.tsx +++ b/compatibility-tests/test-vite-react18/src/main.tsx @@ -1,19 +1,11 @@ -// import ReactDOM from 'react-dom' +import ReactDOM from 'react-dom' +import '@theatre/studio' +import React from 'react' +import App from './App' -import studio from '@theatre/studio' -import {getProject} from '@theatre/core' -// import React from 'react' -// import App from './App' - -studio.initialize({usePersistentStorage: false}) - -const project = getProject('Project') -const sheet = project.sheet('Sheet') -const obj = sheet.object('Obj', {str: 'some string', num: 0}) - -// ReactDOM.render( -// -// -// , -// document.getElementById('root'), -// ) +ReactDOM.render( + + + , + document.getElementById('root'), +) diff --git a/examples/r3f-cra/src/App.js b/examples/r3f-cra/src/App.js index dc3ef8f..5a338c6 100644 --- a/examples/r3f-cra/src/App.js +++ b/examples/r3f-cra/src/App.js @@ -1,11 +1,11 @@ import {getProject} from '@theatre/core' import * as THREE from 'three' -import {useState, useEffect, useRef} from 'react' +import React, {useState, useEffect, useRef} from 'react' import {useFrame, Canvas} from '@react-three/fiber' import {Shadow, softShadows} from '@react-three/drei' -import React from 'react' import studio from '@theatre/studio' -import {editable as e, SheetProvider, extension} from '@theatre/r3f' +import {editable as e, SheetProvider} from '@theatre/r3f' +import extension from '@theatre/r3f/dist/extension' if (process.env.NODE_ENV === 'development') { studio.extend(extension) @@ -90,6 +90,10 @@ function App() { - + , document.getElementById('root'), ) diff --git a/packages/playground/src/shared/instances/index.tsx b/packages/playground/src/shared/instances/index.tsx index 0d0a27b..cae2285 100644 --- a/packages/playground/src/shared/instances/index.tsx +++ b/packages/playground/src/shared/instances/index.tsx @@ -2,7 +2,7 @@ import React from 'react' import ReactDOM from 'react-dom' import App from './App' import studio from '@theatre/studio' -import {extension} from '@theatre/r3f' +import extension from '@theatre/r3f/dist/extension' studio.extend(extension) studio.initialize() diff --git a/packages/playground/src/shared/r3f-rocket/index.tsx b/packages/playground/src/shared/r3f-rocket/index.tsx index 825370d..5507263 100644 --- a/packages/playground/src/shared/r3f-rocket/index.tsx +++ b/packages/playground/src/shared/r3f-rocket/index.tsx @@ -2,7 +2,7 @@ import React from 'react' import ReactDOM from 'react-dom' import App from './App' import studio from '@theatre/studio' -import {extension} from '@theatre/r3f' +import extension from '@theatre/r3f/dist/extension' studio.extend(extension) studio.initialize() diff --git a/packages/r3f/devEnv/bundle.ts b/packages/r3f/devEnv/bundle.ts new file mode 100644 index 0000000..2b7eb84 --- /dev/null +++ b/packages/r3f/devEnv/bundle.ts @@ -0,0 +1,74 @@ +import path = require('path') +import {build} from 'esbuild' + +const definedGlobals = { + 'process.env.version': JSON.stringify(require('../package.json').version), + 'process.env.NODE_ENV': JSON.stringify('production'), +} + +createBundles() + +async function createBundles() { + createMainBundle() + createExtensionBundle() + + async function createMainBundle() { + const pathToEntry = path.join(__dirname, '../src/index.ts') + const esbuildConfig: Parameters[0] = { + entryPoints: [pathToEntry], + target: ['es6'], + loader: {'.svg': 'text', '.png': 'dataurl'}, + bundle: true, + sourcemap: true, + define: {...definedGlobals}, + external: [ + '@theatre/core', + '@theatre/dataverse', + '@theatre/react', + '@theatre/studio', + 'react', + 'react-dom', + 'three', + '@react-three/fiber', + ], + platform: 'browser', + mainFields: ['browser', 'module', 'main'], + conditions: ['browser', 'node'], + outfile: path.join(__dirname, '../dist/index.js'), + format: 'cjs', + metafile: true, + } + + const result = await build(esbuildConfig) + } + + async function createExtensionBundle() { + const pathToEntry = path.join(__dirname, '../src/extension/index.ts') + const esbuildConfig: Parameters[0] = { + entryPoints: [pathToEntry], + target: 'es6', + loader: {'.svg': 'text', '.png': 'dataurl'}, + bundle: true, + sourcemap: true, + define: {...definedGlobals}, + external: [ + '@theatre/core', + '@theatre/studio', + '@theatre/dataverse', + '@theatre/r3f', + // 'three', + // '@react-three/fiber', + // '@react-three/drei', + // 'three-stdlib', + ], + platform: 'browser', + mainFields: ['browser', 'module', 'main'], + conditions: ['browser'], + outfile: path.join(__dirname, '../dist/extension/index.js'), + format: 'cjs', + metafile: true, + } + + const result = await build(esbuildConfig) + } +} diff --git a/packages/r3f/devEnv/tsconfig.json b/packages/r3f/devEnv/tsconfig.json new file mode 100644 index 0000000..077404a --- /dev/null +++ b/packages/r3f/devEnv/tsconfig.json @@ -0,0 +1,3 @@ +{ + +} \ No newline at end of file diff --git a/packages/r3f/package.json b/packages/r3f/package.json index 5e88b76..0b1ffc7 100644 --- a/packages/r3f/package.json +++ b/packages/r3f/package.json @@ -21,31 +21,35 @@ }, "main": "dist/index.js", "module": "dist/index.js", - "types": "dist/index.d.ts", "sideEffects": false, "files": [ "dist/**/*" ], + "exports": { + ".": "./dist/index.js", + "./extension": "./dist/extension/index.js" + }, "scripts": { "prepack": "yarn run build", "typecheck": "yarn run build", - "build": "tsc --build ./tsconfig.json", + "build": "run-s build:ts build:js", + "build:js": "node -r esbuild-register devEnv/bundle.ts", + "build:ts": "tsc --build ./tsconfig.json", "prepublish": "yarn run build", "clean": "rm -rf ./dist && rm -f tsconfig.tsbuildinfo" }, "devDependencies": { + "@react-three/drei": "^7.3.1", + "@theatre/react": "workspace:*", "@types/jest": "^26.0.23", "@types/lodash-es": "^4.17.4", "@types/node": "^15.6.2", "@types/react": "^17.0.9", "@types/styled-components": "^5.1.9", - "npm-run-all": "^4.1.5", - "typescript": "^4.4.2" - }, - "dependencies": { - "@react-three/drei": "^7.3.1", - "@theatre/react": "workspace:*", + "esbuild": "^0.12.15", + "esbuild-register": "^2.5.0", "lodash-es": "^4.17.21", + "npm-run-all": "^4.1.5", "polished": "^4.1.3", "react-icons": "^4.2.0", "react-merge-refs": "^1.1.0", @@ -53,14 +57,15 @@ "react-use-measure": "^2.0.4", "reakit": "^1.3.8", "styled-components": "^5.3.0", + "typescript": "^4.4.2", "zustand": "^3.5.1" }, "peerDependencies": { - "@react-three/fiber": "^7.0.6", + "@react-three/fiber": ">=7.0.6", "@theatre/core": "*", "@theatre/studio": "*", - "react": "^17.0.2", - "react-dom": "^17.0.2", - "three": "^0.131.3" + "react": ">=17.0.2", + "react-dom": ">=17.0.2", + "three": ">=0.131.3" } } diff --git a/packages/r3f/src/components/EditorHelper.tsx b/packages/r3f/src/components/EditorHelper.tsx deleted file mode 100644 index 1221f0b..0000000 --- a/packages/r3f/src/components/EditorHelper.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import type {ComponentProps, ElementType} from 'react' -import React from 'react' -import {useEditorStore} from '../store' -import {createPortal} from '@react-three/fiber' - -export type EditorHelperProps = { - component: T -} & ComponentProps - -const EditorHelper = ({ - component: Component, - ...props -}: EditorHelperProps) => { - const helpersRoot = useEditorStore((state) => state.helpersRoot) - if (process.env.NODE_ENV === 'development') { - return <>{createPortal(, helpersRoot)} - } else { - return null - } -} - -export default EditorHelper diff --git a/packages/r3f/src/extension/.eslintrc.js b/packages/r3f/src/extension/.eslintrc.js new file mode 100644 index 0000000..bf64cd9 --- /dev/null +++ b/packages/r3f/src/extension/.eslintrc.js @@ -0,0 +1,11 @@ +module.exports = { + rules: { + 'no-restricted-syntax': [ + 'error', + { + selector: `ImportDeclaration[importKind!='type'][source.value=/\\u002Fmain\\u002F/]`, + message: `The extension should not be able to import the internals of main. If you need to use this API, expose it as __private_api from @theatre/r3f/src/index.ts`, + }, + ], + }, +} diff --git a/packages/r3f/src/InfiniteGridHelper/index.ts b/packages/r3f/src/extension/InfiniteGridHelper/index.ts similarity index 100% rename from packages/r3f/src/InfiniteGridHelper/index.ts rename to packages/r3f/src/extension/InfiniteGridHelper/index.ts diff --git a/packages/r3f/src/components/DragDetector.tsx b/packages/r3f/src/extension/components/DragDetector.tsx similarity index 100% rename from packages/r3f/src/components/DragDetector.tsx rename to packages/r3f/src/extension/components/DragDetector.tsx diff --git a/packages/r3f/src/components/EditableProxy.tsx b/packages/r3f/src/extension/components/EditableProxy.tsx similarity index 92% rename from packages/r3f/src/components/EditableProxy.tsx rename to packages/r3f/src/extension/components/EditableProxy.tsx index 2608298..e909c85 100644 --- a/packages/r3f/src/components/EditableProxy.tsx +++ b/packages/r3f/src/extension/components/EditableProxy.tsx @@ -2,17 +2,17 @@ import type {Object3D} from 'three' import type {VFC} from 'react' import React, {useEffect, useLayoutEffect, useMemo, useState} from 'react' import {Sphere, Html} from '@react-three/drei' -import {useEditorStore} from '../store' import shallow from 'zustand/shallow' import studio from '@theatre/studio' import {useSelected} from './useSelected' import {useVal} from '@theatre/react' -import {getEditorSheetObject} from './editorStuff' +import {getEditorSheetObject} from '../editorStuff' import type {IconID} from '../icons' import icons from '../icons' -import type {Helper} from '../editableFactoryConfigUtils' +import type {Helper} from '../../main/editableFactoryConfigUtils' import {invalidate, useFrame, useThree} from '@react-three/fiber' import {useDragDetector} from './DragDetector' +import useExtensionStore from '../useExtensionStore' export interface EditableProxyProps { storeKey: string @@ -21,7 +21,7 @@ export interface EditableProxyProps { const EditableProxy: VFC = ({storeKey, object}) => { const editorObject = getEditorSheetObject() - const [setSnapshotProxyObject, editables] = useEditorStore( + const [setSnapshotProxyObject, editables] = useExtensionStore( (state) => [state.setSnapshotProxyObject, state.editables], shallow, ) @@ -122,7 +122,7 @@ const EditableProxy: VFC = ({storeKey, object}) => { e.stopPropagation() const theatreObject = - useEditorStore.getState().editables[storeKey].sheetObject + useExtensionStore.getState().editables[storeKey].sheetObject if (!theatreObject) { console.log('no theatre object for', storeKey) @@ -169,7 +169,7 @@ const EditableProxy: VFC = ({storeKey, object}) => { if (e.delta < 2) { e.stopPropagation() const theatreObject = - useEditorStore.getState().editables[storeKey].sheetObject + useExtensionStore.getState().editables[storeKey].sheetObject if (!theatreObject) { console.log('no theatre object for', storeKey) diff --git a/packages/r3f/src/components/ProxyManager.tsx b/packages/r3f/src/extension/components/ProxyManager.tsx similarity index 97% rename from packages/r3f/src/components/ProxyManager.tsx rename to packages/r3f/src/extension/components/ProxyManager.tsx index 3c01da5..11b3c74 100644 --- a/packages/r3f/src/components/ProxyManager.tsx +++ b/packages/r3f/src/extension/components/ProxyManager.tsx @@ -1,7 +1,6 @@ import type {VFC} from 'react' import React, {useLayoutEffect, useMemo, useRef, useState} from 'react' -import type {Editable} from '../store' -import {useEditorStore} from '../store' +import type {Editable} from '../../main/store' import {createPortal} from '@react-three/fiber' import EditableProxy from './EditableProxy' import type {OrbitControls} from 'three-stdlib' @@ -13,7 +12,8 @@ import type {IScrub} from '@theatre/studio' import studio from '@theatre/studio' import {useSelected} from './useSelected' import {useVal} from '@theatre/react' -import {getEditorSheetObject} from './editorStuff' +import {getEditorSheetObject} from '../editorStuff' +import useExtensionStore from '../useExtensionStore' export interface ProxyManagerProps { orbitControlsRef: React.MutableRefObject @@ -28,7 +28,7 @@ type IEditableProxy = { const ProxyManager: VFC = ({orbitControlsRef}) => { const isBeingEdited = useRef(false) const editorObject = getEditorSheetObject() - const [sceneSnapshot, editables] = useEditorStore( + const [sceneSnapshot, editables] = useExtensionStore( (state) => [state.sceneSnapshot, state.editables], shallow, ) diff --git a/packages/r3f/src/components/ReferenceWindow/ReferenceWindow.tsx b/packages/r3f/src/extension/components/ReferenceWindow/ReferenceWindow.tsx similarity index 64% rename from packages/r3f/src/components/ReferenceWindow/ReferenceWindow.tsx rename to packages/r3f/src/extension/components/ReferenceWindow/ReferenceWindow.tsx index 3c9a9e0..42e31a3 100644 --- a/packages/r3f/src/components/ReferenceWindow/ReferenceWindow.tsx +++ b/packages/r3f/src/extension/components/ReferenceWindow/ReferenceWindow.tsx @@ -1,20 +1,27 @@ import type {VFC} from 'react' +import {useMemo} from 'react' import React, {useEffect, useLayoutEffect, useRef} from 'react' -import {useEditorStore} from '../../store' import shallow from 'zustand/shallow' import type {WebGLRenderer} from 'three' import useMeasure from 'react-use-measure' import styled, {keyframes} from 'styled-components' import {TiWarningOutline} from 'react-icons/ti' -// This is ugly, but pure TS doesn't let you do bundler-stuff -import noiseImageUrl from './noiseImage' +import noiseImageUrl from './noise-transparent.png' +import useExtensionStore from '../../useExtensionStore' +import {useVal} from '@theatre/react' +import {getEditorSheetObject} from '../../editorStuff' +import studio from '@theatre/studio' const Container = styled.div` position: relative; - width: fit-content; - height: fit-content; border-radius: 4px; + pointer-events: auto; + cursor: pointer; overflow: hidden; + + &.hidden { + border-radius: 8px; + } ` const Canvas = styled.canvas` @@ -64,14 +71,27 @@ const Warning = styled.div` opacity: 0.8; ` +const Dot = styled.div` + width: 8px; + height: 8px; + border-radius: 50%; + background-color: red; + pointer-events: auto; + cursor: pointer; +` + interface ReferenceWindowProps { - height: number + maxHeight: number + maxWidth: number } -const ReferenceWindow: VFC = ({height}) => { +const ReferenceWindow: VFC = ({maxHeight, maxWidth}) => { const canvasRef = useRef(null) - const [gl] = useEditorStore((state) => [state.gl], shallow) + const visible = + useVal(getEditorSheetObject()?.props.viewport.showReferenceWindow) ?? true + + const [gl] = useExtensionStore((state) => [state.gl], shallow) const [ref, bounds] = useMeasure() const preserveDrawingBuffer = @@ -85,7 +105,18 @@ const ReferenceWindow: VFC = ({height}) => { } }, [gl, ref]) + const [width, height] = useMemo(() => { + if (!gl) return [0, 0] + const aspectRatio = gl.domElement.width / gl.domElement.height + + const width = Math.min(aspectRatio * maxHeight, maxWidth) + + const height = width / aspectRatio + return [width, height] + }, [gl, maxWidth, maxHeight]) + useEffect(() => { + // if (!visible) return let animationHandle: number const draw = (gl: WebGLRenderer) => () => { animationHandle = requestAnimationFrame(draw(gl)) @@ -95,8 +126,6 @@ const ReferenceWindow: VFC = ({height}) => { return } - const width = (gl.domElement.width / gl.domElement.height) * height - const ctx = canvasRef.current!.getContext('2d')! // https://stackoverflow.com/questions/17861447/html5-canvas-drawimage-how-to-apply-antialiasing @@ -115,20 +144,29 @@ const ReferenceWindow: VFC = ({height}) => { return () => { cancelAnimationFrame(animationHandle) } - }, [gl, height, preserveDrawingBuffer]) + }, [gl, width, height, preserveDrawingBuffer]) + + const toggleVisibility = () => { + studio.transaction(({set}) => { + set(getEditorSheetObject()!.props.viewport.showReferenceWindow, !visible) + }) + } + + // if (!visible) { + // return + // } return ( - + {gl?.domElement && preserveDrawingBuffer ? ( - + ) : ( diff --git a/packages/r3f/src/components/ReferenceWindow/noise-transparent.png b/packages/r3f/src/extension/components/ReferenceWindow/noise-transparent.png similarity index 100% rename from packages/r3f/src/components/ReferenceWindow/noise-transparent.png rename to packages/r3f/src/extension/components/ReferenceWindow/noise-transparent.png diff --git a/packages/r3f/src/components/ReferenceWindow/noiseImage.ts b/packages/r3f/src/extension/components/ReferenceWindow/noiseImage.ts similarity index 100% rename from packages/r3f/src/components/ReferenceWindow/noiseImage.ts rename to packages/r3f/src/extension/components/ReferenceWindow/noiseImage.ts diff --git a/packages/r3f/src/components/SnapshotEditor.tsx b/packages/r3f/src/extension/components/SnapshotEditor.tsx similarity index 87% rename from packages/r3f/src/components/SnapshotEditor.tsx rename to packages/r3f/src/extension/components/SnapshotEditor.tsx index 368d6a6..ecd2607 100644 --- a/packages/r3f/src/components/SnapshotEditor.tsx +++ b/packages/r3f/src/extension/components/SnapshotEditor.tsx @@ -1,8 +1,8 @@ import {useCallback, useEffect, useLayoutEffect, useMemo, useState} from 'react' import React from 'react' import {Canvas, useThree} from '@react-three/fiber' -import type {BaseSheetObjectType} from '../store' -import {allRegisteredObjects, useEditorStore} from '../store' +import type {BaseSheetObjectType} from '../../main/store' +import {__private_allRegisteredObjects as allRegisteredObjects} from '@theatre/r3f' import shallow from 'zustand/shallow' import root from 'react-shadow/styled-components' import ProxyManager from './ProxyManager' @@ -11,11 +11,13 @@ import {useVal} from '@theatre/react' import styled, {createGlobalStyle, StyleSheetManager} from 'styled-components' import type {ISheet} from '@theatre/core' import useSnapshotEditorCamera from './useSnapshotEditorCamera' -import {getEditorSheet, getEditorSheetObject} from './editorStuff' +import {getEditorSheet, getEditorSheetObject} from '../editorStuff' import type {$IntentionalAny} from '@theatre/shared/utils/types' import {InfiniteGridHelper} from '../InfiniteGridHelper' import {DragDetectorProvider} from './DragDetector' import ReferenceWindow from './ReferenceWindow/ReferenceWindow' +import useExtensionStore from '../useExtensionStore' +import useMeasure from 'react-use-measure' const GlobalStyle = createGlobalStyle` :host { @@ -62,7 +64,7 @@ const EditorScene: React.FC<{snapshotEditorSheet: ISheet; paneId: string}> = ({ } }, [gl, viewportLighting, scene, camera]) - const helpersRoot = useEditorStore((state) => state.helpersRoot, shallow) + const helpersRoot = useExtensionStore((state) => state.helpersRoot, shallow) const showGrid = useVal(editorObject?.props.viewport.showGrid) ?? true const showAxes = useVal(editorObject?.props.viewport.showAxes) ?? true @@ -128,11 +130,9 @@ const SnapshotEditor: React.FC<{paneId: string}> = (props) => { const snapshotEditorSheet = getEditorSheet() const paneId = props.paneId const editorObject = getEditorSheetObject() + const [ref, bounds] = useMeasure() - const showReferenceWindow = - useVal(editorObject?.props.viewport.showReferenceWindow) ?? true - - const [sceneSnapshot, createSnapshot] = useEditorStore( + const [sceneSnapshot, createSnapshot] = useExtensionStore( (state) => [state.sceneSnapshot, state.createSnapshot], shallow, ) @@ -184,16 +184,17 @@ const SnapshotEditor: React.FC<{paneId: string}> = (props) => { - {showReferenceWindow && ( - - - - )} + + + {sceneSnapshot ? ( <> - + { gl.setClearColor('white') diff --git a/packages/r3f/src/components/TransformControls.tsx b/packages/r3f/src/extension/components/TransformControls.tsx similarity index 100% rename from packages/r3f/src/components/TransformControls.tsx rename to packages/r3f/src/extension/components/TransformControls.tsx diff --git a/packages/r3f/src/components/useRefAndState.ts b/packages/r3f/src/extension/components/useRefAndState.ts similarity index 100% rename from packages/r3f/src/components/useRefAndState.ts rename to packages/r3f/src/extension/components/useRefAndState.ts diff --git a/packages/r3f/src/components/useSelected.tsx b/packages/r3f/src/extension/components/useSelected.tsx similarity index 86% rename from packages/r3f/src/components/useSelected.tsx rename to packages/r3f/src/extension/components/useSelected.tsx index e158a2c..f55b6a8 100644 --- a/packages/r3f/src/components/useSelected.tsx +++ b/packages/r3f/src/extension/components/useSelected.tsx @@ -1,9 +1,11 @@ import {useLayoutEffect, useRef, useState} from 'react' -import {allRegisteredObjects} from '../store' +import { + __private_allRegisteredObjects as allRegisteredObjects, + __private_makeStoreKey as makeStoreKey, +} from '@theatre/r3f' import studio from '@theatre/studio' import type {ISheetObject} from '@theatre/core' -import type {$IntentionalAny} from '../types' -import {makeStoreKey} from '../utils' +import type {$IntentionalAny} from '../../types' export function useSelected(): undefined | string { const [state, set] = useState(undefined) diff --git a/packages/r3f/src/components/useSnapshotEditorCamera.tsx b/packages/r3f/src/extension/components/useSnapshotEditorCamera.tsx similarity index 100% rename from packages/r3f/src/components/useSnapshotEditorCamera.tsx rename to packages/r3f/src/extension/components/useSnapshotEditorCamera.tsx diff --git a/packages/r3f/src/components/editorStuff.ts b/packages/r3f/src/extension/editorStuff.ts similarity index 100% rename from packages/r3f/src/components/editorStuff.ts rename to packages/r3f/src/extension/editorStuff.ts diff --git a/packages/r3f/src/icons.tsx b/packages/r3f/src/extension/icons.tsx similarity index 100% rename from packages/r3f/src/icons.tsx rename to packages/r3f/src/extension/icons.tsx diff --git a/packages/r3f/src/extension.ts b/packages/r3f/src/extension/index.ts similarity index 98% rename from packages/r3f/src/extension.ts rename to packages/r3f/src/extension/index.ts index 733475a..1f26dc1 100644 --- a/packages/r3f/src/extension.ts +++ b/packages/r3f/src/extension/index.ts @@ -1,11 +1,11 @@ import SnapshotEditor from './components/SnapshotEditor' import type {IExtension} from '@theatre/studio' import {prism, Ticker, val} from '@theatre/dataverse' -import {getEditorSheetObject} from './components/editorStuff' -import {useEditorStore} from './store' +import {getEditorSheetObject} from './editorStuff' import ReactDOM from 'react-dom' import React from 'react' import type {ToolsetConfig} from '@theatre/studio' +import useExtensionStore from './useExtensionStore' const io5CameraOutline = `Camera` const gameIconMove = `` @@ -40,7 +40,7 @@ const r3fExtension: IExtension = { }) }, 'snapshot-editor': (set, studio) => { - const {createSnapshot} = useEditorStore.getState() + const {createSnapshot} = useExtensionStore.getState() const calc = prism(() => { const editorObject = getEditorSheetObject() diff --git a/packages/r3f/src/extension/useExtensionStore.ts b/packages/r3f/src/extension/useExtensionStore.ts new file mode 100644 index 0000000..b12fb94 --- /dev/null +++ b/packages/r3f/src/extension/useExtensionStore.ts @@ -0,0 +1,6 @@ +import {____private_editorStore} from '@theatre/r3f' +import create from 'zustand' + +const useExtensionStore = create(____private_editorStore) + +export default useExtensionStore diff --git a/packages/r3f/src/globals.d.ts b/packages/r3f/src/globals.d.ts index 910aa40..c62f674 100644 --- a/packages/r3f/src/globals.d.ts +++ b/packages/r3f/src/globals.d.ts @@ -1,3 +1,7 @@ declare module '*.txt' { export default string } + +declare module '*.png' { + export default string +} diff --git a/packages/r3f/src/index.ts b/packages/r3f/src/index.ts new file mode 100644 index 0000000..5939ac6 --- /dev/null +++ b/packages/r3f/src/index.ts @@ -0,0 +1,25 @@ +export {default as editable} from './main/editable' +export type {EditableState, BindFunction} from './main/store' +/** + * This is a private API that's exported so that `@theatre/r3f/extension` + * and `@theatre/r3f` can talk to one another. This API _could_ change + * between patch releases, so please don't build on it :) + * + * @internal + */ +export { + editorStore as ____private_editorStore, + allRegisteredObjects as __private_allRegisteredObjects, +} from './main/store' +/** + * This is a private API that's exported so that `@theatre/r3f/extension` + * and `@theatre/r3f` can talk to one another. This API _could_ change + * between patch releases, so please don't build on it :) + * + * @internal + */ +export {makeStoreKey as __private_makeStoreKey} from './main/utils' + +export {default as SheetProvider, useCurrentSheet} from './main/SheetProvider' +export {refreshSnapshot} from './main/utils' +export {default as RefreshSnapshot} from './main/RefreshSnapshot' diff --git a/packages/r3f/src/index.tsx b/packages/r3f/src/index.tsx deleted file mode 100644 index dee332e..0000000 --- a/packages/r3f/src/index.tsx +++ /dev/null @@ -1,8 +0,0 @@ -export {default as extension} from './extension' -export {default as EditorHelper} from './components/EditorHelper' -export type {EditorHelperProps} from './components/EditorHelper' -export {default as editable} from './components/editable' -export type {EditableState, BindFunction} from './store' -export {default as SheetProvider, useCurrentSheet} from './SheetProvider' -export {refreshSnapshot} from './utils' -export {default as RefreshSnapshot} from './components/RefreshSnapshot' diff --git a/packages/r3f/src/main/.eslintrc.js b/packages/r3f/src/main/.eslintrc.js new file mode 100644 index 0000000..dd59867 --- /dev/null +++ b/packages/r3f/src/main/.eslintrc.js @@ -0,0 +1,11 @@ +module.exports = { + rules: { + 'no-restricted-syntax': [ + 'error', + { + selector: `ImportDeclaration[importKind!='type'][source.value=/\\u002Fextension\\u002F/]`, + message: `The main bundle should not be able to import the internals of extension.`, + }, + ], + }, +} diff --git a/packages/r3f/src/components/RefreshSnapshot.tsx b/packages/r3f/src/main/RefreshSnapshot.tsx similarity index 92% rename from packages/r3f/src/components/RefreshSnapshot.tsx rename to packages/r3f/src/main/RefreshSnapshot.tsx index 17ede25..4510723 100644 --- a/packages/r3f/src/components/RefreshSnapshot.tsx +++ b/packages/r3f/src/main/RefreshSnapshot.tsx @@ -1,5 +1,5 @@ import React, {useEffect} from 'react' -import {refreshSnapshot} from '../utils' +import {refreshSnapshot} from './utils' /** * Putting this element in a suspense tree makes sure the snapshot editor diff --git a/packages/r3f/src/SheetProvider.tsx b/packages/r3f/src/main/SheetProvider.tsx similarity index 100% rename from packages/r3f/src/SheetProvider.tsx rename to packages/r3f/src/main/SheetProvider.tsx diff --git a/packages/r3f/src/defaultEditableFactoryConfig.ts b/packages/r3f/src/main/defaultEditableFactoryConfig.ts similarity index 100% rename from packages/r3f/src/defaultEditableFactoryConfig.ts rename to packages/r3f/src/main/defaultEditableFactoryConfig.ts diff --git a/packages/r3f/src/components/editable.tsx b/packages/r3f/src/main/editable.tsx similarity index 91% rename from packages/r3f/src/components/editable.tsx rename to packages/r3f/src/main/editable.tsx index 6c3f341..34969e9 100644 --- a/packages/r3f/src/components/editable.tsx +++ b/packages/r3f/src/main/editable.tsx @@ -1,14 +1,14 @@ import type {ComponentProps, ComponentType, RefAttributes} from 'react' import React, {forwardRef, useLayoutEffect, useRef, useState} from 'react' -import {allRegisteredObjects, useEditorStore} from '../store' +import {allRegisteredObjects, editorStore} from './store' import mergeRefs from 'react-merge-refs' -import type {$FixMe} from '@theatre/shared/utils/types' import type {ISheetObject} from '@theatre/core' import useInvalidate from './useInvalidate' -import {useCurrentSheet} from '../SheetProvider' -import defaultEditableFactoryConfig from '../defaultEditableFactoryConfig' -import type {EditableFactoryConfig} from '../editableFactoryConfigUtils' -import {makeStoreKey} from '../utils' +import {useCurrentSheet} from './SheetProvider' +import defaultEditableFactoryConfig from './defaultEditableFactoryConfig' +import type {EditableFactoryConfig} from './editableFactoryConfigUtils' +import {makeStoreKey} from './utils' +import type {$FixMe} from '../types' const createEditable = ( config: EditableFactoryConfig, @@ -78,7 +78,7 @@ const createEditable = ( if (objRef) objRef!.current = sheetObject - useEditorStore.getState().addEditable(storeKey, { + editorStore.getState().addEditable(storeKey, { type: actualType, sheetObject, visibleOnlyInEditor: visible === 'editor', diff --git a/packages/r3f/src/editableFactoryConfigUtils.ts b/packages/r3f/src/main/editableFactoryConfigUtils.ts similarity index 98% rename from packages/r3f/src/editableFactoryConfigUtils.ts rename to packages/r3f/src/main/editableFactoryConfigUtils.ts index 039c054..da47a34 100644 --- a/packages/r3f/src/editableFactoryConfigUtils.ts +++ b/packages/r3f/src/main/editableFactoryConfigUtils.ts @@ -1,7 +1,7 @@ import type {UnknownShorthandCompoundProps} from '@theatre/core' import {types} from '@theatre/core' import type {Object3D} from 'three' -import type {IconID} from './icons' +import type {IconID} from '../extension/icons' import {Color} from 'three' export type Helper = Object3D & { diff --git a/packages/r3f/src/store.ts b/packages/r3f/src/main/store.ts similarity index 95% rename from packages/r3f/src/store.ts rename to packages/r3f/src/main/store.ts index ca97b09..f1274f2 100644 --- a/packages/r3f/src/store.ts +++ b/packages/r3f/src/main/store.ts @@ -1,5 +1,5 @@ import type {StateCreator} from 'zustand' -import create from 'zustand' +import create from 'zustand/vanilla' import type {Object3D, Scene, WebGLRenderer} from 'three' import {Group} from 'three' import type {ISheetObject} from '@theatre/core' @@ -102,7 +102,7 @@ const config: StateCreator = (set) => { } } -export const useEditorStore = create(config) +export const editorStore = create(config) export type BindFunction = (options: { allowImplicitInstancing?: boolean @@ -111,6 +111,6 @@ export type BindFunction = (options: { }) => void export const bindToCanvas: BindFunction = ({gl, scene}) => { - const init = useEditorStore.getState().init + const init = editorStore.getState().init init(scene, gl) } diff --git a/packages/r3f/src/components/useInvalidate.ts b/packages/r3f/src/main/useInvalidate.ts similarity index 100% rename from packages/r3f/src/components/useInvalidate.ts rename to packages/r3f/src/main/useInvalidate.ts diff --git a/packages/r3f/src/utils.ts b/packages/r3f/src/main/utils.ts similarity index 61% rename from packages/r3f/src/utils.ts rename to packages/r3f/src/main/utils.ts index 85e2046..3c5a941 100644 --- a/packages/r3f/src/utils.ts +++ b/packages/r3f/src/main/utils.ts @@ -1,7 +1,7 @@ -import {useEditorStore} from './store' +import {editorStore} from './store' import type {ISheet} from '@theatre/core' -export const refreshSnapshot = useEditorStore.getState().createSnapshot +export const refreshSnapshot = editorStore.getState().createSnapshot export const makeStoreKey = (sheet: ISheet, name: string) => `${sheet.address.sheetId}:${sheet.address.sheetInstanceId}:${name}` diff --git a/packages/r3f/tsconfig.json b/packages/r3f/tsconfig.json index 5755a12..3c42607 100644 --- a/packages/r3f/tsconfig.json +++ b/packages/r3f/tsconfig.json @@ -5,6 +5,7 @@ "lib": ["ESNext", "DOM"], "rootDir": "src", "types": ["jest", "node"], + "emitDeclarationOnly": true, "composite": true }, "references": [{"path": "../../theatre"}], diff --git a/tsconfig.base.json b/tsconfig.base.json index 70981b5..ab99a31 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -24,7 +24,8 @@ "@theatre/shared/*": ["./theatre/shared/src/*"], "@theatre/dataverse": ["./packages/dataverse/src/index.ts"], "@theatre/react": ["./packages/react/src/index.ts"], - "@theatre/r3f": ["./packages/r3f/src/index.tsx"], + "@theatre/r3f": ["./packages/r3f/src/index.ts"], + "@theatre/r3f/dist/extension": ["./packages/r3f/src/extension/index.ts"], "@theatre/dataverse-experiments": [ "./packages/dataverse-experiments/src/index.ts" ] diff --git a/yarn.lock b/yarn.lock index c5f8922..6272d8a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6605,6 +6605,8 @@ __metadata: "@types/node": ^15.6.2 "@types/react": ^17.0.9 "@types/styled-components": ^5.1.9 + esbuild: ^0.12.15 + esbuild-register: ^2.5.0 lodash-es: ^4.17.21 npm-run-all: ^4.1.5 polished: ^4.1.3 @@ -6617,12 +6619,12 @@ __metadata: typescript: ^4.4.2 zustand: ^3.5.1 peerDependencies: - "@react-three/fiber": ^7.0.6 + "@react-three/fiber": ">=7.0.6" "@theatre/core": "*" "@theatre/studio": "*" - react: ^17.0.2 - react-dom: ^17.0.2 - three: ^0.131.3 + react: ">=17.0.2" + react-dom: ">=17.0.2" + three: ">=0.131.3" languageName: unknown linkType: soft