Single-object viewer POC (#14)

This commit implements a Sketchfab-style single-object viewer as a minimal
proof-of-concept.

### `@storyteller/studio-web`
This is a new package to house web components for interfacing with the
`@storyteller/studio` WASM library. It currently includes a single component,
`<sts-viewer />`, which is exported from `@storyteller/studio-web/viewer`.

### `react-frontend`
This is a new application intended as a proof-of-concept and testbed for React
interop with `@storyteller/studio-web`.

Web components are currently a little bit fiddly to use in a React application.
`react-frontend/src/app/studio-viewer.tsx` demonstrates how a React-idiomatic
component can be written as a thin wrapper around a web component, and
`react-frontend/src/app/app.tsx` demonstrates that wrapper component in use.
This commit is contained in:
Danny McGee
2023-12-07 15:46:32 -05:00
committed by GitHub
parent 155272a5ba
commit cba70a62ae
70 changed files with 71793 additions and 1157 deletions
+11
View File
@@ -0,0 +1,11 @@
# studio-web
This library was generated with [Nx](https://nx.dev).
## Building
Run `nx build studio-web` to build the library.
## Running unit tests
Run `nx test studio-web` to execute the unit tests via [Jest](https://jestjs.io).
+11
View File
@@ -0,0 +1,11 @@
{
"name": "@storyteller/studio-web",
"version": "0.0.1",
"dependencies": {},
"main": "./index.js",
"module": "./index.mjs",
"typings": "./index.d.ts",
"exports": {
"./viewer": "./viewer.element.js"
}
}
+24
View File
@@ -0,0 +1,24 @@
{
"name": "studio-web",
"$schema": "../node_modules/nx/schemas/project-schema.json",
"sourceRoot": "studio-web/src",
"projectType": "library",
"targets": {
"build": {
"executor": "@nx/vite:build",
"outputs": ["{options.outputPath}"],
"options": {
"outputPath": "dist/studio-web"
}
},
"test": {
"executor": "@nx/vite:test",
"outputs": ["{options.reportsDirectory}"],
"options": {
"passWithNoTests": true,
"reportsDirectory": "../coverage/studio-web"
}
}
},
"tags": []
}
+1
View File
@@ -0,0 +1 @@
export * from "./lib/viewer.element";
+60
View File
@@ -0,0 +1,60 @@
import * as studio from "@storyteller/studio";
import { LitElement, PropertyValues, html, unsafeCSS } from "lit";
import { customElement, property } from "lit/decorators.js";
import styles from "./viewer.element.scss?inline";
import { ViewportSize } from "@storyteller/studio";
@customElement("sts-viewer")
export class ViewerElement extends LitElement {
static override styles = unsafeCSS(styles);
@property() objectId?: string;
@property() skyboxId?: string;
protected override createRenderRoot(): Element | ShadowRoot {
return this;
}
protected override updated(changes: PropertyValues<this>): void {
if (!studio.isInitialized()) {
this.#startViewer();
} else {
if (changes.has("objectId") && !!this.objectId)
studio.loadObject(this.objectId);
if (changes.has("skyboxId") && !!this.skyboxId)
studio.setSkybox(this.skyboxId);
}
}
#startViewer(): void {
if (!this.objectId) return;
if (!this.skyboxId) return;
const width = window.innerWidth;
const height = window.innerHeight;
try {
studio.startViewer(
this.objectId,
this.skyboxId,
"#studio_canvas",
"Storyteller Studio",
new ViewportSize(width, height),
);
} catch (err) {
if (!(err as Error).message?.startsWith("Using exceptions for control flow"))
throw err;
}
}
protected override render = () => html`
<canvas id="studio_canvas" alt="Storyteller Studio"></canvas>
`;
}
declare global {
export interface HTMLElementTagNameMap {
"sts-viewer": ViewerElement;
}
}
+22
View File
@@ -0,0 +1,22 @@
{
"extends": "../tsconfig.base.json",
"compilerOptions": {
"module": "commonjs",
"forceConsistentCasingInFileNames": true,
"strict": true,
"noImplicitOverride": true,
"noPropertyAccessFromIndexSignature": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true
},
"files": [],
"include": [],
"references": [
{
"path": "./tsconfig.lib.json"
},
{
"path": "./tsconfig.spec.json"
}
]
}
+10
View File
@@ -0,0 +1,10 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../dist/out-tsc",
"declaration": true,
"types": ["node", "vite/client"]
},
"include": ["src/**/*.ts"],
"exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"]
}
+25
View File
@@ -0,0 +1,25 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../../dist/out-tsc",
"types": [
"vitest/globals",
"vitest/importMeta",
"vite/client",
"node",
"vitest"
]
},
"include": [
"vite.config.ts",
"src/**/*.test.ts",
"src/**/*.spec.ts",
"src/**/*.test.tsx",
"src/**/*.spec.tsx",
"src/**/*.test.js",
"src/**/*.spec.js",
"src/**/*.test.jsx",
"src/**/*.spec.jsx",
"src/**/*.d.ts"
]
}
+50
View File
@@ -0,0 +1,50 @@
/// <reference types='vitest' />
import { defineConfig } from "vite";
import dts from "vite-plugin-dts";
import * as path from "path";
import { nxViteTsPaths } from "@nx/vite/plugins/nx-tsconfig-paths.plugin";
export default defineConfig({
cacheDir: "../node_modules/.vite/studio-web",
plugins: [
nxViteTsPaths(),
dts({
entryRoot: "src",
tsConfigFilePath: path.join(__dirname, "tsconfig.lib.json"),
skipDiagnostics: true,
}),
],
// Uncomment this if you are using workers.
// worker: {
// plugins: [ nxViteTsPaths() ],
// },
// Configuration for building your library.
// See: https://vitejs.dev/guide/build.html#library-mode
build: {
lib: {
// Could also be a dictionary or array of multiple entry points.
entry: "src/index.ts",
name: "studio-web",
fileName: "index",
// Change this to the formats you want to support.
// Don't forget to update your package.json as well.
formats: ["es", "cjs"],
},
rollupOptions: {
// External packages that should not be bundled into your library.
external: [],
},
},
test: {
globals: true,
cache: {
dir: "../node_modules/.vitest",
},
environment: "jsdom",
include: ["src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}"],
},
});