mirror of
https://github.com/storytold/storyteller-bevy.git
synced 2026-10-20 07:54:50 +00:00
UI infrastructure and simple "Help" dialog (#3)
Sets up some WIP infrastructure for building out the UI, and adds a simple popup to show the navigation controls for the app.
This commit is contained in:
@@ -0,0 +1,17 @@
|
||||
:host {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.btn-help {
|
||||
position: absolute;
|
||||
top: 16px;
|
||||
right: 16px;
|
||||
}
|
||||
|
||||
.help {
|
||||
position: absolute;
|
||||
top: 60px;
|
||||
right: 16px;
|
||||
}
|
||||
|
||||
@@ -1,13 +1,40 @@
|
||||
import { on } from "@storyteller/framework";
|
||||
import * as studio from "@storyteller/studio";
|
||||
import { ViewportSize } from "@storyteller/studio";
|
||||
import { throttle } from "lodash";
|
||||
import { LitElement, html, unsafeCSS } from "lit";
|
||||
import { customElement, state } from "lit/decorators.js";
|
||||
|
||||
import "./app.element.scss";
|
||||
import "./button.element";
|
||||
import "./help.element";
|
||||
import "./icons/fa-circle-info.element";
|
||||
|
||||
export class AppElement extends HTMLElement {
|
||||
static observedAttributes = [];
|
||||
import styles from "./app.element.scss?inline";
|
||||
|
||||
connectedCallback(): void {
|
||||
@customElement("app-root")
|
||||
export class AppElement extends LitElement {
|
||||
static override styles = unsafeCSS(styles);
|
||||
|
||||
@state() showHelp = false;
|
||||
|
||||
#studioCanvas?: HTMLCanvasElement | null;
|
||||
|
||||
override connectedCallback(): void {
|
||||
this.#studioCanvas = this.#initStudio();
|
||||
super.connectedCallback();
|
||||
}
|
||||
|
||||
@on("window:resize")
|
||||
onWindowResize(): void {
|
||||
const width = window.innerWidth;
|
||||
const height = window.innerHeight;
|
||||
studio.resize(new ViewportSize(width, height));
|
||||
}
|
||||
|
||||
toggleHelp(): void {
|
||||
this.showHelp = !this.showHelp;
|
||||
}
|
||||
|
||||
#initStudio(): HTMLCanvasElement | null {
|
||||
const width = window.innerWidth;
|
||||
const height = window.innerHeight;
|
||||
try {
|
||||
@@ -17,17 +44,19 @@ export class AppElement extends HTMLElement {
|
||||
throw err;
|
||||
}
|
||||
|
||||
window.addEventListener("resize", this.onResize);
|
||||
return document.querySelector<HTMLCanvasElement>(`canvas[alt="Storyteller Studio"]`);
|
||||
}
|
||||
|
||||
disconnectedCallback(): void {
|
||||
window.removeEventListener("resize", this.onResize);
|
||||
}
|
||||
protected override render = () => html`
|
||||
<sts-button class="btn-help"
|
||||
icon="circle-info"
|
||||
@click=${this.toggleHelp}
|
||||
>
|
||||
Help
|
||||
</sts-button>
|
||||
|
||||
onResize = throttle(() => {
|
||||
const width = window.innerWidth;
|
||||
const height = window.innerHeight;
|
||||
studio.resize(new ViewportSize(width, height));
|
||||
}, 16.666);
|
||||
${this.showHelp ? html`
|
||||
<sts-help class="help"></sts-help>
|
||||
` : null}
|
||||
`;
|
||||
}
|
||||
customElements.define("app-root", AppElement);
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
:host {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
padding: 10px 20px;
|
||||
border-radius: 0.5rem;
|
||||
|
||||
font: {
|
||||
family: "Fira Sans", sans-serif;
|
||||
size: 15px;
|
||||
weight: 500;
|
||||
}
|
||||
line-height: 1;
|
||||
|
||||
pointer-events: auto;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
|
||||
background: #0006;
|
||||
color: #FFF;
|
||||
backdrop-filter: blur(16px);
|
||||
transition: background 0.1s linear;
|
||||
}
|
||||
|
||||
:host(:hover) {
|
||||
background: #0008;
|
||||
}
|
||||
|
||||
.icon {
|
||||
fill: currentColor;
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
import { match } from "@storyteller/utility";
|
||||
import { on } from "@storyteller/framework";
|
||||
import { LitElement, html, unsafeCSS } from "lit";
|
||||
import { customElement, property } from "lit/decorators.js";
|
||||
|
||||
import styles from "./button.element.scss?inline";
|
||||
|
||||
@customElement("sts-button")
|
||||
export class ButtonElement extends LitElement {
|
||||
static override styles = unsafeCSS(styles);
|
||||
|
||||
@property() icon?: string;
|
||||
|
||||
override role = "button";
|
||||
override tabIndex = 0;
|
||||
|
||||
@on("keydown")
|
||||
onKeyDown({ key }: KeyboardEvent) {
|
||||
if (/^( |Enter)$/.test(key))
|
||||
this.click();
|
||||
}
|
||||
|
||||
protected override render = () => html`
|
||||
${match (this.icon, {
|
||||
"circle-info": () => html`<fa-circle-info class="icon"></fa-circle-info>`,
|
||||
_: () => null,
|
||||
})}
|
||||
<slot></slot>
|
||||
`
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
:host {
|
||||
padding: 20px 30px;
|
||||
border-radius: 0.5rem;
|
||||
|
||||
font: {
|
||||
family: "Fira Sans", sans-serif;
|
||||
size: 15px;
|
||||
weight: 400;
|
||||
}
|
||||
|
||||
background: #0006;
|
||||
backdrop-filter: blur(16px);
|
||||
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
th {
|
||||
text-align: start;
|
||||
color: #FFF;
|
||||
font-weight: 700;
|
||||
line-height: 2;
|
||||
}
|
||||
|
||||
td {
|
||||
color: #FFFA;
|
||||
vertical-align: top;
|
||||
line-height: 1.4;
|
||||
|
||||
&:has(+ td) {
|
||||
padding-right: 30px;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
import { LitElement, html, unsafeCSS } from "lit";
|
||||
import { customElement } from "lit/decorators.js";
|
||||
|
||||
import styles from "./help.element.scss?inline";
|
||||
|
||||
@customElement("sts-help")
|
||||
export class HelpElement extends LitElement {
|
||||
static override styles = unsafeCSS(styles);
|
||||
|
||||
protected override render = () => html`
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Action</th>
|
||||
<th>Controls</th>
|
||||
<th>Alternate</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Pan view</td>
|
||||
<td colspan="2">MMB</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Orbit around point</td>
|
||||
<td colspan="2">Alt + LMB</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Zoom</td>
|
||||
<td>Alt + RMB</td>
|
||||
<td>Mouse Wheel</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Free fly</td>
|
||||
<td colspan="2">W, A, S, D</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding-left: 20px">Up / Down</td>
|
||||
<td colspan="2">E, Q</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Free look</td>
|
||||
<td>RMB</td>
|
||||
<td>Shift</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
`;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
import { LitElement, html } from "lit";
|
||||
import { customElement } from "lit/decorators.js";
|
||||
|
||||
@customElement("fa-circle-info")
|
||||
export class CircleInfoIcon extends LitElement {
|
||||
override render = () => html`
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
height="1em"
|
||||
viewBox="0 0 512 512"
|
||||
>
|
||||
<!--!
|
||||
Font Awesome Free 6.4.2 by @fontawesome - https://fontawesome.com
|
||||
License - https://fontawesome.com/license (Commercial License)
|
||||
Copyright 2023 Fonticons, Inc.
|
||||
-->
|
||||
<path d="M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512zM216 336h24V272H216c-13.3 0-24-10.7-24-24s10.7-24 24-24h48c13.3 0 24 10.7 24 24v88h8c13.3 0 24 10.7 24 24s-10.7 24-24 24H216c-13.3 0-24-10.7-24-24s10.7-24 24-24zm40-208a32 32 0 1 1 0 64 32 32 0 1 1 0-64z"
|
||||
/>
|
||||
</svg>
|
||||
`;
|
||||
}
|
||||
Reference in New Issue
Block a user