Files
storyteller-bevy/studio-ui/src/lib/dialog/dialog-header.element.ts
T
Danny McGee 1163192d24 Media Library: Importing and spawning assets (#109)
This PR updates the Media Library feature to implement asset imports
from the local file system, and spawning assets from the library into
the scene. Additionally, some placeholder functionality has been
scaffolded for adding animations onto existing characters in the scene.

### Additional Changes
* Dialogs can be set to `nonBlocking` to allow interaction with
  background elements (note: this is fraught for accessibility and likely
  temporary, to facilitate testing while the UI/UX redesign is in
  progress)
* Dialogs can be moved around the screen by dragging their title-bars
* The `scene` module has been moved into a folder with a separate
  `wasm.rs` file for its submodule. (`scene/mod.rs` remains one of our
  largest single Rust files and could probably stand to be partitioned
  further.)

### To-Do
* Support for adding animations is not yet fully implemented, though
  some prerequisite functionality has been scaffolded. Namely, the "Add to
  Scene" button will be disabled if:
  - There is a mix of animations and non-animations selected in the Media
    Library
  - There is more than one animation selected in the Media Library
  - There is one animation selected in the Media Library, but no
    animatable character selected in the scene
* Adding full Storyteller-format scenes to the existing scene is not a
  use case that has been tested, and may not be a use case we wish to
  support in the long-term, as some of the data stored in Storyteller
  scenes are intended to be "singleton" resources that may conflict with
  existing data in the scene.
  
  **Attempting to spawn a Storyteller scene through the Media Library has
  not been explicitly blocked for now, but may cause unexpected
  behavior.** This needs additional UX consideration and grooming.
2024-03-27 19:20:33 -04:00

125 lines
2.7 KiB
TypeScript

import { drag } from "@storyteller/framework";
import { LitElement, html, nothing, unsafeCSS } from "lit";
import { customElement, property } from "lit/decorators.js";
import { v4 as uuid } from "uuid";
import styles from "./dialog-header.element.scss?inline";
/**
* A configurable header for custom dialogs. This component should be preferred
* over inserting a wholly custom `[part="dialog-header"]`.
*
* @slot (default)
*
* Projected content without a named `slot` attribute will appear as the
* "heading", next to the icon. This should usually be simple text content
* without any markup elements.
*
* @slot `extras`
*
* Use this slot to insert arbitrary content in between the title and close
* button.
*
* @slot `close`
*
* Insert a custom close button, or an empty `[slot="close"]` to remove the
* default close button.
*
* @part `title`
*
* A CSS part for styling the wrapper around the icon and heading.
*
* @part `icon`
*
* A CSS part for styling the icon.
*
* @part `heading`
*
* A CSS part for styling the wrapper around the slotted heading (default slot).
*
* @part `close`
*
* A CSS part for styling the default "close" button. Has no effect if a custom
* `[part="close"]` is inserted.
*/
@customElement("sts-dialog-header")
export class DialogHeaderElement extends LitElement {
static override styles = unsafeCSS(styles);
@property({ reflect: true })
override id = uuid();
@property({ reflect: true })
override slot = "dialog-header";
@property() icon?: string;
#translateX?: number;
#translateY?: number;
onClose(): void {
this.dispatchEvent(new CustomEvent("close", {
bubbles: true,
composed: true,
}));
}
onDrag(event: PointerEvent): void {
this.#translateX ??= 0;
this.#translateY ??= 0;
this.#translateX += event.movementX;
this.#translateY += event.movementY;
this.dispatchEvent(new CustomEvent("translate", {
detail: {
x: this.#translateX,
y: this.#translateY,
},
bubbles: true,
composed: true,
}));
}
protected override render = () => html`
<div
class="title"
part="title"
${drag({
move: this.onDrag,
})}
>
${this.icon ? html`
<sts-icon
class="title__icon"
part="icon"
.icon=${this.icon}
></sts-icon>
` : nothing}
<span
class="title__heading"
part="heading"
>
<slot>Dialog</slot>
</span>
</div>
<slot name="extras"></slot>
<slot name="close">
${/*
Note: "Close" button is intentionally hidden from the a11y tree
because because the Esc key is the ARIA-standard interaction for
closing a dialog.
*/ nothing}
<sts-icon
class="close"
part="close"
icon="xmark"
@click=${this.onClose}
></sts-icon>
</slot>
`;
}