diff --git a/react-frontend/src/app/app.module.scss b/react-frontend/src/app/app.module.scss index d53b537..bdda68f 100644 --- a/react-frontend/src/app/app.module.scss +++ b/react-frontend/src/app/app.module.scss @@ -1,5 +1,58 @@ .root { position: relative; + padding: 64px; + display: flex; + flex-wrap: wrap; + align-items: center; + justify-content: center; + gap: 32px; +} + +.thumbnail { + flex: 0 0 352px; + width: 352px; + height: 352px; + background: #444; + border-radius: 8px; + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + + &:hover { + background: #666; + } +} + +.modalOverlay { + position: fixed; + inset: 0; + background: #2222227F; + backdrop-filter: blur(12px); +} + +.modal { + position: fixed; + width: 1280px; + height: 720px; + top: calc(50vh - 360px); + left: calc(50vw - 640px); + border-radius: 16px; + overflow: hidden; + box-shadow: + 0 0 16px rgba(0,0,0,0.17), + 0 16px 48px rgba(0,0,0,0.3); +} + +.loading { + position: absolute; + inset: 0; + display: flex; + align-items: center; + justify-content: center; + background: #222; + pointer-events: none; + transition: opacity 66.666ms linear; } .controls { @@ -34,13 +87,28 @@ } } -.loading { +.modalClose { position: absolute; - inset: 0; - display: flex; - align-items: center; - justify-content: center; - background: #222; - pointer-events: none; - transition: opacity 66.666ms linear; + top: 16px; + right: 16px; + width: 48px; + height: 48px; + line-height: 48px; + border-radius: 50%; + appearance: none; + border: none; + padding: 0; + margin: 0; + background: #2222227F; + backdrop-filter: blur(16px); + font: unset { + family: inherit; + weight: 400; + size: 40px; + } + cursor: pointer; + + &:hover { + background: #222222AA; + } } diff --git a/react-frontend/src/app/app.tsx b/react-frontend/src/app/app.tsx index c0cb471..69408d9 100644 --- a/react-frontend/src/app/app.tsx +++ b/react-frontend/src/app/app.tsx @@ -2,6 +2,7 @@ import { type SceneStateEvent, SceneState } from "@storyteller/studio"; import { FormEvent, useState } from "react"; import StudioViewer from "./studio-viewer"; +import StudioCanvasProvider from "./studio-canvas-provider"; import style from "./app.module.scss"; @@ -79,14 +80,10 @@ const SKYBOXES = [ ]; export default () => { - const [objectId, setObjectId] = useState("desk"); - const [skyboxId, setSkyboxId] = useState("test_scene"); + const [objectId, setObjectId] = useState(null); + const [skyboxId, setSkyboxId] = useState("meadow_4k"); const [loading, setLoading] = useState(true); - const onObjectSelect = (event: FormEvent) => { - setObjectId((event.target as HTMLSelectElement).value) - } - const onSkyboxSelect = (event: FormEvent) => { setSkyboxId((event.target as HTMLSelectElement).value) } @@ -96,62 +93,72 @@ export default () => { } return ( -
- - -
-

Loading...

+ +
+ {OBJECTS.map(({ id, label }) => ( +
{ + setLoading(true); + setObjectId(id); + }} + > +

{label}

+
+ ))}
-
- - +
+ +
-
+ × + + + } + ); } diff --git a/react-frontend/src/app/studio-canvas-provider.tsx b/react-frontend/src/app/studio-canvas-provider.tsx new file mode 100644 index 0000000..290c762 --- /dev/null +++ b/react-frontend/src/app/studio-canvas-provider.tsx @@ -0,0 +1,15 @@ +import { CanvasProviderElement } from "@storyteller/studio-web/canvas-provider"; +import "@storyteller/studio-web/canvas-provider"; +import React, { ReactNode, forwardRef } from "react"; + +export interface Props extends React.HTMLProps { + children?: ReactNode | ReactNode[]; +} + +export default forwardRef( + ({ children, ...props }, ref) => ( + + {children} + + ) +); diff --git a/react-frontend/src/app/studio-viewer.tsx b/react-frontend/src/app/studio-viewer.tsx index e92a328..a5a2ea5 100644 --- a/react-frontend/src/app/studio-viewer.tsx +++ b/react-frontend/src/app/studio-viewer.tsx @@ -1,9 +1,9 @@ import type { SceneStateEvent } from "@storyteller/studio"; import { ViewerElement } from "@storyteller/studio-web/viewer"; import "@storyteller/studio-web/viewer"; -import { MutableRefObject, forwardRef, useEffect, useRef } from "react"; +import React, { MutableRefObject, forwardRef, useEffect, useRef } from "react"; -export interface Props { +export interface Props extends React.HTMLProps { objectId: string; skyboxId: string; onSceneStateChange?(event: SceneStateEvent): void; @@ -13,6 +13,7 @@ export default forwardRef(({ objectId, skyboxId, onSceneStateChange, + ...props }, ref) => { const viewerRef = useRef(null); @@ -38,6 +39,7 @@ export default forwardRef(({ return ( , ViewerElement, >; + "sts-canvas-provider": React.DetailedHTMLProps< + React.HTMLAttributes, + CanvasProviderElement, + > } } } diff --git a/studio-frontend/src/app/app.element.scss b/studio-frontend/src/app/app.element.scss index d6c1b7f..7d60320 100644 --- a/studio-frontend/src/app/app.element.scss +++ b/studio-frontend/src/app/app.element.scss @@ -32,13 +32,14 @@ font-size: 16px; padding: 10px 12px; pointer-events: auto; + outline: 0 solid transparent; // TODO: Copypasta background: #0006; color: #FFFC; backdrop-filter: blur(16px); transition: { - property: background, color; + property: background, color, outline-width, outline-color; duration: 0.1s; timing-function: linear; } @@ -62,6 +63,11 @@ color: #FFF; } + &:has(:focus-visible) { + z-index: 1; + outline: 4px solid #2F83FFBF; + } + &:has(:checked) { background: #7A2C34E3; color: #FFF;