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:
Danny McGee
2023-10-09 22:19:58 -04:00
committed by GitHub
parent 57d64f08f0
commit c84fd31387
40 changed files with 1608 additions and 590 deletions
+17
View File
@@ -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;
}
+44 -15
View File
@@ -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;
}
+30
View File
@@ -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>
`
}
+32
View File
@@ -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;
}
}
+49
View File
@@ -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>
`;
}