mirror of
https://github.com/storytold/storyteller-bevy.git
synced 2026-10-17 00:13:17 +00:00
WIP Media Library dialog
This commit is contained in:
@@ -8,17 +8,17 @@ import {
|
||||
} from "@storyteller/studio";
|
||||
import { ButtonElement } from "@storyteller/studio-ui/button";
|
||||
import {
|
||||
MediaFileType,
|
||||
SceneHierarchyElement,
|
||||
StorytellerApi,
|
||||
StudioURLParams,
|
||||
} from "@storyteller/studio-web";
|
||||
import { assert, match } from "@storyteller/utility";
|
||||
import { match } from "@storyteller/utility";
|
||||
import { LitElement, html, nothing } from "lit";
|
||||
import { customElement, state } from "lit/decorators.js";
|
||||
import { classMap } from "lit/directives/class-map.js";
|
||||
import { createRef, ref } from "lit/directives/ref.js";
|
||||
import { styleMap } from "lit/directives/style-map.js";
|
||||
import { MediaLibraryElement } from "./media-library.element";
|
||||
|
||||
import "@storyteller/studio-ui/button";
|
||||
import "@storyteller/studio-ui/icon";
|
||||
@@ -37,6 +37,7 @@ import "./anim-editor.element";
|
||||
import "./help.element";
|
||||
|
||||
import "./blocking.element";
|
||||
import "./media-library.element";
|
||||
|
||||
import "./app.element.scss";
|
||||
|
||||
@@ -58,6 +59,7 @@ export class AppElement extends LitElement {
|
||||
#enableAnimTimeline = false; // TODO
|
||||
|
||||
#addToSceneButtonRef = createRef<ButtonElement>();
|
||||
#mediaLibraryRef = createRef<MediaLibraryElement>();
|
||||
#inspectorRef = createRef<HTMLDivElement>();
|
||||
#hierarchyRef = createRef<SceneHierarchyElement>();
|
||||
|
||||
@@ -67,37 +69,6 @@ export class AppElement extends LitElement {
|
||||
return this;
|
||||
}
|
||||
|
||||
// TODO
|
||||
override async connectedCallback(): Promise<void> {
|
||||
super.connectedCallback();
|
||||
|
||||
try {
|
||||
const session = await this.api.session();
|
||||
console.log("session:", session);
|
||||
|
||||
if (!session.logged_in)
|
||||
return; // TODO: Prompt for login?
|
||||
|
||||
assert(session.user?.username != null);
|
||||
|
||||
const mediaFilesResult = await this.api.listMediaFiles({
|
||||
username: session.user.username,
|
||||
filterMediaType: [
|
||||
MediaFileType.Bvh,
|
||||
MediaFileType.Fbx,
|
||||
MediaFileType.Glb,
|
||||
MediaFileType.Gltf,
|
||||
MediaFileType.Mocap,
|
||||
MediaFileType.ScnRon,
|
||||
],
|
||||
});
|
||||
console.log(mediaFilesResult);
|
||||
}
|
||||
catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
}
|
||||
|
||||
@on("scene-saved")
|
||||
async onSceneSaved({ detail: scene }: SceneSavedEvent): Promise<void> {
|
||||
try {
|
||||
@@ -220,7 +191,14 @@ export class AppElement extends LitElement {
|
||||
|
||||
<!-- Studio-mode-only elements -->
|
||||
${this.#params.mode === StudioMode.Editor ? html`
|
||||
<sts-add-to-scene-menu .trigger=${this.#addToSceneButtonRef}></sts-add-to-scene-menu>
|
||||
<sts-add-to-scene-menu
|
||||
.trigger=${this.#addToSceneButtonRef}
|
||||
@open-media-library=${() => {
|
||||
this.#mediaLibraryRef.value?.open();
|
||||
}}
|
||||
></sts-add-to-scene-menu>
|
||||
<sts-media-library ${ref(this.#mediaLibraryRef)}></sts-media-library>
|
||||
|
||||
<sts-transform-toolbar class="v-start h-center"></sts-transform-toolbar>
|
||||
|
||||
${this.showInspector ? html`
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
:host {
|
||||
display: block;
|
||||
min-width: 512px;
|
||||
min-height: 256px;
|
||||
max-width: 80%;
|
||||
max-height: 80%;
|
||||
padding: 1rem;
|
||||
overflow: auto;
|
||||
|
||||
background: #0006;
|
||||
color: #FFFC;
|
||||
backdrop-filter: blur(16px);
|
||||
border-radius: 0.5rem;
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
import { inject, on } from "@storyteller/framework";
|
||||
import {
|
||||
OVERLAY_PROVIDER,
|
||||
type OverlayHandle,
|
||||
type OverlayProvider,
|
||||
} from "@storyteller/studio-ui/overlay-provider";
|
||||
import {
|
||||
type MediaFile,
|
||||
MediaFileType,
|
||||
StorytellerApi,
|
||||
} from "@storyteller/studio-web";
|
||||
import { LitElement, unsafeCSS, type PropertyValues, html } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators.js";
|
||||
import { createRef, ref } from "lit/directives/ref.js";
|
||||
import { until } from "lit/directives/until.js";
|
||||
|
||||
import dialogStyles from "./media-library.element.scss?inline";
|
||||
|
||||
@customElement("sts-media-library")
|
||||
export class MediaLibraryElement extends LitElement {
|
||||
@property({ attribute: false })
|
||||
trigger = createRef<HTMLElement>();
|
||||
|
||||
@inject(OVERLAY_PROVIDER)
|
||||
overlay!: OverlayProvider;
|
||||
|
||||
#overlayHandle?: WeakRef<OverlayHandle>;
|
||||
#dialogRef = createRef<MediaLibraryDialogElement>();
|
||||
|
||||
open(): void {
|
||||
this.#overlayHandle = this.overlay.attach(this, html`
|
||||
<sts-media-library-dialog
|
||||
${ref(this.#dialogRef)}
|
||||
@close=${() => this.close()}
|
||||
></sts-media-library-dialog>
|
||||
`, {
|
||||
containerStyles: {
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
close(): void {
|
||||
if (this.#overlayHandle)
|
||||
this.overlay.detach(this.#overlayHandle);
|
||||
}
|
||||
|
||||
protected override updated(changes: PropertyValues<this>): void {
|
||||
if (changes.has("trigger")) {
|
||||
const prevTrigger = changes.get("trigger");
|
||||
if (prevTrigger?.value) {
|
||||
prevTrigger.value.removeEventListener("click", this.onTriggerClick);
|
||||
}
|
||||
|
||||
const trigger = this.trigger.value;
|
||||
if (!trigger) return;
|
||||
|
||||
trigger.addEventListener("click", this.onTriggerClick);
|
||||
}
|
||||
|
||||
super.updated(changes);
|
||||
}
|
||||
|
||||
override disconnectedCallback(): void {
|
||||
this.trigger.value?.removeEventListener("click", this.onTriggerClick);
|
||||
super.disconnectedCallback();
|
||||
}
|
||||
|
||||
onTriggerClick = (): void => {
|
||||
this.open();
|
||||
}
|
||||
}
|
||||
|
||||
@customElement("sts-media-library-dialog")
|
||||
export class MediaLibraryDialogElement extends LitElement {
|
||||
static override styles = unsafeCSS(dialogStyles);
|
||||
|
||||
@property({ reflect: true })
|
||||
override role = "dialog";
|
||||
|
||||
@state()
|
||||
mediaFiles?: Promise<MediaFile[]>;
|
||||
|
||||
@inject(StorytellerApi)
|
||||
_api!: StorytellerApi;
|
||||
|
||||
override async connectedCallback(): Promise<void> {
|
||||
this.mediaFiles = this._api
|
||||
.session()
|
||||
.then(session => {
|
||||
if (!session.logged_in || !session.user?.username)
|
||||
return Promise.reject(new Error("Not logged in"));
|
||||
|
||||
return this._api.listMediaFiles({
|
||||
username: session.user.username,
|
||||
filterMediaType: [
|
||||
MediaFileType.Bvh,
|
||||
MediaFileType.Glb,
|
||||
MediaFileType.Gltf,
|
||||
MediaFileType.ScnRon,
|
||||
],
|
||||
});
|
||||
})
|
||||
.then(result => Promise.all(
|
||||
result.results.map(({ token }) => this._api.mediaFile(token))
|
||||
))
|
||||
.catch(err => {
|
||||
console.error(err);
|
||||
return [];
|
||||
});
|
||||
|
||||
super.connectedCallback();
|
||||
}
|
||||
|
||||
@on("window:click")
|
||||
onWindowClick(event: PointerEvent): void {
|
||||
if (!event.composedPath().includes(this))
|
||||
this.dispatchEvent(new CustomEvent("close"));
|
||||
}
|
||||
|
||||
protected override render = () => html`
|
||||
${until(
|
||||
this.mediaFiles?.then(files => html`
|
||||
<pre>${JSON.stringify(files, null, " ")}</pre>
|
||||
`),
|
||||
html`<span>Loading...</span>`
|
||||
)}
|
||||
`;
|
||||
}
|
||||
@@ -60,6 +60,9 @@ export class AddToSceneMenuElement extends LitElement {
|
||||
<sts-button
|
||||
role="menuitem"
|
||||
icon="photo-film-music"
|
||||
@click=${() => {
|
||||
this.dispatchEvent(new CustomEvent("open-media-library"));
|
||||
}}
|
||||
>
|
||||
Media library...
|
||||
</sts-button>
|
||||
|
||||
Reference in New Issue
Block a user