Inspector frontend UI (#68)

This PR begins fleshing out the front-end UI for inspecting the selected
entity's components.

### Implemented
* Support for the `Name` component
* Placeholder/WIP support for the `Transform` component

### To-do
* Support for sending changes made in the inspector back to Bevy
* A slider/field combo widget for numeric values (can be adapted from
  [this StackBlitz](https://stackblitz.com/edit/angular-wfikuy?file=src%2Ffloat-input%2Ffloat-input.component.ts))
* Quat -> Euler angles conversion ([example math here](https://github.com/dannymcgee/3DGameMath/blob/5cb7a8ff9310565329e57751cd8b9c33e9bd4cb7/libs/Math/include/math/quat.inl.hpp#L56-L78),
  or use a 3rd-party math library)

### Known issues
* Selecting entities from the hierarchy doesn't always update the
  components list. I suspect this is the same issue as #39
This commit is contained in:
Danny McGee
2024-03-04 15:13:03 -05:00
committed by GitHub
parent 9875ea7cca
commit c9904a5293
26 changed files with 1093 additions and 174 deletions
+37 -13
View File
@@ -1,8 +1,6 @@
import { on } from "@storyteller/framework";
import * as studio from "@storyteller/studio";
import { type EntitySelectEvent } from "@storyteller/studio";
import { LitElement, html, nothing, unsafeCSS } from "lit";
import { customElement, property, state } from "lit/decorators.js";
import { type DynamicProvider, inject, UniqueToken } from "@storyteller/framework";
import { LitElement, type PropertyValues, html, nothing, unsafeCSS } from "lit";
import { customElement, property } from "lit/decorators.js";
import { classMap } from "lit/directives/class-map.js";
import { ifDefined } from "lit/directives/if-defined.js";
@@ -10,6 +8,12 @@ import "./icon.element";
import styles from "./tree.element.scss?inline";
export const TREE_SELECTION_PROVIDER = UniqueToken.create<TreeSelectionProvider>();
export interface TreeSelectionProvider extends DynamicProvider<TreeSelectionProvider> {
readonly selectedId: string | null;
}
export interface Tree {
id?: string;
name: string;
@@ -26,20 +30,40 @@ export class TreeElement extends LitElement {
@property({ attribute: false }) treeChildren?: Tree[];
@property({ type: Boolean }) expanded = true;
@state() selected = false;
get selected() {
return !!this.id && this._selectionProvider?.selectedId === this.id
}
@inject(TREE_SELECTION_PROVIDER)
_selectionProvider?: TreeSelectionProvider;
override connectedCallback(): void {
this._selectionProvider?.addEventListener("property-changes", this.onProviderChanges);
super.connectedCallback();
}
override disconnectedCallback(): void {
this._selectionProvider?.removeEventListener("property-changes", this.onProviderChanges);
super.disconnectedCallback();
}
onProviderChanges = ({ detail: changes }: CustomEvent<PropertyValues<TreeSelectionProvider>>) => {
if (changes.has("selectedId"))
this.requestUpdate();
}
toggleExpanded(): void {
this.expanded = !this.expanded;
}
@on("window:entity-select")
onEntitySelect({ detail: entity }: EntitySelectEvent): void {
this.selected = this.id === entity;
}
selectEntity(): void {
if (this.id)
studio.select(this.id);
if (this.id) {
this.dispatchEvent(new CustomEvent("tree-select", {
detail: this.id,
bubbles: true,
composed: true,
}));
}
}
// TODO: A11y